Add predicate message facility.
[dragonfly.git] / sys / kern / kern_varsym.c
1 /*
2  * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $DragonFly: src/sys/kern/kern_varsym.c,v 1.4 2004/01/20 05:04:06 dillon Exp $
27  */
28
29 /*
30  * This module implements variable storage and management for variant
31  * symlinks.  These variables may also be used for general purposes.
32  */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/ucred.h>
38 #include <sys/resourcevar.h>
39 #include <sys/proc.h>
40 #include <sys/queue.h>
41 #include <sys/sysctl.h>
42 #include <sys/malloc.h>
43 #include <sys/varsym.h>
44 #include <sys/sysproto.h>
45
46 MALLOC_DEFINE(M_VARSYM, "varsym", "variable sets for variant symlinks");
47
48 struct varsymset        varsymset_sys;
49
50 /*
51  * Initialize the variant symlink subsystem
52  */
53 static void
54 varsym_sysinit(void *dummy)
55 {
56     varsymset_init(&varsymset_sys, NULL);
57 }
58 SYSINIT(announce, SI_SUB_INTRINSIC, SI_ORDER_FIRST, varsym_sysinit, NULL);
59
60 /*
61  * varsymreplace() - called from namei
62  *
63  *      Do variant symlink variable substitution
64  */
65 int
66 varsymreplace(char *cp, int linklen, int maxlen)
67 {
68     int rlen;
69     int xlen;
70     int nlen;
71     int i;
72     varsym_t var;
73
74     rlen = linklen;
75     while (linklen > 1) {
76         if (cp[0] == '$' && cp[1] == '{') {
77             for (i = 2; i < linklen; ++i) {
78                 if (cp[i] == '}')
79                     break;
80             }
81             if (i < linklen && 
82                 (var = varsymfind(VARSYM_ALL_MASK, cp + 2, i - 2)) != NULL
83             ) {
84                 xlen = i + 1;                   /* bytes to strike */
85                 nlen = strlen(var->vs_data);    /* bytes to add */
86                 if (linklen + nlen - xlen >= maxlen) {
87                     varsymdrop(var);
88                     return(-1);
89                 }
90                 KKASSERT(linklen >= xlen);
91                 if (linklen != xlen)
92                     bcopy(cp + xlen, cp + nlen, linklen - xlen);
93                 bcopy(var->vs_data, cp, nlen);
94                 linklen += nlen - xlen; /* new relative length */
95                 rlen += nlen - xlen;    /* returned total length */
96                 cp += nlen;             /* adjust past replacement */
97                 linklen -= nlen;        /* adjust past replacement */
98                 maxlen -= nlen;         /* adjust past replacement */
99             } else {
100                 /*
101                  * It's ok if i points to the '}', it will simply be
102                  * skipped.  i could also have hit linklen.
103                  */
104                 cp += i;
105                 linklen -= i;
106                 maxlen -= i;
107             }
108         } else {
109             ++cp;
110             --linklen;
111             --maxlen;
112         }
113     }
114     return(rlen);
115 }
116
117 /*
118  * varsym_set() system call
119  *
120  * (int level, const char *name, const char *data)
121  */
122 int
123 varsym_set(struct varsym_set_args *uap)
124 {
125     char name[MAXVARSYM_NAME];
126     char *buf;
127     int error;
128
129     if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0)
130         goto done2;
131     buf = malloc(MAXVARSYM_DATA, M_TEMP, M_WAITOK);
132     if (uap->data && 
133         (error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0)
134     {
135         goto done1;
136     }
137     switch(uap->level) {
138     case VARSYM_SYS:
139         if ((error = suser(curthread)) != 0)
140             break;
141         /* XXX implement per-jail sys */
142         /* fall through */
143     case VARSYM_USER:
144         /* XXX check jail / implement per-jail user */
145         /* fall through */
146     case VARSYM_PROC:
147         if (uap->data) {
148             (void)varsymmake(uap->level, name, NULL);
149             error = varsymmake(uap->level, name, buf);
150         } else {
151             error = varsymmake(uap->level, name, NULL);
152         }
153         break;
154     }
155 done1:
156     free(buf, M_TEMP);
157 done2:
158     return(error);
159 }
160
161 /*
162  * varsym_get() system call
163  *
164  * (int mask, const char *wild, char *buf, int bufsize)
165  */
166 int
167 varsym_get(struct varsym_get_args *uap)
168 {
169     char wild[MAXVARSYM_NAME];
170     varsym_t sym;
171     int error;
172     int dlen;
173
174     if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0)
175         goto done;
176     sym = varsymfind(uap->mask, wild, strlen(wild));
177     if (sym == NULL) {
178         error = ENOENT;
179         goto done;
180     }
181     dlen = strlen(sym->vs_data);
182     if (dlen < uap->bufsize) {
183         copyout(sym->vs_data, uap->buf, dlen + 1);
184     } else if (uap->bufsize) {
185         copyout("", uap->buf, 1);
186     }
187     uap->sysmsg_result = dlen + 1;
188     varsymdrop(sym);
189 done:
190     return(error);
191 }
192
193 /*
194  * varsym_list() system call
195  *
196  * (int level, char *buf, int maxsize, int *marker)
197  */
198 int
199 varsym_list(struct varsym_list_args *uap)
200 {
201         struct varsymset *vss;
202         struct varsyment *ve;
203         struct proc *p;
204         int i;
205         int error;
206         int bytes;
207         int earlyterm;
208         int marker;
209
210         /*
211          * Get the marker from userspace.
212          */
213         if ((error = copyin(uap->marker, &marker, sizeof(marker))) != 0)
214                 goto done;
215
216         /*
217          * Figure out the varsym set.
218          */
219         p = curproc;
220         vss = NULL;
221
222         switch (uap->level) {
223         case VARSYM_PROC:
224                 if (p)
225                         vss = &p->p_varsymset;
226                 break;
227         case VARSYM_USER:
228                 if (p)
229                         vss = &p->p_ucred->cr_uidinfo->ui_varsymset;
230                 break;
231         case VARSYM_SYS:
232                 vss = &varsymset_sys;
233                 break;
234         }
235         if (vss == NULL) {
236                 error = EINVAL;
237                 goto done;
238         }
239
240         /*
241          * Loop through the variables and dump them to uap->buf
242          */
243         i = 0;
244         bytes = 0;
245         earlyterm = 0;
246
247         TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
248                 varsym_t sym = ve->ve_sym;
249                 int namelen = strlen(sym->vs_name);
250                 int datalen = strlen(sym->vs_data);
251                 int totlen = namelen + datalen + 2;
252
253                 /*
254                  * Skip to our index point
255                  */
256                 if (i < marker) {
257                         ++i;
258                         continue;
259                 }
260
261                 /*
262                  * Stop if there is insufficient space in the user buffer.
263                  * If we haven't stored anything yet return EOVERFLOW. 
264                  * Note that the marker index (i) does not change.
265                  */
266                 if (bytes + totlen > uap->maxsize) {
267                         if (bytes == 0)
268                                 error = EOVERFLOW;
269                         earlyterm = 1;
270                         break;
271                 }
272
273                 error = copyout(sym->vs_name, uap->buf + bytes, namelen + 1);
274                 if (error == 0) {
275                         bytes += namelen + 1;
276                         error = copyout(sym->vs_data, uap->buf + bytes, datalen + 1);
277                         if (error == 0)
278                                 bytes += datalen + 1;
279                         else
280                                 bytes -= namelen + 1;   /* revert if error */
281                 }
282                 if (error) {
283                         earlyterm = 1;
284                         break;
285                 }
286                 ++i;
287         }
288
289         /*
290          * Save the marker back.  If no error occured and earlyterm is clear
291          * the marker is set to -1 indicating that the variable list has been
292          * exhausted.  If no error occured the number of bytes loaded into
293          * the buffer will be returned, otherwise the syscall code returns -1.
294          */
295         if (error == 0 && earlyterm == 0)
296                 marker = -1;
297         else
298                 marker = i;
299         if (error == 0)
300                 error = copyout(&marker, uap->marker, sizeof(marker));
301         uap->sysmsg_result = bytes;
302 done:
303         return(error);
304 }
305
306 /*
307  * Lookup a variant symlink.  XXX use a hash table.
308  */
309 static
310 struct varsyment *
311 varsymlookup(struct varsymset *vss, const char *name, int namelen)
312 {
313     struct varsyment *ve;
314
315     TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
316         varsym_t var = ve->ve_sym;
317         if (var->vs_namelen == namelen && 
318             bcmp(name, var->vs_name, namelen) == 0
319         ) {
320             return(ve);
321         }
322     }
323     return(NULL);
324 }
325
326 varsym_t
327 varsymfind(int mask, const char *name, int namelen)
328 {
329     struct proc *p;
330     struct varsyment *ve = NULL;
331     varsym_t sym;
332
333     if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && (p = curproc) != NULL) {
334         if (mask & VARSYM_PROC_MASK)
335             ve = varsymlookup(&p->p_varsymset, name, namelen);
336         if (ve == NULL && (mask & VARSYM_USER_MASK))
337             ve = varsymlookup(&p->p_ucred->cr_uidinfo->ui_varsymset, name, namelen);
338     }
339     if (ve == NULL && (mask & VARSYM_SYS_MASK))
340         ve = varsymlookup(&varsymset_sys, name, namelen);
341     if (ve) {
342         sym = ve->ve_sym;
343         ++sym->vs_refs;
344         return(sym);
345     } else {
346         return(NULL);
347     }
348 }
349
350 int
351 varsymmake(int level, const char *name, const char *data)
352 {
353     struct varsymset *vss = NULL;
354     struct varsyment *ve;
355     struct proc *p = curproc;
356     varsym_t sym;
357     int namelen = strlen(name);
358     int datalen;
359     int error;
360
361     switch(level) {
362     case VARSYM_PROC:
363         if (p)
364             vss = &p->p_varsymset;
365         break;
366     case VARSYM_USER:
367         if (p)
368             vss = &p->p_ucred->cr_uidinfo->ui_varsymset;
369         break;
370     case VARSYM_SYS:
371         vss = &varsymset_sys;
372         break;
373     }
374     if (vss == NULL) {
375         error = EINVAL;
376     } else if (data && vss->vx_setsize >= MAXVARSYM_SET) {
377         error = E2BIG;
378     } else if (data) {
379         datalen = strlen(data);
380         ve = malloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
381         sym = malloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, M_WAITOK);
382         ve->ve_sym = sym;
383         sym->vs_refs = 1;
384         sym->vs_namelen = namelen;
385         sym->vs_name = (char *)(sym + 1);
386         sym->vs_data = sym->vs_name + namelen + 1;
387         strcpy(sym->vs_name, name);
388         strcpy(sym->vs_data, data);
389         TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry);
390         vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8;
391         error = 0;
392     } else {
393         if ((ve = varsymlookup(vss, name, namelen)) != NULL) {
394             TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
395             vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8;
396             varsymdrop(ve->ve_sym);
397             free(ve, M_VARSYM);
398             error = 0;
399         } else {
400             error = ENOENT;
401         }
402     }
403     return(error);
404 }
405
406 void
407 varsymdrop(varsym_t sym)
408 {
409     KKASSERT(sym->vs_refs > 0);
410     if (--sym->vs_refs == 0) {
411         free(sym, M_VARSYM);
412     }
413 }
414
415 static void
416 varsymdup(struct varsymset *vss, struct varsyment *ve)
417 {
418     struct varsyment *nve;
419
420     nve = malloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
421     nve->ve_sym = ve->ve_sym;
422     ++nve->ve_sym->vs_refs;
423     TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry);
424 }
425
426 void
427 varsymset_init(struct varsymset *vss, struct varsymset *copy)
428 {
429     struct varsyment *ve;
430
431     TAILQ_INIT(&vss->vx_queue);
432     if (copy) {
433         TAILQ_FOREACH(ve, &copy->vx_queue, ve_entry) {
434             varsymdup(vss, ve);
435         }
436         vss->vx_setsize = copy->vx_setsize;
437     }
438 }
439
440 void
441 varsymset_clean(struct varsymset *vss)
442 {
443     struct varsyment *ve;
444
445     while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) {
446         TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
447         varsymdrop(ve->ve_sym);
448         free(ve, M_VARSYM);
449     }
450     vss->vx_setsize = 0;
451 }
452