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
usage()200 usage()
201 {
202 (void) fprintf(stderr, gettext(USAGE));
203 exit(2);
204 }
205
206 int
main(int argc,char ** argv)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
intr(int sig)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
get_rctls(struct ps_prochandle * Pr)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
store_rctls(const char * rctlname,void * walk_data)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 *
store_value_entry(rctlblk_t * rblk,prctl_list_t * list)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 *
store_list_entry(const char * name)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
free_lists()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
print_heading()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
print_rctls(pr_info_handle_t * p)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
match_rctl(struct ps_prochandle * Pr,rctlblk_t ** rctl,char * name,char * valuestringin,int valuein,rctl_priv_t privin,int pidin)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
match_rctl_blk(rctlblk_t * rctl,char * valuestringin,uint64_t valuein,rctl_priv_t privin,int pidin)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
change_action(rctlblk_t * blk)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
prctl_setrctl(struct ps_prochandle * Pr,const char * name,rctlblk_t * old_rblk,rctlblk_t * new_rblk,uint_t flags)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
print_priv(rctl_priv_t local_priv,char * format)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
print_local_action(int action,int * signalp,char * format)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
regrab_process(pid_t pid,pr_info_handle_t * p,int priv,int * gret)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
grab_process_by_id(char * idname,rctl_entity_t type,pr_info_handle_t * p,int priv,int * gret)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
grab_process(pr_info_handle_t * p,int * gret)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
release_process(struct ps_prochandle * Pr)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
preserve_error(char * format,...)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