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
);
985 /* Print any errors that occurred */
986 if (errflg
&& *global_error
!= '\0') {
987 proc_unctrl_psinfo(&(p
.psinfo
));
988 (void) fprintf(stderr
, "%d:\t%.70s\n",
989 (int)p
.pid
, p
.psinfo
.pr_psargs
);
990 warn("%s\n", global_error
);
995 struct project projent
;
996 char buf
[PROJECT_BUFSZ
];
997 char zonename
[ZONENAME_MAX
];
1000 * Hack to allow the user to specify a system
1004 pid
= grab_process_by_id(
1005 target_id
, search_type
, &p
, RCPRIV_BASIC
, &gret
);
1008 * Print system process if user chose specifically
1009 * to inspect a system process.
1011 if (arg_entity_type
== RCENTITY_PROCESS
&&
1015 * Add blank lines between output for
1019 (void) fprintf(stdout
, "\n");
1022 proc_unctrl_psinfo(&(p
.psinfo
));
1024 "process: %d: %s [ system process ]\n",
1025 (int)p
.pid
, p
.psinfo
.pr_psargs
);
1030 } else if (pid
< 0) {
1033 * Mark that an error occurred so that the
1034 * return value can be set, but continue
1035 * on with other processes
1041 errflg
= get_rctls(p
.pr
);
1043 release_process(p
.pr
);
1045 /* handle user interrupt of getting rctls */
1049 /* add blank lines between output for operands */
1051 (void) fprintf(stdout
, "\n");
1053 /* First print any errors */
1055 warn("%s\n", global_error
);
1059 if (getprojbyid(p
.projid
, &projent
, buf
,
1061 p
.projname
= projent
.pj_name
;
1065 if (getzonenamebyid(p
.zoneid
, zonename
,
1066 sizeof (zonename
)) > 0) {
1067 p
.zonename
= zonename
;
1073 /* Free the resource control lists */
1081 * return error if one occurred
1094 * get_rctls(struct ps_prochandle *, const char *)
1096 * If controlname is given, store only controls for that named
1097 * resource. If controlname is NULL, store all controls for all
1100 * This function is Pgrab-safe.
1103 get_rctls(struct ps_prochandle
*Pr
)
1107 if (arg_name
== NULL
) {
1108 if (rctl_walk(store_rctls
, Pr
) != 0)
1111 ret
= store_rctls(arg_name
, Pr
);
1117 * store_rctls(const char *, void *)
1119 * Store resource controls for the given name in a linked list.
1120 * Honor the user's options, and store only the ones they are
1121 * interested in. If priv is not 0, show only controls that match
1122 * the given privilege.
1124 * This function is Pgrab-safe
1127 store_rctls(const char *rctlname
, void *walk_data
)
1129 struct ps_prochandle
*Pr
= walk_data
;
1130 rctlblk_t
*rblk2
, *rblk_tmp
, *rblk1
= NULL
;
1131 prctl_list_t
*list
= NULL
;
1132 rctl_priv_t rblk_priv
;
1133 rctl_entity_t rblk_entity
;
1135 if (((rblk1
= calloc(1, rctlblk_size())) == NULL
) ||
1136 ((rblk2
= calloc(1, rctlblk_size())) == NULL
)) {
1139 preserve_error(gettext("malloc failed: %s"),
1143 if (pr_getrctl(Pr
, rctlname
, NULL
, rblk1
, RCTL_FIRST
)) {
1144 preserve_error(gettext("failed to get resource control "
1145 "for %s: %s"), rctlname
, strerror(errno
));
1150 /* Store control if it matches privilege and enity type criteria */
1151 rblk_priv
= rctlblk_get_privilege(rblk1
);
1153 if (strncmp(rctlname
, "process.",
1154 strlen("process.")) == 0)
1155 rblk_entity
= RCENTITY_PROCESS
;
1156 else if (strncmp(rctlname
, "project.",
1157 strlen("project.")) == 0)
1158 rblk_entity
= RCENTITY_PROJECT
;
1159 else if (strncmp(rctlname
, "task.",
1160 strlen("task.")) == 0)
1161 rblk_entity
= RCENTITY_TASK
;
1162 else if (strncmp(rctlname
, "zone.",
1163 strlen("zone.")) == 0)
1164 rblk_entity
= RCENTITY_ZONE
;
1166 if (((arg_priv
== 0) || (rblk_priv
== arg_priv
)) &&
1167 ((arg_name
== NULL
) ||
1168 strncmp(rctlname
, arg_name
, strlen(arg_name
)) == 0) &&
1169 (arg_entity_string
== NULL
|| rblk_entity
>= arg_entity_type
)) {
1171 /* Once we know we have some controls, store the name */
1172 if ((list
= store_list_entry(rctlname
)) == NULL
) {
1177 if (store_value_entry(rblk1
, list
) == NULL
) {
1183 while (pr_getrctl(Pr
, rctlname
, rblk1
, rblk2
, RCTL_NEXT
) == 0) {
1186 * in case this is stuck for some reason, allow manual
1194 rblk_priv
= rctlblk_get_privilege(rblk2
);
1196 * Store control if it matches privilege and entity type
1199 if (((arg_priv
== 0) || (rblk_priv
== arg_priv
)) &&
1200 ((arg_name
== NULL
) ||
1201 strncmp(rctlname
, arg_name
, strlen(arg_name
)) == 0) &&
1202 (arg_entity_string
== NULL
||
1203 rblk_entity
== arg_entity_type
)) {
1205 /* May not have created the list yet. */
1207 if ((list
= store_list_entry(rctlname
))
1214 if (store_value_entry(rblk2
, list
) == NULL
) {
1226 * Get the current usage for the resource control if it matched the
1227 * privilege and entity type criteria.
1230 if (pr_getrctl(Pr
, rctlname
, NULL
, rblk2
, RCTL_USAGE
) == 0) {
1231 list
->usage
= (rctl_qty_t
*)malloc(sizeof (rctl_qty_t
));
1232 if (list
->usage
== NULL
) {
1233 preserve_error(gettext("malloc failed: %s"),
1239 *list
->usage
= rctlblk_get_value(rblk2
);
1242 if (errno
!= ENOTSUP
) {
1243 preserve_error(gettext("failed to get "
1244 "resource control usage for %s: %s"),
1245 rctlname
, strerror(errno
));
1258 * store_value_entry(rctlblk_t *, prctl_list_t *)
1260 * Store an rblk for a given resource control into the global list.
1262 * This function is Pgrab-safe.
1265 store_value_entry(rctlblk_t
*rblk
, prctl_list_t
*list
)
1267 prctl_value_t
*e
= calloc(1, sizeof (prctl_value_t
));
1268 rctlblk_t
*store_blk
= calloc(1, rctlblk_size());
1269 prctl_value_t
*iter
= list
->val_list
;
1271 if (e
== NULL
|| store_blk
== NULL
) {
1272 preserve_error(gettext("malloc failed %s"),
1276 if (store_blk
!= NULL
)
1283 while (iter
->next
!= NULL
) {
1288 bcopy(rblk
, store_blk
, rctlblk_size());
1290 e
->rblk
= store_blk
;
1296 * store_list_entry(const char *)
1298 * Store a new resource control value in the global list. No checking
1299 * for duplicates done.
1301 * This function is Pgrab-safe.
1304 store_list_entry(const char *name
)
1306 prctl_list_t
*e
= calloc(1, sizeof (prctl_list_t
));
1309 preserve_error(gettext("malloc failed %s"),
1313 if ((e
->name
= calloc(1, strlen(name
) + 1)) == NULL
) {
1314 preserve_error(gettext("malloc failed %s"),
1319 (void) strcpy(e
->name
, name
);
1322 if (global_rctl_list_head
== NULL
) {
1323 global_rctl_list_head
= e
;
1324 global_rctl_list_tail
= e
;
1326 global_rctl_list_tail
->next
= e
;
1327 global_rctl_list_tail
= e
;
1336 * Free all resource control blocks and values from the global lists.
1338 * This function is Pgrab-safe.
1343 prctl_list_t
*new_list
, *old_list
= global_rctl_list_head
;
1344 prctl_value_t
*old_val
, *new_val
;
1346 while (old_list
!= NULL
) {
1347 old_val
= old_list
->val_list
;
1348 while (old_val
!= NULL
) {
1349 free(old_val
->rblk
);
1350 new_val
= old_val
->next
;
1354 free(old_list
->name
);
1355 free(old_list
->usage
);
1356 new_list
= old_list
->next
;
1358 old_list
= new_list
;
1360 global_rctl_list_head
= NULL
;
1361 global_rctl_list_tail
= NULL
;
1368 /* print headings */
1369 (void) fprintf(stdout
, "%-8s%-16s%-9s%-7s%-28s%10s\n",
1370 "NAME", "PRIVILEGE", "VALUE",
1371 "FLAG", "ACTION", "RECIPIENT");
1377 * Print all resource controls from the global list that was
1378 * previously populated by store_rctls.
1381 print_rctls(pr_info_handle_t
*p
)
1383 prctl_list_t
*iter_list
= global_rctl_list_head
;
1384 prctl_value_t
*iter_val
;
1385 rctl_qty_t rblk_value
;
1386 rctl_priv_t rblk_priv
;
1387 uint_t local_action
;
1388 int signal
, local_flags
, global_flags
;
1390 char rctl_valuestring
[SCALED_STRLEN
];
1394 int doneheading
= 0;
1396 if (iter_list
== NULL
)
1399 while (iter_list
!= NULL
) {
1401 if (doneheading
== 0 &&
1402 arg_entity_type
== RCENTITY_PROCESS
) {
1403 proc_unctrl_psinfo(&(p
->psinfo
));
1405 (void) fprintf(stdout
,
1406 "process: %d: %.70s\n", (int)p
->pid
,
1407 p
->psinfo
.pr_psargs
);
1408 if (!arg_parseable_mode
)
1411 if (doneheading
== 0 &&
1412 arg_entity_type
== RCENTITY_TASK
) {
1414 (void) fprintf(stdout
, "task: %d\n", (int)p
->taskid
);
1415 if (!arg_parseable_mode
)
1418 if (doneheading
== 0 &&
1419 arg_entity_type
== RCENTITY_PROJECT
) {
1420 if (!arg_parseable_mode
&& doneheading
)
1421 (void) fprintf(stdout
, "\n");
1423 (void) fprintf(stdout
,
1424 "project: %d: %.70s\n", (int)p
->projid
,
1426 if (!arg_parseable_mode
)
1429 if (doneheading
== 0 &&
1430 arg_entity_type
== RCENTITY_ZONE
) {
1432 (void) fprintf(stdout
,
1433 "zone: %d: %.70s\n", (int)p
->zoneid
,
1435 if (!arg_parseable_mode
)
1438 /* only print name once in normal output */
1439 if (!arg_parseable_mode
)
1440 (void) fprintf(stdout
, "%s\n", iter_list
->name
);
1442 iter_val
= iter_list
->val_list
;
1444 /* if for some reason there are no values, skip */
1449 /* get the global flags the first rctl only */
1450 global_flags
= rctlblk_get_global_flags(iter_val
->rblk
);
1453 if (global_flags
& RCTL_GLOBAL_BYTES
) {
1454 unit
= SCALED_UNIT_BYTES
;
1455 scale
= scale_binary
;
1457 } else if (global_flags
& RCTL_GLOBAL_SECONDS
) {
1458 unit
= SCALED_UNIT_SECONDS
;
1459 scale
= scale_metric
;
1462 unit
= SCALED_UNIT_NONE
;
1463 scale
= scale_metric
;
1466 /* print the current usage for the rctl if available */
1467 if (iter_list
->usage
!= NULL
) {
1468 rblk_value
= *(iter_list
->usage
);
1469 if (!arg_parseable_mode
) {
1470 (void) uint64toscaled(rblk_value
, 4, "E",
1471 rctl_valuestring
, NULL
, NULL
,
1474 (void) fprintf(stdout
, "%8s%-16s%5s%-4s\n",
1475 "", "usage", rctl_valuestring
, unit
);
1477 (void) fprintf(stdout
, "%s %s %llu - - -\n",
1478 iter_list
->name
, "usage", rblk_value
);
1482 /* iterate over an print all control values */
1483 while (iter_val
!= NULL
) {
1485 /* print name or empty name field */
1486 if (!arg_parseable_mode
)
1487 (void) fprintf(stdout
, "%8s", "");
1489 (void) fprintf(stdout
, "%s ", iter_list
->name
);
1492 rblk_priv
= rctlblk_get_privilege(iter_val
->rblk
);
1493 if (!arg_parseable_mode
)
1494 print_priv(rblk_priv
, "%-16s");
1496 print_priv(rblk_priv
, "%s ");
1498 rblk_value
= rctlblk_get_value(iter_val
->rblk
);
1499 if (arg_parseable_mode
) {
1500 (void) fprintf(stdout
, "%llu ", rblk_value
);
1504 (void) uint64toscaled(rblk_value
, 4, "E",
1505 rctl_valuestring
, NULL
, NULL
,
1508 (void) fprintf(stdout
, "%5s",
1510 (void) fprintf(stdout
, "%-4s", unit
);
1512 local_flags
= rctlblk_get_local_flags(iter_val
->rblk
);
1514 if (local_flags
& RCTL_LOCAL_MAXIMAL
) {
1516 if (global_flags
& RCTL_GLOBAL_INFINITE
) {
1524 if (arg_parseable_mode
)
1525 (void) fprintf(stdout
, "%s ", string
);
1527 (void) fprintf(stdout
, "%4s%3s",
1531 local_action
= rctlblk_get_local_action(iter_val
->rblk
,
1534 if (arg_parseable_mode
)
1535 print_local_action(local_action
, &signal
,
1538 print_local_action(local_action
, &signal
,
1541 pid
= rctlblk_get_recipient_pid(iter_val
->rblk
);
1543 if (arg_parseable_mode
) {
1545 (void) fprintf(stdout
, "%s\n", "-");
1547 (void) fprintf(stdout
, "%d\n",
1552 (void) fprintf(stdout
, "%10s\n", "-");
1554 (void) fprintf(stdout
, "%10d\n",
1558 iter_val
= iter_val
->next
;
1560 iter_list
= iter_list
->next
;
1568 * find the first rctl with matching name, value, priv, and recipient pid
1571 match_rctl(struct ps_prochandle
*Pr
, rctlblk_t
**rctl
, char *name
,
1572 char *valuestringin
, int valuein
, rctl_priv_t privin
, int pidin
)
1580 next
= calloc(1, rctlblk_size());
1581 last
= calloc(1, rctlblk_size());
1583 if ((last
== NULL
) || (next
== NULL
)) {
1584 preserve_error(gettext("malloc failed"), strerror(errno
));
1588 * For this resource name, now iterate through all
1589 * the controls, looking for a match to the
1590 * user-specified input.
1592 if (pr_getrctl(Pr
, name
, NULL
, next
, RCTL_FIRST
)) {
1593 preserve_error(gettext("failed to get resource control "
1594 "for %s: %s"), name
, strerror(errno
));
1597 if (match_rctl_blk(next
, valuestringin
, valuein
, privin
, pidin
) == 1) {
1606 while (pr_getrctl(Pr
, name
, last
, next
, RCTL_NEXT
) == 0) {
1608 /* allow user interrupt */
1612 if (match_rctl_blk(next
, valuestringin
, valuein
, privin
, pidin
)
1629 * int match_rctl_blk(rctlblk_t *, char *, uint64, rctl_priv_t, int pid)
1632 * Must supply a valid rctl, value, privilege, and pid to match on.
1633 * If valuestring is NULL, then valuestring and valuein will not be used
1634 * If privilege type is 0 it will not be used.
1635 * If pid is -1 it will not be used.
1638 * Returns 1 if a matching rctl given matches the parameters specified, and
1641 * This function is Pgrab-safe.
1644 match_rctl_blk(rctlblk_t
*rctl
, char *valuestringin
,
1645 uint64_t valuein
, rctl_priv_t privin
, int pidin
)
1655 value
= rctlblk_get_value(rctl
);
1656 priv
= rctlblk_get_privilege(rctl
);
1657 pid
= rctlblk_get_recipient_pid(rctl
);
1659 if (valuestringin
) {
1661 if (arg_modifier
== NULL
) {
1662 valuematch
= (valuein
== value
);
1664 valuematch
= scaledequint64(valuestringin
, value
,
1666 arg_scale
, arg_unit
,
1671 privmatch
= (privin
== priv
);
1674 pidmatch
= (pidin
== pid
);
1676 return (valuematch
&& privmatch
&& pidmatch
);
1680 change_action(rctlblk_t
*blk
)
1685 action
= rctlblk_get_local_action(blk
, &signal
);
1687 if (arg_operation
& ACTION_ENABLE
) {
1689 if (arg_action
& RCTL_LOCAL_SIGNAL
) {
1690 signal
= arg_signal
;
1692 action
= action
| arg_action
;
1693 /* add local action */
1694 rctlblk_set_local_action(blk
, action
, signal
);
1696 } else if (arg_operation
& ACTION_DISABLE
) {
1699 * if deleting signal and signal number is specified,
1700 * then signal number must match
1702 if ((arg_action
& RCTL_LOCAL_SIGNAL
) &&
1703 (arg_signal
!= -1)) {
1704 if (arg_signal
!= signal
) {
1705 preserve_error(gettext("signal name or number "
1706 "does not match existing action"));
1710 /* remove local action */
1711 action
= action
& (~arg_action
);
1712 rctlblk_set_local_action(blk
, RCTL_LOCAL_NOACTION
, 0);
1713 rctlblk_set_local_action(blk
, action
, signal
);
1715 /* enable deny if it must be enabled */
1716 if (arg_global_flags
& RCTL_GLOBAL_DENY_ALWAYS
) {
1717 rctlblk_set_local_action(blk
, RCTL_LOCAL_DENY
| action
,
1727 * This function expects that input has been validated. In the
1728 * case of a replace operation, both old_rblk and new_rblk must
1729 * be valid resource controls. If a resource control is being
1730 * created, only new_rblk must be supplied. If a resource control
1731 * is being deleted, only new_rblk must be supplied.
1733 * If the privilege is a priviliged type, at this time, the process
1734 * tries to take on superuser privileges.
1737 prctl_setrctl(struct ps_prochandle
*Pr
, const char *name
,
1738 rctlblk_t
*old_rblk
, rctlblk_t
*new_rblk
, uint_t flags
)
1741 rctl_priv_t rblk_priv
;
1743 zoneid_t oldzoneid
= GLOBAL_ZONEID
;
1744 prpriv_t
*old_prpriv
= NULL
, *new_prpriv
= NULL
;
1745 priv_set_t
*eset
, *pset
;
1746 boolean_t relinquish_failed
= B_FALSE
;
1748 rblk_priv
= rctlblk_get_privilege(new_rblk
);
1750 if (rblk_priv
== RCPRIV_SYSTEM
) {
1751 preserve_error(gettext("cannot modify system values"));
1754 if (rblk_priv
== RCPRIV_PRIVILEGED
) {
1755 new_prpriv
= proc_get_priv(Pstatus(Pr
)->pr_pid
);
1756 if (new_prpriv
== NULL
) {
1757 preserve_error(gettext("cannot get process privileges "
1758 "for pid %d: %s"), Pstatus(Pr
)->pr_pid
,
1763 * We only have to change the process privileges if it doesn't
1764 * already have PRIV_SYS_RESOURCE. In addition, we want to make
1765 * sure that we don't leave a process with elevated privileges,
1766 * so we make sure the process dies if we exit unexpectedly.
1768 eset
= (priv_set_t
*)
1769 &new_prpriv
->pr_sets
[new_prpriv
->pr_setsize
*
1770 priv_getsetbyname(PRIV_EFFECTIVE
)];
1771 pset
= (priv_set_t
*)
1772 &new_prpriv
->pr_sets
[new_prpriv
->pr_setsize
*
1773 priv_getsetbyname(PRIV_PERMITTED
)];
1774 if (!priv_ismember(eset
, PRIV_SYS_RESOURCE
)) {
1775 /* Keep track of original privileges */
1776 old_prpriv
= proc_get_priv(Pstatus(Pr
)->pr_pid
);
1777 if (old_prpriv
== NULL
) {
1778 preserve_error(gettext("cannot get process "
1779 "privileges for pid %d: %s"),
1780 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1781 proc_free_priv(new_prpriv
);
1784 (void) priv_addset(eset
, PRIV_SYS_RESOURCE
);
1785 (void) priv_addset(pset
, PRIV_SYS_RESOURCE
);
1786 if (Psetflags(Pr
, PR_KLC
) != 0 ||
1787 Psetpriv(Pr
, new_prpriv
) != 0) {
1788 preserve_error(gettext("cannot set process "
1789 "privileges for pid %d: %s"),
1790 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1791 (void) Punsetflags(Pr
, PR_KLC
);
1792 proc_free_priv(new_prpriv
);
1793 proc_free_priv(old_prpriv
);
1798 * If this is a zone.* rctl, it requires more than
1799 * PRIV_SYS_RESOURCE: it wants the process to have global-zone
1800 * credentials. We temporarily grant non-global zone processes
1801 * these credentials, and make sure the process dies if we exit
1805 arg_name_entity
== RCENTITY_ZONE
&&
1806 getzoneid() == GLOBAL_ZONEID
&&
1807 proc_get_psinfo(Pstatus(Pr
)->pr_pid
, &psinfo
) == 0 &&
1808 (oldzoneid
= psinfo
.pr_zoneid
) != GLOBAL_ZONEID
) {
1810 * We need to give this process superuser
1811 * ("super-zone") privileges.
1813 * Must never return without setting this back!
1815 if (Psetflags(Pr
, PR_KLC
) != 0 ||
1816 Psetzoneid(Pr
, GLOBAL_ZONEID
) < 0) {
1817 preserve_error(gettext(
1818 "cannot set global-zone "
1819 "privileges for pid %d: %s"),
1820 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1822 * We couldn't set the zoneid to begin with, so
1823 * there's no point in warning the user about
1824 * trying to un-set it.
1826 oldzoneid
= GLOBAL_ZONEID
;
1832 /* Now, actually populate the rctlblk in the kernel */
1833 if (flags
== RCTL_REPLACE
) {
1835 * Replace should be a delete followed by an insert. This
1836 * allows us to replace rctl value blocks which match in
1837 * privilege and value, but have updated actions, etc.
1838 * setrctl() doesn't allow a direct replace, but we
1839 * should do the right thing for the user in the command.
1841 if (pr_setrctl(Pr
, name
, NULL
,
1842 old_rblk
, RCTL_DELETE
)) {
1843 preserve_error(gettext("failed to delete resource "
1844 "control %s for pid %d: %s"), name
,
1845 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1849 if (pr_setrctl(Pr
, name
, NULL
,
1850 new_rblk
, RCTL_INSERT
)) {
1851 preserve_error(gettext("failed to insert resource "
1852 "control %s for pid %d: %s"), name
,
1853 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1857 } else if (flags
== RCTL_INSERT
) {
1858 if (pr_setrctl(Pr
, name
, NULL
,
1859 new_rblk
, RCTL_INSERT
)) {
1860 preserve_error(gettext("failed to create resource "
1861 "control %s for pid %d: %s"), name
,
1862 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1866 } else if (flags
== RCTL_DELETE
) {
1867 if (pr_setrctl(Pr
, name
, NULL
,
1868 new_rblk
, RCTL_DELETE
)) {
1869 preserve_error(gettext("failed to delete resource "
1870 "control %s for pid %d: %s"), name
,
1871 Pstatus(Pr
)->pr_pid
, strerror(errno
));
1877 if (oldzoneid
!= GLOBAL_ZONEID
) {
1878 if (Psetzoneid(Pr
, oldzoneid
) != 0)
1879 relinquish_failed
= B_TRUE
;
1881 if (old_prpriv
!= NULL
) {
1882 if (Psetpriv(Pr
, old_prpriv
) != 0)
1883 relinquish_failed
= B_TRUE
;
1884 proc_free_priv(old_prpriv
);
1886 if (relinquish_failed
) {
1888 * If this failed, we can't leave a process hanging
1889 * around with elevated privileges, so we'll have to
1890 * release the process from libproc, knowing that it
1891 * will be killed (since we set PR_KLC).
1894 preserve_error(gettext("cannot relinquish privileges "
1895 "for pid %d. The process was killed."),
1896 Pstatus(Pr
)->pr_pid
);
1898 if (Punsetflags(Pr
, PR_KLC
) != 0)
1899 preserve_error(gettext("cannot relinquish privileges "
1900 "for pid %d. The process was killed."),
1901 Pstatus(Pr
)->pr_pid
);
1903 if (new_prpriv
!= NULL
)
1904 proc_free_priv(new_prpriv
);
1910 print_priv(rctl_priv_t local_priv
, char *format
)
1914 switch (local_priv
) {
1916 (void) strcpy(pstring
, "basic");
1918 case RCPRIV_PRIVILEGED
:
1919 (void) strcpy(pstring
, "privileged");
1922 (void) strcpy(pstring
, "system");
1925 (void) sprintf(pstring
, "%d", local_priv
);
1929 (void) fprintf(stdout
, format
, pstring
);
1933 print_local_action(int action
, int *signalp
, char *format
)
1935 char sig
[SIG2STR_MAX
];
1936 char sigstring
[SIG2STR_MAX
+ 7];
1937 char astring
[5 + SIG2STR_MAX
+ 7];
1942 if (action
== RCTL_LOCAL_NOACTION
) {
1943 (void) strcat(astring
, "none");
1946 if (action
& RCTL_LOCAL_DENY
) {
1947 (void) strcat(astring
, "deny");
1950 if ((action
& RCTL_LOCAL_DENY
) &&
1951 (action
& RCTL_LOCAL_SIGNAL
)) {
1952 (void) strcat(astring
, ",");
1954 if (action
& RCTL_LOCAL_SIGNAL
) {
1955 if (sig2str(*signalp
, sig
))
1956 (void) snprintf(sigstring
, sizeof (astring
),
1957 "signal=%d", *signalp
);
1959 (void) snprintf(sigstring
, sizeof (astring
),
1962 (void) strcat(astring
, sigstring
);
1967 (void) fprintf(stdout
, format
, astring
);
1970 (void) fprintf(stdout
, format
, action
);
1974 * This function is used to grab the process matching the recipient pid
1977 regrab_process(pid_t pid
, pr_info_handle_t
*p
, int priv
, int *gret
)
1988 release_process(p
->pr
);
1989 (void) memset(p
, 0, sizeof (*p
));
1991 (void) snprintf(pidstring
, 24, "%d", pid
);
1992 return (grab_process_by_id(
1993 pidstring
, RCENTITY_PROCESS
, p
, priv
, gret
));
1997 * int grab_process_by_id(char *, rctl_entity_t, pr_info_handle_t *, int, int *)
2000 * Supply a non-NULL string containing:
2001 * - logical project/zone name or project/zone number if type is
2002 * RCENTITY_PROJECT or RCENTITY_ZONE
2003 * - task number if type is RCENTITY_TYPE
2004 * - a pid if type is RCENTITY_PID
2005 * Also supply an un-allocated prochandle, and an allocated info_handle.
2006 * This function assumes that the type is set.
2007 * If priv is not RCPRIV_BASIC, the grabbed process is required to have
2008 * PRIV_SYS_RESOURCE in it's limit set.
2011 * Returns 0 on success and 1 on failure. If there is a process
2012 * running under the specified id, success is returned, and
2013 * Pr is pointed to the process. Success will be returned and Pr
2014 * set to NULL if the matching process is our own.
2015 * If success is returned, psinfo will be valid, and pid will
2016 * be the process number. The process will also be held at the
2017 * end, so release_process should be used by the caller.
2019 * This function assumes that signals are caught already so that libproc
2020 * can be safely used.
2023 * pid - Process found and grabbed
2027 grab_process_by_id(char *idname
, rctl_entity_t type
, pr_info_handle_t
*p
,
2028 int priv
, int *gret
)
2030 char prbuf
[PROJECT_BUFSZ
];
2035 struct project proj
;
2037 struct dirent
*dentp
;
2049 /* get our pid se we do not try to operate on self */
2050 pid_self
= getpid();
2052 /* Store integer version of id */
2053 intidname
= strtoul(idname
, &end
, 10);
2054 if (errno
|| *end
!= '\0' || end
== idname
) {
2059 * get our zoneid so we don't try to operate on a project in
2062 zone_self
= getzoneid();
2064 if (idname
== NULL
|| strcmp(idname
, "") == 0) {
2065 warn(gettext("id name cannot be nuint64\n"));
2069 * Set up zoneid, projid or taskid, as appropriate, so that comparisons
2070 * can be done later with the input.
2072 if (type
== RCENTITY_ZONE
) {
2073 if (zone_get_id(idname
, &zoneid
) != 0) {
2074 warn(gettext("%s: unknown zone\n"), idname
);
2077 } else if (type
== RCENTITY_PROJECT
) {
2078 if (getprojbyname(idname
, &proj
, prbuf
, PROJECT_BUFSZ
)
2080 if (getprojbyid(intidname
, &proj
, prbuf
,
2081 PROJECT_BUFSZ
) == NULL
) {
2082 warn(gettext("%s: cannot find project\n"),
2087 projid
= proj
.pj_projid
;
2088 } else if (type
== RCENTITY_TASK
) {
2089 taskid
= (taskid_t
)atol(idname
);
2092 * Projects and tasks need to search through /proc for
2095 if (type
== RCENTITY_ZONE
|| type
== RCENTITY_PROJECT
||
2096 type
== RCENTITY_TASK
) {
2097 if ((dirp
= opendir("/proc")) == NULL
) {
2098 warn(gettext("%s: cannot open /proc directory\n"),
2103 * Look through all processes in /proc. For each process,
2104 * check if the pr_projid in their psinfo matches the
2107 while (dentp
= readdir(dirp
)) {
2108 p
->pid
= atoi(dentp
->d_name
);
2111 if (p
->pid
== pid_self
)
2114 if (proc_get_psinfo(p
->pid
, &(p
->psinfo
)) != 0)
2117 /* Skip process if it is not what we are looking for */
2118 if (type
== RCENTITY_ZONE
&&
2119 (p
->psinfo
).pr_zoneid
!= zoneid
) {
2121 } else if (type
== RCENTITY_PROJECT
&&
2122 ((p
->psinfo
).pr_projid
!= projid
||
2123 (p
->psinfo
).pr_zoneid
!= zone_self
)) {
2125 } else if (type
== RCENTITY_TASK
&&
2126 (p
->psinfo
).pr_taskid
!= taskid
) {
2129 /* attempt to grab process */
2130 if (grab_process(p
, gret
) != 0)
2134 * Re-confirm that this process is still running as
2135 * part of the specified project or task. If it
2136 * doesn't match, release the process and return an
2137 * error. This should only be done if the Pr struct is
2140 if (type
== RCENTITY_PROJECT
) {
2141 if (pr_getprojid(p
->pr
) != projid
||
2142 pr_getzoneid(p
->pr
) != zone_self
) {
2143 release_process(p
->pr
);
2146 } else if (type
== RCENTITY_TASK
) {
2147 if (pr_gettaskid(p
->pr
) != taskid
) {
2148 release_process(p
->pr
);
2151 } else if (type
== RCENTITY_ZONE
) {
2152 if (pr_getzoneid(p
->pr
) != zoneid
) {
2153 release_process(p
->pr
);
2159 * If we are setting a privileged resource control,
2160 * verify that process has PRIV_SYS_RESOURCE in it's
2161 * limit set. If it does not, then we will not be
2162 * able to give this process the privilege it needs
2163 * to set the resource control.
2165 if (priv
!= RCPRIV_BASIC
) {
2166 prpriv
= proc_get_priv(p
->pid
);
2167 if (prpriv
== NULL
) {
2168 release_process(p
->pr
);
2171 prset
= (priv_set_t
*)
2172 &prpriv
->pr_sets
[prpriv
->pr_setsize
*
2173 priv_getsetbyname(PRIV_LIMIT
)];
2174 if (!priv_ismember(prset
, PRIV_SYS_RESOURCE
)) {
2175 proc_free_priv(prpriv
);
2176 release_process(p
->pr
);
2179 proc_free_priv(prpriv
);
2183 p
->taskid
= pr_gettaskid(p
->pr
);
2184 p
->projid
= pr_getprojid(p
->pr
);
2185 p
->zoneid
= pr_getzoneid(p
->pr
);
2189 (void) closedir(dirp
);
2192 warn(gettext("%s: No controllable process found in "
2193 "task, project, or zone.\n"), idname
);
2198 } else if (type
== RCENTITY_PROCESS
) {
2201 if (p
->pid
== pid_self
) {
2203 warn(gettext("%s: cannot control self"), idname
);
2207 * Process types need to be set up with the correct pid
2208 * and psinfo structure.
2210 if ((p
->pid
= proc_arg_psinfo(idname
, PR_ARG_PIDS
,
2211 &(p
->psinfo
), gret
)) == -1) {
2212 warn(gettext("%s: cannot examine: %s"), idname
,
2213 Pgrab_error(*gret
));
2217 ret
= grab_process(p
, gret
);
2219 /* Don't print error if G_SYS is allowed */
2220 if (gret_in
== G_SYS
&& *gret
== G_SYS
) {
2223 warn(gettext("%s: cannot control: %s"), idname
,
2224 Pgrab_error(*gret
));
2227 } else if (ret
== 2) {
2229 warn(gettext("%s: cannot control: %s"), idname
,
2233 p
->taskid
= pr_gettaskid(p
->pr
);
2234 p
->projid
= pr_getprojid(p
->pr
);
2235 p
->zoneid
= pr_getzoneid(p
->pr
);
2240 warn(gettext("%s: unknown resource entity type %d\n"), idname
,
2247 * Do the work required to manipulate a process through libproc.
2248 * If grab_process() returns no errors (0), then release_process()
2249 * must eventually be called.
2252 * 0 Successful creation of agent thread
2254 * 2 Error creating agent
2257 grab_process(pr_info_handle_t
*p
, int *gret
)
2260 if ((p
->pr
= Pgrab(p
->pid
, arg_force
, gret
)) != NULL
) {
2262 if (Psetflags(p
->pr
, PR_RLC
) != 0) {
2266 if (Pcreate_agent(p
->pr
) == 0) {
2279 * Release the specified process. This destroys the agent
2280 * and releases the process. If the process is NULL, nothing
2281 * is done. This function should only be called if grab_process()
2282 * has previously been called and returned success.
2284 * This function is Pgrab-safe.
2287 release_process(struct ps_prochandle
*Pr
)
2297 * preserve_error(char *, ...)
2299 * preserve_error() should be called rather than warn() by any
2300 * function that is called while the victim process is held by Pgrab.
2301 * It will save the error until the process has been un-controlled
2302 * and output is reasonable again.
2304 * Note that multiple errors are not stored. Any error in these
2305 * sections should be critical and return immediately.
2307 * This function is Pgrab-safe.
2309 * Since this function may copy untrusted command line arguments to
2310 * global_error, security practices require that global_error never be
2311 * printed directly. Use printf("%s\n", global_error) or equivalent.
2315 preserve_error(char *format
, ...)
2319 va_start(alist
, format
);
2322 * GLOBAL_ERR_SZ is pretty big. If the error is longer
2323 * than that, just truncate it, rather than chance missing
2324 * the error altogether.
2326 (void) vsnprintf(global_error
, GLOBAL_ERR_SZ
-1, format
, alist
);