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