iscontrol(8): Fix synopsis, sync usage() & improve markup
[dragonfly.git] / sys / kern / kern_varsym.c
blob516b669a58c12b061f6d2d1083b6fabda4fb9f11
1 /*
2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 * $DragonFly: src/sys/kern/kern_varsym.c,v 1.9 2007/04/30 07:18:54 dillon Exp $
38 * This module implements variable storage and management for variant
39 * symlinks. These variables may also be used for general purposes.
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/ucred.h>
46 #include <sys/resourcevar.h>
47 #include <sys/proc.h>
48 #include <sys/priv.h>
49 #include <sys/jail.h>
50 #include <sys/queue.h>
51 #include <sys/sysctl.h>
52 #include <sys/malloc.h>
53 #include <sys/varsym.h>
54 #include <sys/sysproto.h>
56 MALLOC_DEFINE(M_VARSYM, "varsym", "variable sets for variant symlinks");
58 struct varsymset varsymset_sys;
61 * Initialize the variant symlink subsystem
63 static void
64 varsym_sysinit(void *dummy)
66 varsymset_init(&varsymset_sys, NULL);
68 SYSINIT(announce, SI_BOOT2_MACHDEP, SI_ORDER_FIRST, varsym_sysinit, NULL);
71 * varsymreplace() - called from namei
73 * Do variant symlink variable substitution
75 int
76 varsymreplace(char *cp, int linklen, int maxlen)
78 int rlen;
79 int xlen;
80 int nlen;
81 int i;
82 varsym_t var;
84 rlen = linklen;
85 while (linklen > 1) {
86 if (cp[0] == '$' && cp[1] == '{') {
87 for (i = 2; i < linklen; ++i) {
88 if (cp[i] == '}')
89 break;
91 if (i < linklen &&
92 (var = varsymfind(VARSYM_ALL_MASK, cp + 2, i - 2)) != NULL
93 ) {
94 xlen = i + 1; /* bytes to strike */
95 nlen = strlen(var->vs_data); /* bytes to add */
96 if (linklen + nlen - xlen >= maxlen) {
97 varsymdrop(var);
98 return(-1);
100 KKASSERT(linklen >= xlen);
101 if (linklen != xlen)
102 bcopy(cp + xlen, cp + nlen, linklen - xlen);
103 bcopy(var->vs_data, cp, nlen);
104 linklen += nlen - xlen; /* new relative length */
105 rlen += nlen - xlen; /* returned total length */
106 cp += nlen; /* adjust past replacement */
107 linklen -= nlen; /* adjust past replacement */
108 maxlen -= nlen; /* adjust past replacement */
109 } else {
111 * It's ok if i points to the '}', it will simply be
112 * skipped. i could also have hit linklen.
114 cp += i;
115 linklen -= i;
116 maxlen -= i;
118 } else {
119 ++cp;
120 --linklen;
121 --maxlen;
124 return(rlen);
128 * varsym_set() system call
130 * (int level, const char *name, const char *data)
133 sys_varsym_set(struct varsym_set_args *uap)
135 char name[MAXVARSYM_NAME];
136 char *buf;
137 struct proc *p;
138 int error;
140 p = curproc;
142 if ((error = copyinstr(uap->name, name, sizeof(name), NULL)) != 0)
143 goto done2;
144 buf = kmalloc(MAXVARSYM_DATA, M_TEMP, M_WAITOK);
145 if (uap->data &&
146 (error = copyinstr(uap->data, buf, MAXVARSYM_DATA, NULL)) != 0)
148 goto done1;
150 switch(uap->level) {
151 case VARSYM_SYS:
152 if (p != NULL && p->p_ucred->cr_prison != NULL)
153 uap->level = VARSYM_PRISON;
154 case VARSYM_PRISON:
155 if (p != NULL &&
156 (error = priv_check_cred(p->p_ucred, PRIV_VARSYM_SYS, 0)) != 0)
157 break;
158 /* fall through */
159 case VARSYM_USER:
160 /* XXX check jail / implement per-jail user */
161 /* fall through */
162 case VARSYM_PROC:
163 if (uap->data) {
164 (void)varsymmake(uap->level, name, NULL);
165 error = varsymmake(uap->level, name, buf);
166 } else {
167 error = varsymmake(uap->level, name, NULL);
169 break;
171 done1:
172 kfree(buf, M_TEMP);
173 done2:
174 return(error);
178 * varsym_get() system call
180 * (int mask, const char *wild, char *buf, int bufsize)
183 sys_varsym_get(struct varsym_get_args *uap)
185 char wild[MAXVARSYM_NAME];
186 varsym_t sym;
187 int error;
188 int dlen;
190 if ((error = copyinstr(uap->wild, wild, sizeof(wild), NULL)) != 0)
191 goto done;
192 sym = varsymfind(uap->mask, wild, strlen(wild));
193 if (sym == NULL) {
194 error = ENOENT;
195 goto done;
197 dlen = strlen(sym->vs_data);
198 if (dlen < uap->bufsize) {
199 copyout(sym->vs_data, uap->buf, dlen + 1);
200 } else if (uap->bufsize) {
201 copyout("", uap->buf, 1);
203 uap->sysmsg_result = dlen + 1;
204 varsymdrop(sym);
205 done:
206 return(error);
210 * varsym_list() system call
212 * (int level, char *buf, int maxsize, int *marker)
215 sys_varsym_list(struct varsym_list_args *uap)
217 struct varsymset *vss;
218 struct varsyment *ve;
219 struct proc *p;
220 int i;
221 int error;
222 int bytes;
223 int earlyterm;
224 int marker;
227 * Get the marker from userspace.
229 if ((error = copyin(uap->marker, &marker, sizeof(marker))) != 0)
230 goto done;
233 * Figure out the varsym set.
235 p = curproc;
236 vss = NULL;
238 switch (uap->level) {
239 case VARSYM_PROC:
240 if (p)
241 vss = &p->p_varsymset;
242 break;
243 case VARSYM_USER:
244 if (p)
245 vss = &p->p_ucred->cr_uidinfo->ui_varsymset;
246 break;
247 case VARSYM_SYS:
248 vss = &varsymset_sys;
249 break;
250 case VARSYM_PRISON:
251 if (p != NULL && p->p_ucred->cr_prison != NULL)
252 vss = &p->p_ucred->cr_prison->pr_varsymset;
253 break;
255 if (vss == NULL) {
256 error = EINVAL;
257 goto done;
261 * Loop through the variables and dump them to uap->buf
263 i = 0;
264 bytes = 0;
265 earlyterm = 0;
267 lockmgr(&vss->vx_lock, LK_SHARED);
268 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
269 varsym_t sym = ve->ve_sym;
270 int namelen = strlen(sym->vs_name);
271 int datalen = strlen(sym->vs_data);
272 int totlen = namelen + datalen + 2;
275 * Skip to our index point
277 if (i < marker) {
278 ++i;
279 continue;
283 * Stop if there is insufficient space in the user buffer.
284 * If we haven't stored anything yet return EOVERFLOW.
285 * Note that the marker index (i) does not change.
287 if (bytes + totlen > uap->maxsize) {
288 if (bytes == 0)
289 error = EOVERFLOW;
290 earlyterm = 1;
291 break;
294 error = copyout(sym->vs_name, uap->buf + bytes, namelen + 1);
295 if (error == 0) {
296 bytes += namelen + 1;
297 error = copyout(sym->vs_data, uap->buf + bytes, datalen + 1);
298 if (error == 0)
299 bytes += datalen + 1;
300 else
301 bytes -= namelen + 1; /* revert if error */
303 if (error) {
304 earlyterm = 1;
305 break;
307 ++i;
309 lockmgr(&vss->vx_lock, LK_RELEASE);
312 * Save the marker back. If no error occured and earlyterm is clear
313 * the marker is set to -1 indicating that the variable list has been
314 * exhausted. If no error occured the number of bytes loaded into
315 * the buffer will be returned, otherwise the syscall code returns -1.
317 if (error == 0 && earlyterm == 0)
318 marker = -1;
319 else
320 marker = i;
321 if (error == 0)
322 error = copyout(&marker, uap->marker, sizeof(marker));
323 uap->sysmsg_result = bytes;
324 done:
325 return(error);
329 * Lookup a variant symlink. XXX use a hash table.
331 static
332 struct varsyment *
333 varsymlookup(struct varsymset *vss, const char *name, int namelen)
335 struct varsyment *ve;
337 KKASSERT(lockstatus(&vss->vx_lock, curthread) != 0);
338 TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
339 varsym_t var = ve->ve_sym;
340 if (var->vs_namelen == namelen &&
341 bcmp(name, var->vs_name, namelen) == 0
343 return(ve);
346 return(NULL);
349 static
350 void
351 vsslock(struct varsymset **vss, struct varsymset *n)
353 if (*vss) {
354 lockmgr(&(*vss)->vx_lock, LK_RELEASE);
356 lockmgr(&n->vx_lock, LK_SHARED);
357 *vss = n;
360 varsym_t
361 varsymfind(int mask, const char *name, int namelen)
363 struct proc *p = curproc;
364 struct varsyment *ve = NULL;
365 struct varsymset *vss = NULL;
366 varsym_t sym;
368 if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && p != NULL) {
369 if (mask & VARSYM_PROC_MASK) {
370 vsslock(&vss, &p->p_varsymset);
371 ve = varsymlookup(vss, name, namelen);
373 if (ve == NULL && (mask & VARSYM_USER_MASK)) {
374 vsslock(&vss, &p->p_ucred->cr_uidinfo->ui_varsymset);
375 ve = varsymlookup(vss, name, namelen);
378 if (ve == NULL && (mask & VARSYM_SYS_MASK)) {
379 if (p != NULL && p->p_ucred->cr_prison) {
380 vsslock(&vss, &p->p_ucred->cr_prison->pr_varsymset);
381 ve = varsymlookup(vss, name, namelen);
382 } else {
383 vsslock(&vss, &varsymset_sys);
384 ve = varsymlookup(vss, name, namelen);
387 if (ve) {
388 sym = ve->ve_sym;
389 atomic_add_int(&sym->vs_refs, 1);
390 } else {
391 sym = NULL;
393 lockmgr(&vss->vx_lock, LK_RELEASE);
394 return sym;
398 varsymmake(int level, const char *name, const char *data)
400 struct varsymset *vss = NULL;
401 struct varsyment *ve;
402 struct proc *p = curproc;
403 varsym_t sym;
404 int namelen = strlen(name);
405 int datalen;
406 int error;
408 switch(level) {
409 case VARSYM_PROC:
410 if (p)
411 vss = &p->p_varsymset;
412 break;
413 case VARSYM_USER:
414 if (p)
415 vss = &p->p_ucred->cr_uidinfo->ui_varsymset;
416 break;
417 case VARSYM_SYS:
418 vss = &varsymset_sys;
419 break;
420 case VARSYM_PRISON:
421 if (p != NULL && p->p_ucred->cr_prison != NULL)
422 vss = &p->p_ucred->cr_prison->pr_varsymset;
423 break;
425 if (vss == NULL) {
426 return EINVAL;
428 lockmgr(&vss->vx_lock, LK_EXCLUSIVE);
429 if (data && vss->vx_setsize >= MAXVARSYM_SET) {
430 error = E2BIG;
431 } else if (data) {
432 datalen = strlen(data);
433 ve = kmalloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
434 sym = kmalloc(sizeof(struct varsym) + namelen + datalen + 2, M_VARSYM, M_WAITOK);
435 ve->ve_sym = sym;
436 sym->vs_refs = 1;
437 sym->vs_namelen = namelen;
438 sym->vs_name = (char *)(sym + 1);
439 sym->vs_data = sym->vs_name + namelen + 1;
440 strcpy(sym->vs_name, name);
441 strcpy(sym->vs_data, data);
442 TAILQ_INSERT_TAIL(&vss->vx_queue, ve, ve_entry);
443 vss->vx_setsize += sizeof(struct varsyment) + sizeof(struct varsym) + namelen + datalen + 8;
444 error = 0;
445 } else {
446 if ((ve = varsymlookup(vss, name, namelen)) != NULL) {
447 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
448 vss->vx_setsize -= sizeof(struct varsyment) + sizeof(struct varsym) + namelen + strlen(ve->ve_sym->vs_data) + 8;
449 varsymdrop(ve->ve_sym);
450 kfree(ve, M_VARSYM);
451 error = 0;
452 } else {
453 error = ENOENT;
456 lockmgr(&vss->vx_lock, LK_RELEASE);
457 return(error);
460 void
461 varsymdrop(varsym_t sym)
463 KKASSERT(sym->vs_refs > 0);
464 if (atomic_fetchadd_int(&sym->vs_refs, -1) == 1) {
465 kfree(sym, M_VARSYM);
470 * Insert a duplicate of ve in vss. Does not do any locking,
471 * so it is the callers responsibility to make sure nobody
472 * else can mess with the TAILQ in vss at the same time.
474 static void
475 varsymdup(struct varsymset *vss, struct varsyment *ve)
477 struct varsyment *nve;
479 nve = kmalloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
480 nve->ve_sym = ve->ve_sym;
481 ++nve->ve_sym->vs_refs; /* can't be reached, no need for atomic add */
483 * We're only called through varsymset_init() so vss is not yet reachable,
484 * no need to lock.
486 TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry);
489 void
490 varsymset_init(struct varsymset *vss, struct varsymset *copy)
492 struct varsyment *ve;
494 TAILQ_INIT(&vss->vx_queue);
495 lockinit(&vss->vx_lock, "vx", 0, 0);
496 if (copy) {
497 TAILQ_FOREACH(ve, &copy->vx_queue, ve_entry) {
498 varsymdup(vss, ve);
500 vss->vx_setsize = copy->vx_setsize;
504 void
505 varsymset_clean(struct varsymset *vss)
507 struct varsyment *ve;
509 lockmgr(&vss->vx_lock, LK_EXCLUSIVE);
510 while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) {
511 TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
512 varsymdrop(ve->ve_sym);
513 kfree(ve, M_VARSYM);
515 vss->vx_setsize = 0;
516 lockmgr(&vss->vx_lock, LK_RELEASE);