xref: /titanic_41/usr/src/cmd/prctl/prctl.c (revision 159d09a20817016f09b3ea28d1bdada4a336bb91)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <unistd.h>
30 #include <rctl.h>
31 #include <libproc.h>
32 #include <stdio.h>
33 #include <libintl.h>
34 #include <locale.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <strings.h>
38 #include <ctype.h>
39 #include <project.h>
40 #include <sys/types.h>
41 #include <dirent.h>
42 #include <errno.h>
43 #include <stdlib.h>
44 #include <sys/varargs.h>
45 #include <priv.h>
46 #include <zone.h>
47 #include "utils.h"
48 
49 /* Valid user actions */
50 #define	ACTION_DISABLE		0x01
51 #define	ACTION_ENABLE		0x02
52 #define	ACTION_SET		0x04
53 #define	ACTION_REPLACE		0x08
54 #define	ACTION_DELETE		0x10
55 
56 #define	PRCTL_VALUE_WIDTH	4
57 
58 /* Maximum string length for deferred errors */
59 #define	GLOBAL_ERR_SZ		1024
60 
61 /* allow important process values to be passed together easily */
62 typedef struct pr_info_handle {
63 	struct ps_prochandle *pr;
64 	pid_t pid;
65 	psinfo_t psinfo;
66 	taskid_t taskid;
67 	projid_t projid;
68 	char *projname;
69 	zoneid_t zoneid;
70 	char *zonename;
71 
72 } pr_info_handle_t;
73 
74 /* Structures for list of resource controls */
75 typedef struct prctl_value {
76 	rctlblk_t *rblk;
77 	struct prctl_value *next;
78 } prctl_value_t;
79 
80 typedef struct prctl_list {
81 	char *name;
82 	prctl_value_t *val_list;
83 	struct prctl_list *next;
84 } prctl_list_t;
85 
86 static	volatile int	interrupt;
87 
88 static	prctl_list_t	*global_rctl_list_head = NULL;
89 static	prctl_list_t	*global_rctl_list_tail = NULL;
90 static	char		global_error[GLOBAL_ERR_SZ];
91 
92 /* global variables that contain commmand line option info */
93 static	int	arg_operation = 0;
94 static	int	arg_force = 0;
95 
96 
97 /* String and type from -i */
98 static	rctl_entity_t	arg_entity_type = RCENTITY_PROCESS;
99 static	char	*arg_entity_string = NULL;
100 
101 /* -n argument */
102 static  char	*arg_name = NULL;
103 
104 static	rctl_entity_t	arg_name_entity = 0;
105 
106 /* -t argument value */
107 static	int	arg_priv = 0;
108 
109 /* -v argument string */
110 static	char	*arg_valuestring = NULL;
111 
112 /* global flags of rctl name passed to -n */
113 static	int	arg_global_flags = 0;
114 static	rctl_qty_t	arg_global_max;
115 
116 /* appropriate scaling variables determined by rctl unit type */
117 scale_t		*arg_scale;
118 static	char	*arg_unit = NULL;
119 
120 /* -v argument string converted to uint64_t */
121 static	uint64_t arg_value = 0;
122 
123 /* if -v argument is scaled value, points to "K", "M", "G", ... */
124 static  char	*arg_modifier = NULL;
125 
126 /* -e/-d argument string */
127 static	char	*arg_action_string = NULL;
128 
129 /* Set to RCTL_LOCAL_SIGNAL|DENY based on arg_action_string */
130 static	int	arg_action = 0;
131 
132 /* if -e/-d arg is signal=XXX, set to signal number of XXX */
133 static	int	arg_signal = 0;
134 
135 /* -p arg if -p is specified */
136 static	int	arg_pid = -1;
137 static	char	*arg_pid_string = NULL;
138 
139 /* Set to 1 if -P is specified */
140 static	int	arg_parseable_mode = 0;
141 
142 /* interupt handler */
143 static	void	intr(int);
144 
145 static	int	get_rctls(struct ps_prochandle *);
146 static	int	store_rctls(const char *rctlname, void *walk_data);
147 static	prctl_value_t	*store_value_entry(rctlblk_t *rblk, prctl_list_t *list);
148 static	prctl_list_t	*store_list_entry(const char *name);
149 static	void	free_lists();
150 
151 static	int	change_action(rctlblk_t *blk);
152 
153 static	int	prctl_setrctl(struct ps_prochandle *Pr, const char *name,
154 			rctlblk_t *, rctlblk_t *, uint_t);
155 
156 static	int match_rctl(struct ps_prochandle *Pr, rctlblk_t **rctl, char *name,
157 			char *valuestringin, int valuein, rctl_priv_t privin,
158 			int pidin);
159 static	int	match_rctl_blk(rctlblk_t *rctl, char *valuestringin,
160 			uint64_t valuein,
161 			rctl_priv_t privin, int pidin);
162 static	pid_t	regrab_process(pid_t pid, pr_info_handle_t *p, int, int *gret);
163 static	pid_t	grab_process_by_id(char *idname, rctl_entity_t type,
164 			pr_info_handle_t *p, int, int *gret);
165 static	int	grab_process(pr_info_handle_t *p, int *gret);
166 static	void	release_process(struct ps_prochandle *Pr);
167 static	void	preserve_error(char *format, ...);
168 
169 static	void	print_rctls(pr_info_handle_t *p);
170 static	void	print_priv(rctl_priv_t local_priv, char *format);
171 static	void	print_local_action(int action, int *signalp, char *format);
172 
173 static const char USAGE[] = ""
174 "usage:\n"
175 "    Report resource control values and actions:\n"
176 "	prctl [-P] [-t [basic | privileged | system]\n"
177 "	[-n name] [-i process | task | project | zone] id ...\n"
178 "	-P space delimited output\n"
179 "	-t privilege level of rctl values to get\n"
180 "	-n name of resource control values to get\n"
181 "	-i idtype of operand list\n"
182 "    Manipulate resource control values:\n"
183 "	prctl [-t [basic | privileged | system]\n"
184 "	-n name [-srx] [-v value] [-p pid ] [-e | -d action]\n"
185 "	[-i process | task | project | zone] id ...\n"
186 "	-t privilege level of rctl value to set/replace/delete/modify\n"
187 "	-n name of resource control to set/replace/delete/modify\n"
188 "	-s set new resource control value\n"
189 "	-r replace first rctl value of matching privilege\n"
190 "	-x delete first rctl value of matching privilege, value, and \n"
191 "	   recipient pid\n"
192 "	-v value of rctl to set/replace/delete/modify\n"
193 "	-p recipient pid of rctl to set/replace/delete/modify\n"
194 "	-e enable action of first rctl value of matching privilege,\n"
195 "	   value, and recipient pid\n"
196 "	-d disable action of first rctl value of matching privilege,\n"
197 "	   value, and recipient pid\n"
198 "	-i idtype of operand list\n";
199 
200 
201 static void
202 usage()
203 {
204 	(void) fprintf(stderr, gettext(USAGE));
205 	exit(2);
206 }
207 
208 int
209 main(int argc, char **argv)
210 {
211 	int flags;
212 	int opt, errflg = 0;
213 	rctlblk_t *rctlblkA = NULL;
214 	rctlblk_t *rctlblkB = NULL;
215 	rctlblk_t *tmp = NULL;
216 	pid_t pid;
217 	char *target_id;
218 	int search_type;
219 	int signal;
220 	int localaction;
221 	int printed = 0;
222 	int gret;
223 	char *end;
224 
225 	(void) setlocale(LC_ALL, "");
226 	(void) textdomain(TEXT_DOMAIN);
227 	(void) setprogname(argv[0]);
228 
229 	while ((opt = getopt(argc, argv, "sPp:Fd:e:i:n:rt:v:x")) != EOF) {
230 
231 		switch (opt) {
232 		case 'F':	/* force grabbing (no O_EXCL) */
233 			arg_force = PGRAB_FORCE;
234 			break;
235 		case 'i':	/* id type for arguments */
236 			arg_entity_string = optarg;
237 			if (strcmp(optarg, "process") == 0 ||
238 			    strcmp(optarg, "pid") == 0)
239 				arg_entity_type = RCENTITY_PROCESS;
240 			else if (strcmp(optarg, "project") == 0 ||
241 				strcmp(optarg, "projid") == 0)
242 				arg_entity_type = RCENTITY_PROJECT;
243 			else if (strcmp(optarg, "task") == 0 ||
244 				strcmp(optarg, "taskid") == 0)
245 				arg_entity_type = RCENTITY_TASK;
246 			else if (strcmp(optarg, "zone") == 0 ||
247 				strcmp(optarg, "zoneid") == 0)
248 				arg_entity_type = RCENTITY_ZONE;
249 			else {
250 				warn(gettext("unknown idtype %s"), optarg);
251 				errflg = 1;
252 			}
253 			break;
254 		case 'd':
255 			arg_action_string = optarg;
256 			arg_operation |= ACTION_DISABLE;
257 			break;
258 		case 'e':
259 			arg_action_string = optarg;
260 			arg_operation |= ACTION_ENABLE;
261 			break;
262 		case 'n':	/* name of rctl */
263 			arg_name = optarg;
264 			if (strncmp(optarg, "process.",
265 			    strlen("process.")) == 0)
266 				arg_name_entity = RCENTITY_PROCESS;
267 			else if (strncmp(optarg, "project.",
268 			    strlen("project.")) == 0)
269 				arg_name_entity = RCENTITY_PROJECT;
270 			else if (strncmp(optarg, "task.",
271 			    strlen("task.")) == 0)
272 				arg_name_entity = RCENTITY_TASK;
273 			else if (strncmp(optarg, "zone.",
274 			    strlen("zone.")) == 0)
275 				arg_name_entity = RCENTITY_ZONE;
276 			break;
277 		case 'r':
278 			arg_operation |= ACTION_REPLACE;
279 			break;
280 		case 't':	/* rctl type */
281 			if (strcmp(optarg, "basic") == 0)
282 				arg_priv = RCPRIV_BASIC;
283 			else if (strcmp(optarg, "privileged") == 0)
284 				arg_priv = RCPRIV_PRIVILEGED;
285 			else if (strcmp(optarg, "priv") == 0)
286 				arg_priv = RCPRIV_PRIVILEGED;
287 			else if (strcmp(optarg, "system") == 0)
288 				arg_priv = RCPRIV_SYSTEM;
289 			else {
290 				warn(gettext("unknown privilege %s"), optarg);
291 				errflg = 1;
292 			}
293 			break;
294 		case 'v':	/* value */
295 			arg_valuestring = optarg;
296 			break;
297 		case 's':
298 			arg_operation |= ACTION_SET;
299 			break;
300 		case 'x':	/* delete */
301 			arg_operation |= ACTION_DELETE;
302 			break;
303 		case 'p':
304 			errno = 0;
305 			/* Stick with -1 if arg is "-" */
306 			if (strcmp("-", optarg) == 0)
307 				break;
308 
309 			arg_pid_string = optarg;
310 			arg_pid = strtoul(optarg, &end, 10);
311 			if (errno || *end != '\0' || end == optarg) {
312 				warn(gettext("invalid pid %s"), optarg);
313 				errflg = 1;
314 				break;
315 			}
316 			break;
317 		case 'P':
318 			arg_parseable_mode = 1;
319 			break;
320 		default:
321 			warn(gettext("unknown option"));
322 			errflg = 1;
323 			break;
324 		}
325 	}
326 	argc -= optind;
327 	argv += optind;
328 
329 	if (argc < 1) {
330 		warn(gettext("no arguments specified"));
331 		errflg = 1;
332 		goto done_parse;
333 	}
334 	/* if -v is specified without -r, -x, -d, or -e, -s is implied */
335 	if (arg_valuestring &&
336 	    (!(arg_operation & (ACTION_REPLACE | ACTION_DELETE |
337 		ACTION_DISABLE | ACTION_ENABLE)))) {
338 		arg_operation |= ACTION_SET;
339 	}
340 	/* operations require -n */
341 	if (arg_operation && (arg_name == NULL)) {
342 		warn(gettext("-n is required with -s, -r, -x, -e, or -d"));
343 		errflg = 1;
344 		goto done_parse;
345 	}
346 	/* enable and disable are exclusive */
347 	if ((arg_operation & ACTION_ENABLE) &&
348 	    (arg_operation & ACTION_DISABLE)) {
349 		warn(gettext("options -d and -e are exclusive"));
350 		errflg = 1;
351 		goto done_parse;
352 	}
353 	/* -s, -r, and -x are exclusive */
354 	flags = arg_operation &
355 	    (ACTION_REPLACE | ACTION_SET | ACTION_DELETE);
356 	if (flags & (flags - 1)) {
357 		warn(gettext("options -s, -r, and -x are exclusive"));
358 		errflg = 1;
359 		goto done_parse;
360 	}
361 	/* -e or -d makes no sense with -x */
362 	if ((arg_operation & ACTION_DELETE) &
363 	    (arg_operation & (ACTION_ENABLE | ACTION_DISABLE))) {
364 		warn(gettext("options -e or -d  not allowed with -x"));
365 		errflg = 1;
366 		goto done_parse;
367 	}
368 	/* if -r is specified -v must be as well */
369 	if ((arg_operation & ACTION_REPLACE) && (!arg_valuestring)) {
370 		warn(gettext("option -r requires use of option -v"));
371 		errflg = 1;
372 		goto done_parse;
373 	}
374 	/* if -s is specified -v must be as well */
375 	if ((arg_operation & ACTION_SET) && (!arg_valuestring)) {
376 		warn(gettext("option -s requires use of option -v"));
377 		errflg = 1;
378 		goto done_parse;
379 	}
380 	/* Specifying a recipient pid on a non-basic rctl makes no sense */
381 	if (arg_pid != -1 && arg_priv > RCPRIV_BASIC) {
382 		warn(gettext("option -p not allowed on non-basic rctl"));
383 		errflg = 1;
384 		goto done_parse;
385 	}
386 	/* Specifying a recipient pid on a privileged rctl makes no sense */
387 	if (arg_pid != -1 &&
388 	    arg_priv == RCPRIV_PRIVILEGED) {
389 		warn(gettext("option -p not allowed with privileged rctl"));
390 		errflg = 1;
391 		goto done_parse;
392 	}
393 	if (arg_operation) {
394 
395 		/* do additional checks if there is an operation */
396 
397 		if (arg_parseable_mode == 1) {
398 			warn(gettext("-P not valid when manipulating "
399 			    "resource control values"));
400 			errflg = 1;
401 			goto done_parse;
402 		}
403 		/* get rctl global flags to determine if actions are valid */
404 		if ((rctlblkA = calloc(1, rctlblk_size())) == NULL) {
405 			warn(gettext("malloc failed: %s"),
406 			    strerror(errno));
407 			errflg = 1;
408 			goto done_parse;
409 		}
410 		if ((rctlblkB = calloc(1, rctlblk_size())) == NULL) {
411 			warn(gettext("malloc failed: %s"),
412 			    strerror(errno));
413 			errflg = 1;
414 			goto done_parse;
415 		}
416 		/* get system rctl to get global flags and max value */
417 		if (getrctl(arg_name, NULL, rctlblkA, RCTL_FIRST)) {
418 			warn(gettext("failed to get resource control "
419 			    "for %s: %s"), arg_name, strerror(errno));
420 			errflg = 1;
421 			goto done_parse;
422 		}
423 		while (getrctl(arg_name, rctlblkA, rctlblkB, RCTL_NEXT) == 0) {
424 
425 			/* allow user interrupt */
426 			if (interrupt) {
427 				errflg = 1;
428 				goto done_parse;
429 			}
430 			tmp = rctlblkB;
431 			rctlblkB = rctlblkA;
432 			rctlblkA = tmp;
433 
434 			if (rctlblk_get_privilege(rctlblkA) ==
435 			    RCPRIV_SYSTEM) {
436 				break;
437 			}
438 		}
439 		if (rctlblk_get_privilege(rctlblkA) != RCPRIV_SYSTEM) {
440 			warn(gettext("failed to get system resource control "
441 			    "for %s: %s"), arg_name, strerror(errno));
442 			errflg = 1;
443 			goto done_parse;
444 		}
445 		/* figure out the correct scale and unit for this rctl */
446 		arg_global_flags = rctlblk_get_global_flags(rctlblkA);
447 		arg_global_max = rctlblk_get_value(rctlblkA);
448 
449 		if (arg_global_flags & RCTL_GLOBAL_BYTES) {
450 			arg_unit = SCALED_UNIT_BYTES;
451 			arg_scale = scale_binary;
452 
453 		} else if (arg_global_flags & RCTL_GLOBAL_SECONDS) {
454 			arg_unit = SCALED_UNIT_SECONDS;
455 			arg_scale = scale_metric;
456 
457 		} else {
458 			arg_unit = SCALED_UNIT_NONE;
459 			arg_scale = scale_metric;
460 		}
461 		/* parse -v value string */
462 		if (arg_valuestring) {
463 			if (scaledtouint64(arg_valuestring,
464 			    &arg_value, NULL, &arg_modifier, NULL,
465 			    arg_scale, arg_unit,
466 			    SCALED_ALL_FLAGS)) {
467 
468 				warn(gettext("invalid -v value %s"),
469 				    arg_valuestring);
470 				errflg = 1;
471 				goto done_parse;
472 			}
473 			if (arg_value > arg_global_max) {
474 				warn(gettext("-v value %s exceeds system "
475 				    "limit for resource control: %s"),
476 				    arg_valuestring, arg_name);
477 				errflg = 1;
478 				goto done_parse;
479 			}
480 		}
481 		/* parse action */
482 		if (arg_action_string) {
483 
484 			char *sigchr;
485 			char *iter;
486 
487 			if ((strcmp(arg_action_string,
488 			    "signal") == 0) ||
489 			    (strcmp(arg_action_string,
490 				"sig") == 0)) {
491 
492 				if (arg_operation & ACTION_ENABLE) {
493 					warn(gettext(
494 					    "signal name or number must be "
495 					    "specified with -e"));
496 					errflg = 1;
497 					goto done_parse;
498 				}
499 				arg_action = RCTL_LOCAL_SIGNAL;
500 				arg_signal = -1;
501 
502 			} else if ((strncmp(arg_action_string,
503 			    "signal=", strlen("signal=")) == 0) ||
504 			    (strncmp(arg_action_string,
505 				"sig=", strlen("sig=")) == 0)) {
506 
507 				arg_action = RCTL_LOCAL_SIGNAL;
508 				sigchr = strrchr(arg_action_string, '=');
509 				sigchr++;
510 
511 				iter = sigchr;
512 				while (*iter) {
513 					*iter = toupper(*iter);
514 					iter++;
515 				}
516 				if (strncmp("SIG", sigchr, 3) == 0)
517 					sigchr += 3;
518 
519 
520 				if (str2sig(sigchr, &arg_signal) != 0) {
521 					warn(gettext("signal invalid"));
522 					errflg = 1;
523 					goto done_parse;
524 				}
525 			} else if (strcmp(arg_action_string, "deny") == 0) {
526 
527 				arg_action = RCTL_LOCAL_DENY;
528 
529 			} else if (strcmp(arg_action_string, "all") == 0) {
530 
531 				if (arg_operation & ACTION_ENABLE) {
532 					warn(gettext(
533 					    "cannot use action 'all' with -e"));
534 					errflg = 1;
535 					goto done_parse;
536 				}
537 				arg_action = RCTL_LOCAL_DENY |
538 				    RCTL_LOCAL_SIGNAL;
539 				arg_signal = -1;
540 				goto done_parse;
541 			} else {
542 				warn(gettext("action invalid"));
543 				errflg = 1;
544 				goto done_parse;
545 			}
546 		}
547 		/* cannot manipulate system rctls */
548 		if (arg_priv == RCPRIV_SYSTEM) {
549 
550 			warn(gettext("cannot modify system values"));
551 			errflg = 1;
552 			goto done_parse;
553 		}
554 		/* validate that the privilege is allowed */
555 		if ((arg_priv == RCPRIV_BASIC) &&
556 		    (arg_global_flags & RCTL_GLOBAL_NOBASIC)) {
557 
558 			warn(gettext("basic values not allowed on rctl %s"),
559 			    arg_name);
560 			errflg = 1;
561 			goto done_parse;
562 		}
563 		/* validate that actions are appropriate for given rctl */
564 		if ((arg_operation & ACTION_ENABLE) &&
565 		    (arg_action & RCTL_LOCAL_DENY) &&
566 		    (arg_global_flags & RCTL_GLOBAL_DENY_NEVER)) {
567 
568 			warn(gettext("unable to enable deny on rctl with "
569 			    "global flag 'no-deny'"));
570 			errflg = 1;
571 			goto done_parse;
572 		}
573 		if ((arg_operation & ACTION_DISABLE) &&
574 		    (arg_action & RCTL_LOCAL_DENY) &&
575 		    (arg_global_flags & RCTL_GLOBAL_DENY_ALWAYS)) {
576 
577 			warn(gettext("unable to disable deny on rctl with "
578 			    "global flag 'deny'"));
579 			errflg = 1;
580 			goto done_parse;
581 		}
582 		if ((arg_operation & ACTION_ENABLE) &&
583 		    (arg_action & RCTL_LOCAL_SIGNAL) &&
584 		    (arg_global_flags & RCTL_GLOBAL_SIGNAL_NEVER)) {
585 
586 			warn(gettext("unable to enable signal on rctl with "
587 			    "global flag 'no-signal'"));
588 			errflg = 1;
589 			goto done_parse;
590 		}
591 		/* now set defaults for options not supplied */
592 
593 		/*
594 		 * default privilege to basic if this is a seting an rctl
595 		 * operation
596 		 */
597 		if (arg_operation & ACTION_SET) {
598 			if (arg_priv == 0) {
599 				arg_priv = RCPRIV_BASIC;
600 			}
601 		}
602 		/*
603 		 * -p is required when set a basic task,
604 		 * project or zone rctl
605 		 */
606 		if ((arg_pid == -1) &&
607 		    (arg_priv == RCPRIV_BASIC) &&
608 		    (arg_entity_type != RCENTITY_PROCESS) &&
609 		    (arg_operation & ACTION_SET) &&
610 		    (arg_name) &&
611 		    (arg_name_entity == RCENTITY_TASK ||
612 		    arg_name_entity == RCENTITY_PROJECT ||
613 		    arg_name_entity == RCENTITY_ZONE)) {
614 
615 			warn(gettext("-p pid required when setting or "
616 			    "replacing task or project rctl"));
617 			errflg = 1;
618 			goto done_parse;
619 		}
620 	} else {
621 
622 		/* validate for list mode */
623 		/* -p is not valid in list mode */
624 		if (arg_pid != -1) {
625 			warn(gettext("-p pid requires -s, -r, -x, -e, or -d"));
626 			errflg = 1;
627 			goto done_parse;
628 		}
629 	}
630 	/* getting/setting process rctl on task or project is error */
631 	if ((arg_name && (arg_name_entity == RCENTITY_PROCESS)) &&
632 	    ((arg_entity_type == RCENTITY_TASK) ||
633 	    (arg_entity_type == RCENTITY_PROJECT))) {
634 
635 		warn(gettext("cannot get/set process rctl on task "
636 		    "or project"));
637 		errflg = 1;
638 		goto done_parse;
639 	}
640 	/* getting/setting task rctl on project is error */
641 	if ((arg_name && (arg_name_entity == RCENTITY_TASK)) &&
642 	    (arg_entity_type == RCENTITY_PROJECT)) {
643 
644 		warn(gettext("cannot get/set task rctl on project"));
645 		errflg = 1;
646 		goto done_parse;
647 	}
648 
649 done_parse:
650 
651 	/* free any rctlblk's that we may have allocated */
652 	if (rctlblkA) {
653 		free(rctlblkA);
654 		rctlblkA = NULL;
655 	}
656 	if (rctlblkB) {
657 		free(rctlblkB);
658 		rctlblkB = NULL;
659 	}
660 	if (errflg)
661 		usage();
662 
663 	/* catch signals from terminal */
664 	if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
665 		(void) sigset(SIGHUP, intr);
666 	if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
667 		(void) sigset(SIGINT, intr);
668 	if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
669 		(void) sigset(SIGQUIT, intr);
670 	(void) sigset(SIGTERM, intr);
671 
672 	while (--argc >= 0 && !interrupt) {
673 		pr_info_handle_t p;
674 		char *arg = *argv++;
675 		int intarg;
676 		char *end;
677 		errflg = 0;
678 
679 		gret = 0;
680 
681 		/* Store int version of arg */
682 		errno = 0;
683 		intarg = strtoul(arg, &end, 10);
684 		if (errno || *end != '\0' || end == arg) {
685 			intarg = -1;
686 		}
687 
688 		/*
689 		 * -p defaults to arg if basic and collective rctl
690 		 * and -i process is specified
691 		 */
692 		if ((arg_pid == -1) &&
693 		    (arg_priv == RCPRIV_BASIC) &&
694 		    (arg_entity_type == RCENTITY_PROCESS) &&
695 		    (arg_name) &&
696 		    (arg_name_entity == RCENTITY_TASK ||
697 		    arg_name_entity == RCENTITY_PROJECT)) {
698 			arg_pid_string = arg;
699 			errno = 0;
700 			arg_pid = intarg;
701 		}
702 		/* Specifying a recipient pid and -i pid is redundent */
703 		if (arg_pid != -1 && arg_entity_type == RCENTITY_PROCESS &&
704 		    arg_pid != intarg) {
705 			warn(gettext("option -p pid must match -i process"));
706 			errflg = 1;
707 			continue;
708 		}
709 		/* use recipient pid if we have one */
710 		if (arg_pid_string != NULL) {
711 			target_id = arg_pid_string;
712 			search_type = RCENTITY_PROCESS;
713 		} else {
714 			target_id = arg;
715 			search_type = arg_entity_type;
716 		}
717 		(void) fflush(stdout);	/* process-at-a-time */
718 
719 		if (arg_operation != 0) {
720 
721 			if ((pid = grab_process_by_id(target_id,
722 			    search_type, &p, arg_priv, &gret)) < 0) {
723 				/*
724 				 * Mark that an error occurred so that the
725 				 * return value can be set, but continue
726 				 * on with other processes
727 				 */
728 				errflg = 1;
729 				continue;
730 			}
731 
732 			/*
733 			 * At this point, the victim process is held.
734 			 * Do not call any Pgrab-unsafe functions until
735 			 * the process is released via release_process().
736 			 */
737 
738 			errflg = get_rctls(p.pr);
739 
740 			if (arg_operation & ACTION_DELETE) {
741 
742 				/* match by privilege, value, and pid */
743 				if (match_rctl(p.pr, &rctlblkA, arg_name,
744 				    arg_valuestring, arg_value, arg_priv,
745 				    arg_pid) != 0 || rctlblkA == NULL) {
746 
747 					if (interrupt)
748 						goto out;
749 
750 					preserve_error(gettext("no matching "
751 					    "resource control found for "
752 					    "deletion"));
753 					errflg = 1;
754 					goto out;
755 				}
756 				/*
757 				 * grab correct process.  This is neccessary
758 				 * if the recipient pid does not match the
759 				 * one we grabbed
760 				 */
761 				pid = regrab_process(
762 				    rctlblk_get_recipient_pid(rctlblkA),
763 				    &p, arg_priv, &gret);
764 
765 				if (pid < 0) {
766 					errflg = 1;
767 					goto out;
768 				}
769 				if (prctl_setrctl(p.pr, arg_name, NULL,
770 				    rctlblkA, RCTL_DELETE) != 0) {
771 					errflg = 1;
772 					goto out;
773 				}
774 			} else if (arg_operation & ACTION_SET) {
775 
776 				/* match by privilege, value, and pid */
777 
778 				if (match_rctl(p.pr, &rctlblkA, arg_name,
779 				    arg_valuestring, arg_value, arg_priv,
780 				    arg_pid) == 0) {
781 
782 					if (interrupt)
783 						goto out;
784 
785 					preserve_error(gettext("resource "
786 					    "control already exists"));
787 					errflg = 1;
788 					goto out;
789 				}
790 				rctlblkB = calloc(1, rctlblk_size());
791 				if (rctlblkB == NULL) {
792 					preserve_error(gettext(
793 					    "malloc failed"), strerror(errno));
794 					errflg = 1;
795 					goto out;
796 				}
797 				rctlblk_set_value(rctlblkB, arg_value);
798 				rctlblk_set_privilege(rctlblkB, arg_priv);
799 				if (change_action(rctlblkB)) {
800 					errflg = 1;
801 					goto out;
802 				}
803 				if (prctl_setrctl(p.pr, arg_name, NULL,
804 				    rctlblkB, RCTL_INSERT) != 0) {
805 					errflg = 1;
806 					goto out;
807 				}
808 			} else if (arg_operation & ACTION_REPLACE) {
809 				/*
810 				 * match rctl for deletion by privilege and
811 				 * pid only
812 				 */
813 				if (match_rctl(p.pr, &rctlblkA, arg_name,
814 				    NULL, 0, arg_priv,
815 				    arg_pid) != 0 || rctlblkA == NULL) {
816 
817 					if (interrupt)
818 						goto out;
819 
820 					preserve_error(gettext("no matching "
821 					    "resource control to replace"));
822 					errflg = 1;
823 					goto out;
824 				}
825 				/*
826 				 * grab correct process.  This is neccessary
827 				 * if the recipient pid does not match the
828 				 * one we grabbed
829 				 */
830 				pid = regrab_process(
831 				    rctlblk_get_recipient_pid(rctlblkA),
832 				    &p, arg_priv, &gret);
833 				if (pid < 0) {
834 					errflg = 1;
835 					goto out;
836 				}
837 				pid = rctlblk_get_recipient_pid(rctlblkA);
838 
839 				/*
840 				 * match by privilege, value and pid to
841 				 * check if new rctl  already exists
842 				 */
843 				if (match_rctl(p.pr, &rctlblkB, arg_name,
844 				    arg_valuestring, arg_value, arg_priv,
845 				    pid) < 0) {
846 
847 					if (interrupt)
848 						goto out;
849 
850 					preserve_error(gettext(
851 						"Internal Error"));
852 					errflg = 1;
853 					goto out;
854 				}
855 				/*
856 				 * If rctl already exists, and it does not
857 				 *  match the one that we will delete, than
858 				 *  the replace will fail.
859 				 */
860 				if (rctlblkB != NULL &&
861 				    arg_value !=
862 					rctlblk_get_value(rctlblkA)) {
863 
864 					preserve_error(gettext("replacement "
865 					    "resource control already "
866 					    "exists"));
867 
868 					errflg = 1;
869 					goto out;
870 				}
871 				/* create new rctl */
872 				rctlblkB = calloc(1, rctlblk_size());
873 				if (rctlblkB == NULL) {
874 					preserve_error(gettext(
875 					    "malloc failed"), strerror(errno));
876 					errflg = 1;
877 					goto out;
878 				}
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,
1169 		    strlen(arg_name)) == 0) &&
1170 	    (arg_entity_string == NULL ||
1171 		rblk_entity >= arg_entity_type)) {
1172 
1173 		/* Once we know we have some controls, store the name */
1174 		if ((list = store_list_entry(rctlname)) == NULL) {
1175 			free(rblk1);
1176 			free(rblk2);
1177 			return (1);
1178 		}
1179 		if (store_value_entry(rblk1, list) == NULL) {
1180 			free(rblk1);
1181 			free(rblk2);
1182 			return (1);
1183 		}
1184 	}
1185 	while (pr_getrctl(Pr, rctlname, rblk1, rblk2, RCTL_NEXT) == 0) {
1186 
1187 		/*
1188 		 * in case this is stuck for some reason, allow manual
1189 		 * interrupt
1190 		 */
1191 		if (interrupt) {
1192 			free(rblk1);
1193 			free(rblk2);
1194 			return (1);
1195 		}
1196 		rblk_priv = rctlblk_get_privilege(rblk2);
1197 		/*
1198 		 * Store control if it matches privilege and entity type
1199 		 * criteria
1200 		 */
1201 		if (((arg_priv == 0) || (rblk_priv == arg_priv)) &&
1202 		    ((arg_name == NULL) ||
1203 			strncmp(rctlname, arg_name,
1204 			    strlen(arg_name)) == 0) &&
1205 		    (arg_entity_string == NULL ||
1206 			rblk_entity == arg_entity_type)) {
1207 
1208 			/* May not have created the list yet. */
1209 			if (list == NULL) {
1210 				if ((list = store_list_entry(rctlname))
1211 				    == NULL) {
1212 					free(rblk1);
1213 					free(rblk2);
1214 					return (1);
1215 				}
1216 			}
1217 			if (store_value_entry(rblk2, list) == NULL) {
1218 				free(rblk1);
1219 				free(rblk2);
1220 				return (1);
1221 			}
1222 		}
1223 		rblk_tmp = rblk1;
1224 		rblk1 = rblk2;
1225 		rblk2 = rblk_tmp;
1226 	}
1227 	free(rblk1);
1228 	free(rblk2);
1229 	return (0);
1230 }
1231 
1232 /*
1233  * store_value_entry(rctlblk_t *, prctl_list_t *)
1234  *
1235  * Store an rblk for a given resource control into the global list.
1236  *
1237  * This function is Pgrab-safe.
1238  */
1239 prctl_value_t *
1240 store_value_entry(rctlblk_t *rblk, prctl_list_t *list)
1241 {
1242 	prctl_value_t *e = calloc(1, sizeof (prctl_value_t));
1243 	rctlblk_t *store_blk = calloc(1, rctlblk_size());
1244 	prctl_value_t *iter = list->val_list;
1245 
1246 	if (e == NULL || store_blk == NULL) {
1247 		preserve_error(gettext("malloc failed %s"),
1248 		    strerror(errno));
1249 		if (e != NULL)
1250 			free(e);
1251 		if (store_blk != NULL)
1252 			free(store_blk);
1253 		return (NULL);
1254 	}
1255 	if (iter == NULL)
1256 		list->val_list = e;
1257 	else {
1258 		while (iter->next != NULL) {
1259 			iter = iter->next;
1260 		}
1261 		iter->next = e;
1262 	}
1263 	bcopy(rblk, store_blk, rctlblk_size());
1264 
1265 	e->rblk = store_blk;
1266 	e->next = NULL;
1267 	return (e);
1268 }
1269 
1270 /*
1271  * store_list_entry(const char *)
1272  *
1273  * Store a new resource control value in the global list. No checking
1274  * for duplicates done.
1275  *
1276  * This function is Pgrab-safe.
1277  */
1278 prctl_list_t *
1279 store_list_entry(const char *name)
1280 {
1281 	prctl_list_t *e = calloc(1, sizeof (prctl_list_t));
1282 
1283 	if (e == NULL) {
1284 		preserve_error(gettext("malloc failed %s"),
1285 		    strerror(errno));
1286 		return (NULL);
1287 	}
1288 	if ((e->name = calloc(1, strlen(name) + 1)) == NULL) {
1289 		preserve_error(gettext("malloc failed %s"),
1290 		    strerror(errno));
1291 		free(e);
1292 		return (NULL);
1293 	}
1294 	(void) strcpy(e->name, name);
1295 	e->val_list = NULL;
1296 
1297 	if (global_rctl_list_head == NULL) {
1298 		global_rctl_list_head = e;
1299 		global_rctl_list_tail = e;
1300 	} else {
1301 		global_rctl_list_tail->next = e;
1302 		global_rctl_list_tail = e;
1303 	}
1304 	e->next = NULL;
1305 	return (e);
1306 }
1307 
1308 /*
1309  * free_lists()
1310  *
1311  * Free all resource control blocks and values from the global lists.
1312  *
1313  * This function is Pgrab-safe.
1314  */
1315 void
1316 free_lists()
1317 {
1318 	prctl_list_t *new_list, *old_list = global_rctl_list_head;
1319 	prctl_value_t *old_val, *new_val;
1320 
1321 	while (old_list != NULL) {
1322 		old_val = old_list->val_list;
1323 		while (old_val != NULL) {
1324 			free(old_val->rblk);
1325 			new_val = old_val->next;
1326 			free(old_val);
1327 			old_val = new_val;
1328 		}
1329 		free(old_list->name);
1330 		new_list = old_list->next;
1331 		free(old_list);
1332 		old_list = new_list;
1333 	}
1334 	global_rctl_list_head = NULL;
1335 	global_rctl_list_tail = NULL;
1336 }
1337 
1338 void
1339 print_heading()
1340 {
1341 
1342 	/* print headings */
1343 	(void) fprintf(stdout, "%-8s%-16s%-9s%-7s%-28s%10s\n",
1344 	    "NAME", "PRIVILEGE", "VALUE",
1345 	    "FLAG", "ACTION", "RECIPIENT");
1346 }
1347 
1348 /*
1349  * print_rctls()
1350  *
1351  * Print all resource controls from the global list that was
1352  * previously populated by store_rctls.
1353  */
1354 void
1355 print_rctls(pr_info_handle_t *p)
1356 {
1357 	prctl_list_t *iter_list = global_rctl_list_head;
1358 	prctl_value_t *iter_val;
1359 	rctl_qty_t  rblk_value;
1360 	rctl_priv_t rblk_priv;
1361 	uint_t local_action;
1362 	int signal, local_flags, global_flags;
1363 	pid_t pid;
1364 	char rctl_valuestring[SCALED_STRLEN];
1365 	char *unit = NULL;
1366 	scale_t *scale;
1367 	char *string;
1368 	int doneheading = 0;
1369 
1370 	if (iter_list == NULL)
1371 		return;
1372 
1373 	while (iter_list != NULL) {
1374 
1375 		if (doneheading == 0 &&
1376 		    arg_entity_type == RCENTITY_PROCESS) {
1377 			proc_unctrl_psinfo(&(p->psinfo));
1378 			doneheading = 1;
1379 			(void) fprintf(stdout,
1380 			    "process: %d: %.70s\n", (int)p->pid,
1381 			    p->psinfo.pr_psargs);
1382 			if (!arg_parseable_mode)
1383 				print_heading();
1384 		}
1385 		if (doneheading == 0 &&
1386 		    arg_entity_type == RCENTITY_TASK) {
1387 			doneheading = 1;
1388 			(void) fprintf(stdout, "task: %d\n", (int)p->taskid);
1389 			if (!arg_parseable_mode)
1390 				print_heading();
1391 		}
1392 		if (doneheading == 0 &&
1393 		    arg_entity_type == RCENTITY_PROJECT) {
1394 			if (!arg_parseable_mode && doneheading)
1395 				(void) fprintf(stdout, "\n");
1396 			doneheading = 1;
1397 			(void) fprintf(stdout,
1398 			    "project: %d: %.70s\n", (int)p->projid,
1399 			    p->projname);
1400 			if (!arg_parseable_mode)
1401 				print_heading();
1402 		}
1403 		if (doneheading == 0 &&
1404 		    arg_entity_type == RCENTITY_ZONE) {
1405 			doneheading = 1;
1406 			(void) fprintf(stdout,
1407 			    "zone: %d: %.70s\n", (int)p->zoneid,
1408 			    p->zonename);
1409 			if (!arg_parseable_mode)
1410 				print_heading();
1411 		}
1412 		/* only print name once in normal output */
1413 		if (!arg_parseable_mode)
1414 			(void) fprintf(stdout, "%s\n", iter_list->name);
1415 
1416 		iter_val = iter_list->val_list;
1417 
1418 		/* if for some reason there are no values, skip */
1419 		if (iter_val == 0)
1420 			continue;
1421 
1422 
1423 		/* get the global flags the first rctl only */
1424 		global_flags = rctlblk_get_global_flags(iter_val->rblk);
1425 
1426 
1427 		if (global_flags & RCTL_GLOBAL_BYTES) {
1428 			unit = SCALED_UNIT_BYTES;
1429 			scale = scale_binary;
1430 
1431 		} else if (global_flags & RCTL_GLOBAL_SECONDS) {
1432 			unit = SCALED_UNIT_SECONDS;
1433 			scale = scale_metric;
1434 
1435 		} else {
1436 			unit = SCALED_UNIT_NONE;
1437 			scale = scale_metric;
1438 		}
1439 		/* iterate over an print all control values */
1440 		while (iter_val != NULL) {
1441 
1442 			/* print name or empty name field */
1443 			if (!arg_parseable_mode)
1444 				(void) fprintf(stdout, "%8s", "");
1445 			else
1446 				(void) fprintf(stdout, "%s ", iter_list->name);
1447 
1448 
1449 			rblk_priv = rctlblk_get_privilege(iter_val->rblk);
1450 			if (!arg_parseable_mode)
1451 				print_priv(rblk_priv, "%-16s");
1452 			else
1453 				print_priv(rblk_priv, "%s ");
1454 
1455 			rblk_value = rctlblk_get_value(iter_val->rblk);
1456 			if (arg_parseable_mode) {
1457 				(void) fprintf(stdout, "%llu ", rblk_value);
1458 
1459 			} else {
1460 
1461 				(void) uint64toscaled(rblk_value, 4, "E",
1462 				    rctl_valuestring, NULL, NULL,
1463 				    scale, NULL, 0);
1464 
1465 				(void) fprintf(stdout, "%5s",
1466 				    rctl_valuestring);
1467 				(void) fprintf(stdout, "%-4s", unit);
1468 			}
1469 			local_flags = rctlblk_get_local_flags(iter_val->rblk);
1470 
1471 			if (local_flags & RCTL_LOCAL_MAXIMAL) {
1472 
1473 				if (global_flags & RCTL_GLOBAL_INFINITE) {
1474 					string = "inf";
1475 				} else {
1476 					string = "max";
1477 				}
1478 			} else {
1479 				string = "-";
1480 			}
1481 			if (arg_parseable_mode)
1482 				(void) fprintf(stdout, "%s ", string);
1483 			else
1484 				(void) fprintf(stdout, "%4s%3s",
1485 				    string, "");
1486 
1487 
1488 			local_action = rctlblk_get_local_action(iter_val->rblk,
1489 			    &signal);
1490 
1491 			if (arg_parseable_mode)
1492 				print_local_action(local_action, &signal,
1493 				    "%s ");
1494 			else
1495 				print_local_action(local_action, &signal,
1496 				    "%-28s");
1497 
1498 			pid = rctlblk_get_recipient_pid(iter_val->rblk);
1499 
1500 			if (arg_parseable_mode) {
1501 				if (pid < 0) {
1502 					(void) fprintf(stdout, "%s\n", "-");
1503 				} else {
1504 					(void) fprintf(stdout, "%d\n",
1505 					    (int)pid);
1506 				}
1507 			} else {
1508 				if (pid < 0) {
1509 					(void) fprintf(stdout, "%10s\n", "-");
1510 				} else {
1511 					(void) fprintf(stdout, "%10d\n",
1512 					    (int)pid);
1513 				}
1514 			}
1515 			iter_val = iter_val->next;
1516 		}
1517 		iter_list = iter_list->next;
1518 	}
1519 }
1520 
1521 /*
1522  *
1523  * match_rctl
1524  *
1525  * find the first rctl with matching name, value, priv, and recipient pid
1526  */
1527 int
1528 match_rctl(struct ps_prochandle *Pr, rctlblk_t **rctl, char *name,
1529     char *valuestringin, int valuein, rctl_priv_t privin, int pidin)
1530 {
1531 	rctlblk_t *next;
1532 	rctlblk_t *last;
1533 	rctlblk_t *tmp;
1534 
1535 	*rctl = NULL;
1536 
1537 	next = calloc(1, rctlblk_size());
1538 	last = calloc(1, rctlblk_size());
1539 
1540 	if ((last == NULL) || (next == NULL)) {
1541 		preserve_error(gettext("malloc failed"), strerror(errno));
1542 		return (-1);
1543 	}
1544 	/*
1545 	 * For this resource name, now iterate through all
1546 	 * the  controls, looking for a match to the
1547 	 * user-specified input.
1548 	 */
1549 	if (pr_getrctl(Pr, name, NULL, next, RCTL_FIRST)) {
1550 		preserve_error(gettext("failed to get resource control "
1551 		    "for %s: %s"), name, strerror(errno));
1552 		return (-1);
1553 	}
1554 	if (match_rctl_blk(next, valuestringin, valuein, privin, pidin) == 1) {
1555 		free(last);
1556 		*rctl = next;
1557 		return (0);
1558 	}
1559 	tmp = next;
1560 	next = last;
1561 	last = tmp;
1562 
1563 	while (pr_getrctl(Pr, name, last, next,	RCTL_NEXT) == 0) {
1564 
1565 		/* allow user interrupt */
1566 		if (interrupt)
1567 			break;
1568 
1569 		if (match_rctl_blk(next, valuestringin, valuein, privin, pidin)
1570 		    == 1) {
1571 			free(last);
1572 			*rctl = next;
1573 			return (0);
1574 		}
1575 		tmp = next;
1576 		next = last;
1577 		last = tmp;
1578 	}
1579 	free(next);
1580 	free(last);
1581 
1582 	return (1);
1583 }
1584 
1585 /*
1586  * int match_rctl_blk(rctlblk_t *, char *, uint64, rctl_priv_t, int pid)
1587  *
1588  * Input
1589  *   Must supply a valid rctl, value, privilege, and pid to match on.
1590  *   If valuestring is NULL, then valuestring and valuein will not be used
1591  *   If privilege type is 0 it will not be used.
1592  *   If pid is -1 it will not be used.
1593  *
1594  * Return values
1595  *   Returns 1 if a matching rctl given matches the parameters specified, and
1596  *   0 if they do not.
1597  *
1598  * This function is Pgrab-safe.
1599  */
1600 int
1601 match_rctl_blk(rctlblk_t *rctl, char *valuestringin,
1602     uint64_t valuein, rctl_priv_t privin, int pidin)
1603 {
1604 
1605 	rctl_qty_t value;
1606 	rctl_priv_t priv;
1607 	pid_t pid;
1608 	int valuematch = 1;
1609 	int privmatch = 1;
1610 	int pidmatch = 1;
1611 
1612 	value = rctlblk_get_value(rctl);
1613 	priv = rctlblk_get_privilege(rctl);
1614 	pid = rctlblk_get_recipient_pid(rctl);
1615 
1616 	if (valuestringin) {
1617 
1618 		if (arg_modifier == NULL) {
1619 			valuematch = (valuein == value);
1620 		} else {
1621 			valuematch = scaledequint64(valuestringin, value,
1622 			    PRCTL_VALUE_WIDTH,
1623 			    arg_scale, arg_unit,
1624 			    SCALED_ALL_FLAGS);
1625 		}
1626 	}
1627 	if (privin != 0) {
1628 		privmatch = (privin == priv);
1629 	}
1630 	if (pidin != -1) {
1631 		pidmatch = (pidin == pid);
1632 	}
1633 	return (valuematch && privmatch && pidmatch);
1634 }
1635 
1636 static int
1637 change_action(rctlblk_t *blk)
1638 {
1639 	int signal = 0;
1640 	int action;
1641 
1642 	action = rctlblk_get_local_action(blk, &signal);
1643 
1644 	if (arg_operation & ACTION_ENABLE) {
1645 
1646 		if (arg_action & RCTL_LOCAL_SIGNAL) {
1647 			signal = arg_signal;
1648 		}
1649 		action = action | arg_action;
1650 		/* add local action */
1651 		rctlblk_set_local_action(blk, action, signal);
1652 
1653 	} else if (arg_operation & ACTION_DISABLE) {
1654 
1655 		/*
1656 		 * if deleting signal and signal number is specified,
1657 		 * then signal number must match
1658 		 */
1659 		if ((arg_action & RCTL_LOCAL_SIGNAL) &&
1660 		    (arg_signal != -1)) {
1661 			if (arg_signal != signal) {
1662 				preserve_error(gettext("signal name or number "
1663 				    "does not match existing action"));
1664 				return (-1);
1665 			}
1666 		}
1667 		/* remove local action */
1668 		action = action & (~arg_action);
1669 		rctlblk_set_local_action(blk, RCTL_LOCAL_NOACTION, 0);
1670 		rctlblk_set_local_action(blk, action, signal);
1671 	}
1672 	/* enable deny if it must be enabled */
1673 	if (arg_global_flags & RCTL_GLOBAL_DENY_ALWAYS) {
1674 		rctlblk_set_local_action(blk, RCTL_LOCAL_DENY | action,
1675 		signal);
1676 	}
1677 	return (0);
1678 }
1679 
1680 /*
1681  * prctl_setrctl
1682  *
1683  * Input
1684  *   This function expects that input has been validated. In the
1685  *   case of a replace operation, both old_rblk and new_rblk must
1686  *   be valid resource controls. If a resource control is being
1687  *   created, only new_rblk must be supplied. If a resource control
1688  *   is being deleted, only new_rblk must be supplied.
1689  *
1690  * If the privilege is a priviliged type, at this time, the process
1691  * tries to take on superuser privileges.
1692  */
1693 int
1694 prctl_setrctl(struct ps_prochandle *Pr, const char *name,
1695     rctlblk_t *old_rblk, rctlblk_t *new_rblk, uint_t flags)
1696 {
1697 	int ret = 0;
1698 	rctl_priv_t rblk_priv;
1699 	psinfo_t psinfo;
1700 	zoneid_t oldzoneid = GLOBAL_ZONEID;
1701 	prpriv_t *old_prpriv = NULL, *new_prpriv = NULL;
1702 	priv_set_t *eset, *pset;
1703 	boolean_t relinquish_failed = B_FALSE;
1704 
1705 	rblk_priv = rctlblk_get_privilege(new_rblk);
1706 
1707 	if (rblk_priv == RCPRIV_SYSTEM) {
1708 		preserve_error(gettext("cannot modify system values"));
1709 		return (1);
1710 	}
1711 	if (rblk_priv == RCPRIV_PRIVILEGED) {
1712 		new_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid);
1713 		if (new_prpriv == NULL) {
1714 			preserve_error(gettext("cannot get process privileges "
1715 			    "for pid %d: %s"), Pstatus(Pr)->pr_pid,
1716 			    strerror(errno));
1717 			return (1);
1718 		}
1719 		/*
1720 		 * We only have to change the process privileges if it doesn't
1721 		 * already have PRIV_SYS_RESOURCE.  In addition, we want to make
1722 		 * sure that we don't leave a process with elevated privileges,
1723 		 * so we make sure the process dies if we exit unexpectedly.
1724 		 */
1725 		eset = (priv_set_t *)
1726 		    &new_prpriv->pr_sets[new_prpriv->pr_setsize *
1727 		    priv_getsetbyname(PRIV_EFFECTIVE)];
1728 		pset = (priv_set_t *)
1729 		    &new_prpriv->pr_sets[new_prpriv->pr_setsize *
1730 		    priv_getsetbyname(PRIV_PERMITTED)];
1731 		if (!priv_ismember(eset, PRIV_SYS_RESOURCE)) {
1732 			/* Keep track of original privileges */
1733 			old_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid);
1734 			if (old_prpriv == NULL) {
1735 				preserve_error(gettext("cannot get process "
1736 				    "privileges for pid %d: %s"),
1737 				    Pstatus(Pr)->pr_pid, strerror(errno));
1738 				free(new_prpriv);
1739 				return (1);
1740 			}
1741 			(void) priv_addset(eset, PRIV_SYS_RESOURCE);
1742 			(void) priv_addset(pset, PRIV_SYS_RESOURCE);
1743 			if (Psetflags(Pr, PR_KLC) != 0 ||
1744 			    Psetpriv(Pr, new_prpriv) != 0) {
1745 				preserve_error(gettext("cannot set process "
1746 				    "privileges for pid %d: %s"),
1747 				    Pstatus(Pr)->pr_pid, strerror(errno));
1748 				(void) Punsetflags(Pr, PR_KLC);
1749 				free(new_prpriv);
1750 				free(old_prpriv);
1751 				return (1);
1752 			}
1753 		}
1754 		/*
1755 		 * If this is a zone.* rctl, it requires more than
1756 		 * PRIV_SYS_RESOURCE: it wants the process to have global-zone
1757 		 * credentials.  We temporarily grant non-global zone processes
1758 		 * these credentials, and make sure the process dies if we exit
1759 		 * unexpectedly.
1760 		 */
1761 		if (arg_name &&
1762 		    arg_name_entity == RCENTITY_ZONE &&
1763 		    getzoneid() == GLOBAL_ZONEID &&
1764 		    proc_get_psinfo(Pstatus(Pr)->pr_pid, &psinfo) == 0 &&
1765 		    (oldzoneid = psinfo.pr_zoneid) != GLOBAL_ZONEID) {
1766 			/*
1767 			 * We need to give this process superuser
1768 			 * ("super-zone") privileges.
1769 			 *
1770 			 * Must never return without setting this back!
1771 			 */
1772 			if (Psetflags(Pr, PR_KLC) != 0 ||
1773 			    Psetzoneid(Pr, GLOBAL_ZONEID) < 0) {
1774 				preserve_error(gettext(
1775 				    "cannot set global-zone "
1776 				    "privileges for pid %d: %s"),
1777 				    Pstatus(Pr)->pr_pid, strerror(errno));
1778 				/*
1779 				 * We couldn't set the zoneid to begin with, so
1780 				 * there's no point in warning the user about
1781 				 * trying to un-set it.
1782 				 */
1783 				oldzoneid = GLOBAL_ZONEID;
1784 				ret = 1;
1785 				goto bail;
1786 			}
1787 		}
1788 	}
1789 	/* Now, actually populate the rctlblk in the kernel */
1790 	if (flags == RCTL_REPLACE) {
1791 		/*
1792 		 * Replace should be a delete followed by an insert. This
1793 		 * allows us to replace rctl value blocks which match in
1794 		 * privilege and value, but have updated actions, etc.
1795 		 * setrctl() doesn't allow a direct replace, but we
1796 		 * should do the right thing for the user in the command.
1797 		 */
1798 		if (pr_setrctl(Pr, name, NULL,
1799 		    old_rblk, RCTL_DELETE)) {
1800 			preserve_error(gettext("failed to delete resource "
1801 			    "control %s for pid %d: %s"), name,
1802 			    Pstatus(Pr)->pr_pid, strerror(errno));
1803 			ret = 1;
1804 			goto bail;
1805 		}
1806 		if (pr_setrctl(Pr, name, NULL,
1807 		    new_rblk, RCTL_INSERT)) {
1808 			preserve_error(gettext("failed to insert resource "
1809 			    "control %s for pid %d: %s"), name,
1810 			    Pstatus(Pr)->pr_pid, strerror(errno));
1811 			ret = 1;
1812 			goto bail;
1813 		}
1814 	} else if (flags == RCTL_INSERT) {
1815 		if (pr_setrctl(Pr, name, NULL,
1816 		    new_rblk, RCTL_INSERT)) {
1817 			preserve_error(gettext("failed to create resource "
1818 			    "control %s for pid %d: %s"), name,
1819 			    Pstatus(Pr)->pr_pid, strerror(errno));
1820 			ret = 1;
1821 			goto bail;
1822 		}
1823 	} else if (flags == RCTL_DELETE) {
1824 		if (pr_setrctl(Pr, name, NULL,
1825 		    new_rblk, RCTL_DELETE)) {
1826 			preserve_error(gettext("failed to delete resource "
1827 			    "control %s for pid %d: %s"), name,
1828 			    Pstatus(Pr)->pr_pid, strerror(errno));
1829 			ret = 1;
1830 			goto bail;
1831 		}
1832 	}
1833 bail:
1834 	if (oldzoneid != GLOBAL_ZONEID) {
1835 		if (Psetzoneid(Pr, oldzoneid) != 0)
1836 			relinquish_failed = B_TRUE;
1837 	}
1838 	if (old_prpriv != NULL) {
1839 		if (Psetpriv(Pr, old_prpriv) != 0)
1840 			relinquish_failed = B_TRUE;
1841 		free(old_prpriv);
1842 	}
1843 	if (relinquish_failed) {
1844 		/*
1845 		 * If this failed, we can't leave a process hanging
1846 		 * around with elevated privileges, so we'll have to
1847 		 * release the process from libproc, knowing that it
1848 		 * will be killed (since we set PR_KLC).
1849 		 */
1850 		Pdestroy_agent(Pr);
1851 		preserve_error(gettext("cannot relinquish privileges "
1852 			    "for pid %d. The process was killed."),
1853 		    Pstatus(Pr)->pr_pid);
1854 	} else {
1855 		if (Punsetflags(Pr, PR_KLC) != 0)
1856 			preserve_error(gettext("cannot relinquish privileges "
1857 			    "for pid %d. The process was killed."),
1858 			    Pstatus(Pr)->pr_pid);
1859 	}
1860 	if (new_prpriv != NULL)
1861 		free(new_prpriv);
1862 
1863 	return (ret);
1864 }
1865 
1866 void
1867 print_priv(rctl_priv_t local_priv, char *format)
1868 {
1869 	char pstring[11];
1870 
1871 	switch (local_priv) {
1872 	case RCPRIV_BASIC:
1873 		(void) strcpy(pstring, "basic");
1874 		break;
1875 	case RCPRIV_PRIVILEGED:
1876 		(void) strcpy(pstring, "privileged");
1877 		break;
1878 	case RCPRIV_SYSTEM:
1879 		(void) strcpy(pstring, "system");
1880 		break;
1881 	default:
1882 		(void) sprintf(pstring, "%d", local_priv);
1883 		break;
1884 	}
1885 	/* LINTED */
1886 	(void) fprintf(stdout, format, pstring);
1887 }
1888 
1889 void
1890 print_local_action(int action, int *signalp, char *format)
1891 {
1892 	char sig[SIG2STR_MAX];
1893 	char sigstring[SIG2STR_MAX + 7];
1894 	char astring[5 + SIG2STR_MAX + 7];
1895 	int set = 0;
1896 
1897 	astring[0] = '\0';
1898 
1899 	if (action == RCTL_LOCAL_NOACTION) {
1900 		(void) strcat(astring, "none");
1901 		set++;
1902 	}
1903 	if (action & RCTL_LOCAL_DENY) {
1904 		(void) strcat(astring, "deny");
1905 		set++;
1906 	}
1907 	if ((action & RCTL_LOCAL_DENY) &&
1908 	    (action & RCTL_LOCAL_SIGNAL)) {
1909 		(void) strcat(astring, ",");
1910 	}
1911 	if (action & RCTL_LOCAL_SIGNAL) {
1912 		if (sig2str(*signalp, sig))
1913 			(void) snprintf(sigstring, sizeof (astring),
1914 			    "signal=%d", *signalp);
1915 		else
1916 			(void) snprintf(sigstring, sizeof (astring),
1917 			    "signal=%s", sig);
1918 
1919 		(void) strcat(astring, sigstring);
1920 		set++;
1921 	}
1922 	if (set)
1923 		/* LINTED */
1924 		(void) fprintf(stdout, format, astring);
1925 	else
1926 		/* LINTED */
1927 		(void) fprintf(stdout, format, action);
1928 }
1929 
1930 /*
1931  * This function is used to grab the process matching the recipient pid
1932  */
1933 pid_t
1934 regrab_process(pid_t pid, pr_info_handle_t *p, int priv, int *gret)
1935 {
1936 
1937 	char pidstring[24];
1938 
1939 	gret = 0;
1940 	if (pid == -1)
1941 		return (p->pid);
1942 	if (p->pid == pid)
1943 		return (p->pid);
1944 
1945 	release_process(p->pr);
1946 	(void) memset(p, 0, sizeof (*p));
1947 
1948 	(void) snprintf(pidstring, 24, "%d", pid);
1949 	return (grab_process_by_id(
1950 	    pidstring, RCENTITY_PROCESS, p, priv, gret));
1951 }
1952 
1953 /*
1954  * int grab_process_by_id(char *, rctl_entity_t, pr_info_handle_t *, int, int *)
1955  *
1956  * Input
1957  *    Supply a non-NULL string containing:
1958  *	- logical project/zone name or project/zone number if type is
1959  *	RCENTITY_PROJECT or RCENTITY_ZONE
1960  *	- task number if type is RCENTITY_TYPE
1961  *	- a pid if type is RCENTITY_PID
1962  *    Also supply an un-allocated prochandle, and an allocated info_handle.
1963  *    This function assumes that the type is set.
1964  *    If priv is not RCPRIV_BASIC, the grabbed process is required to have
1965  *    PRIV_SYS_RESOURCE in it's limit set.
1966  *
1967  * Return Values
1968  *    Returns 0 on success and 1 on failure. If there is a process
1969  *    running under the specified id, success is returned, and
1970  *    Pr is pointed to the process. Success will be returned and Pr
1971  *    set to NULL if the matching process is our own.
1972  *    If success is returned, psinfo will be valid, and pid will
1973  *    be the process number. The process will also be held at the
1974  *    end, so release_process should be used by the caller.
1975  *
1976  * This function assumes that signals are caught already so that libproc
1977  * can be safely used.
1978  *
1979  * Return Values
1980  *	pid - Process found and grabbed
1981  *	-1 - Error
1982  */
1983 pid_t
1984 grab_process_by_id(char *idname, rctl_entity_t type, pr_info_handle_t *p,
1985     int priv, int *gret)
1986 {
1987 	char prbuf[PROJECT_BUFSZ];
1988 	projid_t projid;
1989 	taskid_t taskid;
1990 	zoneid_t zoneid;
1991 	zoneid_t zone_self;
1992 	struct project proj;
1993 	DIR *dirp;
1994 	struct dirent *dentp;
1995 	int found = 0;
1996 	int pid_self;
1997 	int ret;
1998 	int gret_in;
1999 	int intidname;
2000 	char *end;
2001 	prpriv_t *prpriv;
2002 	priv_set_t *prset;
2003 
2004 	gret_in = *gret;
2005 
2006 	/* get our pid se we do not try to operate on self */
2007 	pid_self = getpid();
2008 
2009 	/* Store integer version of id */
2010 	intidname = strtoul(idname, &end, 10);
2011 	if (errno || *end != '\0' || end == idname) {
2012 		intidname = -1;
2013 	}
2014 
2015 	/*
2016 	 * get our zoneid so we don't try to operate on a project in
2017 	 * another zone
2018 	 */
2019 	zone_self = getzoneid();
2020 
2021 	if (idname == NULL || strcmp(idname, "") == 0) {
2022 		warn(gettext("id name cannot be nuint64\n"));
2023 		return (-1);
2024 	}
2025 	/*
2026 	 * Set up zoneid, projid or taskid, as appropriate, so that comparisons
2027 	 * can be done later with the input.
2028 	 */
2029 	if (type == RCENTITY_ZONE) {
2030 		if (zone_get_id(idname, &zoneid) != 0) {
2031 			warn(gettext("%s: unknown zone\n"), idname);
2032 			return (-1);
2033 		}
2034 	} else if (type == RCENTITY_PROJECT) {
2035 		if (getprojbyname(idname, &proj, prbuf, PROJECT_BUFSZ)
2036 		    == NULL) {
2037 			if (getprojbyid(intidname, &proj, prbuf,
2038 			    PROJECT_BUFSZ) == NULL) {
2039 				warn(gettext("%s: cannot find project\n"),
2040 				    idname);
2041 				return (-1);
2042 			}
2043 		}
2044 		projid = proj.pj_projid;
2045 	} else if (type == RCENTITY_TASK) {
2046 		taskid = (taskid_t)atol(idname);
2047 	}
2048 	/*
2049 	 * Projects and tasks need to search through /proc for
2050 	 * a parent process.
2051 	 */
2052 	if (type == RCENTITY_ZONE || type == RCENTITY_PROJECT ||
2053 	    type == RCENTITY_TASK) {
2054 		if ((dirp = opendir("/proc")) == NULL) {
2055 			warn(gettext("%s: cannot open /proc directory\n"),
2056 			    idname);
2057 			return (-1);
2058 		}
2059 		/*
2060 		 * Look through all processes in /proc. For each process,
2061 		 * check if the pr_projid in their psinfo matches the
2062 		 * specified id.
2063 		 */
2064 		while (dentp = readdir(dirp)) {
2065 			p->pid = atoi(dentp->d_name);
2066 
2067 			/* Skip self */
2068 			if (p->pid == pid_self)
2069 				continue;
2070 
2071 			if (proc_get_psinfo(p->pid, &(p->psinfo)) != 0)
2072 				continue;
2073 
2074 			/* Skip process if it is not what we are looking for */
2075 			if (type == RCENTITY_ZONE &&
2076 			    (p->psinfo).pr_zoneid != zoneid) {
2077 				continue;
2078 			} if (type == RCENTITY_PROJECT &&
2079 			    ((p->psinfo).pr_projid != projid ||
2080 			    (p->psinfo).pr_zoneid != zone_self)) {
2081 				continue;
2082 			} else if (type == RCENTITY_TASK &&
2083 			    (p->psinfo).pr_taskid != taskid) {
2084 				continue;
2085 			}
2086 			/* attempt to grab process */
2087 			if (grab_process(p, gret) != 0)
2088 				continue;
2089 
2090 			/*
2091 			 * Re-confirm that this process is still running as
2092 			 * part	of the specified project or task.  If it
2093 			 * doesn't match, release the process and return an
2094 			 * error. This should only be done if the Pr struct is
2095 			 * not NULL.
2096 			 */
2097 			if (type == RCENTITY_PROJECT) {
2098 				if (pr_getprojid(p->pr) != projid ||
2099 				    pr_getzoneid(p->pr) != zone_self) {
2100 					release_process(p->pr);
2101 					continue;
2102 				}
2103 			} else if (type == RCENTITY_TASK) {
2104 				if (pr_gettaskid(p->pr) != taskid) {
2105 					release_process(p->pr);
2106 					continue;
2107 				}
2108 			} else if (type == RCENTITY_ZONE) {
2109 				if (pr_getzoneid(p->pr) != zoneid) {
2110 					release_process(p->pr);
2111 					continue;
2112 				}
2113 			}
2114 
2115 			/*
2116 			 * If we are setting a privileged resource control,
2117 			 * verify that process has PRIV_SYS_RESOURCE in it's
2118 			 * limit set.  If it does not, then we will not be
2119 			 * able to give this process the privilege it needs
2120 			 * to set the resource control.
2121 			 */
2122 			if (priv != RCPRIV_BASIC) {
2123 				prpriv = proc_get_priv(p->pid);
2124 				if (prpriv == NULL) {
2125 					release_process(p->pr);
2126 					continue;
2127 				}
2128 				prset = (priv_set_t *)
2129 				    &prpriv->pr_sets[prpriv->pr_setsize *
2130 				    priv_getsetbyname(PRIV_LIMIT)];
2131 				if (!priv_ismember(prset, PRIV_SYS_RESOURCE)) {
2132 					release_process(p->pr);
2133 					continue;
2134 				}
2135 			}
2136 			found = 1;
2137 
2138 			p->taskid = pr_gettaskid(p->pr);
2139 			p->projid = pr_getprojid(p->pr);
2140 			p->zoneid = pr_getzoneid(p->pr);
2141 
2142 			break;
2143 		}
2144 		(void) closedir(dirp);
2145 
2146 		if (found == 0) {
2147 			warn(gettext("%s: No controllable process found in "
2148 			    "task, project, or zone.\n"), idname);
2149 			return (-1);
2150 		}
2151 		return (p->pid);
2152 
2153 	} else if (type == RCENTITY_PROCESS) {
2154 
2155 		/* fail if self */
2156 		if (p->pid == pid_self) {
2157 
2158 			warn(gettext("%s: cannot control self"), idname);
2159 			return (-1);
2160 		}
2161 		/*
2162 		 * Process types need to be set up with the correct pid
2163 		 * and psinfo structure.
2164 		 */
2165 		if ((p->pid = proc_arg_psinfo(idname, PR_ARG_PIDS,
2166 		    &(p->psinfo), gret)) == -1) {
2167 			warn(gettext("%s: cannot examine: %s"), idname,
2168 			    Pgrab_error(*gret));
2169 			return (-1);
2170 		}
2171 		/* grab process */
2172 		ret = grab_process(p, gret);
2173 		if (ret == 1) {
2174 			/* Don't print error if G_SYS is allowed */
2175 			if (gret_in == G_SYS && *gret == G_SYS) {
2176 				return (-1);
2177 			} else {
2178 				warn(gettext("%s: cannot control: %s"), idname,
2179 				    Pgrab_error(*gret));
2180 				return (-1);
2181 			}
2182 		} else if (ret == 2) {
2183 			ret = errno;
2184 			warn(gettext("%s: cannot control: %s"), idname,
2185 			    strerror(ret));
2186 			return (-1);
2187 		}
2188 		p->taskid = pr_gettaskid(p->pr);
2189 		p->projid = pr_getprojid(p->pr);
2190 		p->zoneid = pr_getzoneid(p->pr);
2191 
2192 		return (p->pid);
2193 
2194 	} else {
2195 		warn(gettext("%s: unknown resource entity type %d\n"), idname,
2196 		    type);
2197 		return (-1);
2198 	}
2199 }
2200 
2201 /*
2202  * Do the work required to manipulate a process through libproc.
2203  * If grab_process() returns no errors (0), then release_process()
2204  * must eventually be called.
2205  *
2206  * Return values:
2207  *	0 Successful creation of agent thread
2208  *	1 Error grabbing
2209  *	2 Error creating agent
2210  */
2211 int
2212 grab_process(pr_info_handle_t *p, int *gret)
2213 {
2214 
2215 	if ((p->pr = Pgrab(p->pid, arg_force, gret)) != NULL) {
2216 
2217 		if (Psetflags(p->pr, PR_RLC) != 0) {
2218 			Prelease(p->pr, 0);
2219 			return (1);
2220 		}
2221 		if (Pcreate_agent(p->pr) == 0) {
2222 			return (0);
2223 
2224 		} else {
2225 			Prelease(p->pr, 0);
2226 			return (2);
2227 		}
2228 	} else {
2229 		return (1);
2230 	}
2231 }
2232 
2233 /*
2234  * Release the specified process. This destroys the agent
2235  * and releases the process. If the process is NULL, nothing
2236  * is done. This function should only be called if grab_process()
2237  * has previously been called and returned success.
2238  *
2239  * This function is Pgrab-safe.
2240  */
2241 void
2242 release_process(struct ps_prochandle *Pr)
2243 {
2244 	if (Pr == NULL)
2245 		return;
2246 
2247 	Pdestroy_agent(Pr);
2248 	Prelease(Pr, 0);
2249 }
2250 
2251 /*
2252  * preserve_error(char *, ...)
2253  *
2254  * preserve_error() should be called rather than warn() by any
2255  * function that is called while the victim process is held by Pgrab.
2256  * It will save the error until the process has been un-controlled
2257  * and output is reasonable again.
2258  *
2259  * Note that multiple errors are not stored. Any error in these
2260  * sections should be critical and return immediately.
2261  *
2262  * This function is Pgrab-safe.
2263  *
2264  * Since this function may copy untrusted command line arguments to
2265  * global_error, security practices require that global_error never be
2266  * printed directly.  Use printf("%s\n", global_error) or equivalent.
2267  */
2268 /*PRINTFLIKE1*/
2269 void
2270 preserve_error(char *format, ...)
2271 {
2272 	va_list alist;
2273 
2274 	va_start(alist, format);
2275 
2276 	/*
2277 	 * GLOBAL_ERR_SZ is pretty big. If the error is longer
2278 	 * than that, just truncate it, rather than chance missing
2279 	 * the error altogether.
2280 	 */
2281 	(void) vsnprintf(global_error, GLOBAL_ERR_SZ-1, format, alist);
2282 
2283 	va_end(alist);
2284 }
2285