xref: /illumos-gate/usr/src/cmd/prctl/prctl.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
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 /*
23  * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <unistd.h>
27 #include <rctl.h>
28 #include <libproc.h>
29 #include <stdio.h>
30 #include <libintl.h>
31 #include <locale.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <strings.h>
35 #include <ctype.h>
36 #include <project.h>
37 #include <sys/types.h>
38 #include <dirent.h>
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <sys/varargs.h>
42 #include <priv.h>
43 #include <zone.h>
44 #include "utils.h"
45 
46 /* Valid user actions */
47 #define	ACTION_DISABLE		0x01
48 #define	ACTION_ENABLE		0x02
49 #define	ACTION_SET		0x04
50 #define	ACTION_REPLACE		0x08
51 #define	ACTION_DELETE		0x10
52 
53 #define	PRCTL_VALUE_WIDTH	4
54 
55 /* Maximum string length for deferred errors */
56 #define	GLOBAL_ERR_SZ		1024
57 
58 /* allow important process values to be passed together easily */
59 typedef struct pr_info_handle {
60 	struct ps_prochandle *pr;
61 	pid_t pid;
62 	psinfo_t psinfo;
63 	taskid_t taskid;
64 	projid_t projid;
65 	char *projname;
66 	zoneid_t zoneid;
67 	char *zonename;
68 
69 } pr_info_handle_t;
70 
71 /* Structures for list of resource controls */
72 typedef struct prctl_value {
73 	rctlblk_t *rblk;
74 	struct prctl_value *next;
75 } prctl_value_t;
76 
77 typedef struct prctl_list {
78 	char *name;
79 	rctl_qty_t *usage;
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) setpname(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 
1224 	/*
1225 	 * Get the current usage for the resource control if it matched the
1226 	 * privilege and entity type criteria.
1227 	 */
1228 	if (list != NULL) {
1229 		if (pr_getrctl(Pr, rctlname, NULL, rblk2, RCTL_USAGE) == 0) {
1230 			list->usage = (rctl_qty_t *)malloc(sizeof (rctl_qty_t));
1231 			if (list->usage == NULL) {
1232 				preserve_error(gettext("malloc failed: %s"),
1233 				    strerror(errno));
1234 				free(rblk1);
1235 				free(rblk2);
1236 				return (1);
1237 			}
1238 			*list->usage = rctlblk_get_value(rblk2);
1239 		} else {
1240 			list->usage = NULL;
1241 			if (errno != ENOTSUP) {
1242 				preserve_error(gettext("failed to get "
1243 				    "resource control usage for %s: %s"),
1244 				    rctlname, strerror(errno));
1245 				free(rblk1);
1246 				free(rblk2);
1247 				return (1);
1248 			}
1249 		}
1250 	}
1251 	free(rblk1);
1252 	free(rblk2);
1253 	return (0);
1254 }
1255 
1256 /*
1257  * store_value_entry(rctlblk_t *, prctl_list_t *)
1258  *
1259  * Store an rblk for a given resource control into the global list.
1260  *
1261  * This function is Pgrab-safe.
1262  */
1263 prctl_value_t *
1264 store_value_entry(rctlblk_t *rblk, prctl_list_t *list)
1265 {
1266 	prctl_value_t *e = calloc(1, sizeof (prctl_value_t));
1267 	rctlblk_t *store_blk = calloc(1, rctlblk_size());
1268 	prctl_value_t *iter = list->val_list;
1269 
1270 	if (e == NULL || store_blk == NULL) {
1271 		preserve_error(gettext("malloc failed %s"),
1272 		    strerror(errno));
1273 		if (e != NULL)
1274 			free(e);
1275 		if (store_blk != NULL)
1276 			free(store_blk);
1277 		return (NULL);
1278 	}
1279 	if (iter == NULL)
1280 		list->val_list = e;
1281 	else {
1282 		while (iter->next != NULL) {
1283 			iter = iter->next;
1284 		}
1285 		iter->next = e;
1286 	}
1287 	bcopy(rblk, store_blk, rctlblk_size());
1288 
1289 	e->rblk = store_blk;
1290 	e->next = NULL;
1291 	return (e);
1292 }
1293 
1294 /*
1295  * store_list_entry(const char *)
1296  *
1297  * Store a new resource control value in the global list. No checking
1298  * for duplicates done.
1299  *
1300  * This function is Pgrab-safe.
1301  */
1302 prctl_list_t *
1303 store_list_entry(const char *name)
1304 {
1305 	prctl_list_t *e = calloc(1, sizeof (prctl_list_t));
1306 
1307 	if (e == NULL) {
1308 		preserve_error(gettext("malloc failed %s"),
1309 		    strerror(errno));
1310 		return (NULL);
1311 	}
1312 	if ((e->name = calloc(1, strlen(name) + 1)) == NULL) {
1313 		preserve_error(gettext("malloc failed %s"),
1314 		    strerror(errno));
1315 		free(e);
1316 		return (NULL);
1317 	}
1318 	(void) strcpy(e->name, name);
1319 	e->val_list = NULL;
1320 
1321 	if (global_rctl_list_head == NULL) {
1322 		global_rctl_list_head = e;
1323 		global_rctl_list_tail = e;
1324 	} else {
1325 		global_rctl_list_tail->next = e;
1326 		global_rctl_list_tail = e;
1327 	}
1328 	e->next = NULL;
1329 	return (e);
1330 }
1331 
1332 /*
1333  * free_lists()
1334  *
1335  * Free all resource control blocks and values from the global lists.
1336  *
1337  * This function is Pgrab-safe.
1338  */
1339 void
1340 free_lists()
1341 {
1342 	prctl_list_t *new_list, *old_list = global_rctl_list_head;
1343 	prctl_value_t *old_val, *new_val;
1344 
1345 	while (old_list != NULL) {
1346 		old_val = old_list->val_list;
1347 		while (old_val != NULL) {
1348 			free(old_val->rblk);
1349 			new_val = old_val->next;
1350 			free(old_val);
1351 			old_val = new_val;
1352 		}
1353 		free(old_list->name);
1354 		free(old_list->usage);
1355 		new_list = old_list->next;
1356 		free(old_list);
1357 		old_list = new_list;
1358 	}
1359 	global_rctl_list_head = NULL;
1360 	global_rctl_list_tail = NULL;
1361 }
1362 
1363 void
1364 print_heading()
1365 {
1366 
1367 	/* print headings */
1368 	(void) fprintf(stdout, "%-8s%-16s%-9s%-7s%-28s%10s\n",
1369 	    "NAME", "PRIVILEGE", "VALUE",
1370 	    "FLAG", "ACTION", "RECIPIENT");
1371 }
1372 
1373 /*
1374  * print_rctls()
1375  *
1376  * Print all resource controls from the global list that was
1377  * previously populated by store_rctls.
1378  */
1379 void
1380 print_rctls(pr_info_handle_t *p)
1381 {
1382 	prctl_list_t *iter_list = global_rctl_list_head;
1383 	prctl_value_t *iter_val;
1384 	rctl_qty_t  rblk_value;
1385 	rctl_priv_t rblk_priv;
1386 	uint_t local_action;
1387 	int signal, local_flags, global_flags;
1388 	pid_t pid;
1389 	char rctl_valuestring[SCALED_STRLEN];
1390 	char *unit = NULL;
1391 	scale_t *scale;
1392 	char *string;
1393 	int doneheading = 0;
1394 
1395 	if (iter_list == NULL)
1396 		return;
1397 
1398 	while (iter_list != NULL) {
1399 
1400 		if (doneheading == 0 &&
1401 		    arg_entity_type == RCENTITY_PROCESS) {
1402 			proc_unctrl_psinfo(&(p->psinfo));
1403 			doneheading = 1;
1404 			(void) fprintf(stdout,
1405 			    "process: %d: %.70s\n", (int)p->pid,
1406 			    p->psinfo.pr_psargs);
1407 			if (!arg_parseable_mode)
1408 				print_heading();
1409 		}
1410 		if (doneheading == 0 &&
1411 		    arg_entity_type == RCENTITY_TASK) {
1412 			doneheading = 1;
1413 			(void) fprintf(stdout, "task: %d\n", (int)p->taskid);
1414 			if (!arg_parseable_mode)
1415 				print_heading();
1416 		}
1417 		if (doneheading == 0 &&
1418 		    arg_entity_type == RCENTITY_PROJECT) {
1419 			if (!arg_parseable_mode && doneheading)
1420 				(void) fprintf(stdout, "\n");
1421 			doneheading = 1;
1422 			(void) fprintf(stdout,
1423 			    "project: %d: %.70s\n", (int)p->projid,
1424 			    p->projname);
1425 			if (!arg_parseable_mode)
1426 				print_heading();
1427 		}
1428 		if (doneheading == 0 &&
1429 		    arg_entity_type == RCENTITY_ZONE) {
1430 			doneheading = 1;
1431 			(void) fprintf(stdout,
1432 			    "zone: %d: %.70s\n", (int)p->zoneid,
1433 			    p->zonename);
1434 			if (!arg_parseable_mode)
1435 				print_heading();
1436 		}
1437 		/* only print name once in normal output */
1438 		if (!arg_parseable_mode)
1439 			(void) fprintf(stdout, "%s\n", iter_list->name);
1440 
1441 		iter_val = iter_list->val_list;
1442 
1443 		/* if for some reason there are no values, skip */
1444 		if (iter_val == 0)
1445 			continue;
1446 
1447 
1448 		/* get the global flags the first rctl only */
1449 		global_flags = rctlblk_get_global_flags(iter_val->rblk);
1450 
1451 
1452 		if (global_flags & RCTL_GLOBAL_BYTES) {
1453 			unit = SCALED_UNIT_BYTES;
1454 			scale = scale_binary;
1455 
1456 		} else if (global_flags & RCTL_GLOBAL_SECONDS) {
1457 			unit = SCALED_UNIT_SECONDS;
1458 			scale = scale_metric;
1459 
1460 		} else {
1461 			unit = SCALED_UNIT_NONE;
1462 			scale = scale_metric;
1463 		}
1464 
1465 		/* print the current usage for the rctl if available */
1466 		if (iter_list->usage != NULL) {
1467 			rblk_value = *(iter_list->usage);
1468 			if (!arg_parseable_mode) {
1469 				(void) uint64toscaled(rblk_value, 4, "E",
1470 				    rctl_valuestring, NULL, NULL,
1471 				    scale, NULL, 0);
1472 
1473 				(void) fprintf(stdout, "%8s%-16s%5s%-4s\n",
1474 				    "", "usage", rctl_valuestring, unit);
1475 			} else {
1476 				(void) fprintf(stdout, "%s %s %llu - - -\n",
1477 				    iter_list->name, "usage", rblk_value);
1478 			}
1479 		}
1480 
1481 		/* iterate over an print all control values */
1482 		while (iter_val != NULL) {
1483 
1484 			/* print name or empty name field */
1485 			if (!arg_parseable_mode)
1486 				(void) fprintf(stdout, "%8s", "");
1487 			else
1488 				(void) fprintf(stdout, "%s ", iter_list->name);
1489 
1490 
1491 			rblk_priv = rctlblk_get_privilege(iter_val->rblk);
1492 			if (!arg_parseable_mode)
1493 				print_priv(rblk_priv, "%-16s");
1494 			else
1495 				print_priv(rblk_priv, "%s ");
1496 
1497 			rblk_value = rctlblk_get_value(iter_val->rblk);
1498 			if (arg_parseable_mode) {
1499 				(void) fprintf(stdout, "%llu ", rblk_value);
1500 
1501 			} else {
1502 
1503 				(void) uint64toscaled(rblk_value, 4, "E",
1504 				    rctl_valuestring, NULL, NULL,
1505 				    scale, NULL, 0);
1506 
1507 				(void) fprintf(stdout, "%5s",
1508 				    rctl_valuestring);
1509 				(void) fprintf(stdout, "%-4s", unit);
1510 			}
1511 			local_flags = rctlblk_get_local_flags(iter_val->rblk);
1512 
1513 			if (local_flags & RCTL_LOCAL_MAXIMAL) {
1514 
1515 				if (global_flags & RCTL_GLOBAL_INFINITE) {
1516 					string = "inf";
1517 				} else {
1518 					string = "max";
1519 				}
1520 			} else {
1521 				string = "-";
1522 			}
1523 			if (arg_parseable_mode)
1524 				(void) fprintf(stdout, "%s ", string);
1525 			else
1526 				(void) fprintf(stdout, "%4s%3s",
1527 				    string, "");
1528 
1529 
1530 			local_action = rctlblk_get_local_action(iter_val->rblk,
1531 			    &signal);
1532 
1533 			if (arg_parseable_mode)
1534 				print_local_action(local_action, &signal,
1535 				    "%s ");
1536 			else
1537 				print_local_action(local_action, &signal,
1538 				    "%-28s");
1539 
1540 			pid = rctlblk_get_recipient_pid(iter_val->rblk);
1541 
1542 			if (arg_parseable_mode) {
1543 				if (pid < 0) {
1544 					(void) fprintf(stdout, "%s\n", "-");
1545 				} else {
1546 					(void) fprintf(stdout, "%d\n",
1547 					    (int)pid);
1548 				}
1549 			} else {
1550 				if (pid < 0) {
1551 					(void) fprintf(stdout, "%10s\n", "-");
1552 				} else {
1553 					(void) fprintf(stdout, "%10d\n",
1554 					    (int)pid);
1555 				}
1556 			}
1557 			iter_val = iter_val->next;
1558 		}
1559 		iter_list = iter_list->next;
1560 	}
1561 }
1562 
1563 /*
1564  *
1565  * match_rctl
1566  *
1567  * find the first rctl with matching name, value, priv, and recipient pid
1568  */
1569 int
1570 match_rctl(struct ps_prochandle *Pr, rctlblk_t **rctl, char *name,
1571     char *valuestringin, int valuein, rctl_priv_t privin, int pidin)
1572 {
1573 	rctlblk_t *next;
1574 	rctlblk_t *last;
1575 	rctlblk_t *tmp;
1576 
1577 	*rctl = NULL;
1578 
1579 	next = calloc(1, rctlblk_size());
1580 	last = calloc(1, rctlblk_size());
1581 
1582 	if ((last == NULL) || (next == NULL)) {
1583 		preserve_error(gettext("malloc failed"), strerror(errno));
1584 		return (-1);
1585 	}
1586 	/*
1587 	 * For this resource name, now iterate through all
1588 	 * the  controls, looking for a match to the
1589 	 * user-specified input.
1590 	 */
1591 	if (pr_getrctl(Pr, name, NULL, next, RCTL_FIRST)) {
1592 		preserve_error(gettext("failed to get resource control "
1593 		    "for %s: %s"), name, strerror(errno));
1594 		return (-1);
1595 	}
1596 	if (match_rctl_blk(next, valuestringin, valuein, privin, pidin) == 1) {
1597 		free(last);
1598 		*rctl = next;
1599 		return (0);
1600 	}
1601 	tmp = next;
1602 	next = last;
1603 	last = tmp;
1604 
1605 	while (pr_getrctl(Pr, name, last, next,	RCTL_NEXT) == 0) {
1606 
1607 		/* allow user interrupt */
1608 		if (interrupt)
1609 			break;
1610 
1611 		if (match_rctl_blk(next, valuestringin, valuein, privin, pidin)
1612 		    == 1) {
1613 			free(last);
1614 			*rctl = next;
1615 			return (0);
1616 		}
1617 		tmp = next;
1618 		next = last;
1619 		last = tmp;
1620 	}
1621 	free(next);
1622 	free(last);
1623 
1624 	return (1);
1625 }
1626 
1627 /*
1628  * int match_rctl_blk(rctlblk_t *, char *, uint64, rctl_priv_t, int pid)
1629  *
1630  * Input
1631  *   Must supply a valid rctl, value, privilege, and pid to match on.
1632  *   If valuestring is NULL, then valuestring and valuein will not be used
1633  *   If privilege type is 0 it will not be used.
1634  *   If pid is -1 it will not be used.
1635  *
1636  * Return values
1637  *   Returns 1 if a matching rctl given matches the parameters specified, and
1638  *   0 if they do not.
1639  *
1640  * This function is Pgrab-safe.
1641  */
1642 int
1643 match_rctl_blk(rctlblk_t *rctl, char *valuestringin,
1644     uint64_t valuein, rctl_priv_t privin, int pidin)
1645 {
1646 
1647 	rctl_qty_t value;
1648 	rctl_priv_t priv;
1649 	pid_t pid;
1650 	int valuematch = 1;
1651 	int privmatch = 1;
1652 	int pidmatch = 1;
1653 
1654 	value = rctlblk_get_value(rctl);
1655 	priv = rctlblk_get_privilege(rctl);
1656 	pid = rctlblk_get_recipient_pid(rctl);
1657 
1658 	if (valuestringin) {
1659 
1660 		if (arg_modifier == NULL) {
1661 			valuematch = (valuein == value);
1662 		} else {
1663 			valuematch = scaledequint64(valuestringin, value,
1664 			    PRCTL_VALUE_WIDTH,
1665 			    arg_scale, arg_unit,
1666 			    SCALED_ALL_FLAGS);
1667 		}
1668 	}
1669 	if (privin != 0) {
1670 		privmatch = (privin == priv);
1671 	}
1672 	if (pidin != -1) {
1673 		pidmatch = (pidin == pid);
1674 	}
1675 	return (valuematch && privmatch && pidmatch);
1676 }
1677 
1678 static int
1679 change_action(rctlblk_t *blk)
1680 {
1681 	int signal = 0;
1682 	int action;
1683 
1684 	action = rctlblk_get_local_action(blk, &signal);
1685 
1686 	if (arg_operation & ACTION_ENABLE) {
1687 
1688 		if (arg_action & RCTL_LOCAL_SIGNAL) {
1689 			signal = arg_signal;
1690 		}
1691 		action = action | arg_action;
1692 		/* add local action */
1693 		rctlblk_set_local_action(blk, action, signal);
1694 
1695 	} else if (arg_operation & ACTION_DISABLE) {
1696 
1697 		/*
1698 		 * if deleting signal and signal number is specified,
1699 		 * then signal number must match
1700 		 */
1701 		if ((arg_action & RCTL_LOCAL_SIGNAL) &&
1702 		    (arg_signal != -1)) {
1703 			if (arg_signal != signal) {
1704 				preserve_error(gettext("signal name or number "
1705 				    "does not match existing action"));
1706 				return (-1);
1707 			}
1708 		}
1709 		/* remove local action */
1710 		action = action & (~arg_action);
1711 		rctlblk_set_local_action(blk, RCTL_LOCAL_NOACTION, 0);
1712 		rctlblk_set_local_action(blk, action, signal);
1713 	}
1714 	/* enable deny if it must be enabled */
1715 	if (arg_global_flags & RCTL_GLOBAL_DENY_ALWAYS) {
1716 		rctlblk_set_local_action(blk, RCTL_LOCAL_DENY | action,
1717 		    signal);
1718 	}
1719 	return (0);
1720 }
1721 
1722 /*
1723  * prctl_setrctl
1724  *
1725  * Input
1726  *   This function expects that input has been validated. In the
1727  *   case of a replace operation, both old_rblk and new_rblk must
1728  *   be valid resource controls. If a resource control is being
1729  *   created, only new_rblk must be supplied. If a resource control
1730  *   is being deleted, only new_rblk must be supplied.
1731  *
1732  * If the privilege is a priviliged type, at this time, the process
1733  * tries to take on superuser privileges.
1734  */
1735 int
1736 prctl_setrctl(struct ps_prochandle *Pr, const char *name,
1737     rctlblk_t *old_rblk, rctlblk_t *new_rblk, uint_t flags)
1738 {
1739 	int ret = 0;
1740 	rctl_priv_t rblk_priv;
1741 	psinfo_t psinfo;
1742 	zoneid_t oldzoneid = GLOBAL_ZONEID;
1743 	prpriv_t *old_prpriv = NULL, *new_prpriv = NULL;
1744 	priv_set_t *eset, *pset;
1745 	boolean_t relinquish_failed = B_FALSE;
1746 
1747 	rblk_priv = rctlblk_get_privilege(new_rblk);
1748 
1749 	if (rblk_priv == RCPRIV_SYSTEM) {
1750 		preserve_error(gettext("cannot modify system values"));
1751 		return (1);
1752 	}
1753 	if (rblk_priv == RCPRIV_PRIVILEGED) {
1754 		new_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid);
1755 		if (new_prpriv == NULL) {
1756 			preserve_error(gettext("cannot get process privileges "
1757 			    "for pid %d: %s"), Pstatus(Pr)->pr_pid,
1758 			    strerror(errno));
1759 			return (1);
1760 		}
1761 		/*
1762 		 * We only have to change the process privileges if it doesn't
1763 		 * already have PRIV_SYS_RESOURCE.  In addition, we want to make
1764 		 * sure that we don't leave a process with elevated privileges,
1765 		 * so we make sure the process dies if we exit unexpectedly.
1766 		 */
1767 		eset = (priv_set_t *)
1768 		    &new_prpriv->pr_sets[new_prpriv->pr_setsize *
1769 		    priv_getsetbyname(PRIV_EFFECTIVE)];
1770 		pset = (priv_set_t *)
1771 		    &new_prpriv->pr_sets[new_prpriv->pr_setsize *
1772 		    priv_getsetbyname(PRIV_PERMITTED)];
1773 		if (!priv_ismember(eset, PRIV_SYS_RESOURCE)) {
1774 			/* Keep track of original privileges */
1775 			old_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid);
1776 			if (old_prpriv == NULL) {
1777 				preserve_error(gettext("cannot get process "
1778 				    "privileges for pid %d: %s"),
1779 				    Pstatus(Pr)->pr_pid, strerror(errno));
1780 				free(new_prpriv);
1781 				return (1);
1782 			}
1783 			(void) priv_addset(eset, PRIV_SYS_RESOURCE);
1784 			(void) priv_addset(pset, PRIV_SYS_RESOURCE);
1785 			if (Psetflags(Pr, PR_KLC) != 0 ||
1786 			    Psetpriv(Pr, new_prpriv) != 0) {
1787 				preserve_error(gettext("cannot set process "
1788 				    "privileges for pid %d: %s"),
1789 				    Pstatus(Pr)->pr_pid, strerror(errno));
1790 				(void) Punsetflags(Pr, PR_KLC);
1791 				free(new_prpriv);
1792 				free(old_prpriv);
1793 				return (1);
1794 			}
1795 		}
1796 		/*
1797 		 * If this is a zone.* rctl, it requires more than
1798 		 * PRIV_SYS_RESOURCE: it wants the process to have global-zone
1799 		 * credentials.  We temporarily grant non-global zone processes
1800 		 * these credentials, and make sure the process dies if we exit
1801 		 * unexpectedly.
1802 		 */
1803 		if (arg_name &&
1804 		    arg_name_entity == RCENTITY_ZONE &&
1805 		    getzoneid() == GLOBAL_ZONEID &&
1806 		    proc_get_psinfo(Pstatus(Pr)->pr_pid, &psinfo) == 0 &&
1807 		    (oldzoneid = psinfo.pr_zoneid) != GLOBAL_ZONEID) {
1808 			/*
1809 			 * We need to give this process superuser
1810 			 * ("super-zone") privileges.
1811 			 *
1812 			 * Must never return without setting this back!
1813 			 */
1814 			if (Psetflags(Pr, PR_KLC) != 0 ||
1815 			    Psetzoneid(Pr, GLOBAL_ZONEID) < 0) {
1816 				preserve_error(gettext(
1817 				    "cannot set global-zone "
1818 				    "privileges for pid %d: %s"),
1819 				    Pstatus(Pr)->pr_pid, strerror(errno));
1820 				/*
1821 				 * We couldn't set the zoneid to begin with, so
1822 				 * there's no point in warning the user about
1823 				 * trying to un-set it.
1824 				 */
1825 				oldzoneid = GLOBAL_ZONEID;
1826 				ret = 1;
1827 				goto bail;
1828 			}
1829 		}
1830 	}
1831 	/* Now, actually populate the rctlblk in the kernel */
1832 	if (flags == RCTL_REPLACE) {
1833 		/*
1834 		 * Replace should be a delete followed by an insert. This
1835 		 * allows us to replace rctl value blocks which match in
1836 		 * privilege and value, but have updated actions, etc.
1837 		 * setrctl() doesn't allow a direct replace, but we
1838 		 * should do the right thing for the user in the command.
1839 		 */
1840 		if (pr_setrctl(Pr, name, NULL,
1841 		    old_rblk, RCTL_DELETE)) {
1842 			preserve_error(gettext("failed to delete resource "
1843 			    "control %s for pid %d: %s"), name,
1844 			    Pstatus(Pr)->pr_pid, strerror(errno));
1845 			ret = 1;
1846 			goto bail;
1847 		}
1848 		if (pr_setrctl(Pr, name, NULL,
1849 		    new_rblk, RCTL_INSERT)) {
1850 			preserve_error(gettext("failed to insert resource "
1851 			    "control %s for pid %d: %s"), name,
1852 			    Pstatus(Pr)->pr_pid, strerror(errno));
1853 			ret = 1;
1854 			goto bail;
1855 		}
1856 	} else if (flags == RCTL_INSERT) {
1857 		if (pr_setrctl(Pr, name, NULL,
1858 		    new_rblk, RCTL_INSERT)) {
1859 			preserve_error(gettext("failed to create resource "
1860 			    "control %s for pid %d: %s"), name,
1861 			    Pstatus(Pr)->pr_pid, strerror(errno));
1862 			ret = 1;
1863 			goto bail;
1864 		}
1865 	} else if (flags == RCTL_DELETE) {
1866 		if (pr_setrctl(Pr, name, NULL,
1867 		    new_rblk, RCTL_DELETE)) {
1868 			preserve_error(gettext("failed to delete resource "
1869 			    "control %s for pid %d: %s"), name,
1870 			    Pstatus(Pr)->pr_pid, strerror(errno));
1871 			ret = 1;
1872 			goto bail;
1873 		}
1874 	}
1875 bail:
1876 	if (oldzoneid != GLOBAL_ZONEID) {
1877 		if (Psetzoneid(Pr, oldzoneid) != 0)
1878 			relinquish_failed = B_TRUE;
1879 	}
1880 	if (old_prpriv != NULL) {
1881 		if (Psetpriv(Pr, old_prpriv) != 0)
1882 			relinquish_failed = B_TRUE;
1883 		free(old_prpriv);
1884 	}
1885 	if (relinquish_failed) {
1886 		/*
1887 		 * If this failed, we can't leave a process hanging
1888 		 * around with elevated privileges, so we'll have to
1889 		 * release the process from libproc, knowing that it
1890 		 * will be killed (since we set PR_KLC).
1891 		 */
1892 		Pdestroy_agent(Pr);
1893 		preserve_error(gettext("cannot relinquish privileges "
1894 		    "for pid %d. The process was killed."),
1895 		    Pstatus(Pr)->pr_pid);
1896 	} else {
1897 		if (Punsetflags(Pr, PR_KLC) != 0)
1898 			preserve_error(gettext("cannot relinquish privileges "
1899 			    "for pid %d. The process was killed."),
1900 			    Pstatus(Pr)->pr_pid);
1901 	}
1902 	if (new_prpriv != NULL)
1903 		free(new_prpriv);
1904 
1905 	return (ret);
1906 }
1907 
1908 void
1909 print_priv(rctl_priv_t local_priv, char *format)
1910 {
1911 	char pstring[11];
1912 
1913 	switch (local_priv) {
1914 	case RCPRIV_BASIC:
1915 		(void) strcpy(pstring, "basic");
1916 		break;
1917 	case RCPRIV_PRIVILEGED:
1918 		(void) strcpy(pstring, "privileged");
1919 		break;
1920 	case RCPRIV_SYSTEM:
1921 		(void) strcpy(pstring, "system");
1922 		break;
1923 	default:
1924 		(void) sprintf(pstring, "%d", local_priv);
1925 		break;
1926 	}
1927 	/* LINTED */
1928 	(void) fprintf(stdout, format, pstring);
1929 }
1930 
1931 void
1932 print_local_action(int action, int *signalp, char *format)
1933 {
1934 	char sig[SIG2STR_MAX];
1935 	char sigstring[SIG2STR_MAX + 7];
1936 	char astring[5 + SIG2STR_MAX + 7];
1937 	int set = 0;
1938 
1939 	astring[0] = '\0';
1940 
1941 	if (action == RCTL_LOCAL_NOACTION) {
1942 		(void) strcat(astring, "none");
1943 		set++;
1944 	}
1945 	if (action & RCTL_LOCAL_DENY) {
1946 		(void) strcat(astring, "deny");
1947 		set++;
1948 	}
1949 	if ((action & RCTL_LOCAL_DENY) &&
1950 	    (action & RCTL_LOCAL_SIGNAL)) {
1951 		(void) strcat(astring, ",");
1952 	}
1953 	if (action & RCTL_LOCAL_SIGNAL) {
1954 		if (sig2str(*signalp, sig))
1955 			(void) snprintf(sigstring, sizeof (astring),
1956 			    "signal=%d", *signalp);
1957 		else
1958 			(void) snprintf(sigstring, sizeof (astring),
1959 			    "signal=%s", sig);
1960 
1961 		(void) strcat(astring, sigstring);
1962 		set++;
1963 	}
1964 	if (set)
1965 		/* LINTED */
1966 		(void) fprintf(stdout, format, astring);
1967 	else
1968 		/* LINTED */
1969 		(void) fprintf(stdout, format, action);
1970 }
1971 
1972 /*
1973  * This function is used to grab the process matching the recipient pid
1974  */
1975 pid_t
1976 regrab_process(pid_t pid, pr_info_handle_t *p, int priv, int *gret)
1977 {
1978 
1979 	char pidstring[24];
1980 
1981 	gret = 0;
1982 	if (pid == -1)
1983 		return (p->pid);
1984 	if (p->pid == pid)
1985 		return (p->pid);
1986 
1987 	release_process(p->pr);
1988 	(void) memset(p, 0, sizeof (*p));
1989 
1990 	(void) snprintf(pidstring, 24, "%d", pid);
1991 	return (grab_process_by_id(
1992 	    pidstring, RCENTITY_PROCESS, p, priv, gret));
1993 }
1994 
1995 /*
1996  * int grab_process_by_id(char *, rctl_entity_t, pr_info_handle_t *, int, int *)
1997  *
1998  * Input
1999  *    Supply a non-NULL string containing:
2000  *	- logical project/zone name or project/zone number if type is
2001  *	RCENTITY_PROJECT or RCENTITY_ZONE
2002  *	- task number if type is RCENTITY_TYPE
2003  *	- a pid if type is RCENTITY_PID
2004  *    Also supply an un-allocated prochandle, and an allocated info_handle.
2005  *    This function assumes that the type is set.
2006  *    If priv is not RCPRIV_BASIC, the grabbed process is required to have
2007  *    PRIV_SYS_RESOURCE in it's limit set.
2008  *
2009  * Return Values
2010  *    Returns 0 on success and 1 on failure. If there is a process
2011  *    running under the specified id, success is returned, and
2012  *    Pr is pointed to the process. Success will be returned and Pr
2013  *    set to NULL if the matching process is our own.
2014  *    If success is returned, psinfo will be valid, and pid will
2015  *    be the process number. The process will also be held at the
2016  *    end, so release_process should be used by the caller.
2017  *
2018  * This function assumes that signals are caught already so that libproc
2019  * can be safely used.
2020  *
2021  * Return Values
2022  *	pid - Process found and grabbed
2023  *	-1 - Error
2024  */
2025 pid_t
2026 grab_process_by_id(char *idname, rctl_entity_t type, pr_info_handle_t *p,
2027     int priv, int *gret)
2028 {
2029 	char prbuf[PROJECT_BUFSZ];
2030 	projid_t projid;
2031 	taskid_t taskid;
2032 	zoneid_t zoneid;
2033 	zoneid_t zone_self;
2034 	struct project proj;
2035 	DIR *dirp;
2036 	struct dirent *dentp;
2037 	int found = 0;
2038 	int pid_self;
2039 	int ret;
2040 	int gret_in;
2041 	int intidname;
2042 	char *end;
2043 	prpriv_t *prpriv;
2044 	priv_set_t *prset;
2045 
2046 	gret_in = *gret;
2047 
2048 	/* get our pid se we do not try to operate on self */
2049 	pid_self = getpid();
2050 
2051 	/* Store integer version of id */
2052 	intidname = strtoul(idname, &end, 10);
2053 	if (errno || *end != '\0' || end == idname) {
2054 		intidname = -1;
2055 	}
2056 
2057 	/*
2058 	 * get our zoneid so we don't try to operate on a project in
2059 	 * another zone
2060 	 */
2061 	zone_self = getzoneid();
2062 
2063 	if (idname == NULL || strcmp(idname, "") == 0) {
2064 		warn(gettext("id name cannot be nuint64\n"));
2065 		return (-1);
2066 	}
2067 	/*
2068 	 * Set up zoneid, projid or taskid, as appropriate, so that comparisons
2069 	 * can be done later with the input.
2070 	 */
2071 	if (type == RCENTITY_ZONE) {
2072 		if (zone_get_id(idname, &zoneid) != 0) {
2073 			warn(gettext("%s: unknown zone\n"), idname);
2074 			return (-1);
2075 		}
2076 	} else if (type == RCENTITY_PROJECT) {
2077 		if (getprojbyname(idname, &proj, prbuf, PROJECT_BUFSZ)
2078 		    == NULL) {
2079 			if (getprojbyid(intidname, &proj, prbuf,
2080 			    PROJECT_BUFSZ) == NULL) {
2081 				warn(gettext("%s: cannot find project\n"),
2082 				    idname);
2083 				return (-1);
2084 			}
2085 		}
2086 		projid = proj.pj_projid;
2087 	} else if (type == RCENTITY_TASK) {
2088 		taskid = (taskid_t)atol(idname);
2089 	}
2090 	/*
2091 	 * Projects and tasks need to search through /proc for
2092 	 * a parent process.
2093 	 */
2094 	if (type == RCENTITY_ZONE || type == RCENTITY_PROJECT ||
2095 	    type == RCENTITY_TASK) {
2096 		if ((dirp = opendir("/proc")) == NULL) {
2097 			warn(gettext("%s: cannot open /proc directory\n"),
2098 			    idname);
2099 			return (-1);
2100 		}
2101 		/*
2102 		 * Look through all processes in /proc. For each process,
2103 		 * check if the pr_projid in their psinfo matches the
2104 		 * specified id.
2105 		 */
2106 		while (dentp = readdir(dirp)) {
2107 			p->pid = atoi(dentp->d_name);
2108 
2109 			/* Skip self */
2110 			if (p->pid == pid_self)
2111 				continue;
2112 
2113 			if (proc_get_psinfo(p->pid, &(p->psinfo)) != 0)
2114 				continue;
2115 
2116 			/* Skip process if it is not what we are looking for */
2117 			if (type == RCENTITY_ZONE &&
2118 			    (p->psinfo).pr_zoneid != zoneid) {
2119 				continue;
2120 			} else if (type == RCENTITY_PROJECT &&
2121 			    ((p->psinfo).pr_projid != projid ||
2122 			    (p->psinfo).pr_zoneid != zone_self)) {
2123 				continue;
2124 			} else if (type == RCENTITY_TASK &&
2125 			    (p->psinfo).pr_taskid != taskid) {
2126 				continue;
2127 			}
2128 			/* attempt to grab process */
2129 			if (grab_process(p, gret) != 0)
2130 				continue;
2131 
2132 			/*
2133 			 * Re-confirm that this process is still running as
2134 			 * part	of the specified project or task.  If it
2135 			 * doesn't match, release the process and return an
2136 			 * error. This should only be done if the Pr struct is
2137 			 * not NULL.
2138 			 */
2139 			if (type == RCENTITY_PROJECT) {
2140 				if (pr_getprojid(p->pr) != projid ||
2141 				    pr_getzoneid(p->pr) != zone_self) {
2142 					release_process(p->pr);
2143 					continue;
2144 				}
2145 			} else if (type == RCENTITY_TASK) {
2146 				if (pr_gettaskid(p->pr) != taskid) {
2147 					release_process(p->pr);
2148 					continue;
2149 				}
2150 			} else if (type == RCENTITY_ZONE) {
2151 				if (pr_getzoneid(p->pr) != zoneid) {
2152 					release_process(p->pr);
2153 					continue;
2154 				}
2155 			}
2156 
2157 			/*
2158 			 * If we are setting a privileged resource control,
2159 			 * verify that process has PRIV_SYS_RESOURCE in it's
2160 			 * limit set.  If it does not, then we will not be
2161 			 * able to give this process the privilege it needs
2162 			 * to set the resource control.
2163 			 */
2164 			if (priv != RCPRIV_BASIC) {
2165 				prpriv = proc_get_priv(p->pid);
2166 				if (prpriv == NULL) {
2167 					release_process(p->pr);
2168 					continue;
2169 				}
2170 				prset = (priv_set_t *)
2171 				    &prpriv->pr_sets[prpriv->pr_setsize *
2172 				    priv_getsetbyname(PRIV_LIMIT)];
2173 				if (!priv_ismember(prset, PRIV_SYS_RESOURCE)) {
2174 					release_process(p->pr);
2175 					continue;
2176 				}
2177 			}
2178 			found = 1;
2179 
2180 			p->taskid = pr_gettaskid(p->pr);
2181 			p->projid = pr_getprojid(p->pr);
2182 			p->zoneid = pr_getzoneid(p->pr);
2183 
2184 			break;
2185 		}
2186 		(void) closedir(dirp);
2187 
2188 		if (found == 0) {
2189 			warn(gettext("%s: No controllable process found in "
2190 			    "task, project, or zone.\n"), idname);
2191 			return (-1);
2192 		}
2193 		return (p->pid);
2194 
2195 	} else if (type == RCENTITY_PROCESS) {
2196 
2197 		/* fail if self */
2198 		if (p->pid == pid_self) {
2199 
2200 			warn(gettext("%s: cannot control self"), idname);
2201 			return (-1);
2202 		}
2203 		/*
2204 		 * Process types need to be set up with the correct pid
2205 		 * and psinfo structure.
2206 		 */
2207 		if ((p->pid = proc_arg_psinfo(idname, PR_ARG_PIDS,
2208 		    &(p->psinfo), gret)) == -1) {
2209 			warn(gettext("%s: cannot examine: %s"), idname,
2210 			    Pgrab_error(*gret));
2211 			return (-1);
2212 		}
2213 		/* grab process */
2214 		ret = grab_process(p, gret);
2215 		if (ret == 1) {
2216 			/* Don't print error if G_SYS is allowed */
2217 			if (gret_in == G_SYS && *gret == G_SYS) {
2218 				return (-1);
2219 			} else {
2220 				warn(gettext("%s: cannot control: %s"), idname,
2221 				    Pgrab_error(*gret));
2222 				return (-1);
2223 			}
2224 		} else if (ret == 2) {
2225 			ret = errno;
2226 			warn(gettext("%s: cannot control: %s"), idname,
2227 			    strerror(ret));
2228 			return (-1);
2229 		}
2230 		p->taskid = pr_gettaskid(p->pr);
2231 		p->projid = pr_getprojid(p->pr);
2232 		p->zoneid = pr_getzoneid(p->pr);
2233 
2234 		return (p->pid);
2235 
2236 	} else {
2237 		warn(gettext("%s: unknown resource entity type %d\n"), idname,
2238 		    type);
2239 		return (-1);
2240 	}
2241 }
2242 
2243 /*
2244  * Do the work required to manipulate a process through libproc.
2245  * If grab_process() returns no errors (0), then release_process()
2246  * must eventually be called.
2247  *
2248  * Return values:
2249  *	0 Successful creation of agent thread
2250  *	1 Error grabbing
2251  *	2 Error creating agent
2252  */
2253 int
2254 grab_process(pr_info_handle_t *p, int *gret)
2255 {
2256 
2257 	if ((p->pr = Pgrab(p->pid, arg_force, gret)) != NULL) {
2258 
2259 		if (Psetflags(p->pr, PR_RLC) != 0) {
2260 			Prelease(p->pr, 0);
2261 			return (1);
2262 		}
2263 		if (Pcreate_agent(p->pr) == 0) {
2264 			return (0);
2265 
2266 		} else {
2267 			Prelease(p->pr, 0);
2268 			return (2);
2269 		}
2270 	} else {
2271 		return (1);
2272 	}
2273 }
2274 
2275 /*
2276  * Release the specified process. This destroys the agent
2277  * and releases the process. If the process is NULL, nothing
2278  * is done. This function should only be called if grab_process()
2279  * has previously been called and returned success.
2280  *
2281  * This function is Pgrab-safe.
2282  */
2283 void
2284 release_process(struct ps_prochandle *Pr)
2285 {
2286 	if (Pr == NULL)
2287 		return;
2288 
2289 	Pdestroy_agent(Pr);
2290 	Prelease(Pr, 0);
2291 }
2292 
2293 /*
2294  * preserve_error(char *, ...)
2295  *
2296  * preserve_error() should be called rather than warn() by any
2297  * function that is called while the victim process is held by Pgrab.
2298  * It will save the error until the process has been un-controlled
2299  * and output is reasonable again.
2300  *
2301  * Note that multiple errors are not stored. Any error in these
2302  * sections should be critical and return immediately.
2303  *
2304  * This function is Pgrab-safe.
2305  *
2306  * Since this function may copy untrusted command line arguments to
2307  * global_error, security practices require that global_error never be
2308  * printed directly.  Use printf("%s\n", global_error) or equivalent.
2309  */
2310 /*PRINTFLIKE1*/
2311 void
2312 preserve_error(char *format, ...)
2313 {
2314 	va_list alist;
2315 
2316 	va_start(alist, format);
2317 
2318 	/*
2319 	 * GLOBAL_ERR_SZ is pretty big. If the error is longer
2320 	 * than that, just truncate it, rather than chance missing
2321 	 * the error altogether.
2322 	 */
2323 	(void) vsnprintf(global_error, GLOBAL_ERR_SZ-1, format, alist);
2324 
2325 	va_end(alist);
2326 }
2327