dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libproject / common / setproject.c
blobaead14cbaa4d806fcbc6eedc7ad1e033981e3af8
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
27 #include <sys/task.h>
28 #include <sys/types.h>
29 #include <unistd.h>
31 #include <ctype.h>
32 #include <project.h>
33 #include <rctl.h>
34 #include <secdb.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <nss_dbdefs.h>
40 #include <pwd.h>
41 #include <pool.h>
42 #include <libproc.h>
43 #include <priv.h>
44 #include <priv_utils.h>
45 #include <zone.h>
46 #include <sys/pool.h>
47 #include <sys/pool_impl.h>
48 #include <sys/rctl_impl.h>
50 static void
51 xstrtolower(char *s)
53 for (; *s != '\0'; s++)
54 *s = tolower(*s);
57 static void
58 remove_spaces(char *s)
60 char *current;
61 char *next;
63 current = next = s;
65 while (*next != '\0') {
66 while (isspace(*next))
67 next++;
68 *current++ = *next++;
70 *current = '\0';
73 int
74 build_rctlblk(rctlblk_t *blk, int comp_num, char *component)
76 char *signam;
77 int sig = 0;
78 uint_t act = rctlblk_get_local_action(blk, &sig);
80 if (comp_num == 0) {
82 * Setting privilege level for resource control block.
84 xstrtolower(component);
86 if (strcmp("basic", component) == 0) {
87 rctlblk_set_privilege(blk, RCPRIV_BASIC);
88 return (0);
91 if (strcmp("priv", component) == 0 ||
92 strcmp("privileged", component) == 0) {
93 rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED);
94 return (0);
97 return (-1);
100 if (comp_num == 1) {
103 * Setting value for resource control block.
105 unsigned long long val;
106 char *t;
108 /* Negative numbers are not allowed */
109 if (strchr(component, '-') != NULL)
110 return (-1);
112 errno = 0;
113 val = strtoull(component, &t, 10);
114 if (errno != 0 || t == component || *t != '\0')
115 return (-1);
117 rctlblk_set_value(blk, (rctl_qty_t)val);
118 return (0);
122 * Setting one or more actions on this resource control block.
124 if (comp_num >= 2) {
125 if (strcmp("none", component) == 0) {
126 rctlblk_set_local_action(blk, 0, 0);
127 return (0);
130 if (strcmp("deny", component) == 0) {
131 act |= RCTL_LOCAL_DENY;
133 rctlblk_set_local_action(blk, act, sig);
135 return (0);
139 * The last, and trickiest, form of action is the signal
140 * specification.
142 if ((signam = strchr(component, '=')) == NULL)
143 return (-1);
145 *signam++ = '\0';
147 if (strcmp("sig", component) == 0 ||
148 strcmp("signal", component) == 0) {
149 if (strncmp("SIG", signam, 3) == 0)
150 signam += 3;
152 if (str2sig(signam, &sig) == -1)
153 return (-1);
155 act |= RCTL_LOCAL_SIGNAL;
157 rctlblk_set_local_action(blk, act, sig);
159 return (0);
162 return (-1);
166 * States:
168 #define INPAREN 0x1
171 * Errors:
173 #define SETFAILED (-1)
174 #define COMPLETE 1
175 #define NESTING 2
176 #define UNCLOSED 3
177 #define CLOSEBEFOREOPEN 4
178 #define BADSPEC 5
180 static void
181 reinit_blk(rctlblk_t *blk, int local_action)
183 rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED);
184 rctlblk_set_value(blk, 0);
185 rctlblk_set_local_flags(blk, 0);
186 rctlblk_set_local_action(blk, local_action, 0);
189 static int
190 rctl_set(char *ctl_name, char *val, struct ps_prochandle *Pr, int flags)
192 int error = 0;
193 uint_t component = 0;
194 int valuecount = 0;
195 uint_t state = 0;
196 char *component_head;
197 rctlblk_t *blk;
198 rctlblk_t *ablk;
199 int project_entity = 0;
200 int count = 0;
201 char *tmp;
202 int local_act;
203 rctlblk_t *rnext;
204 int teardown_basic = 0;
205 int teardown_priv = 0;
207 /* We cannot modify a zone resource control */
208 if (strncmp(ctl_name, "zone.", strlen("zone.")) == 0) {
209 return (SETFAILED);
212 remove_spaces(val);
214 if (strncmp(ctl_name, "project.", strlen("project.")) == 0) {
215 project_entity = 1;
216 } else if ((strncmp(ctl_name, "process.", strlen("process.")) != 0) &&
217 (strncmp(ctl_name, "task.", strlen("task.")) != 0)) {
218 return (SETFAILED);
221 /* Determine how many attributes we'll be setting */
222 for (tmp = val; *tmp != '\0'; tmp++) {
223 if (*tmp == '(')
224 count++;
226 /* Allocate sufficient memory for rctl blocks */
227 if ((count == 0) || ((ablk =
228 (rctlblk_t *)malloc(rctlblk_size() * count)) == NULL)) {
229 return (SETFAILED);
231 blk = ablk;
234 * In order to set the new rctl's local_action, we'll need the
235 * current value of global_flags. We obtain global_flags by
236 * performing a pr_getrctl().
238 * The ctl_name has been verified as valid, so we have no reason
239 * to suspect that pr_getrctl() will return an error.
241 (void) pr_getrctl(Pr, ctl_name, NULL, blk, RCTL_FIRST);
245 * Set initial local action based on global deny properties.
247 rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED);
248 rctlblk_set_value(blk, 0);
249 rctlblk_set_local_flags(blk, 0);
251 if (rctlblk_get_global_flags(blk) & RCTL_GLOBAL_DENY_ALWAYS)
252 local_act = RCTL_LOCAL_DENY;
253 else
254 local_act = RCTL_LOCAL_NOACTION;
256 rctlblk_set_local_action(blk, local_act, 0);
258 for (; ; val++) {
260 switch (*val) {
261 case '(':
262 if (state & INPAREN) {
263 error = NESTING;
264 break;
267 state |= INPAREN;
268 component_head = (char *)val + 1;
270 break;
271 case ')':
272 if (state & INPAREN) {
273 *val = '\0';
274 if (component < 2) {
275 error = BADSPEC;
276 break;
278 if (build_rctlblk(blk, component,
279 component_head) == -1) {
280 error = BADSPEC;
281 break;
283 state &= ~INPAREN;
284 component = 0;
285 valuecount++;
287 if (project_entity &&
288 (rctlblk_get_privilege(blk) ==
289 RCPRIV_BASIC)) {
290 error = SETFAILED;
291 } else {
292 if (rctlblk_get_privilege(blk)
293 == RCPRIV_BASIC)
294 teardown_basic = 1;
296 if (rctlblk_get_privilege(blk)
297 == RCPRIV_PRIVILEGED)
298 teardown_priv = 1;
300 if (valuecount > count) {
301 free(ablk);
302 return (SETFAILED);
305 if (valuecount != count) {
306 blk = RCTLBLK_INC(ablk,
307 valuecount);
308 /* re-initialize blk */
309 reinit_blk(blk,
310 local_act);
314 } else {
315 error = CLOSEBEFOREOPEN;
317 break;
318 case ',':
319 if (state & INPAREN) {
320 *val = '\0';
321 if (build_rctlblk(blk, component,
322 component_head) == -1)
323 error = BADSPEC;
325 component++;
326 component_head = (char *)val + 1;
329 break;
330 case '\0':
331 if (valuecount == 0)
332 error = BADSPEC;
333 else if (state & INPAREN)
334 error = UNCLOSED;
335 else
336 error = COMPLETE;
337 break;
338 default:
339 if (!(state & INPAREN))
340 error = BADSPEC;
341 break;
344 if (error)
345 break;
347 /* ablk points to array of rctlblk_t */
349 if (valuecount == 0)
350 error = BADSPEC;
352 if (error != COMPLETE) {
353 free(ablk);
354 return (error);
357 /* teardown rctls if required */
358 if (!project_entity) {
360 if ((rnext = (rctlblk_t *)malloc(rctlblk_size())) == NULL) {
361 free(ablk);
362 return (SETFAILED);
365 restart:
366 if (pr_getrctl(Pr, ctl_name, NULL, rnext, RCTL_FIRST) == 0) {
367 while (1) {
368 if ((rctlblk_get_privilege(rnext) ==
369 RCPRIV_PRIVILEGED) &&
370 (teardown_priv == 1)) {
371 (void) pr_setrctl(Pr, ctl_name, NULL,
372 rnext, RCTL_DELETE);
373 goto restart;
375 if ((rctlblk_get_privilege(rnext) ==
376 RCPRIV_BASIC) && (teardown_basic == 1)) {
377 (void) pr_setrctl(Pr, ctl_name, NULL,
378 rnext, RCTL_DELETE);
379 goto restart;
382 if (pr_getrctl(Pr, ctl_name, rnext, rnext,
383 RCTL_NEXT) == -1)
384 break;
388 free(rnext);
391 /* set rctls */
393 blk = ablk;
395 if (project_entity) {
396 if (pr_setprojrctl(Pr, ctl_name, blk, count, flags) == -1)
397 error = SETFAILED;
398 } else {
399 valuecount = 0;
400 while (valuecount < count) {
401 if (pr_setrctl(Pr, ctl_name,
402 NULL, blk, RCTL_INSERT) == -1) {
403 error = SETFAILED;
404 break;
406 valuecount++;
407 blk = RCTLBLK_INC(ablk, valuecount);
413 free(ablk);
415 if (error != COMPLETE)
416 return (error);
418 return (0);
421 static int
422 rctlwalkfunc(const char *name, void *data)
425 if (strcmp(name, (char *)data) == 0)
426 return (-1);
427 else
428 return (0);
433 * This routine determines if /dev/pool device is present on the system and
434 * pools are currently enabled. We want to do this directly from libproject
435 * without using libpool's pool_get_status() routine because pools could be
436 * completely removed from the system. Return 1 if pools are enabled, or
437 * 0 otherwise. When used inside local zones, always pretend that pools
438 * are disabled because binding is not allowed and we're already in the
439 * right pool.
441 static int
442 pools_enabled(void)
444 pool_status_t status;
445 int fd;
447 if (getzoneid() != GLOBAL_ZONEID)
448 return (0);
449 if ((fd = open("/dev/pool", O_RDONLY)) < 0)
450 return (0);
451 if (ioctl(fd, POOL_STATUSQ, &status) < 0) {
452 (void) close(fd);
453 return (0);
455 (void) close(fd);
456 return (status.ps_io_state);
460 * A pool_name of NULL means to attempt to bind to the default pool.
461 * If the "force" flag is non-zero, the value of "system.bind-default" will be
462 * ignored, and the process will be bound to the default pool if one exists.
464 static int
465 bind_to_pool(const char *pool_name, pid_t pid, int force)
467 pool_value_t *pvals[] = { NULL, NULL };
468 pool_t **pools;
469 uint_t nelem;
470 uchar_t bval;
471 pool_conf_t *conf;
472 const char *nm;
473 int retval;
475 if ((conf = pool_conf_alloc()) == NULL)
476 return (-1);
477 if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY) < 0) {
479 * Pools configuration file is corrupted; allow logins.
481 pool_conf_free(conf);
482 return (0);
484 if (pool_name != NULL && pool_get_pool(conf, pool_name) != NULL) {
486 * There was a project.pool entry, and the pool it refers to
487 * is a valid (active) pool.
489 (void) pool_conf_close(conf);
490 pool_conf_free(conf);
491 if (pool_set_binding(pool_name, P_PID, pid) != PO_SUCCESS) {
492 if (pool_error() != POE_SYSTEM)
493 errno = EINVAL;
494 return (-1);
496 return (0);
500 * Bind to the pool with 'pool.default' = 'true' if
501 * 'system.bind-default' = 'true'.
503 if ((pvals[0] = pool_value_alloc()) == NULL) {
504 pool_conf_close(conf);
505 pool_conf_free(conf);
506 return (-1);
508 if (!force && pool_get_property(conf, pool_conf_to_elem(conf),
509 "system.bind-default", pvals[0]) != POC_BOOL ||
510 pool_value_get_bool(pvals[0], &bval) != PO_SUCCESS ||
511 bval == PO_FALSE) {
512 pool_value_free(pvals[0]);
513 pool_conf_close(conf);
514 pool_conf_free(conf);
515 errno = pool_name == NULL ? EACCES : ESRCH;
516 return (-1);
518 (void) pool_value_set_name(pvals[0], "pool.default");
519 pool_value_set_bool(pvals[0], PO_TRUE);
520 if ((pools = pool_query_pools(conf, &nelem, pvals)) == NULL) {
522 * No default pools exist.
524 pool_value_free(pvals[0]);
525 pool_conf_close(conf);
526 pool_conf_free(conf);
527 errno = pool_name == NULL ? EACCES : ESRCH;
528 return (-1);
530 if (nelem != 1 ||
531 pool_get_property(conf, pool_to_elem(conf, pools[0]), "pool.name",
532 pvals[0]) != POC_STRING) {
534 * Configuration is invalid.
536 free(pools);
537 pool_value_free(pvals[0]);
538 (void) pool_conf_close(conf);
539 pool_conf_free(conf);
540 return (0);
542 free(pools);
543 (void) pool_conf_close(conf);
544 pool_conf_free(conf);
545 (void) pool_value_get_string(pvals[0], &nm);
546 if (pool_set_binding(nm, P_PID, pid) != PO_SUCCESS) {
547 if (pool_error() != POE_SYSTEM)
548 errno = EINVAL;
549 retval = -1;
550 } else {
551 retval = 0;
553 pool_value_free(pvals[0]);
554 return (retval);
558 * Changes the assigned project, task and resource pool of a stopped target
559 * process.
561 * We may not have access to the project table if our target process is in
562 * getprojbyname()'s execution path. Similarly, we may not be able to get user
563 * information if the target process is in getpwnam()'s execution path. Thus we
564 * give the caller the option of skipping these checks by providing a pointer to
565 * a pre-validated project structure in proj (whose name matches project_name)
566 * and taking responsibility for ensuring that the target process' owner is a
567 * member of the target project.
569 * Callers of this function should always provide a pre-validated project
570 * structure in proj unless they can be sure that the target process will never
571 * be in setproject_proc()'s execution path.
574 projid_t
575 setproject_proc(const char *project_name, const char *user_name, int flags,
576 pid_t pid, struct ps_prochandle *Pr, struct project *proj)
578 char pwdbuf[NSS_BUFLEN_PASSWD];
579 char prbuf[PROJECT_BUFSZ];
580 projid_t projid;
581 struct passwd pwd;
582 int i;
583 int unknown = 0;
584 int ret = 0;
585 kva_t *kv_array;
586 struct project local_proj; /* space to store proj if not provided */
587 const char *pool_name = NULL;
589 if (project_name != NULL) {
591 * Sanity checks.
593 if (strcmp(project_name, "") == 0 ||
594 user_name == NULL) {
595 errno = EINVAL;
596 return (SETPROJ_ERR_TASK);
600 * If proj is NULL, acquire project information to ensure that
601 * project_name is a valid project, and confirm that user_name
602 * exists and is a member of the specified project.
604 if (proj == NULL) {
605 struct passwd *result;
606 if ((proj = getprojbyname(project_name, &local_proj,
607 prbuf, PROJECT_BUFSZ)) == NULL) {
608 errno = ESRCH;
609 return (SETPROJ_ERR_TASK);
612 getpwnam_r(user_name, &pwd, pwdbuf, NSS_BUFLEN_PASSWD,
613 &result);
614 if (!result) {
615 errno = ESRCH;
616 return (SETPROJ_ERR_TASK);
619 * Root can join any project.
621 if (pwd.pw_uid != (uid_t)0 &&
622 !inproj(user_name, project_name, prbuf,
623 PROJECT_BUFSZ)) {
624 errno = ESRCH;
625 return (SETPROJ_ERR_TASK);
628 projid = proj->pj_projid;
629 } else {
630 projid = getprojid();
634 if ((kv_array = _str2kva(proj->pj_attr, KV_ASSIGN,
635 KV_DELIMITER)) != NULL) {
636 for (i = 0; i < kv_array->length; i++) {
637 if (strcmp(kv_array->data[i].key,
638 "project.pool") == 0) {
639 pool_name = kv_array->data[i].value;
641 if (strcmp(kv_array->data[i].key, "task.final") == 0) {
642 flags |= TASK_FINAL;
648 * Bind process to a pool only if pools are configured
650 if (pools_enabled() == 1) {
651 char *old_pool_name;
653 * Attempt to bind to pool before calling
654 * settaskid().
656 old_pool_name = pool_get_binding(pid);
657 if (bind_to_pool(pool_name, pid, 0) != 0) {
658 free(old_pool_name);
659 _kva_free(kv_array);
660 return (SETPROJ_ERR_POOL);
662 if (pr_settaskid(Pr, projid, flags & TASK_MASK) == -1) {
663 int saved_errno = errno;
666 * Undo pool binding.
668 (void) bind_to_pool(old_pool_name, pid, 1);
669 free(old_pool_name);
670 _kva_free(kv_array);
672 * Restore errno
674 errno = saved_errno;
675 return (SETPROJ_ERR_TASK);
677 free(old_pool_name);
678 } else {
680 * Pools are not configured, so simply create new task.
682 if (pr_settaskid(Pr, projid, flags & TASK_MASK) == -1) {
683 _kva_free(kv_array);
684 return (SETPROJ_ERR_TASK);
688 if (project_name == NULL) {
690 * In the case that we are starting a new task in the
691 * current project, we are finished, since the current
692 * resource controls will still apply. (Implicit behaviour:
693 * a project must be entirely logged out before name
694 * service changes will take effect.)
696 _kva_free(kv_array);
697 return (projid);
700 if (kv_array == NULL)
701 return (0);
703 for (i = 0; i < kv_array->length; i++) {
705 * Providing a special, i.e. a non-resource control, key? Then
706 * parse that key here and end with "continue;".
710 * For generic bindings, the kernel performs the binding, as
711 * these are resource controls advertised by kernel subsystems.
715 * Check for known attribute name.
717 errno = 0;
718 if (rctl_walk(rctlwalkfunc, (void *)kv_array->data[i].key)
719 == 0)
720 continue;
721 if (errno) {
722 _kva_free(kv_array);
723 return (SETPROJ_ERR_TASK);
726 ret = rctl_set(kv_array->data[i].key,
727 kv_array->data[i].value, Pr, flags & TASK_PROJ_MASK);
729 if (ret && unknown == 0) {
731 * We only report the first failure.
733 unknown = i + 1;
736 if (ret && ret != SETFAILED) {
738 * We abort if we couldn't set a component, but if
739 * it's merely that the system didn't recognize it, we
740 * continue, as this could be a third party attribute.
742 break;
745 _kva_free(kv_array);
747 return (unknown);
750 projid_t
751 setproject(const char *project_name, const char *user_name, int flags)
753 return (setproject_proc(project_name, user_name, flags, P_MYID, NULL,
754 NULL));
758 priv_set_t *
759 setproject_initpriv(void)
761 static priv_t taskpriv = PRIV_PROC_TASKID;
762 static priv_t rctlpriv = PRIV_SYS_RESOURCE;
763 static priv_t poolpriv = PRIV_SYS_RES_CONFIG;
764 static priv_t schedpriv = PRIV_PROC_PRIOCNTL;
765 int res;
767 priv_set_t *nset;
769 if (getzoneid() == GLOBAL_ZONEID) {
770 res = __init_suid_priv(0, taskpriv, rctlpriv, poolpriv,
771 schedpriv, NULL);
772 } else {
773 res = __init_suid_priv(0, taskpriv, rctlpriv, NULL);
776 if (res != 0)
777 return (NULL);
779 nset = priv_allocset();
780 if (nset != NULL) {
781 priv_emptyset(nset);
782 (void) priv_addset(nset, taskpriv);
783 (void) priv_addset(nset, rctlpriv);
785 * Only need these if we need to change pools, which can
786 * only happen if the target is in the global zone. Rather
787 * than checking the target's zone just check our own
788 * (since if we're in a non-global zone we won't be able
789 * to control processes in other zones).
791 if (getzoneid() == GLOBAL_ZONEID) {
792 (void) priv_addset(nset, poolpriv);
793 (void) priv_addset(nset, schedpriv);
796 return (nset);