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.
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.
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]
23 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2015, Joyent, Inc.
38 #include <sys/types.h>
42 #include <sys/varargs.h>
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
54 #define PRCTL_VALUE_WIDTH 4
56 /* Maximum string length for deferred errors */
57 #define GLOBAL_ERR_SZ 1024
59 /* allow important process values to be passed together easily */
60 typedef struct pr_info_handle
{
61 struct ps_prochandle
*pr
;
72 /* Structures for list of resource controls */
73 typedef struct prctl_value
{
75 struct prctl_value
*next
;
78 typedef struct prctl_list
{
81 prctl_value_t
*val_list
;
82 struct prctl_list
*next
;
85 static volatile int interrupt
;
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
];
91 /* global variables that contain commmand line option info */
92 static int arg_operation
= 0;
93 static int arg_force
= 0;
96 /* String and type from -i */
97 static rctl_entity_t arg_entity_type
= RCENTITY_PROCESS
;
98 static char *arg_entity_string
= NULL
;
101 static char *arg_name
= NULL
;
103 static rctl_entity_t arg_name_entity
= 0;
105 /* -t argument value */
106 static int arg_priv
= 0;
108 /* -v argument string */
109 static char *arg_valuestring
= NULL
;
111 /* global flags of rctl name passed to -n */
112 static int arg_global_flags
= 0;
113 static rctl_qty_t arg_global_max
;
115 /* appropriate scaling variables determined by rctl unit type */
117 static char *arg_unit
= NULL
;
119 /* -v argument string converted to uint64_t */
120 static uint64_t arg_value
= 0;
122 /* if -v argument is scaled value, points to "K", "M", "G", ... */
123 static char *arg_modifier
= NULL
;
125 /* -e/-d argument string */
126 static char *arg_action_string
= NULL
;
128 /* Set to RCTL_LOCAL_SIGNAL|DENY based on arg_action_string */
129 static int arg_action
= 0;
131 /* if -e/-d arg is signal=XXX, set to signal number of XXX */
132 static int arg_signal
= 0;
134 /* -p arg if -p is specified */
135 static int arg_pid
= -1;
136 static char *arg_pid_string
= NULL
;
138 /* Set to 1 if -P is specified */
139 static int arg_parseable_mode
= 0;
141 /* interupt handler */
142 static void intr(int);
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();
150 static int change_action(rctlblk_t
*blk
);
152 static int prctl_setrctl(struct ps_prochandle
*Pr
, const char *name
,
153 rctlblk_t
*, rctlblk_t
*, uint_t
);
155 static int match_rctl(struct ps_prochandle
*Pr
, rctlblk_t
**rctl
, char *name
,
156 char *valuestringin
, int valuein
, rctl_priv_t privin
,
158 static int match_rctl_blk(rctlblk_t
*rctl
, char *valuestringin
,
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
, ...);
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
);
172 static const char USAGE
[] = ""
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"
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";
203 (void) fprintf(stderr
, gettext(USAGE
));
208 main(int argc
, char **argv
)
212 rctlblk_t
*rctlblkA
= NULL
;
213 rctlblk_t
*rctlblkB
= NULL
;
214 rctlblk_t
*tmp
= NULL
;
224 (void) setlocale(LC_ALL
, "");
225 (void) textdomain(TEXT_DOMAIN
);
226 (void) setpname(argv
[0]);
228 while ((opt
= getopt(argc
, argv
, "sPp:Fd:e:i:n:rt:v:x")) != EOF
) {
231 case 'F': /* force grabbing (no O_EXCL) */
232 arg_force
= PGRAB_FORCE
;
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
;
249 warn(gettext("unknown idtype %s"), optarg
);
254 arg_action_string
= optarg
;
255 arg_operation
|= ACTION_DISABLE
;
258 arg_action_string
= optarg
;
259 arg_operation
|= ACTION_ENABLE
;
261 case 'n': /* name of rctl */
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
;
277 arg_operation
|= ACTION_REPLACE
;
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
;
289 warn(gettext("unknown privilege %s"), optarg
);
293 case 'v': /* value */
294 arg_valuestring
= optarg
;
297 arg_operation
|= ACTION_SET
;
299 case 'x': /* delete */
300 arg_operation
|= ACTION_DELETE
;
304 /* Stick with -1 if arg is "-" */
305 if (strcmp("-", optarg
) == 0)
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
);
317 arg_parseable_mode
= 1;
320 warn(gettext("unknown option"));
329 warn(gettext("no arguments specified"));
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
;
339 /* operations require -n */
340 if (arg_operation
&& (arg_name
== NULL
)) {
341 warn(gettext("-n is required with -s, -r, -x, -e, or -d"));
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"));
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"));
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"));
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"));
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"));
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"));
385 /* Specifying a recipient pid on a privileged rctl makes no sense */
387 arg_priv
== RCPRIV_PRIVILEGED
) {
388 warn(gettext("option -p not allowed with privileged rctl"));
394 /* do additional checks if there is an operation */
396 if (arg_parseable_mode
== 1) {
397 warn(gettext("-P not valid when manipulating "
398 "resource control values"));
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"),
409 if ((rctlblkB
= calloc(1, rctlblk_size())) == NULL
) {
410 warn(gettext("malloc failed: %s"),
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
));
422 while (getrctl(arg_name
, rctlblkA
, rctlblkB
, RCTL_NEXT
) == 0) {
424 /* allow user interrupt */
433 if (rctlblk_get_privilege(rctlblkA
) ==
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
));
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
);
448 if (arg_global_flags
& RCTL_GLOBAL_BYTES
) {
449 arg_unit
= SCALED_UNIT_BYTES
;
450 arg_scale
= scale_binary
;
452 } else if (arg_global_flags
& RCTL_GLOBAL_SECONDS
) {
453 arg_unit
= SCALED_UNIT_SECONDS
;
454 arg_scale
= scale_metric
;
457 arg_unit
= SCALED_UNIT_NONE
;
458 arg_scale
= scale_metric
;
460 /* parse -v value string */
461 if (arg_valuestring
) {
462 if (scaledtouint64(arg_valuestring
,
463 &arg_value
, NULL
, &arg_modifier
, NULL
,
467 warn(gettext("invalid -v value %s"),
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
);
481 if (arg_action_string
) {
486 if ((strcmp(arg_action_string
, "signal") == 0) ||
487 (strcmp(arg_action_string
, "sig") == 0)) {
489 if (arg_operation
& ACTION_ENABLE
) {
491 "signal name or number must be "
492 "specified with -e"));
496 arg_action
= RCTL_LOCAL_SIGNAL
;
499 } else if ((strncmp(arg_action_string
,
500 "signal=", strlen("signal=")) == 0) ||
501 (strncmp(arg_action_string
,
502 "sig=", strlen("sig=")) == 0)) {
504 arg_action
= RCTL_LOCAL_SIGNAL
;
505 sigchr
= strrchr(arg_action_string
, '=');
510 *iter
= toupper(*iter
);
513 if (strncmp("SIG", sigchr
, 3) == 0)
517 if (str2sig(sigchr
, &arg_signal
) != 0) {
518 warn(gettext("signal invalid"));
522 } else if (strcmp(arg_action_string
, "deny") == 0) {
524 arg_action
= RCTL_LOCAL_DENY
;
526 } else if (strcmp(arg_action_string
, "all") == 0) {
528 if (arg_operation
& ACTION_ENABLE
) {
530 "cannot use action 'all' with -e"));
534 arg_action
= RCTL_LOCAL_DENY
|
539 warn(gettext("action invalid"));
544 /* cannot manipulate system rctls */
545 if (arg_priv
== RCPRIV_SYSTEM
) {
547 warn(gettext("cannot modify system values"));
551 /* validate that the privilege is allowed */
552 if ((arg_priv
== RCPRIV_BASIC
) &&
553 (arg_global_flags
& RCTL_GLOBAL_NOBASIC
)) {
555 warn(gettext("basic values not allowed on rctl %s"),
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
)) {
565 warn(gettext("unable to enable deny on rctl with "
566 "global flag 'no-deny'"));
570 if ((arg_operation
& ACTION_DISABLE
) &&
571 (arg_action
& RCTL_LOCAL_DENY
) &&
572 (arg_global_flags
& RCTL_GLOBAL_DENY_ALWAYS
)) {
574 warn(gettext("unable to disable deny on rctl with "
575 "global flag 'deny'"));
579 if ((arg_operation
& ACTION_ENABLE
) &&
580 (arg_action
& RCTL_LOCAL_SIGNAL
) &&
581 (arg_global_flags
& RCTL_GLOBAL_SIGNAL_NEVER
)) {
583 warn(gettext("unable to enable signal on rctl with "
584 "global flag 'no-signal'"));
588 /* now set defaults for options not supplied */
591 * default privilege to basic if this is a seting an rctl
594 if (arg_operation
& ACTION_SET
) {
596 arg_priv
= RCPRIV_BASIC
;
600 * -p is required when set a basic task,
601 * project or zone rctl
603 if ((arg_pid
== -1) &&
604 (arg_priv
== RCPRIV_BASIC
) &&
605 (arg_entity_type
!= RCENTITY_PROCESS
) &&
606 (arg_operation
& ACTION_SET
) &&
608 (arg_name_entity
== RCENTITY_TASK
||
609 arg_name_entity
== RCENTITY_PROJECT
||
610 arg_name_entity
== RCENTITY_ZONE
)) {
612 warn(gettext("-p pid required when setting or "
613 "replacing task or project rctl"));
619 /* validate for list mode */
620 /* -p is not valid in list mode */
622 warn(gettext("-p pid requires -s, -r, -x, -e, or -d"));
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
))) {
632 warn(gettext("cannot get/set process rctl on task "
637 /* getting/setting task rctl on project is error */
638 if ((arg_name
&& (arg_name_entity
== RCENTITY_TASK
)) &&
639 (arg_entity_type
== RCENTITY_PROJECT
)) {
641 warn(gettext("cannot get/set task rctl on project"));
648 /* free any rctlblk's that we may have allocated */
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
);
669 while (--argc
>= 0 && !interrupt
) {
678 /* Store int version of arg */
680 intarg
= strtoul(arg
, &end
, 10);
681 if (errno
|| *end
!= '\0' || end
== arg
) {
686 * -p defaults to arg if basic and collective rctl
687 * and -i process is specified
689 if ((arg_pid
== -1) &&
690 (arg_priv
== RCPRIV_BASIC
) &&
691 (arg_entity_type
== RCENTITY_PROCESS
) &&
693 (arg_name_entity
== RCENTITY_TASK
||
694 arg_name_entity
== RCENTITY_PROJECT
)) {
695 arg_pid_string
= arg
;
699 /* Specifying a recipient pid and -i pid is redundent */
700 if (arg_pid
!= -1 && arg_entity_type
== RCENTITY_PROCESS
&&
702 warn(gettext("option -p pid must match -i process"));
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
;
712 search_type
= arg_entity_type
;
714 (void) fflush(stdout
); /* process-at-a-time */
716 if (arg_operation
!= 0) {
718 if ((pid
= grab_process_by_id(target_id
,
719 search_type
, &p
, arg_priv
, &gret
)) < 0) {
721 * Mark that an error occurred so that the
722 * return value can be set, but continue
723 * on with other processes
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().
735 errflg
= get_rctls(p
.pr
);
737 if (arg_operation
& ACTION_DELETE
) {
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
) {
747 preserve_error(gettext("no matching "
748 "resource control found for "
754 * grab correct process. This is neccessary
755 * if the recipient pid does not match the
758 pid
= regrab_process(
759 rctlblk_get_recipient_pid(rctlblkA
),
760 &p
, arg_priv
, &gret
);
766 if (prctl_setrctl(p
.pr
, arg_name
, NULL
,
767 rctlblkA
, RCTL_DELETE
) != 0) {
771 } else if (arg_operation
& ACTION_SET
) {
773 /* match by privilege, value, and pid */
775 if (match_rctl(p
.pr
, &rctlblkA
, arg_name
,
776 arg_valuestring
, arg_value
, arg_priv
,
782 preserve_error(gettext("resource "
783 "control already exists"));
787 rctlblkB
= calloc(1, rctlblk_size());
788 if (rctlblkB
== NULL
) {
789 preserve_error(gettext(
790 "malloc failed"), strerror(errno
));
794 rctlblk_set_value(rctlblkB
, arg_value
);
795 rctlblk_set_privilege(rctlblkB
, arg_priv
);
796 if (change_action(rctlblkB
)) {
800 if (prctl_setrctl(p
.pr
, arg_name
, NULL
,
801 rctlblkB
, RCTL_INSERT
) != 0) {
805 } else if (arg_operation
& ACTION_REPLACE
) {
807 * match rctl for deletion by privilege and
810 if (match_rctl(p
.pr
, &rctlblkA
, arg_name
,
812 arg_pid
) != 0 || rctlblkA
== NULL
) {
817 preserve_error(gettext("no matching "
818 "resource control to replace"));
823 * grab correct process. This is neccessary
824 * if the recipient pid does not match the
827 pid
= regrab_process(
828 rctlblk_get_recipient_pid(rctlblkA
),
829 &p
, arg_priv
, &gret
);
834 pid
= rctlblk_get_recipient_pid(rctlblkA
);
837 * match by privilege, value and pid to
838 * check if new rctl already exists
840 if (match_rctl(p
.pr
, &rctlblkB
, arg_name
,
841 arg_valuestring
, arg_value
, arg_priv
,
847 preserve_error(gettext(
853 * If rctl already exists, and it does not
854 * match the one that we will delete, than
855 * the replace will fail.
857 if (rctlblkB
!= NULL
&&
858 arg_value
!= rctlblk_get_value(rctlblkA
)) {
860 preserve_error(gettext("replacement "
861 "resource control already "
867 /* create new rctl */
868 rctlblkB
= calloc(1, rctlblk_size());
869 if (rctlblkB
== NULL
) {
870 preserve_error(gettext(
871 "malloc failed"), strerror(errno
));
876 rctlblk_get_local_action(rctlblkA
, &signal
);
877 rctlblk_set_local_action(rctlblkB
, localaction
,
879 rctlblk_set_value(rctlblkB
, arg_value
);
880 rctlblk_set_privilege(rctlblkB
,
881 rctlblk_get_privilege(rctlblkA
));
882 if (change_action(rctlblkB
)) {
887 if (prctl_setrctl(p
.pr
, arg_name
, rctlblkA
,
888 rctlblkB
, RCTL_REPLACE
) != 0) {
892 } else if (arg_operation
&
893 (ACTION_ENABLE
| ACTION_DISABLE
)) {
895 rctlblkB
= calloc(1, rctlblk_size());
896 if (rctlblkB
== NULL
) {
897 preserve_error(gettext(
898 "malloc failed"), strerror(errno
));
902 /* match by privilege, value, and pid */
903 if (match_rctl(p
.pr
, &rctlblkA
, arg_name
,
904 arg_valuestring
, arg_value
, arg_priv
,
910 /* if no match, just set new rctl */
912 arg_priv
= RCPRIV_BASIC
;
914 if ((arg_priv
== RCPRIV_BASIC
) &&
917 (arg_pid_string
== NULL
)) {
918 preserve_error(gettext(
919 "-p required when setting "
924 rctlblk_set_value(rctlblkB
,
926 rctlblk_set_privilege(
928 if (change_action(rctlblkB
)) {
932 if (prctl_setrctl(p
.pr
,
933 arg_name
, NULL
, rctlblkB
,
940 if (rctlblkA
== NULL
) {
941 preserve_error(gettext("no matching "
942 "resource control found"));
947 * grab correct process. This is neccessary
948 * if the recipient pid does not match the
951 pid
= regrab_process(
952 rctlblk_get_recipient_pid(rctlblkA
),
953 &p
, arg_priv
, &gret
);
959 rctlblk_get_local_action(rctlblkA
,
961 rctlblk_set_local_action(rctlblkB
, localaction
,
963 rctlblk_set_privilege(rctlblkB
,
964 rctlblk_get_privilege(rctlblkA
));
965 rctlblk_set_value(rctlblkB
,
966 rctlblk_get_value(rctlblkA
));
968 if (change_action(rctlblkB
)) {
972 if (prctl_setrctl(p
.pr
, arg_name
, rctlblkA
,
973 rctlblkB
, RCTL_REPLACE
) != 0) {
979 release_process(p
.pr
);
983 /* Print any errors that occurred */
984 if (errflg
&& *global_error
!= '\0') {
985 proc_unctrl_psinfo(&(p
.psinfo
));
986 (void) fprintf(stderr
, "%d:\t%.70s\n",
987 (int)p
.pid
, p
.psinfo
.pr_psargs
);
988 warn("%s\n", global_error
);
993 struct project projent
;
994 char buf
[PROJECT_BUFSZ
];
995 char zonename
[ZONENAME_MAX
];
998 * Hack to allow the user to specify a system
1002 pid
= grab_process_by_id(
1003 target_id
, search_type
, &p
, RCPRIV_BASIC
, &gret
);
1006 * Print system process if user chose specifically
1007 * to inspect a system process.
1009 if (arg_entity_type
== RCENTITY_PROCESS
&&
1013 * Add blank lines between output for
1017 (void) fprintf(stdout
, "\n");
1020 proc_unctrl_psinfo(&(p
.psinfo
));
1022 "process: %d: %s [ system process ]\n",
1023 (int)p
.pid
, p
.psinfo
.pr_psargs
);
1028 } else if (pid
< 0) {
1031 * Mark that an error occurred so that the
1032 * return value can be set, but continue
1033 * on with other processes
1039 errflg
= get_rctls(p
.pr
);
1041 release_process(p
.pr
);
1043 /* handle user interrupt of getting rctls */
1047 /* add blank lines between output for operands */
1049 (void) fprintf(stdout
, "\n");
1051 /* First print any errors */
1053 warn("%s\n", global_error
);
1057 if (getprojbyid(p
.projid
, &projent
, buf
,
1059 p
.projname
= projent
.pj_name
;
1063 if (getzonenamebyid(p
.zoneid
, zonename
,
1064 sizeof (zonename
)) > 0) {
1065 p
.zonename
= zonename
;
1071 /* Free the resource control lists */
1079 * return error if one occurred
1092 * get_rctls(struct ps_prochandle *, const char *)
1094 * If controlname is given, store only controls for that named
1095 * resource. If controlname is NULL, store all controls for all
1098 * This function is Pgrab-safe.
1101 get_rctls(struct ps_prochandle
*Pr
)
1105 if (arg_name
== NULL
) {
1106 if (rctl_walk(store_rctls
, Pr
) != 0)
1109 ret
= store_rctls(arg_name
, Pr
);
1115 * store_rctls(const char *, void *)
1117 * Store resource controls for the given name in a linked list.
1118 * Honor the user's options, and store only the ones they are
1119 * interested in. If priv is not 0, show only controls that match
1120 * the given privilege.
1122 * This function is Pgrab-safe
1125 store_rctls(const char *rctlname
, void *walk_data
)
1127 struct ps_prochandle
*Pr
= walk_data
;
1128 rctlblk_t
*rblk2
, *rblk_tmp
, *rblk1
= NULL
;
1129 prctl_list_t
*list
= NULL
;
1130 rctl_priv_t rblk_priv
;
1131 rctl_entity_t rblk_entity
;
1133 if (((rblk1
= calloc(1, rctlblk_size())) == NULL
) ||
1134 ((rblk2
= calloc(1, rctlblk_size())) == NULL
)) {
1136 preserve_error(gettext("malloc failed: %s"),
1140 if (pr_getrctl(Pr
, rctlname
, NULL
, rblk1
, RCTL_FIRST
)) {
1141 preserve_error(gettext("failed to get resource control "
1142 "for %s: %s"), rctlname
, strerror(errno
));
1147 /* Store control if it matches privilege and enity type criteria */
1148 rblk_priv
= rctlblk_get_privilege(rblk1
);
1150 if (strncmp(rctlname
, "process.",
1151 strlen("process.")) == 0)
1152 rblk_entity
= RCENTITY_PROCESS
;
1153 else if (strncmp(rctlname
, "project.",
1154 strlen("project.")) == 0)
1155 rblk_entity
= RCENTITY_PROJECT
;
1156 else if (strncmp(rctlname
, "task.",
1157 strlen("task.")) == 0)
1158 rblk_entity
= RCENTITY_TASK
;
1159 else if (strncmp(rctlname
, "zone.",
1160 strlen("zone.")) == 0)
1161 rblk_entity
= RCENTITY_ZONE
;
1163 if (((arg_priv
== 0) || (rblk_priv
== arg_priv
)) &&
1164 ((arg_name
== NULL
) ||
1165 strncmp(rctlname
, arg_name
, strlen(arg_name
)) == 0) &&
1166 (arg_entity_string
== NULL
|| rblk_entity
>= arg_entity_type
)) {
1168 /* Once we know we have some controls, store the name */
1169 if ((list
= store_list_entry(rctlname
)) == NULL
) {
1174 if (store_value_entry(rblk1
, list
) == NULL
) {
1180 while (pr_getrctl(Pr
, rctlname
, rblk1
, rblk2
, RCTL_NEXT
) == 0) {
1183 * in case this is stuck for some reason, allow manual
1191 rblk_priv
= rctlblk_get_privilege(rblk2
);
1193 * Store control if it matches privilege and entity type
1196 if (((arg_priv
== 0) || (rblk_priv
== arg_priv
)) &&
1197 ((arg_name
== NULL
) ||
1198 strncmp(rctlname
, arg_name
, strlen(arg_name
)) == 0) &&
1199 (arg_entity_string
== NULL
||
1200 rblk_entity
== arg_entity_type
)) {
1202 /* May not have created the list yet. */
1204 if ((list
= store_list_entry(rctlname
))
1211 if (store_value_entry(rblk2
, list
) == NULL
) {
1223 * Get the current usage for the resource control if it matched the
1224 * privilege and entity type criteria.
1227 if (pr_getrctl(Pr
, rctlname
, NULL
, rblk2
, RCTL_USAGE
) == 0) {
1228 list
->usage
= (rctl_qty_t
*)malloc(sizeof (rctl_qty_t
));
1229 if (list
->usage
== NULL
) {
1230 preserve_error(gettext("malloc failed: %s"),
1236 *list
->usage
= rctlblk_get_value(rblk2
);
1239 if (errno
!= ENOTSUP
) {
1240 preserve_error(gettext("failed to get "
1241 "resource control usage for %s: %s"),
1242 rctlname
, strerror(errno
));
1255 * store_value_entry(rctlblk_t *, prctl_list_t *)
1257 * Store an rblk for a given resource control into the global list.
1259 * This function is Pgrab-safe.
1262 store_value_entry(rctlblk_t
*rblk
, prctl_list_t
*list
)
1264 prctl_value_t
*e
= calloc(1, sizeof (prctl_value_t
));
1265 rctlblk_t
*store_blk
= calloc(1, rctlblk_size());
1266 prctl_value_t
*iter
= list
->val_list
;
1268 if (e
== NULL
|| store_blk
== NULL
) {
1269 preserve_error(gettext("malloc failed %s"),
1278 while (iter
->next
!= NULL
) {
1283 bcopy(rblk
, store_blk
, rctlblk_size());
1285 e
->rblk
= store_blk
;
1291 * store_list_entry(const char *)
1293 * Store a new resource control value in the global list. No checking
1294 * for duplicates done.
1296 * This function is Pgrab-safe.
1299 store_list_entry(const char *name
)
1301 prctl_list_t
*e
= calloc(1, sizeof (prctl_list_t
));
1304 preserve_error(gettext("malloc failed %s"),
1308 if ((e
->name
= calloc(1, strlen(name
) + 1)) == NULL
) {
1309 preserve_error(gettext("malloc failed %s"),
1314 (void) strcpy(e
->name
, name
);
1317 if (global_rctl_list_head
== NULL
) {
1318 global_rctl_list_head
= e
;
1319 global_rctl_list_tail
= e
;
1321 global_rctl_list_tail
->next
= e
;
1322 global_rctl_list_tail
= e
;
1331 * Free all resource control blocks and values from the global lists.
1333 * This function is Pgrab-safe.
1338 prctl_list_t
*new_list
, *old_list
= global_rctl_list_head
;
1339 prctl_value_t
*old_val
, *new_val
;
1341 while (old_list
!= NULL
) {
1342 old_val
= old_list
->val_list
;
1343 while (old_val
!= NULL
) {
1344 free(old_val
->rblk
);
1345 new_val
= old_val
->next
;
1349 free(old_list
->name
);
1350 free(old_list
->usage
);
1351 new_list
= old_list
->next
;
1353 old_list
= new_list
;
1355 global_rctl_list_head
= NULL
;
1356 global_rctl_list_tail
= NULL
;
1363 /* print headings */
1364 (void) fprintf(stdout
, "%-8s%-16s%-9s%-7s%-28s%10s\n",
1365 "NAME", "PRIVILEGE", "VALUE",
1366 "FLAG", "ACTION", "RECIPIENT");
1372 * Print all resource controls from the global list that was
1373 * previously populated by store_rctls.
1376 print_rctls(pr_info_handle_t
*p
)
1378 prctl_list_t
*iter_list
= global_rctl_list_head
;
1379 prctl_value_t
*iter_val
;
1380 rctl_qty_t rblk_value
;
1381 rctl_priv_t rblk_priv
;
1382 uint_t local_action
;
1383 int signal
, local_flags
, global_flags
;
1385 char rctl_valuestring
[SCALED_STRLEN
];
1389 int doneheading
= 0;
1391 if (iter_list
== NULL
)
1394 while (iter_list
!= NULL
) {
1396 if (doneheading
== 0 &&
1397 arg_entity_type
== RCENTITY_PROCESS
) {
1398 proc_unctrl_psinfo(&(p
->psinfo
));
1400 (void) fprintf(stdout
,
1401 "process: %d: %.70s\n", (int)p
->pid
,
1402 p
->psinfo
.pr_psargs
);
1403 if (!arg_parseable_mode
)
1406 if (doneheading
== 0 &&
1407 arg_entity_type
== RCENTITY_TASK
) {
1409 (void) fprintf(stdout
, "task: %d\n", (int)p
->taskid
);
1410 if (!arg_parseable_mode
)
1413 if (doneheading
== 0 &&
1414 arg_entity_type
== RCENTITY_PROJECT
) {
1415 if (!arg_parseable_mode
&& doneheading
)
1416 (void) fprintf(stdout
, "\n");
1418 (void) fprintf(stdout
,
1419 "project: %d: %.70s\n", (int)p
->projid
,
1421 if (!arg_parseable_mode
)
1424 if (doneheading
== 0 &&
1425 arg_entity_type
== RCENTITY_ZONE
) {
1427 (void) fprintf(stdout
,
1428 "zone: %d: %.70s\n", (int)p
->zoneid
,
1430 if (!arg_parseable_mode
)
1433 /* only print name once in normal output */
1434 if (!arg_parseable_mode
)
1435 (void) fprintf(stdout
, "%s\n", iter_list
->name
);
1437 iter_val
= iter_list
->val_list
;
1439 /* if for some reason there are no values, skip */
1444 /* get the global flags the first rctl only */
1445 global_flags
= rctlblk_get_global_flags(iter_val
->rblk
);
1448 if (global_flags
& RCTL_GLOBAL_BYTES
) {
1449 unit
= SCALED_UNIT_BYTES
;
1450 scale
= scale_binary
;
1452 } else if (global_flags
& RCTL_GLOBAL_SECONDS
) {
1453 unit
= SCALED_UNIT_SECONDS
;
1454 scale
= scale_metric
;
1457 unit
= SCALED_UNIT_NONE
;
1458 scale
= scale_metric
;
1461 /* print the current usage for the rctl if available */
1462 if (iter_list
->usage
!= NULL
) {
1463 rblk_value
= *(iter_list
->usage
);
1464 if (!arg_parseable_mode
) {
1465 (void) uint64toscaled(rblk_value
, 4, "E",
1466 rctl_valuestring
, NULL
, NULL
,
1469 (void) fprintf(stdout
, "%8s%-16s%5s%-4s\n",
1470 "", "usage", rctl_valuestring
, unit
);
1472 (void) fprintf(stdout
, "%s %s %llu - - -\n",
1473 iter_list
->name
, "usage", rblk_value
);
1477 /* iterate over an print all control values */
1478 while (iter_val
!= NULL
) {
1480 /* print name or empty name field */
1481 if (!arg_parseable_mode
)
1482 (void) fprintf(stdout
, "%8s", "");
1484 (void) fprintf(stdout
, "%s ", iter_list
->name
);
1487 rblk_priv
= rctlblk_get_privilege(iter_val
->rblk
);
1488 if (!arg_parseable_mode
)
1489 print_priv(rblk_priv
, "%-16s");
1491 print_priv(rblk_priv
, "%s ");
1493 rblk_value
= rctlblk_get_value(iter_val
->rblk
);
1494 if (arg_parseable_mode
) {
1495 (void) fprintf(stdout
, "%llu ", rblk_value
);
1499 (void) uint64toscaled(rblk_value
, 4, "E",
1500 rctl_valuestring
, NULL
, NULL
,
1503 (void) fprintf(stdout
, "%5s",
1505 (void) fprintf(stdout
, "%-4s", unit
);
1507 local_flags
= rctlblk_get_local_flags(iter_val
->rblk
);
1509 if (local_flags
& RCTL_LOCAL_MAXIMAL
) {
1511 if (global_flags
& RCTL_GLOBAL_INFINITE
) {
1519 if (arg_parseable_mode
)
1520 (void) fprintf(stdout
, "%s ", string
);
1522 (void) fprintf(stdout
, "%4s%3s",
1526 local_action
= rctlblk_get_local_action(iter_val
->rblk
,
1529 if (arg_parseable_mode
)
1530 print_local_action(local_action
, &signal
,
1533 print_local_action(local_action
, &signal
,
1536 pid
= rctlblk_get_recipient_pid(iter_val
->rblk
);
1538 if (arg_parseable_mode
) {
1540 (void) fprintf(stdout
, "%s\n", "-");
1542 (void) fprintf(stdout
, "%d\n",
1547 (void) fprintf(stdout
, "%10s\n", "-");
1549 (void) fprintf(stdout
, "%10d\n",
1553 iter_val
= iter_val
->next
;
1555 iter_list
= iter_list
->next
;
1563 * find the first rctl with matching name, value, priv, and recipient pid
1566 match_rctl(struct ps_prochandle
*Pr
, rctlblk_t
**rctl
, char *name
,
1567 char *valuestringin
, int valuein
, rctl_priv_t privin
, int pidin
)
1575 next
= calloc(1, rctlblk_size());
1576 last
= calloc(1, rctlblk_size());
1578 if ((last
== NULL
) || (next
== NULL
)) {
1579 preserve_error(gettext("malloc failed"), strerror(errno
));
1583 * For this resource name, now iterate through all
1584 * the controls, looking for a match to the
1585 * user-specified input.
1587 if (pr_getrctl(Pr
, name
, NULL
, next
, RCTL_FIRST
)) {
1588 preserve_error(gettext("failed to get resource control "
1589 "for %s: %s"), name
, strerror(errno
));
1592 if (match_rctl_blk(next
, valuestringin
, valuein
, privin
, pidin
) == 1) {
1601 while (pr_getrctl(Pr
, name
, last
, next
, RCTL_NEXT
) == 0) {
1603 /* allow user interrupt */
1607 if (match_rctl_blk(next
, valuestringin
, valuein
, privin
, pidin
)
1624 * int match_rctl_blk(rctlblk_t *, char *, uint64, rctl_priv_t, int pid)
1627 * Must supply a valid rctl, value, privilege, and pid to match on.
1628 * If valuestring is NULL, then valuestring and valuein will not be used
1629 * If privilege type is 0 it will not be used.
1630 * If pid is -1 it will not be used.
1633 * Returns 1 if a matching rctl given matches the parameters specified, and
1636 * This function is Pgrab-safe.
1639 match_rctl_blk(rctlblk_t
*rctl
, char *valuestringin
,
1640 uint64_t valuein
, rctl_priv_t privin
, int pidin
)
1650 value
= rctlblk_get_value(rctl
);
1651 priv
= rctlblk_get_privilege(rctl
);
1652 pid
= rctlblk_get_recipient_pid(rctl
);
1654 if (valuestringin
) {
1656 if (arg_modifier
== NULL
) {
1657 valuematch
= (valuein
== value
);
1659 valuematch
= scaledequint64(valuestringin
, value
,
1661 arg_scale
, arg_unit
,
1666 privmatch
= (privin
== priv
);
1669 pidmatch
= (pidin
== pid
);
1671 return (valuematch
&& privmatch
&& pidmatch
);
1675 change_action(rctlblk_t
*blk
)
1680 action
= rctlblk_get_local_action(blk
, &signal
);
1682 if (arg_operation
& ACTION_ENABLE
) {
1684 if (arg_action
& RCTL_LOCAL_SIGNAL
) {
1685 signal
= arg_signal
;
1687 action
= action
| arg_action
;
1688 /* add local action */
1689 rctlblk_set_local_action(blk
, action
, signal
);
1691 } else if (arg_operation
& ACTION_DISABLE
) {
1694 * if deleting signal and signal number is specified,
1695 * then signal number must match
1697 if ((arg_action
& RCTL_LOCAL_SIGNAL
) &&
1698 (arg_signal
!= -1)) {
1699 if (arg_signal
!= signal
) {
1700 preserve_error(gettext("signal name or number "
1701 "does not match existing action"));
1705 /* remove local action */
1706 action
= action
& (~arg_action
);
1707 rctlblk_set_local_action(blk
, RCTL_LOCAL_NOACTION
, 0);
1708 rctlblk_set_local_action(blk
, action
, signal
);
1710 /* enable deny if it must be enabled */
1711 if (arg_global_flags
& RCTL_GLOBAL_DENY_ALWAYS
) {
1712 rctlblk_set_local_action(blk
, RCTL_LOCAL_DENY
| action
,
1722 * This function expects that input has been validated. In the
1723 * case of a replace operation, both old_rblk and new_rblk must
1724 * be valid resource controls. If a resource control is being
1725 * created, only new_rblk must be supplied. If a resource control
1726 * is being deleted, only new_rblk must be supplied.
1728 * If the privilege is a priviliged type, at this time, the process
1729 * tries to take on superuser privileges.
1732 prctl_setrctl(struct ps_prochandle
*Pr
, const char *name
,
1733 rctlblk_t
*old_rblk
, rctlblk_t
*new_rblk
, uint_t flags
)
1736 rctl_priv_t rblk_priv
;
1738 zoneid_t oldzoneid
= GLOBAL_ZONEID
;
1739 prpriv_t
*old_prpriv
= NULL
, *new_prpriv
= NULL
;
1740 priv_set_t
*eset
, *pset
;
1741 boolean_t relinquish_failed
= B_FALSE
;
1743 rblk_priv
= rctlblk_get_privilege(new_rblk
);
1745 if (rblk_priv
== RCPRIV_SYSTEM
) {
1746 preserve_error(gettext("cannot modify system values"));
1749 if (rblk_priv
== RCPRIV_PRIVILEGED
) {
1750 new_prpriv
= proc_get_priv(Pstatus(Pr
)->pr_pid
);
1751 if (new_prpriv
== NULL
) {
1752 preserve_error(gettext("cannot get process privileges "
1753 "for pid %d: %s"), Pstatus(Pr
)->pr_pid
,
1758 * We only have to change the process privileges if it doesn't
1759 * already have PRIV_SYS_RESOURCE. In addition, we want to make
1760 * sure that we don't leave a process with elevated privileges,
1761 * so we make sure the process dies if we exit unexpectedly.
1763 eset
= (priv_set_t
*)
1764 &new_prpriv
->pr_sets
[new_prpriv
->pr_setsize
*
1765 priv_getsetbyname(PRIV_EFFECTIVE
)];
1766 pset
= (priv_set_t
*)
1767 &new_prpriv
->pr_sets
[new_prpriv
->pr_setsize
*
1768 priv_getsetbyname(PRIV_PERMITTED
)];
1769 if (!priv_ismember(eset
, PRIV_SYS_RESOURCE
)) {
1770 /* Keep track of original privileges */
1771 old_prpriv
= proc_get_priv(Pstatus(Pr
)->pr_pid
);
1772 if (old_prpriv
== NULL
) {
1773 preserve_error(gettext("cannot get process "
1774 "privileges for pid %d: %s"),
1775 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1776 proc_free_priv(new_prpriv
);
1779 (void) priv_addset(eset
, PRIV_SYS_RESOURCE
);
1780 (void) priv_addset(pset
, PRIV_SYS_RESOURCE
);
1781 if (Psetflags(Pr
, PR_KLC
) != 0 ||
1782 Psetpriv(Pr
, new_prpriv
) != 0) {
1783 preserve_error(gettext("cannot set process "
1784 "privileges for pid %d: %s"),
1785 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1786 (void) Punsetflags(Pr
, PR_KLC
);
1787 proc_free_priv(new_prpriv
);
1788 proc_free_priv(old_prpriv
);
1793 * If this is a zone.* rctl, it requires more than
1794 * PRIV_SYS_RESOURCE: it wants the process to have global-zone
1795 * credentials. We temporarily grant non-global zone processes
1796 * these credentials, and make sure the process dies if we exit
1800 arg_name_entity
== RCENTITY_ZONE
&&
1801 getzoneid() == GLOBAL_ZONEID
&&
1802 proc_get_psinfo(Pstatus(Pr
)->pr_pid
, &psinfo
) == 0 &&
1803 (oldzoneid
= psinfo
.pr_zoneid
) != GLOBAL_ZONEID
) {
1805 * We need to give this process superuser
1806 * ("super-zone") privileges.
1808 * Must never return without setting this back!
1810 if (Psetflags(Pr
, PR_KLC
) != 0 ||
1811 Psetzoneid(Pr
, GLOBAL_ZONEID
) < 0) {
1812 preserve_error(gettext(
1813 "cannot set global-zone "
1814 "privileges for pid %d: %s"),
1815 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1817 * We couldn't set the zoneid to begin with, so
1818 * there's no point in warning the user about
1819 * trying to un-set it.
1821 oldzoneid
= GLOBAL_ZONEID
;
1827 /* Now, actually populate the rctlblk in the kernel */
1828 if (flags
== RCTL_REPLACE
) {
1830 * Replace should be a delete followed by an insert. This
1831 * allows us to replace rctl value blocks which match in
1832 * privilege and value, but have updated actions, etc.
1833 * setrctl() doesn't allow a direct replace, but we
1834 * should do the right thing for the user in the command.
1836 if (pr_setrctl(Pr
, name
, NULL
,
1837 old_rblk
, RCTL_DELETE
)) {
1838 preserve_error(gettext("failed to delete resource "
1839 "control %s for pid %d: %s"), name
,
1840 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1844 if (pr_setrctl(Pr
, name
, NULL
,
1845 new_rblk
, RCTL_INSERT
)) {
1846 preserve_error(gettext("failed to insert resource "
1847 "control %s for pid %d: %s"), name
,
1848 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1852 } else if (flags
== RCTL_INSERT
) {
1853 if (pr_setrctl(Pr
, name
, NULL
,
1854 new_rblk
, RCTL_INSERT
)) {
1855 preserve_error(gettext("failed to create resource "
1856 "control %s for pid %d: %s"), name
,
1857 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1861 } else if (flags
== RCTL_DELETE
) {
1862 if (pr_setrctl(Pr
, name
, NULL
,
1863 new_rblk
, RCTL_DELETE
)) {
1864 preserve_error(gettext("failed to delete resource "
1865 "control %s for pid %d: %s"), name
,
1866 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1872 if (oldzoneid
!= GLOBAL_ZONEID
) {
1873 if (Psetzoneid(Pr
, oldzoneid
) != 0)
1874 relinquish_failed
= B_TRUE
;
1876 if (old_prpriv
!= NULL
) {
1877 if (Psetpriv(Pr
, old_prpriv
) != 0)
1878 relinquish_failed
= B_TRUE
;
1879 proc_free_priv(old_prpriv
);
1881 if (relinquish_failed
) {
1883 * If this failed, we can't leave a process hanging
1884 * around with elevated privileges, so we'll have to
1885 * release the process from libproc, knowing that it
1886 * will be killed (since we set PR_KLC).
1889 preserve_error(gettext("cannot relinquish privileges "
1890 "for pid %d. The process was killed."),
1891 Pstatus(Pr
)->pr_pid
);
1893 if (Punsetflags(Pr
, PR_KLC
) != 0)
1894 preserve_error(gettext("cannot relinquish privileges "
1895 "for pid %d. The process was killed."),
1896 Pstatus(Pr
)->pr_pid
);
1898 proc_free_priv(new_prpriv
);
1904 print_priv(rctl_priv_t local_priv
, char *format
)
1908 switch (local_priv
) {
1910 (void) strcpy(pstring
, "basic");
1912 case RCPRIV_PRIVILEGED
:
1913 (void) strcpy(pstring
, "privileged");
1916 (void) strcpy(pstring
, "system");
1919 (void) sprintf(pstring
, "%d", local_priv
);
1923 (void) fprintf(stdout
, format
, pstring
);
1927 print_local_action(int action
, int *signalp
, char *format
)
1929 char sig
[SIG2STR_MAX
];
1930 char sigstring
[SIG2STR_MAX
+ 7];
1931 char astring
[5 + SIG2STR_MAX
+ 7];
1936 if (action
== RCTL_LOCAL_NOACTION
) {
1937 (void) strcat(astring
, "none");
1940 if (action
& RCTL_LOCAL_DENY
) {
1941 (void) strcat(astring
, "deny");
1944 if ((action
& RCTL_LOCAL_DENY
) &&
1945 (action
& RCTL_LOCAL_SIGNAL
)) {
1946 (void) strcat(astring
, ",");
1948 if (action
& RCTL_LOCAL_SIGNAL
) {
1949 if (sig2str(*signalp
, sig
))
1950 (void) snprintf(sigstring
, sizeof (astring
),
1951 "signal=%d", *signalp
);
1953 (void) snprintf(sigstring
, sizeof (astring
),
1956 (void) strcat(astring
, sigstring
);
1961 (void) fprintf(stdout
, format
, astring
);
1964 (void) fprintf(stdout
, format
, action
);
1968 * This function is used to grab the process matching the recipient pid
1971 regrab_process(pid_t pid
, pr_info_handle_t
*p
, int priv
, int *gret
)
1982 release_process(p
->pr
);
1983 (void) memset(p
, 0, sizeof (*p
));
1985 (void) snprintf(pidstring
, 24, "%d", pid
);
1986 return (grab_process_by_id(
1987 pidstring
, RCENTITY_PROCESS
, p
, priv
, gret
));
1991 * int grab_process_by_id(char *, rctl_entity_t, pr_info_handle_t *, int, int *)
1994 * Supply a non-NULL string containing:
1995 * - logical project/zone name or project/zone number if type is
1996 * RCENTITY_PROJECT or RCENTITY_ZONE
1997 * - task number if type is RCENTITY_TYPE
1998 * - a pid if type is RCENTITY_PID
1999 * Also supply an un-allocated prochandle, and an allocated info_handle.
2000 * This function assumes that the type is set.
2001 * If priv is not RCPRIV_BASIC, the grabbed process is required to have
2002 * PRIV_SYS_RESOURCE in it's limit set.
2005 * Returns 0 on success and 1 on failure. If there is a process
2006 * running under the specified id, success is returned, and
2007 * Pr is pointed to the process. Success will be returned and Pr
2008 * set to NULL if the matching process is our own.
2009 * If success is returned, psinfo will be valid, and pid will
2010 * be the process number. The process will also be held at the
2011 * end, so release_process should be used by the caller.
2013 * This function assumes that signals are caught already so that libproc
2014 * can be safely used.
2017 * pid - Process found and grabbed
2021 grab_process_by_id(char *idname
, rctl_entity_t type
, pr_info_handle_t
*p
,
2022 int priv
, int *gret
)
2024 char prbuf
[PROJECT_BUFSZ
];
2029 struct project proj
;
2031 struct dirent
*dentp
;
2043 /* get our pid se we do not try to operate on self */
2044 pid_self
= getpid();
2046 /* Store integer version of id */
2047 intidname
= strtoul(idname
, &end
, 10);
2048 if (errno
|| *end
!= '\0' || end
== idname
) {
2053 * get our zoneid so we don't try to operate on a project in
2056 zone_self
= getzoneid();
2058 if (idname
== NULL
|| strcmp(idname
, "") == 0) {
2059 warn(gettext("id name cannot be nuint64\n"));
2063 * Set up zoneid, projid or taskid, as appropriate, so that comparisons
2064 * can be done later with the input.
2066 if (type
== RCENTITY_ZONE
) {
2067 if (zone_get_id(idname
, &zoneid
) != 0) {
2068 warn(gettext("%s: unknown zone\n"), idname
);
2071 } else if (type
== RCENTITY_PROJECT
) {
2072 if (getprojbyname(idname
, &proj
, prbuf
, PROJECT_BUFSZ
)
2074 if (getprojbyid(intidname
, &proj
, prbuf
,
2075 PROJECT_BUFSZ
) == NULL
) {
2076 warn(gettext("%s: cannot find project\n"),
2081 projid
= proj
.pj_projid
;
2082 } else if (type
== RCENTITY_TASK
) {
2083 taskid
= (taskid_t
)atol(idname
);
2086 * Projects and tasks need to search through /proc for
2089 if (type
== RCENTITY_ZONE
|| type
== RCENTITY_PROJECT
||
2090 type
== RCENTITY_TASK
) {
2091 if ((dirp
= opendir("/proc")) == NULL
) {
2092 warn(gettext("%s: cannot open /proc directory\n"),
2097 * Look through all processes in /proc. For each process,
2098 * check if the pr_projid in their psinfo matches the
2101 while (dentp
= readdir(dirp
)) {
2102 p
->pid
= atoi(dentp
->d_name
);
2105 if (p
->pid
== pid_self
)
2108 if (proc_get_psinfo(p
->pid
, &(p
->psinfo
)) != 0)
2111 /* Skip process if it is not what we are looking for */
2112 if (type
== RCENTITY_ZONE
&&
2113 (p
->psinfo
).pr_zoneid
!= zoneid
) {
2115 } else if (type
== RCENTITY_PROJECT
&&
2116 ((p
->psinfo
).pr_projid
!= projid
||
2117 (p
->psinfo
).pr_zoneid
!= zone_self
)) {
2119 } else if (type
== RCENTITY_TASK
&&
2120 (p
->psinfo
).pr_taskid
!= taskid
) {
2123 /* attempt to grab process */
2124 if (grab_process(p
, gret
) != 0)
2128 * Re-confirm that this process is still running as
2129 * part of the specified project or task. If it
2130 * doesn't match, release the process and return an
2131 * error. This should only be done if the Pr struct is
2134 if (type
== RCENTITY_PROJECT
) {
2135 if (pr_getprojid(p
->pr
) != projid
||
2136 pr_getzoneid(p
->pr
) != zone_self
) {
2137 release_process(p
->pr
);
2140 } else if (type
== RCENTITY_TASK
) {
2141 if (pr_gettaskid(p
->pr
) != taskid
) {
2142 release_process(p
->pr
);
2145 } else if (type
== RCENTITY_ZONE
) {
2146 if (pr_getzoneid(p
->pr
) != zoneid
) {
2147 release_process(p
->pr
);
2153 * If we are setting a privileged resource control,
2154 * verify that process has PRIV_SYS_RESOURCE in it's
2155 * limit set. If it does not, then we will not be
2156 * able to give this process the privilege it needs
2157 * to set the resource control.
2159 if (priv
!= RCPRIV_BASIC
) {
2160 prpriv
= proc_get_priv(p
->pid
);
2161 if (prpriv
== NULL
) {
2162 release_process(p
->pr
);
2165 prset
= (priv_set_t
*)
2166 &prpriv
->pr_sets
[prpriv
->pr_setsize
*
2167 priv_getsetbyname(PRIV_LIMIT
)];
2168 if (!priv_ismember(prset
, PRIV_SYS_RESOURCE
)) {
2169 proc_free_priv(prpriv
);
2170 release_process(p
->pr
);
2173 proc_free_priv(prpriv
);
2177 p
->taskid
= pr_gettaskid(p
->pr
);
2178 p
->projid
= pr_getprojid(p
->pr
);
2179 p
->zoneid
= pr_getzoneid(p
->pr
);
2183 (void) closedir(dirp
);
2186 warn(gettext("%s: No controllable process found in "
2187 "task, project, or zone.\n"), idname
);
2192 } else if (type
== RCENTITY_PROCESS
) {
2195 if (p
->pid
== pid_self
) {
2197 warn(gettext("%s: cannot control self"), idname
);
2201 * Process types need to be set up with the correct pid
2202 * and psinfo structure.
2204 if ((p
->pid
= proc_arg_psinfo(idname
, PR_ARG_PIDS
,
2205 &(p
->psinfo
), gret
)) == -1) {
2206 warn(gettext("%s: cannot examine: %s"), idname
,
2207 Pgrab_error(*gret
));
2211 ret
= grab_process(p
, gret
);
2213 /* Don't print error if G_SYS is allowed */
2214 if (gret_in
== G_SYS
&& *gret
== G_SYS
) {
2217 warn(gettext("%s: cannot control: %s"), idname
,
2218 Pgrab_error(*gret
));
2221 } else if (ret
== 2) {
2223 warn(gettext("%s: cannot control: %s"), idname
,
2227 p
->taskid
= pr_gettaskid(p
->pr
);
2228 p
->projid
= pr_getprojid(p
->pr
);
2229 p
->zoneid
= pr_getzoneid(p
->pr
);
2234 warn(gettext("%s: unknown resource entity type %d\n"), idname
,
2241 * Do the work required to manipulate a process through libproc.
2242 * If grab_process() returns no errors (0), then release_process()
2243 * must eventually be called.
2246 * 0 Successful creation of agent thread
2248 * 2 Error creating agent
2251 grab_process(pr_info_handle_t
*p
, int *gret
)
2254 if ((p
->pr
= Pgrab(p
->pid
, arg_force
, gret
)) != NULL
) {
2256 if (Psetflags(p
->pr
, PR_RLC
) != 0) {
2260 if (Pcreate_agent(p
->pr
) == 0) {
2273 * Release the specified process. This destroys the agent
2274 * and releases the process. If the process is NULL, nothing
2275 * is done. This function should only be called if grab_process()
2276 * has previously been called and returned success.
2278 * This function is Pgrab-safe.
2281 release_process(struct ps_prochandle
*Pr
)
2291 * preserve_error(char *, ...)
2293 * preserve_error() should be called rather than warn() by any
2294 * function that is called while the victim process is held by Pgrab.
2295 * It will save the error until the process has been un-controlled
2296 * and output is reasonable again.
2298 * Note that multiple errors are not stored. Any error in these
2299 * sections should be critical and return immediately.
2301 * This function is Pgrab-safe.
2303 * Since this function may copy untrusted command line arguments to
2304 * global_error, security practices require that global_error never be
2305 * printed directly. Use printf("%s\n", global_error) or equivalent.
2309 preserve_error(char *format
, ...)
2313 va_start(alist
, format
);
2316 * GLOBAL_ERR_SZ is pretty big. If the error is longer
2317 * than that, just truncate it, rather than chance missing
2318 * the error altogether.
2320 (void) vsnprintf(global_error
, GLOBAL_ERR_SZ
-1, format
, alist
);