Home | History | Annotate | Download | only in prctl
      1 /*
      2  * CDDL HEADER START
      3  *
      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.
      7  *
      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.
     12  *
     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]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 
     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"
     46 
     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
     53 
     54 #define	PRCTL_VALUE_WIDTH	4
     55 
     56 /* Maximum string length for deferred errors */
     57 #define	GLOBAL_ERR_SZ		1024
     58 
     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;
     69 
     70 } pr_info_handle_t;
     71 
     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;
     77 
     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;
     84 
     85 static	volatile int	interrupt;
     86 
     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];
     90 
     91 /* global variables that contain commmand line option info */
     92 static	int	arg_operation = 0;
     93 static	int	arg_force = 0;
     94 
     95 
     96 /* String and type from -i */
     97 static	rctl_entity_t	arg_entity_type = RCENTITY_PROCESS;
     98 static	char	*arg_entity_string = NULL;
     99 
    100 /* -n argument */
    101 static  char	*arg_name = NULL;
    102 
    103 static	rctl_entity_t	arg_name_entity = 0;
    104 
    105 /* -t argument value */
    106 static	int	arg_priv = 0;
    107 
    108 /* -v argument string */
    109 static	char	*arg_valuestring = NULL;
    110 
    111 /* global flags of rctl name passed to -n */
    112 static	int	arg_global_flags = 0;
    113 static	rctl_qty_t	arg_global_max;
    114 
    115 /* appropriate scaling variables determined by rctl unit type */
    116 scale_t		*arg_scale;
    117 static	char	*arg_unit = NULL;
    118 
    119 /* -v argument string converted to uint64_t */
    120 static	uint64_t arg_value = 0;
    121 
    122 /* if -v argument is scaled value, points to "K", "M", "G", ... */
    123 static  char	*arg_modifier = NULL;
    124 
    125 /* -e/-d argument string */
    126 static	char	*arg_action_string = NULL;
    127 
    128 /* Set to RCTL_LOCAL_SIGNAL|DENY based on arg_action_string */
    129 static	int	arg_action = 0;
    130 
    131 /* if -e/-d arg is signal=XXX, set to signal number of XXX */
    132 static	int	arg_signal = 0;
    133 
    134 /* -p arg if -p is specified */
    135 static	int	arg_pid = -1;
    136 static	char	*arg_pid_string = NULL;
    137 
    138 /* Set to 1 if -P is specified */
    139 static	int	arg_parseable_mode = 0;
    140 
    141 /* interupt handler */
    142 static	void	intr(int);
    143 
    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();
    149 
    150 static	int	change_action(rctlblk_t *blk);
    151 
    152 static	int	prctl_setrctl(struct ps_prochandle *Pr, const char *name,
    153 			rctlblk_t *, rctlblk_t *, uint_t);
    154 
    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, ...);
    167 
    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);
    171 
    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";
    198 
    199 
    200 static void
    201 usage()
    202 {
    203 	(void) fprintf(stderr, gettext(USAGE));
    204 	exit(2);
    205 }
    206 
    207 int
    208 main(int argc, char **argv)
    209 {
    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;
    223 
    224 	(void) setlocale(LC_ALL, "");
    225 	(void) textdomain(TEXT_DOMAIN);
    226 	(void) setprogname(argv[0]);
    227 
    228 	while ((opt = getopt(argc, argv, "sPp:Fd:e:i:n:rt:v:x")) != EOF) {
    229 
    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;
    251 			}
    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;
    291 			}
    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;
    307 
    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;
    314 			}
    315 			break;
    316 		case 'P':
    317 			arg_parseable_mode = 1;
    318 			break;
    319 		default:
    320 			warn(gettext("unknown option"));
    321 			errflg = 1;
    322 			break;
    323 		}
    324 	}
    325 	argc -= optind;
    326 	argv += optind;
    327 
    328 	if (argc < 1) {
    329 		warn(gettext("no arguments specified"));
    330 		errflg = 1;
    331 		goto done_parse;
    332 	}
    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;
    338 	}
    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;
    344 	}
    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;
    351 	}
    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;
    359 	}
    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;
    366 	}
    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;
    372 	}
    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;
    378 	}
    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;
    384 	}
    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;
    391 	}
    392 	if (arg_operation) {
    393 
    394 		/* do additional checks if there is an operation */
    395 
    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;
    401 		}
    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;
    408 		}
    409 		if ((rctlblkB = calloc(1, rctlblk_size())) == NULL) {
    410 			warn(gettext("malloc failed: %s"),
    411 			    strerror(errno));
    412 			errflg = 1;
    413 			goto done_parse;
    414 		}
    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;
    421 		}
    422 		while (getrctl(arg_name, rctlblkA, rctlblkB, RCTL_NEXT) == 0) {
    423 
    424 			/* allow user interrupt */
    425 			if (interrupt) {
    426 				errflg = 1;
    427 				goto done_parse;
    428 			}
    429 			tmp = rctlblkB;
    430 			rctlblkB = rctlblkA;
    431 			rctlblkA = tmp;
    432 
    433 			if (rctlblk_get_privilege(rctlblkA) ==
    434 			    RCPRIV_SYSTEM) {
    435 				break;
    436 			}
    437 		}
    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;
    443 		}
    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);
    447 
    448 		if (arg_global_flags & RCTL_GLOBAL_BYTES) {
    449 			arg_unit = SCALED_UNIT_BYTES;
    450 			arg_scale = scale_binary;
    451 
    452 		} else if (arg_global_flags & RCTL_GLOBAL_SECONDS) {
    453 			arg_unit = SCALED_UNIT_SECONDS;
    454 			arg_scale = scale_metric;
    455 
    456 		} else {
    457 			arg_unit = SCALED_UNIT_NONE;
    458 			arg_scale = scale_metric;
    459 		}
    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)) {
    466 
    467 				warn(gettext("invalid -v value %s"),
    468 				    arg_valuestring);
    469 				errflg = 1;
    470 				goto done_parse;
    471 			}
    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;
    478 			}
    479 		}
    480 		/* parse action */
    481 		if (arg_action_string) {
    482 
    483 			char *sigchr;
    484 			char *iter;
    485 
    486 			if ((strcmp(arg_action_string, "signal") == 0) ||
    487 			    (strcmp(arg_action_string, "sig") == 0)) {
    488 
    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;
    495 				}
    496 				arg_action = RCTL_LOCAL_SIGNAL;
    497 				arg_signal = -1;
    498 
    499 			} else if ((strncmp(arg_action_string,
    500 			    "signal=", strlen("signal=")) == 0) ||
    501 			    (strncmp(arg_action_string,
    502 			    "sig=", strlen("sig=")) == 0)) {
    503 
    504 				arg_action = RCTL_LOCAL_SIGNAL;
    505 				sigchr = strrchr(arg_action_string, '=');
    506 				sigchr++;
    507 
    508 				iter = sigchr;
    509 				while (*iter) {
    510 					*iter = toupper(*iter);
    511 					iter++;
    512 				}
    513 				if (strncmp("SIG", sigchr, 3) == 0)
    514 					sigchr += 3;
    515 
    516 
    517 				if (str2sig(sigchr, &arg_signal) != 0) {
    518 					warn(gettext("signal invalid"));
    519 					errflg = 1;
    520 					goto done_parse;
    521 				}
    522 			} else if (strcmp(arg_action_string, "deny") == 0) {
    523 
    524 				arg_action = RCTL_LOCAL_DENY;
    525 
    526 			} else if (strcmp(arg_action_string, "all") == 0) {
    527 
    528 				if (arg_operation & ACTION_ENABLE) {
    529 					warn(gettext(
    530 					    "cannot use action 'all' with -e"));
    531 					errflg = 1;
    532 					goto done_parse;
    533 				}
    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;
    542 			}
    543 		}
    544 		/* cannot manipulate system rctls */
    545 		if (arg_priv == RCPRIV_SYSTEM) {
    546 
    547 			warn(gettext("cannot modify system values"));
    548 			errflg = 1;
    549 			goto done_parse;
    550 		}
    551 		/* validate that the privilege is allowed */
    552 		if ((arg_priv == RCPRIV_BASIC) &&
    553 		    (arg_global_flags & RCTL_GLOBAL_NOBASIC)) {
    554 
    555 			warn(gettext("basic values not allowed on rctl %s"),
    556 			    arg_name);
    557 			errflg = 1;
    558 			goto done_parse;
    559 		}
    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)) {
    564 
    565 			warn(gettext("unable to enable deny on rctl with "
    566 			    "global flag 'no-deny'"));
    567 			errflg = 1;
    568 			goto done_parse;
    569 		}
    570 		if ((arg_operation & ACTION_DISABLE) &&
    571 		    (arg_action & RCTL_LOCAL_DENY) &&
    572 		    (arg_global_flags & RCTL_GLOBAL_DENY_ALWAYS)) {
    573 
    574 			warn(gettext("unable to disable deny on rctl with "
    575 			    "global flag 'deny'"));
    576 			errflg = 1;
    577 			goto done_parse;
    578 		}
    579 		if ((arg_operation & ACTION_ENABLE) &&
    580 		    (arg_action & RCTL_LOCAL_SIGNAL) &&
    581 		    (arg_global_flags & RCTL_GLOBAL_SIGNAL_NEVER)) {
    582 
    583 			warn(gettext("unable to enable signal on rctl with "
    584 			    "global flag 'no-signal'"));
    585 			errflg = 1;
    586 			goto done_parse;
    587 		}
    588 		/* now set defaults for options not supplied */
    589 
    590 		/*
    591 		 * default privilege to basic if this is a seting an rctl
    592 		 * operation
    593 		 */
    594 		if (arg_operation & ACTION_SET) {
    595 			if (arg_priv == 0) {
    596 				arg_priv = RCPRIV_BASIC;
    597 			}
    598 		}
    599 		/*
    600 		 * -p is required when set a basic task,
    601 		 * project or zone rctl
    602 		 */
    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)) {
    611 
    612 			warn(gettext("-p pid required when setting or "
    613 			    "replacing task or project rctl"));
    614 			errflg = 1;
    615 			goto done_parse;
    616 		}
    617 	} else {
    618 
    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;
    625 		}
    626 	}
    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))) {
    631 
    632 		warn(gettext("cannot get/set process rctl on task "
    633 		    "or project"));
    634 		errflg = 1;
    635 		goto done_parse;
    636 	}
    637 	/* getting/setting task rctl on project is error */
    638 	if ((arg_name && (arg_name_entity == RCENTITY_TASK)) &&
    639 	    (arg_entity_type == RCENTITY_PROJECT)) {
    640 
    641 		warn(gettext("cannot get/set task rctl on project"));
    642 		errflg = 1;
    643 		goto done_parse;
    644 	}
    645 
    646 done_parse:
    647 
    648 	/* free any rctlblk's that we may have allocated */
    649 	if (rctlblkA) {
    650 		free(rctlblkA);
    651 		rctlblkA = NULL;
    652 	}
    653 	if (rctlblkB) {
    654 		free(rctlblkB);
    655 		rctlblkB = NULL;
    656 	}
    657 	if (errflg)
    658 		usage();
    659 
    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);
    668 
    669 	while (--argc >= 0 && !interrupt) {
    670 		pr_info_handle_t p;
    671 		char *arg = *argv++;
    672 		int intarg;
    673 		char *end;
    674 		errflg = 0;
    675 
    676 		gret = 0;
    677 
    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;
    683 		}
    684 
    685 		/*
    686 		 * -p defaults to arg if basic and collective rctl
    687 		 * and -i process is specified
    688 		 */
    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;
    698 		}
    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;
    705 		}
    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;
    713 		}
    714 		(void) fflush(stdout);	/* process-at-a-time */
    715 
    716 		if (arg_operation != 0) {
    717 
    718 			if ((pid = grab_process_by_id(target_id,
    719 			    search_type, &p, arg_priv, &gret)) < 0) {
    720 				/*
    721 				 * Mark that an error occurred so that the
    722 				 * return value can be set, but continue
    723 				 * on with other processes
    724 				 */
    725 				errflg = 1;
    726 				continue;
    727 			}
    728 
    729 			/*
    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().
    733 			 */
    734 
    735 			errflg = get_rctls(p.pr);
    736 
    737 			if (arg_operation & ACTION_DELETE) {
    738 
    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) {
    743 
    744 					if (interrupt)
    745 						goto out;
    746 
    747 					preserve_error(gettext("no matching "
    748 					    "resource control found for "
    749 					    "deletion"));
    750 					errflg = 1;
    751 					goto out;
    752 				}
    753 				/*
    754 				 * grab correct process.  This is neccessary
    755 				 * if the recipient pid does not match the
    756 				 * one we grabbed
    757 				 */
    758 				pid = regrab_process(
    759 				    rctlblk_get_recipient_pid(rctlblkA),
    760 				    &p, arg_priv, &gret);
    761 
    762 				if (pid < 0) {
    763 					errflg = 1;
    764 					goto out;
    765 				}
    766 				if (prctl_setrctl(p.pr, arg_name, NULL,
    767 				    rctlblkA, RCTL_DELETE) != 0) {
    768 					errflg = 1;
    769 					goto out;
    770 				}
    771 			} else if (arg_operation & ACTION_SET) {
    772 
    773 				/* match by privilege, value, and pid */
    774 
    775 				if (match_rctl(p.pr, &rctlblkA, arg_name,
    776 				    arg_valuestring, arg_value, arg_priv,
    777 				    arg_pid) == 0) {
    778 
    779 					if (interrupt)
    780 						goto out;
    781 
    782 					preserve_error(gettext("resource "
    783 					    "control already exists"));
    784 					errflg = 1;
    785 					goto out;
    786 				}
    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;
    793 				}
    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;
    799 				}
    800 				if (prctl_setrctl(p.pr, arg_name, NULL,
    801 				    rctlblkB, RCTL_INSERT) != 0) {
    802 					errflg = 1;
    803 					goto out;
    804 				}
    805 			} else if (arg_operation & ACTION_REPLACE) {
    806 				/*
    807 				 * match rctl for deletion by privilege and
    808 				 * pid only
    809 				 */
    810 				if (match_rctl(p.pr, &rctlblkA, arg_name,
    811 				    NULL, 0, arg_priv,
    812 				    arg_pid) != 0 || rctlblkA == NULL) {
    813 
    814 					if (interrupt)
    815 						goto out;
    816 
    817 					preserve_error(gettext("no matching "
    818 					    "resource control to replace"));
    819 					errflg = 1;
    820 					goto out;
    821 				}
    822 				/*
    823 				 * grab correct process.  This is neccessary
    824 				 * if the recipient pid does not match the
    825 				 * one we grabbed
    826 				 */
    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;
    833 				}
    834 				pid = rctlblk_get_recipient_pid(rctlblkA);
    835 
    836 				/*
    837 				 * match by privilege, value and pid to
    838 				 * check if new rctl  already exists
    839 				 */
    840 				if (match_rctl(p.pr, &rctlblkB, arg_name,
    841 				    arg_valuestring, arg_value, arg_priv,
    842 				    pid) < 0) {
    843 
    844 					if (interrupt)
    845 						goto out;
    846 
    847 					preserve_error(gettext(
    848 					    "Internal Error"));
    849 					errflg = 1;
    850 					goto out;
    851 				}
    852 				/*
    853 				 * If rctl already exists, and it does not
    854 				 *  match the one that we will delete, than
    855 				 *  the replace will fail.
    856 				 */
    857 				if (rctlblkB != NULL &&
    858 				    arg_value != rctlblk_get_value(rctlblkA)) {
    859 
    860 					preserve_error(gettext("replacement "
    861 					    "resource control already "
    862 					    "exists"));
    863 
    864 					errflg = 1;
    865 					goto out;
    866 				}
    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;
    874 				}
    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;
    885 				}
    886 				/* do replacement */
    887 				if (prctl_setrctl(p.pr, arg_name, rctlblkA,
    888 				    rctlblkB, RCTL_REPLACE) != 0) {
    889 					errflg = 1;
    890 					goto out;
    891 				}
    892 			} else if (arg_operation &
    893 			    (ACTION_ENABLE | ACTION_DISABLE)) {
    894 
    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;
    901 				}
    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) {
    906 
    907 					if (interrupt)
    908 						goto out;
    909 
    910 					/* if no match, just set new rctl */
    911 					if (arg_priv == 0)
    912 						arg_priv = RCPRIV_BASIC;
    913 
    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;
    923 					}
    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;
    931 					}
    932 					if (prctl_setrctl(p.pr,
    933 					    arg_name, NULL, rctlblkB,
    934 					    RCTL_INSERT) != 0) {
    935 						errflg = 1;
    936 						goto out;
    937 					}
    938 					goto out;
    939 				}
    940 				if (rctlblkA == NULL) {
    941 					preserve_error(gettext("no matching "
    942 					    "resource control found"));
    943 					errflg = 1;
    944 					goto out;
    945 				}
    946 				/*
    947 				 * grab correct process.  This is neccessary
    948 				 * if the recipient pid does not match the
    949 				 * one we grabbed
    950 				 */
    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;
    957 				}
    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));
    967 
    968 				if (change_action(rctlblkB)) {
    969 					errflg = 1;
    970 					goto out;
    971 				}
    972 				if (prctl_setrctl(p.pr, arg_name, rctlblkA,
    973 				    rctlblkB, RCTL_REPLACE) != 0) {
    974 					errflg = 1;
    975 					goto out;
    976 				}
    977 			}
    978 out:
    979 			release_process(p.pr);
    980 			if (rctlblkA)
    981 				free(rctlblkA);
    982 			if (rctlblkB)
    983 				free(rctlblkB);
    984 
    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;
    992 			}
    993 		} else {
    994 
    995 			struct project projent;
    996 			char buf[PROJECT_BUFSZ];
    997 			char zonename[ZONENAME_MAX];
    998 
    999 			/*
   1000 			 * Hack to allow the user to specify a system
   1001 			 * process.
   1002 			 */
   1003 			gret = G_SYS;
   1004 			pid = grab_process_by_id(
   1005 			    target_id, search_type, &p, RCPRIV_BASIC, &gret);
   1006 
   1007 			/*
   1008 			 * Print system process if user chose specifically
   1009 			 * to inspect a system process.
   1010 			 */
   1011 			if (arg_entity_type == RCENTITY_PROCESS &&
   1012 			    pid < 0 &&
   1013 			    gret == G_SYS) {
   1014 				/*
   1015 				 * Add blank lines between output for
   1016 				 * operands.
   1017 				 */
   1018 				if (printed) {
   1019 					(void) fprintf(stdout, "\n");
   1020 				}
   1021 
   1022 				proc_unctrl_psinfo(&(p.psinfo));
   1023 				(void) printf(
   1024 				    "process: %d: %s [ system process ]\n",
   1025 				    (int)p.pid, p.psinfo.pr_psargs);
   1026 
   1027 				printed = 1;
   1028 				continue;
   1029 
   1030 			} else if (pid < 0) {
   1031 
   1032 				/*
   1033 				 * Mark that an error occurred so that the
   1034 				 * return value can be set, but continue
   1035 				 * on with other processes
   1036 				 */
   1037 				errflg = 1;
   1038 				continue;
   1039 			}
   1040 
   1041 			errflg = get_rctls(p.pr);
   1042 
   1043 			release_process(p.pr);
   1044 
   1045 			/* handle user interrupt of getting rctls */
   1046 			if (interrupt)
   1047 				break;
   1048 
   1049 			/* add blank lines between output for operands */
   1050 			if (printed) {
   1051 				(void) fprintf(stdout, "\n");
   1052 			}
   1053 			/* First print any errors */
   1054 			if (errflg) {
   1055 				warn("%s\n", global_error);
   1056 				free_lists();
   1057 				break;
   1058 			}
   1059 			if (getprojbyid(p.projid, &projent, buf,
   1060 			    sizeof (buf))) {
   1061 				p.projname = projent.pj_name;
   1062 			} else {
   1063 				p.projname = "";
   1064 			}
   1065 			if (getzonenamebyid(p.zoneid, zonename,
   1066 			    sizeof (zonename)) > 0) {
   1067 				p.zonename = zonename;
   1068 			} else {
   1069 				p.zonename = "";
   1070 			}
   1071 			print_rctls(&p);
   1072 			printed = 1;
   1073 			/* Free the resource control lists */
   1074 			free_lists();
   1075 		}
   1076 	}
   1077 	if (interrupt)
   1078 		errflg = 1;
   1079 
   1080 	/*
   1081 	 * return error if one occurred
   1082 	 */
   1083 	return (errflg);
   1084 }
   1085 
   1086 
   1087 static void
   1088 intr(int sig)
   1089 {
   1090 	interrupt = sig;
   1091 }
   1092 
   1093 /*
   1094  * get_rctls(struct ps_prochandle *, const char *)
   1095  *
   1096  * If controlname is given, store only controls for that named
   1097  * resource. If controlname is NULL, store all controls for all
   1098  * resources.
   1099  *
   1100  * This function is Pgrab-safe.
   1101  */
   1102 static int
   1103 get_rctls(struct ps_prochandle *Pr)
   1104 {
   1105 	int ret = 0;
   1106 
   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);
   1112 	}
   1113 	return (ret);
   1114 }
   1115 
   1116 /*
   1117  * store_rctls(const char *, void *)
   1118  *
   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.
   1123  *
   1124  * This function is Pgrab-safe
   1125  */
   1126 static int
   1127 store_rctls(const char *rctlname, void *walk_data)
   1128 {
   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;
   1134 
   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);
   1142 	}
   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);
   1149 	}
   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;
   1165 
   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)) {
   1170 
   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);
   1176 		}
   1177 		if (store_value_entry(rblk1, list) == NULL) {
   1178 			free(rblk1);
   1179 			free(rblk2);
   1180 			return (1);
   1181 		}
   1182 	}
   1183 	while (pr_getrctl(Pr, rctlname, rblk1, rblk2, RCTL_NEXT) == 0) {
   1184 
   1185 		/*
   1186 		 * in case this is stuck for some reason, allow manual
   1187 		 * interrupt
   1188 		 */
   1189 		if (interrupt) {
   1190 			free(rblk1);
   1191 			free(rblk2);
   1192 			return (1);
   1193 		}
   1194 		rblk_priv = rctlblk_get_privilege(rblk2);
   1195 		/*
   1196 		 * Store control if it matches privilege and entity type
   1197 		 * criteria
   1198 		 */
   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)) {
   1204 
   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);
   1212 				}
   1213 			}
   1214 			if (store_value_entry(rblk2, list) == NULL) {
   1215 				free(rblk1);
   1216 				free(rblk2);
   1217 				return (1);
   1218 			}
   1219 		}
   1220 		rblk_tmp = rblk1;
   1221 		rblk1 = rblk2;
   1222 		rblk2 = rblk_tmp;
   1223 	}
   1224 
   1225 	/*
   1226 	 * Get the current usage for the resource control if it matched the
   1227 	 * privilege and entity type criteria.
   1228 	 */
   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);
   1238 			}
   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);
   1249 			}
   1250 		}
   1251 	}
   1252 	free(rblk1);
   1253 	free(rblk2);
   1254 	return (0);
   1255 }
   1256 
   1257 /*
   1258  * store_value_entry(rctlblk_t *, prctl_list_t *)
   1259  *
   1260  * Store an rblk for a given resource control into the global list.
   1261  *
   1262  * This function is Pgrab-safe.
   1263  */
   1264 prctl_value_t *
   1265 store_value_entry(rctlblk_t *rblk, prctl_list_t *list)
   1266 {
   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;
   1270 
   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);
   1279 	}
   1280 	if (iter == NULL)
   1281 		list->val_list = e;
   1282 	else {
   1283 		while (iter->next != NULL) {
   1284 			iter = iter->next;
   1285 		}
   1286 		iter->next = e;
   1287 	}
   1288 	bcopy(rblk, store_blk, rctlblk_size());
   1289 
   1290 	e->rblk = store_blk;
   1291 	e->next = NULL;
   1292 	return (e);
   1293 }
   1294 
   1295 /*
   1296  * store_list_entry(const char *)
   1297  *
   1298  * Store a new resource control value in the global list. No checking
   1299  * for duplicates done.
   1300  *
   1301  * This function is Pgrab-safe.
   1302  */
   1303 prctl_list_t *
   1304 store_list_entry(const char *name)
   1305 {
   1306 	prctl_list_t *e = calloc(1, sizeof (prctl_list_t));
   1307 
   1308 	if (e == NULL) {
   1309 		preserve_error(gettext("malloc failed %s"),
   1310 		    strerror(errno));
   1311 		return (NULL);
   1312 	}
   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);
   1318 	}
   1319 	(void) strcpy(e->name, name);
   1320 	e->val_list = NULL;
   1321 
   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;
   1328 	}
   1329 	e->next = NULL;
   1330 	return (e);
   1331 }
   1332 
   1333 /*
   1334  * free_lists()
   1335  *
   1336  * Free all resource control blocks and values from the global lists.
   1337  *
   1338  * This function is Pgrab-safe.
   1339  */
   1340 void
   1341 free_lists()
   1342 {
   1343 	prctl_list_t *new_list, *old_list = global_rctl_list_head;
   1344 	prctl_value_t *old_val, *new_val;
   1345 
   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;
   1353 		}
   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;
   1359 	}
   1360 	global_rctl_list_head = NULL;
   1361 	global_rctl_list_tail = NULL;
   1362 }
   1363 
   1364 void
   1365 print_heading()
   1366 {
   1367 
   1368 	/* print headings */
   1369 	(void) fprintf(stdout, "%-8s%-16s%-9s%-7s%-28s%10s\n",
   1370 	    "NAME", "PRIVILEGE", "VALUE",
   1371 	    "FLAG", "ACTION", "RECIPIENT");
   1372 }
   1373 
   1374 /*
   1375  * print_rctls()
   1376  *
   1377  * Print all resource controls from the global list that was
   1378  * previously populated by store_rctls.
   1379  */
   1380 void
   1381 print_rctls(pr_info_handle_t *p)
   1382 {
   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;
   1395 
   1396 	if (iter_list == NULL)
   1397 		return;
   1398 
   1399 	while (iter_list != NULL) {
   1400 
   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();
   1410 		}
   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();
   1417 		}
   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();
   1428 		}
   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();
   1437 		}
   1438 		/* only print name once in normal output */
   1439 		if (!arg_parseable_mode)
   1440 			(void) fprintf(stdout, "%s\n", iter_list->name);
   1441 
   1442 		iter_val = iter_list->val_list;
   1443 
   1444 		/* if for some reason there are no values, skip */
   1445 		if (iter_val == 0)
   1446 			continue;
   1447 
   1448 
   1449 		/* get the global flags the first rctl only */
   1450 		global_flags = rctlblk_get_global_flags(iter_val->rblk);
   1451 
   1452 
   1453 		if (global_flags & RCTL_GLOBAL_BYTES) {
   1454 			unit = SCALED_UNIT_BYTES;
   1455 			scale = scale_binary;
   1456 
   1457 		} else if (global_flags & RCTL_GLOBAL_SECONDS) {
   1458 			unit = SCALED_UNIT_SECONDS;
   1459 			scale = scale_metric;
   1460 
   1461 		} else {
   1462 			unit = SCALED_UNIT_NONE;
   1463 			scale = scale_metric;
   1464 		}
   1465 
   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);
   1473 
   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);
   1479 			}
   1480 		}
   1481 
   1482 		/* iterate over an print all control values */
   1483 		while (iter_val != NULL) {
   1484 
   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);
   1490 
   1491 
   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 ");
   1497 
   1498 			rblk_value = rctlblk_get_value(iter_val->rblk);
   1499 			if (arg_parseable_mode) {
   1500 				(void) fprintf(stdout, "%llu ", rblk_value);
   1501 
   1502 			} else {
   1503 
   1504 				(void) uint64toscaled(rblk_value, 4, "E",
   1505 				    rctl_valuestring, NULL, NULL,
   1506 				    scale, NULL, 0);
   1507 
   1508 				(void) fprintf(stdout, "%5s",
   1509 				    rctl_valuestring);
   1510 				(void) fprintf(stdout, "%-4s", unit);
   1511 			}
   1512 			local_flags = rctlblk_get_local_flags(iter_val->rblk);
   1513 
   1514 			if (local_flags & RCTL_LOCAL_MAXIMAL) {
   1515 
   1516 				if (global_flags & RCTL_GLOBAL_INFINITE) {
   1517 					string = "inf";
   1518 				} else {
   1519 					string = "max";
   1520 				}
   1521 			} else {
   1522 				string = "-";
   1523 			}
   1524 			if (arg_parseable_mode)
   1525 				(void) fprintf(stdout, "%s ", string);
   1526 			else
   1527 				(void) fprintf(stdout, "%4s%3s",
   1528 				    string, "");
   1529 
   1530 
   1531 			local_action = rctlblk_get_local_action(iter_val->rblk,
   1532 			    &signal);
   1533 
   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");
   1540 
   1541 			pid = rctlblk_get_recipient_pid(iter_val->rblk);
   1542 
   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);
   1549 				}
   1550 			} else {
   1551 				if (pid < 0) {
   1552 					(void) fprintf(stdout, "%10s\n", "-");
   1553 				} else {
   1554 					(void) fprintf(stdout, "%10d\n",
   1555 					    (int)pid);
   1556 				}
   1557 			}
   1558 			iter_val = iter_val->next;
   1559 		}
   1560 		iter_list = iter_list->next;
   1561 	}
   1562 }
   1563 
   1564 /*
   1565  *
   1566  * match_rctl
   1567  *
   1568  * find the first rctl with matching name, value, priv, and recipient pid
   1569  */
   1570 int
   1571 match_rctl(struct ps_prochandle *Pr, rctlblk_t **rctl, char *name,
   1572     char *valuestringin, int valuein, rctl_priv_t privin, int pidin)
   1573 {
   1574 	rctlblk_t *next;
   1575 	rctlblk_t *last;
   1576 	rctlblk_t *tmp;
   1577 
   1578 	*rctl = NULL;
   1579 
   1580 	next = calloc(1, rctlblk_size());
   1581 	last = calloc(1, rctlblk_size());
   1582 
   1583 	if ((last == NULL) || (next == NULL)) {
   1584 		preserve_error(gettext("malloc failed"), strerror(errno));
   1585 		return (-1);
   1586 	}
   1587 	/*
   1588 	 * For this resource name, now iterate through all
   1589 	 * the  controls, looking for a match to the
   1590 	 * user-specified input.
   1591 	 */
   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);
   1596 	}
   1597 	if (match_rctl_blk(next, valuestringin, valuein, privin, pidin) == 1) {
   1598 		free(last);
   1599 		*rctl = next;
   1600 		return (0);
   1601 	}
   1602 	tmp = next;
   1603 	next = last;
   1604 	last = tmp;
   1605 
   1606 	while (pr_getrctl(Pr, name, last, next,	RCTL_NEXT) == 0) {
   1607 
   1608 		/* allow user interrupt */
   1609 		if (interrupt)
   1610 			break;
   1611 
   1612 		if (match_rctl_blk(next, valuestringin, valuein, privin, pidin)
   1613 		    == 1) {
   1614 			free(last);
   1615 			*rctl = next;
   1616 			return (0);
   1617 		}
   1618 		tmp = next;
   1619 		next = last;
   1620 		last = tmp;
   1621 	}
   1622 	free(next);
   1623 	free(last);
   1624 
   1625 	return (1);
   1626 }
   1627 
   1628 /*
   1629  * int match_rctl_blk(rctlblk_t *, char *, uint64, rctl_priv_t, int pid)
   1630  *
   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.
   1636  *
   1637  * Return values
   1638  *   Returns 1 if a matching rctl given matches the parameters specified, and
   1639  *   0 if they do not.
   1640  *
   1641  * This function is Pgrab-safe.
   1642  */
   1643 int
   1644 match_rctl_blk(rctlblk_t *rctl, char *valuestringin,
   1645     uint64_t valuein, rctl_priv_t privin, int pidin)
   1646 {
   1647 
   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;
   1654 
   1655 	value = rctlblk_get_value(rctl);
   1656 	priv = rctlblk_get_privilege(rctl);
   1657 	pid = rctlblk_get_recipient_pid(rctl);
   1658 
   1659 	if (valuestringin) {
   1660 
   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);
   1668 		}
   1669 	}
   1670 	if (privin != 0) {
   1671 		privmatch = (privin == priv);
   1672 	}
   1673 	if (pidin != -1) {
   1674 		pidmatch = (pidin == pid);
   1675 	}
   1676 	return (valuematch && privmatch && pidmatch);
   1677 }
   1678 
   1679 static int
   1680 change_action(rctlblk_t *blk)
   1681 {
   1682 	int signal = 0;
   1683 	int action;
   1684 
   1685 	action = rctlblk_get_local_action(blk, &signal);
   1686 
   1687 	if (arg_operation & ACTION_ENABLE) {
   1688 
   1689 		if (arg_action & RCTL_LOCAL_SIGNAL) {
   1690 			signal = arg_signal;
   1691 		}
   1692 		action = action | arg_action;
   1693 		/* add local action */
   1694 		rctlblk_set_local_action(blk, action, signal);
   1695 
   1696 	} else if (arg_operation & ACTION_DISABLE) {
   1697 
   1698 		/*
   1699 		 * if deleting signal and signal number is specified,
   1700 		 * then signal number must match
   1701 		 */
   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);
   1708 			}
   1709 		}
   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);
   1714 	}
   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);
   1719 	}
   1720 	return (0);
   1721 }
   1722 
   1723 /*
   1724  * prctl_setrctl
   1725  *
   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.
   1732  *
   1733  * If the privilege is a priviliged type, at this time, the process
   1734  * tries to take on superuser privileges.
   1735  */
   1736 int
   1737 prctl_setrctl(struct ps_prochandle *Pr, const char *name,
   1738     rctlblk_t *old_rblk, rctlblk_t *new_rblk, uint_t flags)
   1739 {
   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;
   1747 
   1748 	rblk_priv = rctlblk_get_privilege(new_rblk);
   1749 
   1750 	if (rblk_priv == RCPRIV_SYSTEM) {
   1751 		preserve_error(gettext("cannot modify system values"));
   1752 		return (1);
   1753 	}
   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);
   1761 		}
   1762 		/*
   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.
   1767 		 */
   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 				free(new_prpriv);
   1782 				return (1);
   1783 			}
   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 				free(new_prpriv);
   1793 				free(old_prpriv);
   1794 				return (1);
   1795 			}
   1796 		}
   1797 		/*
   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.
   1803 		 */
   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) {
   1809 			/*
   1810 			 * We need to give this process superuser
   1811 			 * ("super-zone") privileges.
   1812 			 *
   1813 			 * Must never return without setting this back!
   1814 			 */
   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));
   1821 				/*
   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.
   1825 				 */
   1826 				oldzoneid = GLOBAL_ZONEID;
   1827 				ret = 1;
   1828 				goto bail;
   1829 			}
   1830 		}
   1831 	}
   1832 	/* Now, actually populate the rctlblk in the kernel */
   1833 	if (flags == RCTL_REPLACE) {
   1834 		/*
   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.
   1840 		 */
   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;
   1848 		}
   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;
   1856 		}
   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;
   1865 		}
   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;
   1874 		}
   1875 	}
   1876 bail:
   1877 	if (oldzoneid != GLOBAL_ZONEID) {
   1878 		if (Psetzoneid(Pr, oldzoneid) != 0)
   1879 			relinquish_failed = B_TRUE;
   1880 	}
   1881 	if (old_prpriv != NULL) {
   1882 		if (Psetpriv(Pr, old_prpriv) != 0)
   1883 			relinquish_failed = B_TRUE;
   1884 		free(old_prpriv);
   1885 	}
   1886 	if (relinquish_failed) {
   1887 		/*
   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).
   1892 		 */
   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);
   1902 	}
   1903 	if (new_prpriv != NULL)
   1904 		free(new_prpriv);
   1905 
   1906 	return (ret);
   1907 }
   1908 
   1909 void
   1910 print_priv(rctl_priv_t local_priv, char *format)
   1911 {
   1912 	char pstring[11];
   1913 
   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;
   1927 	}
   1928 	/* LINTED */
   1929 	(void) fprintf(stdout, format, pstring);
   1930 }
   1931 
   1932 void
   1933 print_local_action(int action, int *signalp, char *format)
   1934 {
   1935 	char sig[SIG2STR_MAX];
   1936 	char sigstring[SIG2STR_MAX + 7];
   1937 	char astring[5 + SIG2STR_MAX + 7];
   1938 	int set = 0;
   1939 
   1940 	astring[0] = '\0';
   1941 
   1942 	if (action == RCTL_LOCAL_NOACTION) {
   1943 		(void) strcat(astring, "none");
   1944 		set++;
   1945 	}
   1946 	if (action & RCTL_LOCAL_DENY) {
   1947 		(void) strcat(astring, "deny");
   1948 		set++;
   1949 	}
   1950 	if ((action & RCTL_LOCAL_DENY) &&
   1951 	    (action & RCTL_LOCAL_SIGNAL)) {
   1952 		(void) strcat(astring, ",");
   1953 	}
   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);
   1961 
   1962 		(void) strcat(astring, sigstring);
   1963 		set++;
   1964 	}
   1965 	if (set)
   1966 		/* LINTED */
   1967 		(void) fprintf(stdout, format, astring);
   1968 	else
   1969 		/* LINTED */
   1970 		(void) fprintf(stdout, format, action);
   1971 }
   1972 
   1973 /*
   1974  * This function is used to grab the process matching the recipient pid
   1975  */
   1976 pid_t
   1977 regrab_process(pid_t pid, pr_info_handle_t *p, int priv, int *gret)
   1978 {
   1979 
   1980 	char pidstring[24];
   1981 
   1982 	gret = 0;
   1983 	if (pid == -1)
   1984 		return (p->pid);
   1985 	if (p->pid == pid)
   1986 		return (p->pid);
   1987 
   1988 	release_process(p->pr);
   1989 	(void) memset(p, 0, sizeof (*p));
   1990 
   1991 	(void) snprintf(pidstring, 24, "%d", pid);
   1992 	return (grab_process_by_id(
   1993 	    pidstring, RCENTITY_PROCESS, p, priv, gret));
   1994 }
   1995 
   1996 /*
   1997  * int grab_process_by_id(char *, rctl_entity_t, pr_info_handle_t *, int, int *)
   1998  *
   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.
   2009  *
   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.
   2018  *
   2019  * This function assumes that signals are caught already so that libproc
   2020  * can be safely used.
   2021  *
   2022  * Return Values
   2023  *	pid - Process found and grabbed
   2024  *	-1 - Error
   2025  */
   2026 pid_t
   2027 grab_process_by_id(char *idname, rctl_entity_t type, pr_info_handle_t *p,
   2028     int priv, int *gret)
   2029 {
   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;
   2046 
   2047 	gret_in = *gret;
   2048 
   2049 	/* get our pid se we do not try to operate on self */
   2050 	pid_self = getpid();
   2051 
   2052 	/* Store integer version of id */
   2053 	intidname = strtoul(idname, &end, 10);
   2054 	if (errno || *end != '\0' || end == idname) {
   2055 		intidname = -1;
   2056 	}
   2057 
   2058 	/*
   2059 	 * get our zoneid so we don't try to operate on a project in
   2060 	 * another zone
   2061 	 */
   2062 	zone_self = getzoneid();
   2063 
   2064 	if (idname == NULL || strcmp(idname, "") == 0) {
   2065 		warn(gettext("id name cannot be nuint64\n"));
   2066 		return (-1);
   2067 	}
   2068 	/*
   2069 	 * Set up zoneid, projid or taskid, as appropriate, so that comparisons
   2070 	 * can be done later with the input.
   2071 	 */
   2072 	if (type == RCENTITY_ZONE) {
   2073 		if (zone_get_id(idname, &zoneid) != 0) {
   2074 			warn(gettext("%s: unknown zone\n"), idname);
   2075 			return (-1);
   2076 		}
   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);
   2085 			}
   2086 		}
   2087 		projid = proj.pj_projid;
   2088 	} else if (type == RCENTITY_TASK) {
   2089 		taskid = (taskid_t)atol(idname);
   2090 	}
   2091 	/*
   2092 	 * Projects and tasks need to search through /proc for
   2093 	 * a parent process.
   2094 	 */
   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);
   2101 		}
   2102 		/*
   2103 		 * Look through all processes in /proc. For each process,
   2104 		 * check if the pr_projid in their psinfo matches the
   2105 		 * specified id.
   2106 		 */
   2107 		while (dentp = readdir(dirp)) {
   2108 			p->pid = atoi(dentp->d_name);
   2109 
   2110 			/* Skip self */
   2111 			if (p->pid == pid_self)
   2112 				continue;
   2113 
   2114 			if (proc_get_psinfo(p->pid, &(p->psinfo)) != 0)
   2115 				continue;
   2116 
   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;
   2128 			}
   2129 			/* attempt to grab process */
   2130 			if (grab_process(p, gret) != 0)
   2131 				continue;
   2132 
   2133 			/*
   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.
   2139 			 */
   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;
   2145 				}
   2146 			} else if (type == RCENTITY_TASK) {
   2147 				if (pr_gettaskid(p->pr) != taskid) {
   2148 					release_process(p->pr);
   2149 					continue;
   2150 				}
   2151 			} else if (type == RCENTITY_ZONE) {
   2152 				if (pr_getzoneid(p->pr) != zoneid) {
   2153 					release_process(p->pr);
   2154 					continue;
   2155 				}
   2156 			}
   2157 
   2158 			/*
   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.
   2164 			 */
   2165 			if (priv != RCPRIV_BASIC) {
   2166 				prpriv = proc_get_priv(p->pid);
   2167 				if (prpriv == NULL) {
   2168 					release_process(p->pr);
   2169 					continue;
   2170 				}
   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 					release_process(p->pr);
   2176 					continue;
   2177 				}
   2178 			}
   2179 			found = 1;
   2180 
   2181 			p->taskid = pr_gettaskid(p->pr);
   2182 			p->projid = pr_getprojid(p->pr);
   2183 			p->zoneid = pr_getzoneid(p->pr);
   2184 
   2185 			break;
   2186 		}
   2187 		(void) closedir(dirp);
   2188 
   2189 		if (found == 0) {
   2190 			warn(gettext("%s: No controllable process found in "
   2191 			    "task, project, or zone.\n"), idname);
   2192 			return (-1);
   2193 		}
   2194 		return (p->pid);
   2195 
   2196 	} else if (type == RCENTITY_PROCESS) {
   2197 
   2198 		/* fail if self */
   2199 		if (p->pid == pid_self) {
   2200 
   2201 			warn(gettext("%s: cannot control self"), idname);
   2202 			return (-1);
   2203 		}
   2204 		/*
   2205 		 * Process types need to be set up with the correct pid
   2206 		 * and psinfo structure.
   2207 		 */
   2208 		if ((p->pid = proc_arg_psinfo(idname, PR_ARG_PIDS,
   2209 		    &(p->psinfo), gret)) == -1) {
   2210 			warn(gettext("%s: cannot examine: %s"), idname,
   2211 			    Pgrab_error(*gret));
   2212 			return (-1);
   2213 		}
   2214 		/* grab process */
   2215 		ret = grab_process(p, gret);
   2216 		if (ret == 1) {
   2217 			/* Don't print error if G_SYS is allowed */
   2218 			if (gret_in == G_SYS && *gret == G_SYS) {
   2219 				return (-1);
   2220 			} else {
   2221 				warn(gettext("%s: cannot control: %s"), idname,
   2222 				    Pgrab_error(*gret));
   2223 				return (-1);
   2224 			}
   2225 		} else if (ret == 2) {
   2226 			ret = errno;
   2227 			warn(gettext("%s: cannot control: %s"), idname,
   2228 			    strerror(ret));
   2229 			return (-1);
   2230 		}
   2231 		p->taskid = pr_gettaskid(p->pr);
   2232 		p->projid = pr_getprojid(p->pr);
   2233 		p->zoneid = pr_getzoneid(p->pr);
   2234 
   2235 		return (p->pid);
   2236 
   2237 	} else {
   2238 		warn(gettext("%s: unknown resource entity type %d\n"), idname,
   2239 		    type);
   2240 		return (-1);
   2241 	}
   2242 }
   2243 
   2244 /*
   2245  * Do the work required to manipulate a process through libproc.
   2246  * If grab_process() returns no errors (0), then release_process()
   2247  * must eventually be called.
   2248  *
   2249  * Return values:
   2250  *	0 Successful creation of agent thread
   2251  *	1 Error grabbing
   2252  *	2 Error creating agent
   2253  */
   2254 int
   2255 grab_process(pr_info_handle_t *p, int *gret)
   2256 {
   2257 
   2258 	if ((p->pr = Pgrab(p->pid, arg_force, gret)) != NULL) {
   2259 
   2260 		if (Psetflags(p->pr, PR_RLC) != 0) {
   2261 			Prelease(p->pr, 0);
   2262 			return (1);
   2263 		}
   2264 		if (Pcreate_agent(p->pr) == 0) {
   2265 			return (0);
   2266 
   2267 		} else {
   2268 			Prelease(p->pr, 0);
   2269 			return (2);
   2270 		}
   2271 	} else {
   2272 		return (1);
   2273 	}
   2274 }
   2275 
   2276 /*
   2277  * Release the specified process. This destroys the agent
   2278  * and releases the process. If the process is NULL, nothing
   2279  * is done. This function should only be called if grab_process()
   2280  * has previously been called and returned success.
   2281  *
   2282  * This function is Pgrab-safe.
   2283  */
   2284 void
   2285 release_process(struct ps_prochandle *Pr)
   2286 {
   2287 	if (Pr == NULL)
   2288 		return;
   2289 
   2290 	Pdestroy_agent(Pr);
   2291 	Prelease(Pr, 0);
   2292 }
   2293 
   2294 /*
   2295  * preserve_error(char *, ...)
   2296  *
   2297  * preserve_error() should be called rather than warn() by any
   2298  * function that is called while the victim process is held by Pgrab.
   2299  * It will save the error until the process has been un-controlled
   2300  * and output is reasonable again.
   2301  *
   2302  * Note that multiple errors are not stored. Any error in these
   2303  * sections should be critical and return immediately.
   2304  *
   2305  * This function is Pgrab-safe.
   2306  *
   2307  * Since this function may copy untrusted command line arguments to
   2308  * global_error, security practices require that global_error never be
   2309  * printed directly.  Use printf("%s\n", global_error) or equivalent.
   2310  */
   2311 /*PRINTFLIKE1*/
   2312 void
   2313 preserve_error(char *format, ...)
   2314 {
   2315 	va_list alist;
   2316 
   2317 	va_start(alist, format);
   2318 
   2319 	/*
   2320 	 * GLOBAL_ERR_SZ is pretty big. If the error is longer
   2321 	 * than that, just truncate it, rather than chance missing
   2322 	 * the error altogether.
   2323 	 */
   2324 	(void) vsnprintf(global_error, GLOBAL_ERR_SZ-1, format, alist);
   2325 
   2326 	va_end(alist);
   2327 }
   2328