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