8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / prctl / prctl.c
blob1dfbe4192663e564033b1b962bcfc4b8df33721b
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 if (rctlblkA)
981 free(rctlblkA);
982 if (rctlblkB)
983 free(rctlblkB);
985 /* Print any errors that occurred */
986 if (errflg && *global_error != '\0') {
987 proc_unctrl_psinfo(&(p.psinfo));
988 (void) fprintf(stderr, "%d:\t%.70s\n",
989 (int)p.pid, p.psinfo.pr_psargs);
990 warn("%s\n", global_error);
991 break;
993 } else {
995 struct project projent;
996 char buf[PROJECT_BUFSZ];
997 char zonename[ZONENAME_MAX];
1000 * Hack to allow the user to specify a system
1001 * process.
1003 gret = G_SYS;
1004 pid = grab_process_by_id(
1005 target_id, search_type, &p, RCPRIV_BASIC, &gret);
1008 * Print system process if user chose specifically
1009 * to inspect a system process.
1011 if (arg_entity_type == RCENTITY_PROCESS &&
1012 pid < 0 &&
1013 gret == G_SYS) {
1015 * Add blank lines between output for
1016 * operands.
1018 if (printed) {
1019 (void) fprintf(stdout, "\n");
1022 proc_unctrl_psinfo(&(p.psinfo));
1023 (void) printf(
1024 "process: %d: %s [ system process ]\n",
1025 (int)p.pid, p.psinfo.pr_psargs);
1027 printed = 1;
1028 continue;
1030 } else if (pid < 0) {
1033 * Mark that an error occurred so that the
1034 * return value can be set, but continue
1035 * on with other processes
1037 errflg = 1;
1038 continue;
1041 errflg = get_rctls(p.pr);
1043 release_process(p.pr);
1045 /* handle user interrupt of getting rctls */
1046 if (interrupt)
1047 break;
1049 /* add blank lines between output for operands */
1050 if (printed) {
1051 (void) fprintf(stdout, "\n");
1053 /* First print any errors */
1054 if (errflg) {
1055 warn("%s\n", global_error);
1056 free_lists();
1057 break;
1059 if (getprojbyid(p.projid, &projent, buf,
1060 sizeof (buf))) {
1061 p.projname = projent.pj_name;
1062 } else {
1063 p.projname = "";
1065 if (getzonenamebyid(p.zoneid, zonename,
1066 sizeof (zonename)) > 0) {
1067 p.zonename = zonename;
1068 } else {
1069 p.zonename = "";
1071 print_rctls(&p);
1072 printed = 1;
1073 /* Free the resource control lists */
1074 free_lists();
1077 if (interrupt)
1078 errflg = 1;
1081 * return error if one occurred
1083 return (errflg);
1087 static void
1088 intr(int sig)
1090 interrupt = sig;
1094 * get_rctls(struct ps_prochandle *, const char *)
1096 * If controlname is given, store only controls for that named
1097 * resource. If controlname is NULL, store all controls for all
1098 * resources.
1100 * This function is Pgrab-safe.
1102 static int
1103 get_rctls(struct ps_prochandle *Pr)
1105 int ret = 0;
1107 if (arg_name == NULL) {
1108 if (rctl_walk(store_rctls, Pr) != 0)
1109 ret = 1;
1110 } else {
1111 ret = store_rctls(arg_name, Pr);
1113 return (ret);
1117 * store_rctls(const char *, void *)
1119 * Store resource controls for the given name in a linked list.
1120 * Honor the user's options, and store only the ones they are
1121 * interested in. If priv is not 0, show only controls that match
1122 * the given privilege.
1124 * This function is Pgrab-safe
1126 static int
1127 store_rctls(const char *rctlname, void *walk_data)
1129 struct ps_prochandle *Pr = walk_data;
1130 rctlblk_t *rblk2, *rblk_tmp, *rblk1 = NULL;
1131 prctl_list_t *list = NULL;
1132 rctl_priv_t rblk_priv;
1133 rctl_entity_t rblk_entity;
1135 if (((rblk1 = calloc(1, rctlblk_size())) == NULL) ||
1136 ((rblk2 = calloc(1, rctlblk_size())) == NULL)) {
1137 if (rblk1 != NULL)
1138 free(rblk1);
1139 preserve_error(gettext("malloc failed: %s"),
1140 strerror(errno));
1141 return (1);
1143 if (pr_getrctl(Pr, rctlname, NULL, rblk1, RCTL_FIRST)) {
1144 preserve_error(gettext("failed to get resource control "
1145 "for %s: %s"), rctlname, strerror(errno));
1146 free(rblk1);
1147 free(rblk2);
1148 return (1);
1150 /* Store control if it matches privilege and enity type criteria */
1151 rblk_priv = rctlblk_get_privilege(rblk1);
1152 rblk_entity = 0;
1153 if (strncmp(rctlname, "process.",
1154 strlen("process.")) == 0)
1155 rblk_entity = RCENTITY_PROCESS;
1156 else if (strncmp(rctlname, "project.",
1157 strlen("project.")) == 0)
1158 rblk_entity = RCENTITY_PROJECT;
1159 else if (strncmp(rctlname, "task.",
1160 strlen("task.")) == 0)
1161 rblk_entity = RCENTITY_TASK;
1162 else if (strncmp(rctlname, "zone.",
1163 strlen("zone.")) == 0)
1164 rblk_entity = RCENTITY_ZONE;
1166 if (((arg_priv == 0) || (rblk_priv == arg_priv)) &&
1167 ((arg_name == NULL) ||
1168 strncmp(rctlname, arg_name, strlen(arg_name)) == 0) &&
1169 (arg_entity_string == NULL || rblk_entity >= arg_entity_type)) {
1171 /* Once we know we have some controls, store the name */
1172 if ((list = store_list_entry(rctlname)) == NULL) {
1173 free(rblk1);
1174 free(rblk2);
1175 return (1);
1177 if (store_value_entry(rblk1, list) == NULL) {
1178 free(rblk1);
1179 free(rblk2);
1180 return (1);
1183 while (pr_getrctl(Pr, rctlname, rblk1, rblk2, RCTL_NEXT) == 0) {
1186 * in case this is stuck for some reason, allow manual
1187 * interrupt
1189 if (interrupt) {
1190 free(rblk1);
1191 free(rblk2);
1192 return (1);
1194 rblk_priv = rctlblk_get_privilege(rblk2);
1196 * Store control if it matches privilege and entity type
1197 * criteria
1199 if (((arg_priv == 0) || (rblk_priv == arg_priv)) &&
1200 ((arg_name == NULL) ||
1201 strncmp(rctlname, arg_name, strlen(arg_name)) == 0) &&
1202 (arg_entity_string == NULL ||
1203 rblk_entity == arg_entity_type)) {
1205 /* May not have created the list yet. */
1206 if (list == NULL) {
1207 if ((list = store_list_entry(rctlname))
1208 == NULL) {
1209 free(rblk1);
1210 free(rblk2);
1211 return (1);
1214 if (store_value_entry(rblk2, list) == NULL) {
1215 free(rblk1);
1216 free(rblk2);
1217 return (1);
1220 rblk_tmp = rblk1;
1221 rblk1 = rblk2;
1222 rblk2 = rblk_tmp;
1226 * Get the current usage for the resource control if it matched the
1227 * privilege and entity type criteria.
1229 if (list != NULL) {
1230 if (pr_getrctl(Pr, rctlname, NULL, rblk2, RCTL_USAGE) == 0) {
1231 list->usage = (rctl_qty_t *)malloc(sizeof (rctl_qty_t));
1232 if (list->usage == NULL) {
1233 preserve_error(gettext("malloc failed: %s"),
1234 strerror(errno));
1235 free(rblk1);
1236 free(rblk2);
1237 return (1);
1239 *list->usage = rctlblk_get_value(rblk2);
1240 } else {
1241 list->usage = NULL;
1242 if (errno != ENOTSUP) {
1243 preserve_error(gettext("failed to get "
1244 "resource control usage for %s: %s"),
1245 rctlname, strerror(errno));
1246 free(rblk1);
1247 free(rblk2);
1248 return (1);
1252 free(rblk1);
1253 free(rblk2);
1254 return (0);
1258 * store_value_entry(rctlblk_t *, prctl_list_t *)
1260 * Store an rblk for a given resource control into the global list.
1262 * This function is Pgrab-safe.
1264 prctl_value_t *
1265 store_value_entry(rctlblk_t *rblk, prctl_list_t *list)
1267 prctl_value_t *e = calloc(1, sizeof (prctl_value_t));
1268 rctlblk_t *store_blk = calloc(1, rctlblk_size());
1269 prctl_value_t *iter = list->val_list;
1271 if (e == NULL || store_blk == NULL) {
1272 preserve_error(gettext("malloc failed %s"),
1273 strerror(errno));
1274 if (e != NULL)
1275 free(e);
1276 if (store_blk != NULL)
1277 free(store_blk);
1278 return (NULL);
1280 if (iter == NULL)
1281 list->val_list = e;
1282 else {
1283 while (iter->next != NULL) {
1284 iter = iter->next;
1286 iter->next = e;
1288 bcopy(rblk, store_blk, rctlblk_size());
1290 e->rblk = store_blk;
1291 e->next = NULL;
1292 return (e);
1296 * store_list_entry(const char *)
1298 * Store a new resource control value in the global list. No checking
1299 * for duplicates done.
1301 * This function is Pgrab-safe.
1303 prctl_list_t *
1304 store_list_entry(const char *name)
1306 prctl_list_t *e = calloc(1, sizeof (prctl_list_t));
1308 if (e == NULL) {
1309 preserve_error(gettext("malloc failed %s"),
1310 strerror(errno));
1311 return (NULL);
1313 if ((e->name = calloc(1, strlen(name) + 1)) == NULL) {
1314 preserve_error(gettext("malloc failed %s"),
1315 strerror(errno));
1316 free(e);
1317 return (NULL);
1319 (void) strcpy(e->name, name);
1320 e->val_list = NULL;
1322 if (global_rctl_list_head == NULL) {
1323 global_rctl_list_head = e;
1324 global_rctl_list_tail = e;
1325 } else {
1326 global_rctl_list_tail->next = e;
1327 global_rctl_list_tail = e;
1329 e->next = NULL;
1330 return (e);
1334 * free_lists()
1336 * Free all resource control blocks and values from the global lists.
1338 * This function is Pgrab-safe.
1340 void
1341 free_lists()
1343 prctl_list_t *new_list, *old_list = global_rctl_list_head;
1344 prctl_value_t *old_val, *new_val;
1346 while (old_list != NULL) {
1347 old_val = old_list->val_list;
1348 while (old_val != NULL) {
1349 free(old_val->rblk);
1350 new_val = old_val->next;
1351 free(old_val);
1352 old_val = new_val;
1354 free(old_list->name);
1355 free(old_list->usage);
1356 new_list = old_list->next;
1357 free(old_list);
1358 old_list = new_list;
1360 global_rctl_list_head = NULL;
1361 global_rctl_list_tail = NULL;
1364 void
1365 print_heading()
1368 /* print headings */
1369 (void) fprintf(stdout, "%-8s%-16s%-9s%-7s%-28s%10s\n",
1370 "NAME", "PRIVILEGE", "VALUE",
1371 "FLAG", "ACTION", "RECIPIENT");
1375 * print_rctls()
1377 * Print all resource controls from the global list that was
1378 * previously populated by store_rctls.
1380 void
1381 print_rctls(pr_info_handle_t *p)
1383 prctl_list_t *iter_list = global_rctl_list_head;
1384 prctl_value_t *iter_val;
1385 rctl_qty_t rblk_value;
1386 rctl_priv_t rblk_priv;
1387 uint_t local_action;
1388 int signal, local_flags, global_flags;
1389 pid_t pid;
1390 char rctl_valuestring[SCALED_STRLEN];
1391 char *unit = NULL;
1392 scale_t *scale;
1393 char *string;
1394 int doneheading = 0;
1396 if (iter_list == NULL)
1397 return;
1399 while (iter_list != NULL) {
1401 if (doneheading == 0 &&
1402 arg_entity_type == RCENTITY_PROCESS) {
1403 proc_unctrl_psinfo(&(p->psinfo));
1404 doneheading = 1;
1405 (void) fprintf(stdout,
1406 "process: %d: %.70s\n", (int)p->pid,
1407 p->psinfo.pr_psargs);
1408 if (!arg_parseable_mode)
1409 print_heading();
1411 if (doneheading == 0 &&
1412 arg_entity_type == RCENTITY_TASK) {
1413 doneheading = 1;
1414 (void) fprintf(stdout, "task: %d\n", (int)p->taskid);
1415 if (!arg_parseable_mode)
1416 print_heading();
1418 if (doneheading == 0 &&
1419 arg_entity_type == RCENTITY_PROJECT) {
1420 if (!arg_parseable_mode && doneheading)
1421 (void) fprintf(stdout, "\n");
1422 doneheading = 1;
1423 (void) fprintf(stdout,
1424 "project: %d: %.70s\n", (int)p->projid,
1425 p->projname);
1426 if (!arg_parseable_mode)
1427 print_heading();
1429 if (doneheading == 0 &&
1430 arg_entity_type == RCENTITY_ZONE) {
1431 doneheading = 1;
1432 (void) fprintf(stdout,
1433 "zone: %d: %.70s\n", (int)p->zoneid,
1434 p->zonename);
1435 if (!arg_parseable_mode)
1436 print_heading();
1438 /* only print name once in normal output */
1439 if (!arg_parseable_mode)
1440 (void) fprintf(stdout, "%s\n", iter_list->name);
1442 iter_val = iter_list->val_list;
1444 /* if for some reason there are no values, skip */
1445 if (iter_val == 0)
1446 continue;
1449 /* get the global flags the first rctl only */
1450 global_flags = rctlblk_get_global_flags(iter_val->rblk);
1453 if (global_flags & RCTL_GLOBAL_BYTES) {
1454 unit = SCALED_UNIT_BYTES;
1455 scale = scale_binary;
1457 } else if (global_flags & RCTL_GLOBAL_SECONDS) {
1458 unit = SCALED_UNIT_SECONDS;
1459 scale = scale_metric;
1461 } else {
1462 unit = SCALED_UNIT_NONE;
1463 scale = scale_metric;
1466 /* print the current usage for the rctl if available */
1467 if (iter_list->usage != NULL) {
1468 rblk_value = *(iter_list->usage);
1469 if (!arg_parseable_mode) {
1470 (void) uint64toscaled(rblk_value, 4, "E",
1471 rctl_valuestring, NULL, NULL,
1472 scale, NULL, 0);
1474 (void) fprintf(stdout, "%8s%-16s%5s%-4s\n",
1475 "", "usage", rctl_valuestring, unit);
1476 } else {
1477 (void) fprintf(stdout, "%s %s %llu - - -\n",
1478 iter_list->name, "usage", rblk_value);
1482 /* iterate over an print all control values */
1483 while (iter_val != NULL) {
1485 /* print name or empty name field */
1486 if (!arg_parseable_mode)
1487 (void) fprintf(stdout, "%8s", "");
1488 else
1489 (void) fprintf(stdout, "%s ", iter_list->name);
1492 rblk_priv = rctlblk_get_privilege(iter_val->rblk);
1493 if (!arg_parseable_mode)
1494 print_priv(rblk_priv, "%-16s");
1495 else
1496 print_priv(rblk_priv, "%s ");
1498 rblk_value = rctlblk_get_value(iter_val->rblk);
1499 if (arg_parseable_mode) {
1500 (void) fprintf(stdout, "%llu ", rblk_value);
1502 } else {
1504 (void) uint64toscaled(rblk_value, 4, "E",
1505 rctl_valuestring, NULL, NULL,
1506 scale, NULL, 0);
1508 (void) fprintf(stdout, "%5s",
1509 rctl_valuestring);
1510 (void) fprintf(stdout, "%-4s", unit);
1512 local_flags = rctlblk_get_local_flags(iter_val->rblk);
1514 if (local_flags & RCTL_LOCAL_MAXIMAL) {
1516 if (global_flags & RCTL_GLOBAL_INFINITE) {
1517 string = "inf";
1518 } else {
1519 string = "max";
1521 } else {
1522 string = "-";
1524 if (arg_parseable_mode)
1525 (void) fprintf(stdout, "%s ", string);
1526 else
1527 (void) fprintf(stdout, "%4s%3s",
1528 string, "");
1531 local_action = rctlblk_get_local_action(iter_val->rblk,
1532 &signal);
1534 if (arg_parseable_mode)
1535 print_local_action(local_action, &signal,
1536 "%s ");
1537 else
1538 print_local_action(local_action, &signal,
1539 "%-28s");
1541 pid = rctlblk_get_recipient_pid(iter_val->rblk);
1543 if (arg_parseable_mode) {
1544 if (pid < 0) {
1545 (void) fprintf(stdout, "%s\n", "-");
1546 } else {
1547 (void) fprintf(stdout, "%d\n",
1548 (int)pid);
1550 } else {
1551 if (pid < 0) {
1552 (void) fprintf(stdout, "%10s\n", "-");
1553 } else {
1554 (void) fprintf(stdout, "%10d\n",
1555 (int)pid);
1558 iter_val = iter_val->next;
1560 iter_list = iter_list->next;
1566 * match_rctl
1568 * find the first rctl with matching name, value, priv, and recipient pid
1571 match_rctl(struct ps_prochandle *Pr, rctlblk_t **rctl, char *name,
1572 char *valuestringin, int valuein, rctl_priv_t privin, int pidin)
1574 rctlblk_t *next;
1575 rctlblk_t *last;
1576 rctlblk_t *tmp;
1578 *rctl = NULL;
1580 next = calloc(1, rctlblk_size());
1581 last = calloc(1, rctlblk_size());
1583 if ((last == NULL) || (next == NULL)) {
1584 preserve_error(gettext("malloc failed"), strerror(errno));
1585 return (-1);
1588 * For this resource name, now iterate through all
1589 * the controls, looking for a match to the
1590 * user-specified input.
1592 if (pr_getrctl(Pr, name, NULL, next, RCTL_FIRST)) {
1593 preserve_error(gettext("failed to get resource control "
1594 "for %s: %s"), name, strerror(errno));
1595 return (-1);
1597 if (match_rctl_blk(next, valuestringin, valuein, privin, pidin) == 1) {
1598 free(last);
1599 *rctl = next;
1600 return (0);
1602 tmp = next;
1603 next = last;
1604 last = tmp;
1606 while (pr_getrctl(Pr, name, last, next, RCTL_NEXT) == 0) {
1608 /* allow user interrupt */
1609 if (interrupt)
1610 break;
1612 if (match_rctl_blk(next, valuestringin, valuein, privin, pidin)
1613 == 1) {
1614 free(last);
1615 *rctl = next;
1616 return (0);
1618 tmp = next;
1619 next = last;
1620 last = tmp;
1622 free(next);
1623 free(last);
1625 return (1);
1629 * int match_rctl_blk(rctlblk_t *, char *, uint64, rctl_priv_t, int pid)
1631 * Input
1632 * Must supply a valid rctl, value, privilege, and pid to match on.
1633 * If valuestring is NULL, then valuestring and valuein will not be used
1634 * If privilege type is 0 it will not be used.
1635 * If pid is -1 it will not be used.
1637 * Return values
1638 * Returns 1 if a matching rctl given matches the parameters specified, and
1639 * 0 if they do not.
1641 * This function is Pgrab-safe.
1644 match_rctl_blk(rctlblk_t *rctl, char *valuestringin,
1645 uint64_t valuein, rctl_priv_t privin, int pidin)
1648 rctl_qty_t value;
1649 rctl_priv_t priv;
1650 pid_t pid;
1651 int valuematch = 1;
1652 int privmatch = 1;
1653 int pidmatch = 1;
1655 value = rctlblk_get_value(rctl);
1656 priv = rctlblk_get_privilege(rctl);
1657 pid = rctlblk_get_recipient_pid(rctl);
1659 if (valuestringin) {
1661 if (arg_modifier == NULL) {
1662 valuematch = (valuein == value);
1663 } else {
1664 valuematch = scaledequint64(valuestringin, value,
1665 PRCTL_VALUE_WIDTH,
1666 arg_scale, arg_unit,
1667 SCALED_ALL_FLAGS);
1670 if (privin != 0) {
1671 privmatch = (privin == priv);
1673 if (pidin != -1) {
1674 pidmatch = (pidin == pid);
1676 return (valuematch && privmatch && pidmatch);
1679 static int
1680 change_action(rctlblk_t *blk)
1682 int signal = 0;
1683 int action;
1685 action = rctlblk_get_local_action(blk, &signal);
1687 if (arg_operation & ACTION_ENABLE) {
1689 if (arg_action & RCTL_LOCAL_SIGNAL) {
1690 signal = arg_signal;
1692 action = action | arg_action;
1693 /* add local action */
1694 rctlblk_set_local_action(blk, action, signal);
1696 } else if (arg_operation & ACTION_DISABLE) {
1699 * if deleting signal and signal number is specified,
1700 * then signal number must match
1702 if ((arg_action & RCTL_LOCAL_SIGNAL) &&
1703 (arg_signal != -1)) {
1704 if (arg_signal != signal) {
1705 preserve_error(gettext("signal name or number "
1706 "does not match existing action"));
1707 return (-1);
1710 /* remove local action */
1711 action = action & (~arg_action);
1712 rctlblk_set_local_action(blk, RCTL_LOCAL_NOACTION, 0);
1713 rctlblk_set_local_action(blk, action, signal);
1715 /* enable deny if it must be enabled */
1716 if (arg_global_flags & RCTL_GLOBAL_DENY_ALWAYS) {
1717 rctlblk_set_local_action(blk, RCTL_LOCAL_DENY | action,
1718 signal);
1720 return (0);
1724 * prctl_setrctl
1726 * Input
1727 * This function expects that input has been validated. In the
1728 * case of a replace operation, both old_rblk and new_rblk must
1729 * be valid resource controls. If a resource control is being
1730 * created, only new_rblk must be supplied. If a resource control
1731 * is being deleted, only new_rblk must be supplied.
1733 * If the privilege is a priviliged type, at this time, the process
1734 * tries to take on superuser privileges.
1737 prctl_setrctl(struct ps_prochandle *Pr, const char *name,
1738 rctlblk_t *old_rblk, rctlblk_t *new_rblk, uint_t flags)
1740 int ret = 0;
1741 rctl_priv_t rblk_priv;
1742 psinfo_t psinfo;
1743 zoneid_t oldzoneid = GLOBAL_ZONEID;
1744 prpriv_t *old_prpriv = NULL, *new_prpriv = NULL;
1745 priv_set_t *eset, *pset;
1746 boolean_t relinquish_failed = B_FALSE;
1748 rblk_priv = rctlblk_get_privilege(new_rblk);
1750 if (rblk_priv == RCPRIV_SYSTEM) {
1751 preserve_error(gettext("cannot modify system values"));
1752 return (1);
1754 if (rblk_priv == RCPRIV_PRIVILEGED) {
1755 new_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid);
1756 if (new_prpriv == NULL) {
1757 preserve_error(gettext("cannot get process privileges "
1758 "for pid %d: %s"), Pstatus(Pr)->pr_pid,
1759 strerror(errno));
1760 return (1);
1763 * We only have to change the process privileges if it doesn't
1764 * already have PRIV_SYS_RESOURCE. In addition, we want to make
1765 * sure that we don't leave a process with elevated privileges,
1766 * so we make sure the process dies if we exit unexpectedly.
1768 eset = (priv_set_t *)
1769 &new_prpriv->pr_sets[new_prpriv->pr_setsize *
1770 priv_getsetbyname(PRIV_EFFECTIVE)];
1771 pset = (priv_set_t *)
1772 &new_prpriv->pr_sets[new_prpriv->pr_setsize *
1773 priv_getsetbyname(PRIV_PERMITTED)];
1774 if (!priv_ismember(eset, PRIV_SYS_RESOURCE)) {
1775 /* Keep track of original privileges */
1776 old_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid);
1777 if (old_prpriv == NULL) {
1778 preserve_error(gettext("cannot get process "
1779 "privileges for pid %d: %s"),
1780 Pstatus(Pr)->pr_pid, strerror(errno));
1781 proc_free_priv(new_prpriv);
1782 return (1);
1784 (void) priv_addset(eset, PRIV_SYS_RESOURCE);
1785 (void) priv_addset(pset, PRIV_SYS_RESOURCE);
1786 if (Psetflags(Pr, PR_KLC) != 0 ||
1787 Psetpriv(Pr, new_prpriv) != 0) {
1788 preserve_error(gettext("cannot set process "
1789 "privileges for pid %d: %s"),
1790 Pstatus(Pr)->pr_pid, strerror(errno));
1791 (void) Punsetflags(Pr, PR_KLC);
1792 proc_free_priv(new_prpriv);
1793 proc_free_priv(old_prpriv);
1794 return (1);
1798 * If this is a zone.* rctl, it requires more than
1799 * PRIV_SYS_RESOURCE: it wants the process to have global-zone
1800 * credentials. We temporarily grant non-global zone processes
1801 * these credentials, and make sure the process dies if we exit
1802 * unexpectedly.
1804 if (arg_name &&
1805 arg_name_entity == RCENTITY_ZONE &&
1806 getzoneid() == GLOBAL_ZONEID &&
1807 proc_get_psinfo(Pstatus(Pr)->pr_pid, &psinfo) == 0 &&
1808 (oldzoneid = psinfo.pr_zoneid) != GLOBAL_ZONEID) {
1810 * We need to give this process superuser
1811 * ("super-zone") privileges.
1813 * Must never return without setting this back!
1815 if (Psetflags(Pr, PR_KLC) != 0 ||
1816 Psetzoneid(Pr, GLOBAL_ZONEID) < 0) {
1817 preserve_error(gettext(
1818 "cannot set global-zone "
1819 "privileges for pid %d: %s"),
1820 Pstatus(Pr)->pr_pid, strerror(errno));
1822 * We couldn't set the zoneid to begin with, so
1823 * there's no point in warning the user about
1824 * trying to un-set it.
1826 oldzoneid = GLOBAL_ZONEID;
1827 ret = 1;
1828 goto bail;
1832 /* Now, actually populate the rctlblk in the kernel */
1833 if (flags == RCTL_REPLACE) {
1835 * Replace should be a delete followed by an insert. This
1836 * allows us to replace rctl value blocks which match in
1837 * privilege and value, but have updated actions, etc.
1838 * setrctl() doesn't allow a direct replace, but we
1839 * should do the right thing for the user in the command.
1841 if (pr_setrctl(Pr, name, NULL,
1842 old_rblk, RCTL_DELETE)) {
1843 preserve_error(gettext("failed to delete resource "
1844 "control %s for pid %d: %s"), name,
1845 Pstatus(Pr)->pr_pid, strerror(errno));
1846 ret = 1;
1847 goto bail;
1849 if (pr_setrctl(Pr, name, NULL,
1850 new_rblk, RCTL_INSERT)) {
1851 preserve_error(gettext("failed to insert resource "
1852 "control %s for pid %d: %s"), name,
1853 Pstatus(Pr)->pr_pid, strerror(errno));
1854 ret = 1;
1855 goto bail;
1857 } else if (flags == RCTL_INSERT) {
1858 if (pr_setrctl(Pr, name, NULL,
1859 new_rblk, RCTL_INSERT)) {
1860 preserve_error(gettext("failed to create resource "
1861 "control %s for pid %d: %s"), name,
1862 Pstatus(Pr)->pr_pid, strerror(errno));
1863 ret = 1;
1864 goto bail;
1866 } else if (flags == RCTL_DELETE) {
1867 if (pr_setrctl(Pr, name, NULL,
1868 new_rblk, RCTL_DELETE)) {
1869 preserve_error(gettext("failed to delete resource "
1870 "control %s for pid %d: %s"), name,
1871 Pstatus(Pr)->pr_pid, strerror(errno));
1872 ret = 1;
1873 goto bail;
1876 bail:
1877 if (oldzoneid != GLOBAL_ZONEID) {
1878 if (Psetzoneid(Pr, oldzoneid) != 0)
1879 relinquish_failed = B_TRUE;
1881 if (old_prpriv != NULL) {
1882 if (Psetpriv(Pr, old_prpriv) != 0)
1883 relinquish_failed = B_TRUE;
1884 proc_free_priv(old_prpriv);
1886 if (relinquish_failed) {
1888 * If this failed, we can't leave a process hanging
1889 * around with elevated privileges, so we'll have to
1890 * release the process from libproc, knowing that it
1891 * will be killed (since we set PR_KLC).
1893 Pdestroy_agent(Pr);
1894 preserve_error(gettext("cannot relinquish privileges "
1895 "for pid %d. The process was killed."),
1896 Pstatus(Pr)->pr_pid);
1897 } else {
1898 if (Punsetflags(Pr, PR_KLC) != 0)
1899 preserve_error(gettext("cannot relinquish privileges "
1900 "for pid %d. The process was killed."),
1901 Pstatus(Pr)->pr_pid);
1903 if (new_prpriv != NULL)
1904 proc_free_priv(new_prpriv);
1906 return (ret);
1909 void
1910 print_priv(rctl_priv_t local_priv, char *format)
1912 char pstring[11];
1914 switch (local_priv) {
1915 case RCPRIV_BASIC:
1916 (void) strcpy(pstring, "basic");
1917 break;
1918 case RCPRIV_PRIVILEGED:
1919 (void) strcpy(pstring, "privileged");
1920 break;
1921 case RCPRIV_SYSTEM:
1922 (void) strcpy(pstring, "system");
1923 break;
1924 default:
1925 (void) sprintf(pstring, "%d", local_priv);
1926 break;
1928 /* LINTED */
1929 (void) fprintf(stdout, format, pstring);
1932 void
1933 print_local_action(int action, int *signalp, char *format)
1935 char sig[SIG2STR_MAX];
1936 char sigstring[SIG2STR_MAX + 7];
1937 char astring[5 + SIG2STR_MAX + 7];
1938 int set = 0;
1940 astring[0] = '\0';
1942 if (action == RCTL_LOCAL_NOACTION) {
1943 (void) strcat(astring, "none");
1944 set++;
1946 if (action & RCTL_LOCAL_DENY) {
1947 (void) strcat(astring, "deny");
1948 set++;
1950 if ((action & RCTL_LOCAL_DENY) &&
1951 (action & RCTL_LOCAL_SIGNAL)) {
1952 (void) strcat(astring, ",");
1954 if (action & RCTL_LOCAL_SIGNAL) {
1955 if (sig2str(*signalp, sig))
1956 (void) snprintf(sigstring, sizeof (astring),
1957 "signal=%d", *signalp);
1958 else
1959 (void) snprintf(sigstring, sizeof (astring),
1960 "signal=%s", sig);
1962 (void) strcat(astring, sigstring);
1963 set++;
1965 if (set)
1966 /* LINTED */
1967 (void) fprintf(stdout, format, astring);
1968 else
1969 /* LINTED */
1970 (void) fprintf(stdout, format, action);
1974 * This function is used to grab the process matching the recipient pid
1976 pid_t
1977 regrab_process(pid_t pid, pr_info_handle_t *p, int priv, int *gret)
1980 char pidstring[24];
1982 gret = 0;
1983 if (pid == -1)
1984 return (p->pid);
1985 if (p->pid == pid)
1986 return (p->pid);
1988 release_process(p->pr);
1989 (void) memset(p, 0, sizeof (*p));
1991 (void) snprintf(pidstring, 24, "%d", pid);
1992 return (grab_process_by_id(
1993 pidstring, RCENTITY_PROCESS, p, priv, gret));
1997 * int grab_process_by_id(char *, rctl_entity_t, pr_info_handle_t *, int, int *)
1999 * Input
2000 * Supply a non-NULL string containing:
2001 * - logical project/zone name or project/zone number if type is
2002 * RCENTITY_PROJECT or RCENTITY_ZONE
2003 * - task number if type is RCENTITY_TYPE
2004 * - a pid if type is RCENTITY_PID
2005 * Also supply an un-allocated prochandle, and an allocated info_handle.
2006 * This function assumes that the type is set.
2007 * If priv is not RCPRIV_BASIC, the grabbed process is required to have
2008 * PRIV_SYS_RESOURCE in it's limit set.
2010 * Return Values
2011 * Returns 0 on success and 1 on failure. If there is a process
2012 * running under the specified id, success is returned, and
2013 * Pr is pointed to the process. Success will be returned and Pr
2014 * set to NULL if the matching process is our own.
2015 * If success is returned, psinfo will be valid, and pid will
2016 * be the process number. The process will also be held at the
2017 * end, so release_process should be used by the caller.
2019 * This function assumes that signals are caught already so that libproc
2020 * can be safely used.
2022 * Return Values
2023 * pid - Process found and grabbed
2024 * -1 - Error
2026 pid_t
2027 grab_process_by_id(char *idname, rctl_entity_t type, pr_info_handle_t *p,
2028 int priv, int *gret)
2030 char prbuf[PROJECT_BUFSZ];
2031 projid_t projid;
2032 taskid_t taskid;
2033 zoneid_t zoneid;
2034 zoneid_t zone_self;
2035 struct project proj;
2036 DIR *dirp;
2037 struct dirent *dentp;
2038 int found = 0;
2039 int pid_self;
2040 int ret;
2041 int gret_in;
2042 int intidname;
2043 char *end;
2044 prpriv_t *prpriv;
2045 priv_set_t *prset;
2047 gret_in = *gret;
2049 /* get our pid se we do not try to operate on self */
2050 pid_self = getpid();
2052 /* Store integer version of id */
2053 intidname = strtoul(idname, &end, 10);
2054 if (errno || *end != '\0' || end == idname) {
2055 intidname = -1;
2059 * get our zoneid so we don't try to operate on a project in
2060 * another zone
2062 zone_self = getzoneid();
2064 if (idname == NULL || strcmp(idname, "") == 0) {
2065 warn(gettext("id name cannot be nuint64\n"));
2066 return (-1);
2069 * Set up zoneid, projid or taskid, as appropriate, so that comparisons
2070 * can be done later with the input.
2072 if (type == RCENTITY_ZONE) {
2073 if (zone_get_id(idname, &zoneid) != 0) {
2074 warn(gettext("%s: unknown zone\n"), idname);
2075 return (-1);
2077 } else if (type == RCENTITY_PROJECT) {
2078 if (getprojbyname(idname, &proj, prbuf, PROJECT_BUFSZ)
2079 == NULL) {
2080 if (getprojbyid(intidname, &proj, prbuf,
2081 PROJECT_BUFSZ) == NULL) {
2082 warn(gettext("%s: cannot find project\n"),
2083 idname);
2084 return (-1);
2087 projid = proj.pj_projid;
2088 } else if (type == RCENTITY_TASK) {
2089 taskid = (taskid_t)atol(idname);
2092 * Projects and tasks need to search through /proc for
2093 * a parent process.
2095 if (type == RCENTITY_ZONE || type == RCENTITY_PROJECT ||
2096 type == RCENTITY_TASK) {
2097 if ((dirp = opendir("/proc")) == NULL) {
2098 warn(gettext("%s: cannot open /proc directory\n"),
2099 idname);
2100 return (-1);
2103 * Look through all processes in /proc. For each process,
2104 * check if the pr_projid in their psinfo matches the
2105 * specified id.
2107 while (dentp = readdir(dirp)) {
2108 p->pid = atoi(dentp->d_name);
2110 /* Skip self */
2111 if (p->pid == pid_self)
2112 continue;
2114 if (proc_get_psinfo(p->pid, &(p->psinfo)) != 0)
2115 continue;
2117 /* Skip process if it is not what we are looking for */
2118 if (type == RCENTITY_ZONE &&
2119 (p->psinfo).pr_zoneid != zoneid) {
2120 continue;
2121 } else if (type == RCENTITY_PROJECT &&
2122 ((p->psinfo).pr_projid != projid ||
2123 (p->psinfo).pr_zoneid != zone_self)) {
2124 continue;
2125 } else if (type == RCENTITY_TASK &&
2126 (p->psinfo).pr_taskid != taskid) {
2127 continue;
2129 /* attempt to grab process */
2130 if (grab_process(p, gret) != 0)
2131 continue;
2134 * Re-confirm that this process is still running as
2135 * part of the specified project or task. If it
2136 * doesn't match, release the process and return an
2137 * error. This should only be done if the Pr struct is
2138 * not NULL.
2140 if (type == RCENTITY_PROJECT) {
2141 if (pr_getprojid(p->pr) != projid ||
2142 pr_getzoneid(p->pr) != zone_self) {
2143 release_process(p->pr);
2144 continue;
2146 } else if (type == RCENTITY_TASK) {
2147 if (pr_gettaskid(p->pr) != taskid) {
2148 release_process(p->pr);
2149 continue;
2151 } else if (type == RCENTITY_ZONE) {
2152 if (pr_getzoneid(p->pr) != zoneid) {
2153 release_process(p->pr);
2154 continue;
2159 * If we are setting a privileged resource control,
2160 * verify that process has PRIV_SYS_RESOURCE in it's
2161 * limit set. If it does not, then we will not be
2162 * able to give this process the privilege it needs
2163 * to set the resource control.
2165 if (priv != RCPRIV_BASIC) {
2166 prpriv = proc_get_priv(p->pid);
2167 if (prpriv == NULL) {
2168 release_process(p->pr);
2169 continue;
2171 prset = (priv_set_t *)
2172 &prpriv->pr_sets[prpriv->pr_setsize *
2173 priv_getsetbyname(PRIV_LIMIT)];
2174 if (!priv_ismember(prset, PRIV_SYS_RESOURCE)) {
2175 proc_free_priv(prpriv);
2176 release_process(p->pr);
2177 continue;
2179 proc_free_priv(prpriv);
2181 found = 1;
2183 p->taskid = pr_gettaskid(p->pr);
2184 p->projid = pr_getprojid(p->pr);
2185 p->zoneid = pr_getzoneid(p->pr);
2187 break;
2189 (void) closedir(dirp);
2191 if (found == 0) {
2192 warn(gettext("%s: No controllable process found in "
2193 "task, project, or zone.\n"), idname);
2194 return (-1);
2196 return (p->pid);
2198 } else if (type == RCENTITY_PROCESS) {
2200 /* fail if self */
2201 if (p->pid == pid_self) {
2203 warn(gettext("%s: cannot control self"), idname);
2204 return (-1);
2207 * Process types need to be set up with the correct pid
2208 * and psinfo structure.
2210 if ((p->pid = proc_arg_psinfo(idname, PR_ARG_PIDS,
2211 &(p->psinfo), gret)) == -1) {
2212 warn(gettext("%s: cannot examine: %s"), idname,
2213 Pgrab_error(*gret));
2214 return (-1);
2216 /* grab process */
2217 ret = grab_process(p, gret);
2218 if (ret == 1) {
2219 /* Don't print error if G_SYS is allowed */
2220 if (gret_in == G_SYS && *gret == G_SYS) {
2221 return (-1);
2222 } else {
2223 warn(gettext("%s: cannot control: %s"), idname,
2224 Pgrab_error(*gret));
2225 return (-1);
2227 } else if (ret == 2) {
2228 ret = errno;
2229 warn(gettext("%s: cannot control: %s"), idname,
2230 strerror(ret));
2231 return (-1);
2233 p->taskid = pr_gettaskid(p->pr);
2234 p->projid = pr_getprojid(p->pr);
2235 p->zoneid = pr_getzoneid(p->pr);
2237 return (p->pid);
2239 } else {
2240 warn(gettext("%s: unknown resource entity type %d\n"), idname,
2241 type);
2242 return (-1);
2247 * Do the work required to manipulate a process through libproc.
2248 * If grab_process() returns no errors (0), then release_process()
2249 * must eventually be called.
2251 * Return values:
2252 * 0 Successful creation of agent thread
2253 * 1 Error grabbing
2254 * 2 Error creating agent
2257 grab_process(pr_info_handle_t *p, int *gret)
2260 if ((p->pr = Pgrab(p->pid, arg_force, gret)) != NULL) {
2262 if (Psetflags(p->pr, PR_RLC) != 0) {
2263 Prelease(p->pr, 0);
2264 return (1);
2266 if (Pcreate_agent(p->pr) == 0) {
2267 return (0);
2269 } else {
2270 Prelease(p->pr, 0);
2271 return (2);
2273 } else {
2274 return (1);
2279 * Release the specified process. This destroys the agent
2280 * and releases the process. If the process is NULL, nothing
2281 * is done. This function should only be called if grab_process()
2282 * has previously been called and returned success.
2284 * This function is Pgrab-safe.
2286 void
2287 release_process(struct ps_prochandle *Pr)
2289 if (Pr == NULL)
2290 return;
2292 Pdestroy_agent(Pr);
2293 Prelease(Pr, 0);
2297 * preserve_error(char *, ...)
2299 * preserve_error() should be called rather than warn() by any
2300 * function that is called while the victim process is held by Pgrab.
2301 * It will save the error until the process has been un-controlled
2302 * and output is reasonable again.
2304 * Note that multiple errors are not stored. Any error in these
2305 * sections should be critical and return immediately.
2307 * This function is Pgrab-safe.
2309 * Since this function may copy untrusted command line arguments to
2310 * global_error, security practices require that global_error never be
2311 * printed directly. Use printf("%s\n", global_error) or equivalent.
2313 /*PRINTFLIKE1*/
2314 void
2315 preserve_error(char *format, ...)
2317 va_list alist;
2319 va_start(alist, format);
2322 * GLOBAL_ERR_SZ is pretty big. If the error is longer
2323 * than that, just truncate it, rather than chance missing
2324 * the error altogether.
2326 (void) vsnprintf(global_error, GLOBAL_ERR_SZ-1, format, alist);
2328 va_end(alist);