dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / prctl / prctl.c
blob06f167e3fe7504ddc341addb37f8b7931b5c4036
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
23 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2015, Joyent, Inc.
27 #include <unistd.h>
28 #include <rctl.h>
29 #include <libproc.h>
30 #include <stdio.h>
31 #include <libintl.h>
32 #include <locale.h>
33 #include <string.h>
34 #include <signal.h>
35 #include <strings.h>
36 #include <ctype.h>
37 #include <project.h>
38 #include <sys/types.h>
39 #include <dirent.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <sys/varargs.h>
43 #include <priv.h>
44 #include <zone.h>
45 #include "utils.h"
47 /* Valid user actions */
48 #define ACTION_DISABLE 0x01
49 #define ACTION_ENABLE 0x02
50 #define ACTION_SET 0x04
51 #define ACTION_REPLACE 0x08
52 #define ACTION_DELETE 0x10
54 #define PRCTL_VALUE_WIDTH 4
56 /* Maximum string length for deferred errors */
57 #define GLOBAL_ERR_SZ 1024
59 /* allow important process values to be passed together easily */
60 typedef struct pr_info_handle {
61 struct ps_prochandle *pr;
62 pid_t pid;
63 psinfo_t psinfo;
64 taskid_t taskid;
65 projid_t projid;
66 char *projname;
67 zoneid_t zoneid;
68 char *zonename;
70 } pr_info_handle_t;
72 /* Structures for list of resource controls */
73 typedef struct prctl_value {
74 rctlblk_t *rblk;
75 struct prctl_value *next;
76 } prctl_value_t;
78 typedef struct prctl_list {
79 char *name;
80 rctl_qty_t *usage;
81 prctl_value_t *val_list;
82 struct prctl_list *next;
83 } prctl_list_t;
85 static volatile int interrupt;
87 static prctl_list_t *global_rctl_list_head = NULL;
88 static prctl_list_t *global_rctl_list_tail = NULL;
89 static char global_error[GLOBAL_ERR_SZ];
91 /* global variables that contain commmand line option info */
92 static int arg_operation = 0;
93 static int arg_force = 0;
96 /* String and type from -i */
97 static rctl_entity_t arg_entity_type = RCENTITY_PROCESS;
98 static char *arg_entity_string = NULL;
100 /* -n argument */
101 static char *arg_name = NULL;
103 static rctl_entity_t arg_name_entity = 0;
105 /* -t argument value */
106 static int arg_priv = 0;
108 /* -v argument string */
109 static char *arg_valuestring = NULL;
111 /* global flags of rctl name passed to -n */
112 static int arg_global_flags = 0;
113 static rctl_qty_t arg_global_max;
115 /* appropriate scaling variables determined by rctl unit type */
116 scale_t *arg_scale;
117 static char *arg_unit = NULL;
119 /* -v argument string converted to uint64_t */
120 static uint64_t arg_value = 0;
122 /* if -v argument is scaled value, points to "K", "M", "G", ... */
123 static char *arg_modifier = NULL;
125 /* -e/-d argument string */
126 static char *arg_action_string = NULL;
128 /* Set to RCTL_LOCAL_SIGNAL|DENY based on arg_action_string */
129 static int arg_action = 0;
131 /* if -e/-d arg is signal=XXX, set to signal number of XXX */
132 static int arg_signal = 0;
134 /* -p arg if -p is specified */
135 static int arg_pid = -1;
136 static char *arg_pid_string = NULL;
138 /* Set to 1 if -P is specified */
139 static int arg_parseable_mode = 0;
141 /* interupt handler */
142 static void intr(int);
144 static int get_rctls(struct ps_prochandle *);
145 static int store_rctls(const char *rctlname, void *walk_data);
146 static prctl_value_t *store_value_entry(rctlblk_t *rblk, prctl_list_t *list);
147 static prctl_list_t *store_list_entry(const char *name);
148 static void free_lists();
150 static int change_action(rctlblk_t *blk);
152 static int prctl_setrctl(struct ps_prochandle *Pr, const char *name,
153 rctlblk_t *, rctlblk_t *, uint_t);
155 static int match_rctl(struct ps_prochandle *Pr, rctlblk_t **rctl, char *name,
156 char *valuestringin, int valuein, rctl_priv_t privin,
157 int pidin);
158 static int match_rctl_blk(rctlblk_t *rctl, char *valuestringin,
159 uint64_t valuein,
160 rctl_priv_t privin, int pidin);
161 static pid_t regrab_process(pid_t pid, pr_info_handle_t *p, int, int *gret);
162 static pid_t grab_process_by_id(char *idname, rctl_entity_t type,
163 pr_info_handle_t *p, int, int *gret);
164 static int grab_process(pr_info_handle_t *p, int *gret);
165 static void release_process(struct ps_prochandle *Pr);
166 static void preserve_error(char *format, ...);
168 static void print_rctls(pr_info_handle_t *p);
169 static void print_priv(rctl_priv_t local_priv, char *format);
170 static void print_local_action(int action, int *signalp, char *format);
172 static const char USAGE[] = ""
173 "usage:\n"
174 " Report resource control values and actions:\n"
175 " prctl [-P] [-t [basic | privileged | system]\n"
176 " [-n name] [-i process | task | project | zone] id ...\n"
177 " -P space delimited output\n"
178 " -t privilege level of rctl values to get\n"
179 " -n name of resource control values to get\n"
180 " -i idtype of operand list\n"
181 " Manipulate resource control values:\n"
182 " prctl [-t [basic | privileged | system]\n"
183 " -n name [-srx] [-v value] [-p pid ] [-e | -d action]\n"
184 " [-i process | task | project | zone] id ...\n"
185 " -t privilege level of rctl value to set/replace/delete/modify\n"
186 " -n name of resource control to set/replace/delete/modify\n"
187 " -s set new resource control value\n"
188 " -r replace first rctl value of matching privilege\n"
189 " -x delete first rctl value of matching privilege, value, and \n"
190 " recipient pid\n"
191 " -v value of rctl to set/replace/delete/modify\n"
192 " -p recipient pid of rctl to set/replace/delete/modify\n"
193 " -e enable action of first rctl value of matching privilege,\n"
194 " value, and recipient pid\n"
195 " -d disable action of first rctl value of matching privilege,\n"
196 " value, and recipient pid\n"
197 " -i idtype of operand list\n";
200 static void
201 usage()
203 (void) fprintf(stderr, gettext(USAGE));
204 exit(2);
208 main(int argc, char **argv)
210 int flags;
211 int opt, errflg = 0;
212 rctlblk_t *rctlblkA = NULL;
213 rctlblk_t *rctlblkB = NULL;
214 rctlblk_t *tmp = NULL;
215 pid_t pid;
216 char *target_id;
217 int search_type;
218 int signal;
219 int localaction;
220 int printed = 0;
221 int gret;
222 char *end;
224 (void) setlocale(LC_ALL, "");
225 (void) textdomain(TEXT_DOMAIN);
226 (void) setpname(argv[0]);
228 while ((opt = getopt(argc, argv, "sPp:Fd:e:i:n:rt:v:x")) != EOF) {
230 switch (opt) {
231 case 'F': /* force grabbing (no O_EXCL) */
232 arg_force = PGRAB_FORCE;
233 break;
234 case 'i': /* id type for arguments */
235 arg_entity_string = optarg;
236 if (strcmp(optarg, "process") == 0 ||
237 strcmp(optarg, "pid") == 0)
238 arg_entity_type = RCENTITY_PROCESS;
239 else if (strcmp(optarg, "project") == 0 ||
240 strcmp(optarg, "projid") == 0)
241 arg_entity_type = RCENTITY_PROJECT;
242 else if (strcmp(optarg, "task") == 0 ||
243 strcmp(optarg, "taskid") == 0)
244 arg_entity_type = RCENTITY_TASK;
245 else if (strcmp(optarg, "zone") == 0 ||
246 strcmp(optarg, "zoneid") == 0)
247 arg_entity_type = RCENTITY_ZONE;
248 else {
249 warn(gettext("unknown idtype %s"), optarg);
250 errflg = 1;
252 break;
253 case 'd':
254 arg_action_string = optarg;
255 arg_operation |= ACTION_DISABLE;
256 break;
257 case 'e':
258 arg_action_string = optarg;
259 arg_operation |= ACTION_ENABLE;
260 break;
261 case 'n': /* name of rctl */
262 arg_name = optarg;
263 if (strncmp(optarg, "process.",
264 strlen("process.")) == 0)
265 arg_name_entity = RCENTITY_PROCESS;
266 else if (strncmp(optarg, "project.",
267 strlen("project.")) == 0)
268 arg_name_entity = RCENTITY_PROJECT;
269 else if (strncmp(optarg, "task.",
270 strlen("task.")) == 0)
271 arg_name_entity = RCENTITY_TASK;
272 else if (strncmp(optarg, "zone.",
273 strlen("zone.")) == 0)
274 arg_name_entity = RCENTITY_ZONE;
275 break;
276 case 'r':
277 arg_operation |= ACTION_REPLACE;
278 break;
279 case 't': /* rctl type */
280 if (strcmp(optarg, "basic") == 0)
281 arg_priv = RCPRIV_BASIC;
282 else if (strcmp(optarg, "privileged") == 0)
283 arg_priv = RCPRIV_PRIVILEGED;
284 else if (strcmp(optarg, "priv") == 0)
285 arg_priv = RCPRIV_PRIVILEGED;
286 else if (strcmp(optarg, "system") == 0)
287 arg_priv = RCPRIV_SYSTEM;
288 else {
289 warn(gettext("unknown privilege %s"), optarg);
290 errflg = 1;
292 break;
293 case 'v': /* value */
294 arg_valuestring = optarg;
295 break;
296 case 's':
297 arg_operation |= ACTION_SET;
298 break;
299 case 'x': /* delete */
300 arg_operation |= ACTION_DELETE;
301 break;
302 case 'p':
303 errno = 0;
304 /* Stick with -1 if arg is "-" */
305 if (strcmp("-", optarg) == 0)
306 break;
308 arg_pid_string = optarg;
309 arg_pid = strtoul(optarg, &end, 10);
310 if (errno || *end != '\0' || end == optarg) {
311 warn(gettext("invalid pid %s"), optarg);
312 errflg = 1;
313 break;
315 break;
316 case 'P':
317 arg_parseable_mode = 1;
318 break;
319 default:
320 warn(gettext("unknown option"));
321 errflg = 1;
322 break;
325 argc -= optind;
326 argv += optind;
328 if (argc < 1) {
329 warn(gettext("no arguments specified"));
330 errflg = 1;
331 goto done_parse;
333 /* if -v is specified without -r, -x, -d, or -e, -s is implied */
334 if (arg_valuestring &&
335 (!(arg_operation & (ACTION_REPLACE | ACTION_DELETE |
336 ACTION_DISABLE | ACTION_ENABLE)))) {
337 arg_operation |= ACTION_SET;
339 /* operations require -n */
340 if (arg_operation && (arg_name == NULL)) {
341 warn(gettext("-n is required with -s, -r, -x, -e, or -d"));
342 errflg = 1;
343 goto done_parse;
345 /* enable and disable are exclusive */
346 if ((arg_operation & ACTION_ENABLE) &&
347 (arg_operation & ACTION_DISABLE)) {
348 warn(gettext("options -d and -e are exclusive"));
349 errflg = 1;
350 goto done_parse;
352 /* -s, -r, and -x are exclusive */
353 flags = arg_operation &
354 (ACTION_REPLACE | ACTION_SET | ACTION_DELETE);
355 if (flags & (flags - 1)) {
356 warn(gettext("options -s, -r, and -x are exclusive"));
357 errflg = 1;
358 goto done_parse;
360 /* -e or -d makes no sense with -x */
361 if ((arg_operation & ACTION_DELETE) &
362 (arg_operation & (ACTION_ENABLE | ACTION_DISABLE))) {
363 warn(gettext("options -e or -d not allowed with -x"));
364 errflg = 1;
365 goto done_parse;
367 /* if -r is specified -v must be as well */
368 if ((arg_operation & ACTION_REPLACE) && (!arg_valuestring)) {
369 warn(gettext("option -r requires use of option -v"));
370 errflg = 1;
371 goto done_parse;
373 /* if -s is specified -v must be as well */
374 if ((arg_operation & ACTION_SET) && (!arg_valuestring)) {
375 warn(gettext("option -s requires use of option -v"));
376 errflg = 1;
377 goto done_parse;
379 /* Specifying a recipient pid on a non-basic rctl makes no sense */
380 if (arg_pid != -1 && arg_priv > RCPRIV_BASIC) {
381 warn(gettext("option -p not allowed on non-basic rctl"));
382 errflg = 1;
383 goto done_parse;
385 /* Specifying a recipient pid on a privileged rctl makes no sense */
386 if (arg_pid != -1 &&
387 arg_priv == RCPRIV_PRIVILEGED) {
388 warn(gettext("option -p not allowed with privileged rctl"));
389 errflg = 1;
390 goto done_parse;
392 if (arg_operation) {
394 /* do additional checks if there is an operation */
396 if (arg_parseable_mode == 1) {
397 warn(gettext("-P not valid when manipulating "
398 "resource control values"));
399 errflg = 1;
400 goto done_parse;
402 /* get rctl global flags to determine if actions are valid */
403 if ((rctlblkA = calloc(1, rctlblk_size())) == NULL) {
404 warn(gettext("malloc failed: %s"),
405 strerror(errno));
406 errflg = 1;
407 goto done_parse;
409 if ((rctlblkB = calloc(1, rctlblk_size())) == NULL) {
410 warn(gettext("malloc failed: %s"),
411 strerror(errno));
412 errflg = 1;
413 goto done_parse;
415 /* get system rctl to get global flags and max value */
416 if (getrctl(arg_name, NULL, rctlblkA, RCTL_FIRST)) {
417 warn(gettext("failed to get resource control "
418 "for %s: %s"), arg_name, strerror(errno));
419 errflg = 1;
420 goto done_parse;
422 while (getrctl(arg_name, rctlblkA, rctlblkB, RCTL_NEXT) == 0) {
424 /* allow user interrupt */
425 if (interrupt) {
426 errflg = 1;
427 goto done_parse;
429 tmp = rctlblkB;
430 rctlblkB = rctlblkA;
431 rctlblkA = tmp;
433 if (rctlblk_get_privilege(rctlblkA) ==
434 RCPRIV_SYSTEM) {
435 break;
438 if (rctlblk_get_privilege(rctlblkA) != RCPRIV_SYSTEM) {
439 warn(gettext("failed to get system resource control "
440 "for %s: %s"), arg_name, strerror(errno));
441 errflg = 1;
442 goto done_parse;
444 /* figure out the correct scale and unit for this rctl */
445 arg_global_flags = rctlblk_get_global_flags(rctlblkA);
446 arg_global_max = rctlblk_get_value(rctlblkA);
448 if (arg_global_flags & RCTL_GLOBAL_BYTES) {
449 arg_unit = SCALED_UNIT_BYTES;
450 arg_scale = scale_binary;
452 } else if (arg_global_flags & RCTL_GLOBAL_SECONDS) {
453 arg_unit = SCALED_UNIT_SECONDS;
454 arg_scale = scale_metric;
456 } else {
457 arg_unit = SCALED_UNIT_NONE;
458 arg_scale = scale_metric;
460 /* parse -v value string */
461 if (arg_valuestring) {
462 if (scaledtouint64(arg_valuestring,
463 &arg_value, NULL, &arg_modifier, NULL,
464 arg_scale, arg_unit,
465 SCALED_ALL_FLAGS)) {
467 warn(gettext("invalid -v value %s"),
468 arg_valuestring);
469 errflg = 1;
470 goto done_parse;
472 if (arg_value > arg_global_max) {
473 warn(gettext("-v value %s exceeds system "
474 "limit for resource control: %s"),
475 arg_valuestring, arg_name);
476 errflg = 1;
477 goto done_parse;
480 /* parse action */
481 if (arg_action_string) {
483 char *sigchr;
484 char *iter;
486 if ((strcmp(arg_action_string, "signal") == 0) ||
487 (strcmp(arg_action_string, "sig") == 0)) {
489 if (arg_operation & ACTION_ENABLE) {
490 warn(gettext(
491 "signal name or number must be "
492 "specified with -e"));
493 errflg = 1;
494 goto done_parse;
496 arg_action = RCTL_LOCAL_SIGNAL;
497 arg_signal = -1;
499 } else if ((strncmp(arg_action_string,
500 "signal=", strlen("signal=")) == 0) ||
501 (strncmp(arg_action_string,
502 "sig=", strlen("sig=")) == 0)) {
504 arg_action = RCTL_LOCAL_SIGNAL;
505 sigchr = strrchr(arg_action_string, '=');
506 sigchr++;
508 iter = sigchr;
509 while (*iter) {
510 *iter = toupper(*iter);
511 iter++;
513 if (strncmp("SIG", sigchr, 3) == 0)
514 sigchr += 3;
517 if (str2sig(sigchr, &arg_signal) != 0) {
518 warn(gettext("signal invalid"));
519 errflg = 1;
520 goto done_parse;
522 } else if (strcmp(arg_action_string, "deny") == 0) {
524 arg_action = RCTL_LOCAL_DENY;
526 } else if (strcmp(arg_action_string, "all") == 0) {
528 if (arg_operation & ACTION_ENABLE) {
529 warn(gettext(
530 "cannot use action 'all' with -e"));
531 errflg = 1;
532 goto done_parse;
534 arg_action = RCTL_LOCAL_DENY |
535 RCTL_LOCAL_SIGNAL;
536 arg_signal = -1;
537 goto done_parse;
538 } else {
539 warn(gettext("action invalid"));
540 errflg = 1;
541 goto done_parse;
544 /* cannot manipulate system rctls */
545 if (arg_priv == RCPRIV_SYSTEM) {
547 warn(gettext("cannot modify system values"));
548 errflg = 1;
549 goto done_parse;
551 /* validate that the privilege is allowed */
552 if ((arg_priv == RCPRIV_BASIC) &&
553 (arg_global_flags & RCTL_GLOBAL_NOBASIC)) {
555 warn(gettext("basic values not allowed on rctl %s"),
556 arg_name);
557 errflg = 1;
558 goto done_parse;
560 /* validate that actions are appropriate for given rctl */
561 if ((arg_operation & ACTION_ENABLE) &&
562 (arg_action & RCTL_LOCAL_DENY) &&
563 (arg_global_flags & RCTL_GLOBAL_DENY_NEVER)) {
565 warn(gettext("unable to enable deny on rctl with "
566 "global flag 'no-deny'"));
567 errflg = 1;
568 goto done_parse;
570 if ((arg_operation & ACTION_DISABLE) &&
571 (arg_action & RCTL_LOCAL_DENY) &&
572 (arg_global_flags & RCTL_GLOBAL_DENY_ALWAYS)) {
574 warn(gettext("unable to disable deny on rctl with "
575 "global flag 'deny'"));
576 errflg = 1;
577 goto done_parse;
579 if ((arg_operation & ACTION_ENABLE) &&
580 (arg_action & RCTL_LOCAL_SIGNAL) &&
581 (arg_global_flags & RCTL_GLOBAL_SIGNAL_NEVER)) {
583 warn(gettext("unable to enable signal on rctl with "
584 "global flag 'no-signal'"));
585 errflg = 1;
586 goto done_parse;
588 /* now set defaults for options not supplied */
591 * default privilege to basic if this is a seting an rctl
592 * operation
594 if (arg_operation & ACTION_SET) {
595 if (arg_priv == 0) {
596 arg_priv = RCPRIV_BASIC;
600 * -p is required when set a basic task,
601 * project or zone rctl
603 if ((arg_pid == -1) &&
604 (arg_priv == RCPRIV_BASIC) &&
605 (arg_entity_type != RCENTITY_PROCESS) &&
606 (arg_operation & ACTION_SET) &&
607 (arg_name) &&
608 (arg_name_entity == RCENTITY_TASK ||
609 arg_name_entity == RCENTITY_PROJECT ||
610 arg_name_entity == RCENTITY_ZONE)) {
612 warn(gettext("-p pid required when setting or "
613 "replacing task or project rctl"));
614 errflg = 1;
615 goto done_parse;
617 } else {
619 /* validate for list mode */
620 /* -p is not valid in list mode */
621 if (arg_pid != -1) {
622 warn(gettext("-p pid requires -s, -r, -x, -e, or -d"));
623 errflg = 1;
624 goto done_parse;
627 /* getting/setting process rctl on task or project is error */
628 if ((arg_name && (arg_name_entity == RCENTITY_PROCESS)) &&
629 ((arg_entity_type == RCENTITY_TASK) ||
630 (arg_entity_type == RCENTITY_PROJECT))) {
632 warn(gettext("cannot get/set process rctl on task "
633 "or project"));
634 errflg = 1;
635 goto done_parse;
637 /* getting/setting task rctl on project is error */
638 if ((arg_name && (arg_name_entity == RCENTITY_TASK)) &&
639 (arg_entity_type == RCENTITY_PROJECT)) {
641 warn(gettext("cannot get/set task rctl on project"));
642 errflg = 1;
643 goto done_parse;
646 done_parse:
648 /* free any rctlblk's that we may have allocated */
649 if (rctlblkA) {
650 free(rctlblkA);
651 rctlblkA = NULL;
653 if (rctlblkB) {
654 free(rctlblkB);
655 rctlblkB = NULL;
657 if (errflg)
658 usage();
660 /* catch signals from terminal */
661 if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
662 (void) sigset(SIGHUP, intr);
663 if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
664 (void) sigset(SIGINT, intr);
665 if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
666 (void) sigset(SIGQUIT, intr);
667 (void) sigset(SIGTERM, intr);
669 while (--argc >= 0 && !interrupt) {
670 pr_info_handle_t p;
671 char *arg = *argv++;
672 int intarg;
673 char *end;
674 errflg = 0;
676 gret = 0;
678 /* Store int version of arg */
679 errno = 0;
680 intarg = strtoul(arg, &end, 10);
681 if (errno || *end != '\0' || end == arg) {
682 intarg = -1;
686 * -p defaults to arg if basic and collective rctl
687 * and -i process is specified
689 if ((arg_pid == -1) &&
690 (arg_priv == RCPRIV_BASIC) &&
691 (arg_entity_type == RCENTITY_PROCESS) &&
692 (arg_name) &&
693 (arg_name_entity == RCENTITY_TASK ||
694 arg_name_entity == RCENTITY_PROJECT)) {
695 arg_pid_string = arg;
696 errno = 0;
697 arg_pid = intarg;
699 /* Specifying a recipient pid and -i pid is redundent */
700 if (arg_pid != -1 && arg_entity_type == RCENTITY_PROCESS &&
701 arg_pid != intarg) {
702 warn(gettext("option -p pid must match -i process"));
703 errflg = 1;
704 continue;
706 /* use recipient pid if we have one */
707 if (arg_pid_string != NULL) {
708 target_id = arg_pid_string;
709 search_type = RCENTITY_PROCESS;
710 } else {
711 target_id = arg;
712 search_type = arg_entity_type;
714 (void) fflush(stdout); /* process-at-a-time */
716 if (arg_operation != 0) {
718 if ((pid = grab_process_by_id(target_id,
719 search_type, &p, arg_priv, &gret)) < 0) {
721 * Mark that an error occurred so that the
722 * return value can be set, but continue
723 * on with other processes
725 errflg = 1;
726 continue;
730 * At this point, the victim process is held.
731 * Do not call any Pgrab-unsafe functions until
732 * the process is released via release_process().
735 errflg = get_rctls(p.pr);
737 if (arg_operation & ACTION_DELETE) {
739 /* match by privilege, value, and pid */
740 if (match_rctl(p.pr, &rctlblkA, arg_name,
741 arg_valuestring, arg_value, arg_priv,
742 arg_pid) != 0 || rctlblkA == NULL) {
744 if (interrupt)
745 goto out;
747 preserve_error(gettext("no matching "
748 "resource control found for "
749 "deletion"));
750 errflg = 1;
751 goto out;
754 * grab correct process. This is neccessary
755 * if the recipient pid does not match the
756 * one we grabbed
758 pid = regrab_process(
759 rctlblk_get_recipient_pid(rctlblkA),
760 &p, arg_priv, &gret);
762 if (pid < 0) {
763 errflg = 1;
764 goto out;
766 if (prctl_setrctl(p.pr, arg_name, NULL,
767 rctlblkA, RCTL_DELETE) != 0) {
768 errflg = 1;
769 goto out;
771 } else if (arg_operation & ACTION_SET) {
773 /* match by privilege, value, and pid */
775 if (match_rctl(p.pr, &rctlblkA, arg_name,
776 arg_valuestring, arg_value, arg_priv,
777 arg_pid) == 0) {
779 if (interrupt)
780 goto out;
782 preserve_error(gettext("resource "
783 "control already exists"));
784 errflg = 1;
785 goto out;
787 rctlblkB = calloc(1, rctlblk_size());
788 if (rctlblkB == NULL) {
789 preserve_error(gettext(
790 "malloc failed"), strerror(errno));
791 errflg = 1;
792 goto out;
794 rctlblk_set_value(rctlblkB, arg_value);
795 rctlblk_set_privilege(rctlblkB, arg_priv);
796 if (change_action(rctlblkB)) {
797 errflg = 1;
798 goto out;
800 if (prctl_setrctl(p.pr, arg_name, NULL,
801 rctlblkB, RCTL_INSERT) != 0) {
802 errflg = 1;
803 goto out;
805 } else if (arg_operation & ACTION_REPLACE) {
807 * match rctl for deletion by privilege and
808 * pid only
810 if (match_rctl(p.pr, &rctlblkA, arg_name,
811 NULL, 0, arg_priv,
812 arg_pid) != 0 || rctlblkA == NULL) {
814 if (interrupt)
815 goto out;
817 preserve_error(gettext("no matching "
818 "resource control to replace"));
819 errflg = 1;
820 goto out;
823 * grab correct process. This is neccessary
824 * if the recipient pid does not match the
825 * one we grabbed
827 pid = regrab_process(
828 rctlblk_get_recipient_pid(rctlblkA),
829 &p, arg_priv, &gret);
830 if (pid < 0) {
831 errflg = 1;
832 goto out;
834 pid = rctlblk_get_recipient_pid(rctlblkA);
837 * match by privilege, value and pid to
838 * check if new rctl already exists
840 if (match_rctl(p.pr, &rctlblkB, arg_name,
841 arg_valuestring, arg_value, arg_priv,
842 pid) < 0) {
844 if (interrupt)
845 goto out;
847 preserve_error(gettext(
848 "Internal Error"));
849 errflg = 1;
850 goto out;
853 * If rctl already exists, and it does not
854 * match the one that we will delete, than
855 * the replace will fail.
857 if (rctlblkB != NULL &&
858 arg_value != rctlblk_get_value(rctlblkA)) {
860 preserve_error(gettext("replacement "
861 "resource control already "
862 "exists"));
864 errflg = 1;
865 goto out;
867 /* create new rctl */
868 rctlblkB = calloc(1, rctlblk_size());
869 if (rctlblkB == NULL) {
870 preserve_error(gettext(
871 "malloc failed"), strerror(errno));
872 errflg = 1;
873 goto out;
875 localaction =
876 rctlblk_get_local_action(rctlblkA, &signal);
877 rctlblk_set_local_action(rctlblkB, localaction,
878 signal);
879 rctlblk_set_value(rctlblkB, arg_value);
880 rctlblk_set_privilege(rctlblkB,
881 rctlblk_get_privilege(rctlblkA));
882 if (change_action(rctlblkB)) {
883 errflg = 1;
884 goto out;
886 /* do replacement */
887 if (prctl_setrctl(p.pr, arg_name, rctlblkA,
888 rctlblkB, RCTL_REPLACE) != 0) {
889 errflg = 1;
890 goto out;
892 } else if (arg_operation &
893 (ACTION_ENABLE | ACTION_DISABLE)) {
895 rctlblkB = calloc(1, rctlblk_size());
896 if (rctlblkB == NULL) {
897 preserve_error(gettext(
898 "malloc failed"), strerror(errno));
899 errflg = 1;
900 goto out;
902 /* match by privilege, value, and pid */
903 if (match_rctl(p.pr, &rctlblkA, arg_name,
904 arg_valuestring, arg_value, arg_priv,
905 arg_pid) != 0) {
907 if (interrupt)
908 goto out;
910 /* if no match, just set new rctl */
911 if (arg_priv == 0)
912 arg_priv = RCPRIV_BASIC;
914 if ((arg_priv == RCPRIV_BASIC) &&
915 (arg_entity_type !=
916 RCENTITY_PROCESS) &&
917 (arg_pid_string == NULL)) {
918 preserve_error(gettext(
919 "-p required when setting "
920 "basic rctls"));
921 errflg = 1;
922 goto out;
924 rctlblk_set_value(rctlblkB,
925 arg_value);
926 rctlblk_set_privilege(
927 rctlblkB, arg_priv);
928 if (change_action(rctlblkB)) {
929 errflg = 1;
930 goto out;
932 if (prctl_setrctl(p.pr,
933 arg_name, NULL, rctlblkB,
934 RCTL_INSERT) != 0) {
935 errflg = 1;
936 goto out;
938 goto out;
940 if (rctlblkA == NULL) {
941 preserve_error(gettext("no matching "
942 "resource control found"));
943 errflg = 1;
944 goto out;
947 * grab correct process. This is neccessary
948 * if the recipient pid does not match the
949 * one we grabbed
951 pid = regrab_process(
952 rctlblk_get_recipient_pid(rctlblkA),
953 &p, arg_priv, &gret);
954 if (pid < 0) {
955 errflg = 1;
956 goto out;
958 localaction =
959 rctlblk_get_local_action(rctlblkA,
960 &signal);
961 rctlblk_set_local_action(rctlblkB, localaction,
962 signal);
963 rctlblk_set_privilege(rctlblkB,
964 rctlblk_get_privilege(rctlblkA));
965 rctlblk_set_value(rctlblkB,
966 rctlblk_get_value(rctlblkA));
968 if (change_action(rctlblkB)) {
969 errflg = 1;
970 goto out;
972 if (prctl_setrctl(p.pr, arg_name, rctlblkA,
973 rctlblkB, RCTL_REPLACE) != 0) {
974 errflg = 1;
975 goto out;
978 out:
979 release_process(p.pr);
980 free(rctlblkA);
981 free(rctlblkB);
983 /* Print any errors that occurred */
984 if (errflg && *global_error != '\0') {
985 proc_unctrl_psinfo(&(p.psinfo));
986 (void) fprintf(stderr, "%d:\t%.70s\n",
987 (int)p.pid, p.psinfo.pr_psargs);
988 warn("%s\n", global_error);
989 break;
991 } else {
993 struct project projent;
994 char buf[PROJECT_BUFSZ];
995 char zonename[ZONENAME_MAX];
998 * Hack to allow the user to specify a system
999 * process.
1001 gret = G_SYS;
1002 pid = grab_process_by_id(
1003 target_id, search_type, &p, RCPRIV_BASIC, &gret);
1006 * Print system process if user chose specifically
1007 * to inspect a system process.
1009 if (arg_entity_type == RCENTITY_PROCESS &&
1010 pid < 0 &&
1011 gret == G_SYS) {
1013 * Add blank lines between output for
1014 * operands.
1016 if (printed) {
1017 (void) fprintf(stdout, "\n");
1020 proc_unctrl_psinfo(&(p.psinfo));
1021 (void) printf(
1022 "process: %d: %s [ system process ]\n",
1023 (int)p.pid, p.psinfo.pr_psargs);
1025 printed = 1;
1026 continue;
1028 } else if (pid < 0) {
1031 * Mark that an error occurred so that the
1032 * return value can be set, but continue
1033 * on with other processes
1035 errflg = 1;
1036 continue;
1039 errflg = get_rctls(p.pr);
1041 release_process(p.pr);
1043 /* handle user interrupt of getting rctls */
1044 if (interrupt)
1045 break;
1047 /* add blank lines between output for operands */
1048 if (printed) {
1049 (void) fprintf(stdout, "\n");
1051 /* First print any errors */
1052 if (errflg) {
1053 warn("%s\n", global_error);
1054 free_lists();
1055 break;
1057 if (getprojbyid(p.projid, &projent, buf,
1058 sizeof (buf))) {
1059 p.projname = projent.pj_name;
1060 } else {
1061 p.projname = "";
1063 if (getzonenamebyid(p.zoneid, zonename,
1064 sizeof (zonename)) > 0) {
1065 p.zonename = zonename;
1066 } else {
1067 p.zonename = "";
1069 print_rctls(&p);
1070 printed = 1;
1071 /* Free the resource control lists */
1072 free_lists();
1075 if (interrupt)
1076 errflg = 1;
1079 * return error if one occurred
1081 return (errflg);
1085 static void
1086 intr(int sig)
1088 interrupt = sig;
1092 * get_rctls(struct ps_prochandle *, const char *)
1094 * If controlname is given, store only controls for that named
1095 * resource. If controlname is NULL, store all controls for all
1096 * resources.
1098 * This function is Pgrab-safe.
1100 static int
1101 get_rctls(struct ps_prochandle *Pr)
1103 int ret = 0;
1105 if (arg_name == NULL) {
1106 if (rctl_walk(store_rctls, Pr) != 0)
1107 ret = 1;
1108 } else {
1109 ret = store_rctls(arg_name, Pr);
1111 return (ret);
1115 * store_rctls(const char *, void *)
1117 * Store resource controls for the given name in a linked list.
1118 * Honor the user's options, and store only the ones they are
1119 * interested in. If priv is not 0, show only controls that match
1120 * the given privilege.
1122 * This function is Pgrab-safe
1124 static int
1125 store_rctls(const char *rctlname, void *walk_data)
1127 struct ps_prochandle *Pr = walk_data;
1128 rctlblk_t *rblk2, *rblk_tmp, *rblk1 = NULL;
1129 prctl_list_t *list = NULL;
1130 rctl_priv_t rblk_priv;
1131 rctl_entity_t rblk_entity;
1133 if (((rblk1 = calloc(1, rctlblk_size())) == NULL) ||
1134 ((rblk2 = calloc(1, rctlblk_size())) == NULL)) {
1135 free(rblk1);
1136 preserve_error(gettext("malloc failed: %s"),
1137 strerror(errno));
1138 return (1);
1140 if (pr_getrctl(Pr, rctlname, NULL, rblk1, RCTL_FIRST)) {
1141 preserve_error(gettext("failed to get resource control "
1142 "for %s: %s"), rctlname, strerror(errno));
1143 free(rblk1);
1144 free(rblk2);
1145 return (1);
1147 /* Store control if it matches privilege and enity type criteria */
1148 rblk_priv = rctlblk_get_privilege(rblk1);
1149 rblk_entity = 0;
1150 if (strncmp(rctlname, "process.",
1151 strlen("process.")) == 0)
1152 rblk_entity = RCENTITY_PROCESS;
1153 else if (strncmp(rctlname, "project.",
1154 strlen("project.")) == 0)
1155 rblk_entity = RCENTITY_PROJECT;
1156 else if (strncmp(rctlname, "task.",
1157 strlen("task.")) == 0)
1158 rblk_entity = RCENTITY_TASK;
1159 else if (strncmp(rctlname, "zone.",
1160 strlen("zone.")) == 0)
1161 rblk_entity = RCENTITY_ZONE;
1163 if (((arg_priv == 0) || (rblk_priv == arg_priv)) &&
1164 ((arg_name == NULL) ||
1165 strncmp(rctlname, arg_name, strlen(arg_name)) == 0) &&
1166 (arg_entity_string == NULL || rblk_entity >= arg_entity_type)) {
1168 /* Once we know we have some controls, store the name */
1169 if ((list = store_list_entry(rctlname)) == NULL) {
1170 free(rblk1);
1171 free(rblk2);
1172 return (1);
1174 if (store_value_entry(rblk1, list) == NULL) {
1175 free(rblk1);
1176 free(rblk2);
1177 return (1);
1180 while (pr_getrctl(Pr, rctlname, rblk1, rblk2, RCTL_NEXT) == 0) {
1183 * in case this is stuck for some reason, allow manual
1184 * interrupt
1186 if (interrupt) {
1187 free(rblk1);
1188 free(rblk2);
1189 return (1);
1191 rblk_priv = rctlblk_get_privilege(rblk2);
1193 * Store control if it matches privilege and entity type
1194 * criteria
1196 if (((arg_priv == 0) || (rblk_priv == arg_priv)) &&
1197 ((arg_name == NULL) ||
1198 strncmp(rctlname, arg_name, strlen(arg_name)) == 0) &&
1199 (arg_entity_string == NULL ||
1200 rblk_entity == arg_entity_type)) {
1202 /* May not have created the list yet. */
1203 if (list == NULL) {
1204 if ((list = store_list_entry(rctlname))
1205 == NULL) {
1206 free(rblk1);
1207 free(rblk2);
1208 return (1);
1211 if (store_value_entry(rblk2, list) == NULL) {
1212 free(rblk1);
1213 free(rblk2);
1214 return (1);
1217 rblk_tmp = rblk1;
1218 rblk1 = rblk2;
1219 rblk2 = rblk_tmp;
1223 * Get the current usage for the resource control if it matched the
1224 * privilege and entity type criteria.
1226 if (list != NULL) {
1227 if (pr_getrctl(Pr, rctlname, NULL, rblk2, RCTL_USAGE) == 0) {
1228 list->usage = (rctl_qty_t *)malloc(sizeof (rctl_qty_t));
1229 if (list->usage == NULL) {
1230 preserve_error(gettext("malloc failed: %s"),
1231 strerror(errno));
1232 free(rblk1);
1233 free(rblk2);
1234 return (1);
1236 *list->usage = rctlblk_get_value(rblk2);
1237 } else {
1238 list->usage = NULL;
1239 if (errno != ENOTSUP) {
1240 preserve_error(gettext("failed to get "
1241 "resource control usage for %s: %s"),
1242 rctlname, strerror(errno));
1243 free(rblk1);
1244 free(rblk2);
1245 return (1);
1249 free(rblk1);
1250 free(rblk2);
1251 return (0);
1255 * store_value_entry(rctlblk_t *, prctl_list_t *)
1257 * Store an rblk for a given resource control into the global list.
1259 * This function is Pgrab-safe.
1261 prctl_value_t *
1262 store_value_entry(rctlblk_t *rblk, prctl_list_t *list)
1264 prctl_value_t *e = calloc(1, sizeof (prctl_value_t));
1265 rctlblk_t *store_blk = calloc(1, rctlblk_size());
1266 prctl_value_t *iter = list->val_list;
1268 if (e == NULL || store_blk == NULL) {
1269 preserve_error(gettext("malloc failed %s"),
1270 strerror(errno));
1271 free(e);
1272 free(store_blk);
1273 return (NULL);
1275 if (iter == NULL)
1276 list->val_list = e;
1277 else {
1278 while (iter->next != NULL) {
1279 iter = iter->next;
1281 iter->next = e;
1283 bcopy(rblk, store_blk, rctlblk_size());
1285 e->rblk = store_blk;
1286 e->next = NULL;
1287 return (e);
1291 * store_list_entry(const char *)
1293 * Store a new resource control value in the global list. No checking
1294 * for duplicates done.
1296 * This function is Pgrab-safe.
1298 prctl_list_t *
1299 store_list_entry(const char *name)
1301 prctl_list_t *e = calloc(1, sizeof (prctl_list_t));
1303 if (e == NULL) {
1304 preserve_error(gettext("malloc failed %s"),
1305 strerror(errno));
1306 return (NULL);
1308 if ((e->name = calloc(1, strlen(name) + 1)) == NULL) {
1309 preserve_error(gettext("malloc failed %s"),
1310 strerror(errno));
1311 free(e);
1312 return (NULL);
1314 (void) strcpy(e->name, name);
1315 e->val_list = NULL;
1317 if (global_rctl_list_head == NULL) {
1318 global_rctl_list_head = e;
1319 global_rctl_list_tail = e;
1320 } else {
1321 global_rctl_list_tail->next = e;
1322 global_rctl_list_tail = e;
1324 e->next = NULL;
1325 return (e);
1329 * free_lists()
1331 * Free all resource control blocks and values from the global lists.
1333 * This function is Pgrab-safe.
1335 void
1336 free_lists()
1338 prctl_list_t *new_list, *old_list = global_rctl_list_head;
1339 prctl_value_t *old_val, *new_val;
1341 while (old_list != NULL) {
1342 old_val = old_list->val_list;
1343 while (old_val != NULL) {
1344 free(old_val->rblk);
1345 new_val = old_val->next;
1346 free(old_val);
1347 old_val = new_val;
1349 free(old_list->name);
1350 free(old_list->usage);
1351 new_list = old_list->next;
1352 free(old_list);
1353 old_list = new_list;
1355 global_rctl_list_head = NULL;
1356 global_rctl_list_tail = NULL;
1359 void
1360 print_heading()
1363 /* print headings */
1364 (void) fprintf(stdout, "%-8s%-16s%-9s%-7s%-28s%10s\n",
1365 "NAME", "PRIVILEGE", "VALUE",
1366 "FLAG", "ACTION", "RECIPIENT");
1370 * print_rctls()
1372 * Print all resource controls from the global list that was
1373 * previously populated by store_rctls.
1375 void
1376 print_rctls(pr_info_handle_t *p)
1378 prctl_list_t *iter_list = global_rctl_list_head;
1379 prctl_value_t *iter_val;
1380 rctl_qty_t rblk_value;
1381 rctl_priv_t rblk_priv;
1382 uint_t local_action;
1383 int signal, local_flags, global_flags;
1384 pid_t pid;
1385 char rctl_valuestring[SCALED_STRLEN];
1386 char *unit = NULL;
1387 scale_t *scale;
1388 char *string;
1389 int doneheading = 0;
1391 if (iter_list == NULL)
1392 return;
1394 while (iter_list != NULL) {
1396 if (doneheading == 0 &&
1397 arg_entity_type == RCENTITY_PROCESS) {
1398 proc_unctrl_psinfo(&(p->psinfo));
1399 doneheading = 1;
1400 (void) fprintf(stdout,
1401 "process: %d: %.70s\n", (int)p->pid,
1402 p->psinfo.pr_psargs);
1403 if (!arg_parseable_mode)
1404 print_heading();
1406 if (doneheading == 0 &&
1407 arg_entity_type == RCENTITY_TASK) {
1408 doneheading = 1;
1409 (void) fprintf(stdout, "task: %d\n", (int)p->taskid);
1410 if (!arg_parseable_mode)
1411 print_heading();
1413 if (doneheading == 0 &&
1414 arg_entity_type == RCENTITY_PROJECT) {
1415 if (!arg_parseable_mode && doneheading)
1416 (void) fprintf(stdout, "\n");
1417 doneheading = 1;
1418 (void) fprintf(stdout,
1419 "project: %d: %.70s\n", (int)p->projid,
1420 p->projname);
1421 if (!arg_parseable_mode)
1422 print_heading();
1424 if (doneheading == 0 &&
1425 arg_entity_type == RCENTITY_ZONE) {
1426 doneheading = 1;
1427 (void) fprintf(stdout,
1428 "zone: %d: %.70s\n", (int)p->zoneid,
1429 p->zonename);
1430 if (!arg_parseable_mode)
1431 print_heading();
1433 /* only print name once in normal output */
1434 if (!arg_parseable_mode)
1435 (void) fprintf(stdout, "%s\n", iter_list->name);
1437 iter_val = iter_list->val_list;
1439 /* if for some reason there are no values, skip */
1440 if (iter_val == 0)
1441 continue;
1444 /* get the global flags the first rctl only */
1445 global_flags = rctlblk_get_global_flags(iter_val->rblk);
1448 if (global_flags & RCTL_GLOBAL_BYTES) {
1449 unit = SCALED_UNIT_BYTES;
1450 scale = scale_binary;
1452 } else if (global_flags & RCTL_GLOBAL_SECONDS) {
1453 unit = SCALED_UNIT_SECONDS;
1454 scale = scale_metric;
1456 } else {
1457 unit = SCALED_UNIT_NONE;
1458 scale = scale_metric;
1461 /* print the current usage for the rctl if available */
1462 if (iter_list->usage != NULL) {
1463 rblk_value = *(iter_list->usage);
1464 if (!arg_parseable_mode) {
1465 (void) uint64toscaled(rblk_value, 4, "E",
1466 rctl_valuestring, NULL, NULL,
1467 scale, NULL, 0);
1469 (void) fprintf(stdout, "%8s%-16s%5s%-4s\n",
1470 "", "usage", rctl_valuestring, unit);
1471 } else {
1472 (void) fprintf(stdout, "%s %s %llu - - -\n",
1473 iter_list->name, "usage", rblk_value);
1477 /* iterate over an print all control values */
1478 while (iter_val != NULL) {
1480 /* print name or empty name field */
1481 if (!arg_parseable_mode)
1482 (void) fprintf(stdout, "%8s", "");
1483 else
1484 (void) fprintf(stdout, "%s ", iter_list->name);
1487 rblk_priv = rctlblk_get_privilege(iter_val->rblk);
1488 if (!arg_parseable_mode)
1489 print_priv(rblk_priv, "%-16s");
1490 else
1491 print_priv(rblk_priv, "%s ");
1493 rblk_value = rctlblk_get_value(iter_val->rblk);
1494 if (arg_parseable_mode) {
1495 (void) fprintf(stdout, "%llu ", rblk_value);
1497 } else {
1499 (void) uint64toscaled(rblk_value, 4, "E",
1500 rctl_valuestring, NULL, NULL,
1501 scale, NULL, 0);
1503 (void) fprintf(stdout, "%5s",
1504 rctl_valuestring);
1505 (void) fprintf(stdout, "%-4s", unit);
1507 local_flags = rctlblk_get_local_flags(iter_val->rblk);
1509 if (local_flags & RCTL_LOCAL_MAXIMAL) {
1511 if (global_flags & RCTL_GLOBAL_INFINITE) {
1512 string = "inf";
1513 } else {
1514 string = "max";
1516 } else {
1517 string = "-";
1519 if (arg_parseable_mode)
1520 (void) fprintf(stdout, "%s ", string);
1521 else
1522 (void) fprintf(stdout, "%4s%3s",
1523 string, "");
1526 local_action = rctlblk_get_local_action(iter_val->rblk,
1527 &signal);
1529 if (arg_parseable_mode)
1530 print_local_action(local_action, &signal,
1531 "%s ");
1532 else
1533 print_local_action(local_action, &signal,
1534 "%-28s");
1536 pid = rctlblk_get_recipient_pid(iter_val->rblk);
1538 if (arg_parseable_mode) {
1539 if (pid < 0) {
1540 (void) fprintf(stdout, "%s\n", "-");
1541 } else {
1542 (void) fprintf(stdout, "%d\n",
1543 (int)pid);
1545 } else {
1546 if (pid < 0) {
1547 (void) fprintf(stdout, "%10s\n", "-");
1548 } else {
1549 (void) fprintf(stdout, "%10d\n",
1550 (int)pid);
1553 iter_val = iter_val->next;
1555 iter_list = iter_list->next;
1561 * match_rctl
1563 * find the first rctl with matching name, value, priv, and recipient pid
1566 match_rctl(struct ps_prochandle *Pr, rctlblk_t **rctl, char *name,
1567 char *valuestringin, int valuein, rctl_priv_t privin, int pidin)
1569 rctlblk_t *next;
1570 rctlblk_t *last;
1571 rctlblk_t *tmp;
1573 *rctl = NULL;
1575 next = calloc(1, rctlblk_size());
1576 last = calloc(1, rctlblk_size());
1578 if ((last == NULL) || (next == NULL)) {
1579 preserve_error(gettext("malloc failed"), strerror(errno));
1580 return (-1);
1583 * For this resource name, now iterate through all
1584 * the controls, looking for a match to the
1585 * user-specified input.
1587 if (pr_getrctl(Pr, name, NULL, next, RCTL_FIRST)) {
1588 preserve_error(gettext("failed to get resource control "
1589 "for %s: %s"), name, strerror(errno));
1590 return (-1);
1592 if (match_rctl_blk(next, valuestringin, valuein, privin, pidin) == 1) {
1593 free(last);
1594 *rctl = next;
1595 return (0);
1597 tmp = next;
1598 next = last;
1599 last = tmp;
1601 while (pr_getrctl(Pr, name, last, next, RCTL_NEXT) == 0) {
1603 /* allow user interrupt */
1604 if (interrupt)
1605 break;
1607 if (match_rctl_blk(next, valuestringin, valuein, privin, pidin)
1608 == 1) {
1609 free(last);
1610 *rctl = next;
1611 return (0);
1613 tmp = next;
1614 next = last;
1615 last = tmp;
1617 free(next);
1618 free(last);
1620 return (1);
1624 * int match_rctl_blk(rctlblk_t *, char *, uint64, rctl_priv_t, int pid)
1626 * Input
1627 * Must supply a valid rctl, value, privilege, and pid to match on.
1628 * If valuestring is NULL, then valuestring and valuein will not be used
1629 * If privilege type is 0 it will not be used.
1630 * If pid is -1 it will not be used.
1632 * Return values
1633 * Returns 1 if a matching rctl given matches the parameters specified, and
1634 * 0 if they do not.
1636 * This function is Pgrab-safe.
1639 match_rctl_blk(rctlblk_t *rctl, char *valuestringin,
1640 uint64_t valuein, rctl_priv_t privin, int pidin)
1643 rctl_qty_t value;
1644 rctl_priv_t priv;
1645 pid_t pid;
1646 int valuematch = 1;
1647 int privmatch = 1;
1648 int pidmatch = 1;
1650 value = rctlblk_get_value(rctl);
1651 priv = rctlblk_get_privilege(rctl);
1652 pid = rctlblk_get_recipient_pid(rctl);
1654 if (valuestringin) {
1656 if (arg_modifier == NULL) {
1657 valuematch = (valuein == value);
1658 } else {
1659 valuematch = scaledequint64(valuestringin, value,
1660 PRCTL_VALUE_WIDTH,
1661 arg_scale, arg_unit,
1662 SCALED_ALL_FLAGS);
1665 if (privin != 0) {
1666 privmatch = (privin == priv);
1668 if (pidin != -1) {
1669 pidmatch = (pidin == pid);
1671 return (valuematch && privmatch && pidmatch);
1674 static int
1675 change_action(rctlblk_t *blk)
1677 int signal = 0;
1678 int action;
1680 action = rctlblk_get_local_action(blk, &signal);
1682 if (arg_operation & ACTION_ENABLE) {
1684 if (arg_action & RCTL_LOCAL_SIGNAL) {
1685 signal = arg_signal;
1687 action = action | arg_action;
1688 /* add local action */
1689 rctlblk_set_local_action(blk, action, signal);
1691 } else if (arg_operation & ACTION_DISABLE) {
1694 * if deleting signal and signal number is specified,
1695 * then signal number must match
1697 if ((arg_action & RCTL_LOCAL_SIGNAL) &&
1698 (arg_signal != -1)) {
1699 if (arg_signal != signal) {
1700 preserve_error(gettext("signal name or number "
1701 "does not match existing action"));
1702 return (-1);
1705 /* remove local action */
1706 action = action & (~arg_action);
1707 rctlblk_set_local_action(blk, RCTL_LOCAL_NOACTION, 0);
1708 rctlblk_set_local_action(blk, action, signal);
1710 /* enable deny if it must be enabled */
1711 if (arg_global_flags & RCTL_GLOBAL_DENY_ALWAYS) {
1712 rctlblk_set_local_action(blk, RCTL_LOCAL_DENY | action,
1713 signal);
1715 return (0);
1719 * prctl_setrctl
1721 * Input
1722 * This function expects that input has been validated. In the
1723 * case of a replace operation, both old_rblk and new_rblk must
1724 * be valid resource controls. If a resource control is being
1725 * created, only new_rblk must be supplied. If a resource control
1726 * is being deleted, only new_rblk must be supplied.
1728 * If the privilege is a priviliged type, at this time, the process
1729 * tries to take on superuser privileges.
1732 prctl_setrctl(struct ps_prochandle *Pr, const char *name,
1733 rctlblk_t *old_rblk, rctlblk_t *new_rblk, uint_t flags)
1735 int ret = 0;
1736 rctl_priv_t rblk_priv;
1737 psinfo_t psinfo;
1738 zoneid_t oldzoneid = GLOBAL_ZONEID;
1739 prpriv_t *old_prpriv = NULL, *new_prpriv = NULL;
1740 priv_set_t *eset, *pset;
1741 boolean_t relinquish_failed = B_FALSE;
1743 rblk_priv = rctlblk_get_privilege(new_rblk);
1745 if (rblk_priv == RCPRIV_SYSTEM) {
1746 preserve_error(gettext("cannot modify system values"));
1747 return (1);
1749 if (rblk_priv == RCPRIV_PRIVILEGED) {
1750 new_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid);
1751 if (new_prpriv == NULL) {
1752 preserve_error(gettext("cannot get process privileges "
1753 "for pid %d: %s"), Pstatus(Pr)->pr_pid,
1754 strerror(errno));
1755 return (1);
1758 * We only have to change the process privileges if it doesn't
1759 * already have PRIV_SYS_RESOURCE. In addition, we want to make
1760 * sure that we don't leave a process with elevated privileges,
1761 * so we make sure the process dies if we exit unexpectedly.
1763 eset = (priv_set_t *)
1764 &new_prpriv->pr_sets[new_prpriv->pr_setsize *
1765 priv_getsetbyname(PRIV_EFFECTIVE)];
1766 pset = (priv_set_t *)
1767 &new_prpriv->pr_sets[new_prpriv->pr_setsize *
1768 priv_getsetbyname(PRIV_PERMITTED)];
1769 if (!priv_ismember(eset, PRIV_SYS_RESOURCE)) {
1770 /* Keep track of original privileges */
1771 old_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid);
1772 if (old_prpriv == NULL) {
1773 preserve_error(gettext("cannot get process "
1774 "privileges for pid %d: %s"),
1775 Pstatus(Pr)->pr_pid, strerror(errno));
1776 proc_free_priv(new_prpriv);
1777 return (1);
1779 (void) priv_addset(eset, PRIV_SYS_RESOURCE);
1780 (void) priv_addset(pset, PRIV_SYS_RESOURCE);
1781 if (Psetflags(Pr, PR_KLC) != 0 ||
1782 Psetpriv(Pr, new_prpriv) != 0) {
1783 preserve_error(gettext("cannot set process "
1784 "privileges for pid %d: %s"),
1785 Pstatus(Pr)->pr_pid, strerror(errno));
1786 (void) Punsetflags(Pr, PR_KLC);
1787 proc_free_priv(new_prpriv);
1788 proc_free_priv(old_prpriv);
1789 return (1);
1793 * If this is a zone.* rctl, it requires more than
1794 * PRIV_SYS_RESOURCE: it wants the process to have global-zone
1795 * credentials. We temporarily grant non-global zone processes
1796 * these credentials, and make sure the process dies if we exit
1797 * unexpectedly.
1799 if (arg_name &&
1800 arg_name_entity == RCENTITY_ZONE &&
1801 getzoneid() == GLOBAL_ZONEID &&
1802 proc_get_psinfo(Pstatus(Pr)->pr_pid, &psinfo) == 0 &&
1803 (oldzoneid = psinfo.pr_zoneid) != GLOBAL_ZONEID) {
1805 * We need to give this process superuser
1806 * ("super-zone") privileges.
1808 * Must never return without setting this back!
1810 if (Psetflags(Pr, PR_KLC) != 0 ||
1811 Psetzoneid(Pr, GLOBAL_ZONEID) < 0) {
1812 preserve_error(gettext(
1813 "cannot set global-zone "
1814 "privileges for pid %d: %s"),
1815 Pstatus(Pr)->pr_pid, strerror(errno));
1817 * We couldn't set the zoneid to begin with, so
1818 * there's no point in warning the user about
1819 * trying to un-set it.
1821 oldzoneid = GLOBAL_ZONEID;
1822 ret = 1;
1823 goto bail;
1827 /* Now, actually populate the rctlblk in the kernel */
1828 if (flags == RCTL_REPLACE) {
1830 * Replace should be a delete followed by an insert. This
1831 * allows us to replace rctl value blocks which match in
1832 * privilege and value, but have updated actions, etc.
1833 * setrctl() doesn't allow a direct replace, but we
1834 * should do the right thing for the user in the command.
1836 if (pr_setrctl(Pr, name, NULL,
1837 old_rblk, RCTL_DELETE)) {
1838 preserve_error(gettext("failed to delete resource "
1839 "control %s for pid %d: %s"), name,
1840 Pstatus(Pr)->pr_pid, strerror(errno));
1841 ret = 1;
1842 goto bail;
1844 if (pr_setrctl(Pr, name, NULL,
1845 new_rblk, RCTL_INSERT)) {
1846 preserve_error(gettext("failed to insert resource "
1847 "control %s for pid %d: %s"), name,
1848 Pstatus(Pr)->pr_pid, strerror(errno));
1849 ret = 1;
1850 goto bail;
1852 } else if (flags == RCTL_INSERT) {
1853 if (pr_setrctl(Pr, name, NULL,
1854 new_rblk, RCTL_INSERT)) {
1855 preserve_error(gettext("failed to create resource "
1856 "control %s for pid %d: %s"), name,
1857 Pstatus(Pr)->pr_pid, strerror(errno));
1858 ret = 1;
1859 goto bail;
1861 } else if (flags == RCTL_DELETE) {
1862 if (pr_setrctl(Pr, name, NULL,
1863 new_rblk, RCTL_DELETE)) {
1864 preserve_error(gettext("failed to delete resource "
1865 "control %s for pid %d: %s"), name,
1866 Pstatus(Pr)->pr_pid, strerror(errno));
1867 ret = 1;
1868 goto bail;
1871 bail:
1872 if (oldzoneid != GLOBAL_ZONEID) {
1873 if (Psetzoneid(Pr, oldzoneid) != 0)
1874 relinquish_failed = B_TRUE;
1876 if (old_prpriv != NULL) {
1877 if (Psetpriv(Pr, old_prpriv) != 0)
1878 relinquish_failed = B_TRUE;
1879 proc_free_priv(old_prpriv);
1881 if (relinquish_failed) {
1883 * If this failed, we can't leave a process hanging
1884 * around with elevated privileges, so we'll have to
1885 * release the process from libproc, knowing that it
1886 * will be killed (since we set PR_KLC).
1888 Pdestroy_agent(Pr);
1889 preserve_error(gettext("cannot relinquish privileges "
1890 "for pid %d. The process was killed."),
1891 Pstatus(Pr)->pr_pid);
1892 } else {
1893 if (Punsetflags(Pr, PR_KLC) != 0)
1894 preserve_error(gettext("cannot relinquish privileges "
1895 "for pid %d. The process was killed."),
1896 Pstatus(Pr)->pr_pid);
1898 proc_free_priv(new_prpriv);
1900 return (ret);
1903 void
1904 print_priv(rctl_priv_t local_priv, char *format)
1906 char pstring[11];
1908 switch (local_priv) {
1909 case RCPRIV_BASIC:
1910 (void) strcpy(pstring, "basic");
1911 break;
1912 case RCPRIV_PRIVILEGED:
1913 (void) strcpy(pstring, "privileged");
1914 break;
1915 case RCPRIV_SYSTEM:
1916 (void) strcpy(pstring, "system");
1917 break;
1918 default:
1919 (void) sprintf(pstring, "%d", local_priv);
1920 break;
1922 /* LINTED */
1923 (void) fprintf(stdout, format, pstring);
1926 void
1927 print_local_action(int action, int *signalp, char *format)
1929 char sig[SIG2STR_MAX];
1930 char sigstring[SIG2STR_MAX + 7];
1931 char astring[5 + SIG2STR_MAX + 7];
1932 int set = 0;
1934 astring[0] = '\0';
1936 if (action == RCTL_LOCAL_NOACTION) {
1937 (void) strcat(astring, "none");
1938 set++;
1940 if (action & RCTL_LOCAL_DENY) {
1941 (void) strcat(astring, "deny");
1942 set++;
1944 if ((action & RCTL_LOCAL_DENY) &&
1945 (action & RCTL_LOCAL_SIGNAL)) {
1946 (void) strcat(astring, ",");
1948 if (action & RCTL_LOCAL_SIGNAL) {
1949 if (sig2str(*signalp, sig))
1950 (void) snprintf(sigstring, sizeof (astring),
1951 "signal=%d", *signalp);
1952 else
1953 (void) snprintf(sigstring, sizeof (astring),
1954 "signal=%s", sig);
1956 (void) strcat(astring, sigstring);
1957 set++;
1959 if (set)
1960 /* LINTED */
1961 (void) fprintf(stdout, format, astring);
1962 else
1963 /* LINTED */
1964 (void) fprintf(stdout, format, action);
1968 * This function is used to grab the process matching the recipient pid
1970 pid_t
1971 regrab_process(pid_t pid, pr_info_handle_t *p, int priv, int *gret)
1974 char pidstring[24];
1976 gret = 0;
1977 if (pid == -1)
1978 return (p->pid);
1979 if (p->pid == pid)
1980 return (p->pid);
1982 release_process(p->pr);
1983 (void) memset(p, 0, sizeof (*p));
1985 (void) snprintf(pidstring, 24, "%d", pid);
1986 return (grab_process_by_id(
1987 pidstring, RCENTITY_PROCESS, p, priv, gret));
1991 * int grab_process_by_id(char *, rctl_entity_t, pr_info_handle_t *, int, int *)
1993 * Input
1994 * Supply a non-NULL string containing:
1995 * - logical project/zone name or project/zone number if type is
1996 * RCENTITY_PROJECT or RCENTITY_ZONE
1997 * - task number if type is RCENTITY_TYPE
1998 * - a pid if type is RCENTITY_PID
1999 * Also supply an un-allocated prochandle, and an allocated info_handle.
2000 * This function assumes that the type is set.
2001 * If priv is not RCPRIV_BASIC, the grabbed process is required to have
2002 * PRIV_SYS_RESOURCE in it's limit set.
2004 * Return Values
2005 * Returns 0 on success and 1 on failure. If there is a process
2006 * running under the specified id, success is returned, and
2007 * Pr is pointed to the process. Success will be returned and Pr
2008 * set to NULL if the matching process is our own.
2009 * If success is returned, psinfo will be valid, and pid will
2010 * be the process number. The process will also be held at the
2011 * end, so release_process should be used by the caller.
2013 * This function assumes that signals are caught already so that libproc
2014 * can be safely used.
2016 * Return Values
2017 * pid - Process found and grabbed
2018 * -1 - Error
2020 pid_t
2021 grab_process_by_id(char *idname, rctl_entity_t type, pr_info_handle_t *p,
2022 int priv, int *gret)
2024 char prbuf[PROJECT_BUFSZ];
2025 projid_t projid;
2026 taskid_t taskid;
2027 zoneid_t zoneid;
2028 zoneid_t zone_self;
2029 struct project proj;
2030 DIR *dirp;
2031 struct dirent *dentp;
2032 int found = 0;
2033 int pid_self;
2034 int ret;
2035 int gret_in;
2036 int intidname;
2037 char *end;
2038 prpriv_t *prpriv;
2039 priv_set_t *prset;
2041 gret_in = *gret;
2043 /* get our pid se we do not try to operate on self */
2044 pid_self = getpid();
2046 /* Store integer version of id */
2047 intidname = strtoul(idname, &end, 10);
2048 if (errno || *end != '\0' || end == idname) {
2049 intidname = -1;
2053 * get our zoneid so we don't try to operate on a project in
2054 * another zone
2056 zone_self = getzoneid();
2058 if (idname == NULL || strcmp(idname, "") == 0) {
2059 warn(gettext("id name cannot be nuint64\n"));
2060 return (-1);
2063 * Set up zoneid, projid or taskid, as appropriate, so that comparisons
2064 * can be done later with the input.
2066 if (type == RCENTITY_ZONE) {
2067 if (zone_get_id(idname, &zoneid) != 0) {
2068 warn(gettext("%s: unknown zone\n"), idname);
2069 return (-1);
2071 } else if (type == RCENTITY_PROJECT) {
2072 if (getprojbyname(idname, &proj, prbuf, PROJECT_BUFSZ)
2073 == NULL) {
2074 if (getprojbyid(intidname, &proj, prbuf,
2075 PROJECT_BUFSZ) == NULL) {
2076 warn(gettext("%s: cannot find project\n"),
2077 idname);
2078 return (-1);
2081 projid = proj.pj_projid;
2082 } else if (type == RCENTITY_TASK) {
2083 taskid = (taskid_t)atol(idname);
2086 * Projects and tasks need to search through /proc for
2087 * a parent process.
2089 if (type == RCENTITY_ZONE || type == RCENTITY_PROJECT ||
2090 type == RCENTITY_TASK) {
2091 if ((dirp = opendir("/proc")) == NULL) {
2092 warn(gettext("%s: cannot open /proc directory\n"),
2093 idname);
2094 return (-1);
2097 * Look through all processes in /proc. For each process,
2098 * check if the pr_projid in their psinfo matches the
2099 * specified id.
2101 while (dentp = readdir(dirp)) {
2102 p->pid = atoi(dentp->d_name);
2104 /* Skip self */
2105 if (p->pid == pid_self)
2106 continue;
2108 if (proc_get_psinfo(p->pid, &(p->psinfo)) != 0)
2109 continue;
2111 /* Skip process if it is not what we are looking for */
2112 if (type == RCENTITY_ZONE &&
2113 (p->psinfo).pr_zoneid != zoneid) {
2114 continue;
2115 } else if (type == RCENTITY_PROJECT &&
2116 ((p->psinfo).pr_projid != projid ||
2117 (p->psinfo).pr_zoneid != zone_self)) {
2118 continue;
2119 } else if (type == RCENTITY_TASK &&
2120 (p->psinfo).pr_taskid != taskid) {
2121 continue;
2123 /* attempt to grab process */
2124 if (grab_process(p, gret) != 0)
2125 continue;
2128 * Re-confirm that this process is still running as
2129 * part of the specified project or task. If it
2130 * doesn't match, release the process and return an
2131 * error. This should only be done if the Pr struct is
2132 * not NULL.
2134 if (type == RCENTITY_PROJECT) {
2135 if (pr_getprojid(p->pr) != projid ||
2136 pr_getzoneid(p->pr) != zone_self) {
2137 release_process(p->pr);
2138 continue;
2140 } else if (type == RCENTITY_TASK) {
2141 if (pr_gettaskid(p->pr) != taskid) {
2142 release_process(p->pr);
2143 continue;
2145 } else if (type == RCENTITY_ZONE) {
2146 if (pr_getzoneid(p->pr) != zoneid) {
2147 release_process(p->pr);
2148 continue;
2153 * If we are setting a privileged resource control,
2154 * verify that process has PRIV_SYS_RESOURCE in it's
2155 * limit set. If it does not, then we will not be
2156 * able to give this process the privilege it needs
2157 * to set the resource control.
2159 if (priv != RCPRIV_BASIC) {
2160 prpriv = proc_get_priv(p->pid);
2161 if (prpriv == NULL) {
2162 release_process(p->pr);
2163 continue;
2165 prset = (priv_set_t *)
2166 &prpriv->pr_sets[prpriv->pr_setsize *
2167 priv_getsetbyname(PRIV_LIMIT)];
2168 if (!priv_ismember(prset, PRIV_SYS_RESOURCE)) {
2169 proc_free_priv(prpriv);
2170 release_process(p->pr);
2171 continue;
2173 proc_free_priv(prpriv);
2175 found = 1;
2177 p->taskid = pr_gettaskid(p->pr);
2178 p->projid = pr_getprojid(p->pr);
2179 p->zoneid = pr_getzoneid(p->pr);
2181 break;
2183 (void) closedir(dirp);
2185 if (found == 0) {
2186 warn(gettext("%s: No controllable process found in "
2187 "task, project, or zone.\n"), idname);
2188 return (-1);
2190 return (p->pid);
2192 } else if (type == RCENTITY_PROCESS) {
2194 /* fail if self */
2195 if (p->pid == pid_self) {
2197 warn(gettext("%s: cannot control self"), idname);
2198 return (-1);
2201 * Process types need to be set up with the correct pid
2202 * and psinfo structure.
2204 if ((p->pid = proc_arg_psinfo(idname, PR_ARG_PIDS,
2205 &(p->psinfo), gret)) == -1) {
2206 warn(gettext("%s: cannot examine: %s"), idname,
2207 Pgrab_error(*gret));
2208 return (-1);
2210 /* grab process */
2211 ret = grab_process(p, gret);
2212 if (ret == 1) {
2213 /* Don't print error if G_SYS is allowed */
2214 if (gret_in == G_SYS && *gret == G_SYS) {
2215 return (-1);
2216 } else {
2217 warn(gettext("%s: cannot control: %s"), idname,
2218 Pgrab_error(*gret));
2219 return (-1);
2221 } else if (ret == 2) {
2222 ret = errno;
2223 warn(gettext("%s: cannot control: %s"), idname,
2224 strerror(ret));
2225 return (-1);
2227 p->taskid = pr_gettaskid(p->pr);
2228 p->projid = pr_getprojid(p->pr);
2229 p->zoneid = pr_getzoneid(p->pr);
2231 return (p->pid);
2233 } else {
2234 warn(gettext("%s: unknown resource entity type %d\n"), idname,
2235 type);
2236 return (-1);
2241 * Do the work required to manipulate a process through libproc.
2242 * If grab_process() returns no errors (0), then release_process()
2243 * must eventually be called.
2245 * Return values:
2246 * 0 Successful creation of agent thread
2247 * 1 Error grabbing
2248 * 2 Error creating agent
2251 grab_process(pr_info_handle_t *p, int *gret)
2254 if ((p->pr = Pgrab(p->pid, arg_force, gret)) != NULL) {
2256 if (Psetflags(p->pr, PR_RLC) != 0) {
2257 Prelease(p->pr, 0);
2258 return (1);
2260 if (Pcreate_agent(p->pr) == 0) {
2261 return (0);
2263 } else {
2264 Prelease(p->pr, 0);
2265 return (2);
2267 } else {
2268 return (1);
2273 * Release the specified process. This destroys the agent
2274 * and releases the process. If the process is NULL, nothing
2275 * is done. This function should only be called if grab_process()
2276 * has previously been called and returned success.
2278 * This function is Pgrab-safe.
2280 void
2281 release_process(struct ps_prochandle *Pr)
2283 if (Pr == NULL)
2284 return;
2286 Pdestroy_agent(Pr);
2287 Prelease(Pr, 0);
2291 * preserve_error(char *, ...)
2293 * preserve_error() should be called rather than warn() by any
2294 * function that is called while the victim process is held by Pgrab.
2295 * It will save the error until the process has been un-controlled
2296 * and output is reasonable again.
2298 * Note that multiple errors are not stored. Any error in these
2299 * sections should be critical and return immediately.
2301 * This function is Pgrab-safe.
2303 * Since this function may copy untrusted command line arguments to
2304 * global_error, security practices require that global_error never be
2305 * printed directly. Use printf("%s\n", global_error) or equivalent.
2307 /*PRINTFLIKE1*/
2308 void
2309 preserve_error(char *format, ...)
2311 va_list alist;
2313 va_start(alist, format);
2316 * GLOBAL_ERR_SZ is pretty big. If the error is longer
2317 * than that, just truncate it, rather than chance missing
2318 * the error altogether.
2320 (void) vsnprintf(global_error, GLOBAL_ERR_SZ-1, format, alist);
2322 va_end(alist);