/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015, Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" /* Valid user actions */ #define ACTION_DISABLE 0x01 #define ACTION_ENABLE 0x02 #define ACTION_SET 0x04 #define ACTION_REPLACE 0x08 #define ACTION_DELETE 0x10 #define PRCTL_VALUE_WIDTH 4 /* Maximum string length for deferred errors */ #define GLOBAL_ERR_SZ 1024 /* allow important process values to be passed together easily */ typedef struct pr_info_handle { struct ps_prochandle *pr; pid_t pid; psinfo_t psinfo; taskid_t taskid; projid_t projid; char *projname; zoneid_t zoneid; char *zonename; } pr_info_handle_t; /* Structures for list of resource controls */ typedef struct prctl_value { rctlblk_t *rblk; struct prctl_value *next; } prctl_value_t; typedef struct prctl_list { char *name; rctl_qty_t *usage; prctl_value_t *val_list; struct prctl_list *next; } prctl_list_t; static volatile int interrupt; static prctl_list_t *global_rctl_list_head = NULL; static prctl_list_t *global_rctl_list_tail = NULL; static char global_error[GLOBAL_ERR_SZ]; /* global variables that contain commmand line option info */ static int arg_operation = 0; static int arg_force = 0; /* String and type from -i */ static rctl_entity_t arg_entity_type = RCENTITY_PROCESS; static char *arg_entity_string = NULL; /* -n argument */ static char *arg_name = NULL; static rctl_entity_t arg_name_entity = 0; /* -t argument value */ static int arg_priv = 0; /* -v argument string */ static char *arg_valuestring = NULL; /* global flags of rctl name passed to -n */ static int arg_global_flags = 0; static rctl_qty_t arg_global_max; /* appropriate scaling variables determined by rctl unit type */ scale_t *arg_scale; static char *arg_unit = NULL; /* -v argument string converted to uint64_t */ static uint64_t arg_value = 0; /* if -v argument is scaled value, points to "K", "M", "G", ... */ static char *arg_modifier = NULL; /* -e/-d argument string */ static char *arg_action_string = NULL; /* Set to RCTL_LOCAL_SIGNAL|DENY based on arg_action_string */ static int arg_action = 0; /* if -e/-d arg is signal=XXX, set to signal number of XXX */ static int arg_signal = 0; /* -p arg if -p is specified */ static int arg_pid = -1; static char *arg_pid_string = NULL; /* Set to 1 if -P is specified */ static int arg_parseable_mode = 0; /* interupt handler */ static void intr(int); static int get_rctls(struct ps_prochandle *); static int store_rctls(const char *rctlname, void *walk_data); static prctl_value_t *store_value_entry(rctlblk_t *rblk, prctl_list_t *list); static prctl_list_t *store_list_entry(const char *name); static void free_lists(); static int change_action(rctlblk_t *blk); static int prctl_setrctl(struct ps_prochandle *Pr, const char *name, rctlblk_t *, rctlblk_t *, uint_t); static int match_rctl(struct ps_prochandle *Pr, rctlblk_t **rctl, char *name, char *valuestringin, int valuein, rctl_priv_t privin, int pidin); static int match_rctl_blk(rctlblk_t *rctl, char *valuestringin, uint64_t valuein, rctl_priv_t privin, int pidin); static pid_t regrab_process(pid_t pid, pr_info_handle_t *p, int, int *gret); static pid_t grab_process_by_id(char *idname, rctl_entity_t type, pr_info_handle_t *p, int, int *gret); static int grab_process(pr_info_handle_t *p, int *gret); static void release_process(struct ps_prochandle *Pr); static void preserve_error(char *format, ...); static void print_rctls(pr_info_handle_t *p); static void print_priv(rctl_priv_t local_priv, char *format); static void print_local_action(int action, int *signalp, char *format); static const char USAGE[] = "" "usage:\n" " Report resource control values and actions:\n" " prctl [-P] [-t [basic | privileged | system]\n" " [-n name] [-i process | task | project | zone] id ...\n" " -P space delimited output\n" " -t privilege level of rctl values to get\n" " -n name of resource control values to get\n" " -i idtype of operand list\n" " Manipulate resource control values:\n" " prctl [-t [basic | privileged | system]\n" " -n name [-srx] [-v value] [-p pid ] [-e | -d action]\n" " [-i process | task | project | zone] id ...\n" " -t privilege level of rctl value to set/replace/delete/modify\n" " -n name of resource control to set/replace/delete/modify\n" " -s set new resource control value\n" " -r replace first rctl value of matching privilege\n" " -x delete first rctl value of matching privilege, value, and \n" " recipient pid\n" " -v value of rctl to set/replace/delete/modify\n" " -p recipient pid of rctl to set/replace/delete/modify\n" " -e enable action of first rctl value of matching privilege,\n" " value, and recipient pid\n" " -d disable action of first rctl value of matching privilege,\n" " value, and recipient pid\n" " -i idtype of operand list\n"; static void usage() { (void) fprintf(stderr, gettext(USAGE)); exit(2); } int main(int argc, char **argv) { int flags; int opt, errflg = 0; rctlblk_t *rctlblkA = NULL; rctlblk_t *rctlblkB = NULL; rctlblk_t *tmp = NULL; pid_t pid; char *target_id; int search_type; int signal; int localaction; int printed = 0; int gret; char *end; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); (void) setpname(argv[0]); while ((opt = getopt(argc, argv, "sPp:Fd:e:i:n:rt:v:x")) != EOF) { switch (opt) { case 'F': /* force grabbing (no O_EXCL) */ arg_force = PGRAB_FORCE; break; case 'i': /* id type for arguments */ arg_entity_string = optarg; if (strcmp(optarg, "process") == 0 || strcmp(optarg, "pid") == 0) arg_entity_type = RCENTITY_PROCESS; else if (strcmp(optarg, "project") == 0 || strcmp(optarg, "projid") == 0) arg_entity_type = RCENTITY_PROJECT; else if (strcmp(optarg, "task") == 0 || strcmp(optarg, "taskid") == 0) arg_entity_type = RCENTITY_TASK; else if (strcmp(optarg, "zone") == 0 || strcmp(optarg, "zoneid") == 0) arg_entity_type = RCENTITY_ZONE; else { warn(gettext("unknown idtype %s"), optarg); errflg = 1; } break; case 'd': arg_action_string = optarg; arg_operation |= ACTION_DISABLE; break; case 'e': arg_action_string = optarg; arg_operation |= ACTION_ENABLE; break; case 'n': /* name of rctl */ arg_name = optarg; if (strncmp(optarg, "process.", strlen("process.")) == 0) arg_name_entity = RCENTITY_PROCESS; else if (strncmp(optarg, "project.", strlen("project.")) == 0) arg_name_entity = RCENTITY_PROJECT; else if (strncmp(optarg, "task.", strlen("task.")) == 0) arg_name_entity = RCENTITY_TASK; else if (strncmp(optarg, "zone.", strlen("zone.")) == 0) arg_name_entity = RCENTITY_ZONE; break; case 'r': arg_operation |= ACTION_REPLACE; break; case 't': /* rctl type */ if (strcmp(optarg, "basic") == 0) arg_priv = RCPRIV_BASIC; else if (strcmp(optarg, "privileged") == 0) arg_priv = RCPRIV_PRIVILEGED; else if (strcmp(optarg, "priv") == 0) arg_priv = RCPRIV_PRIVILEGED; else if (strcmp(optarg, "system") == 0) arg_priv = RCPRIV_SYSTEM; else { warn(gettext("unknown privilege %s"), optarg); errflg = 1; } break; case 'v': /* value */ arg_valuestring = optarg; break; case 's': arg_operation |= ACTION_SET; break; case 'x': /* delete */ arg_operation |= ACTION_DELETE; break; case 'p': errno = 0; /* Stick with -1 if arg is "-" */ if (strcmp("-", optarg) == 0) break; arg_pid_string = optarg; arg_pid = strtoul(optarg, &end, 10); if (errno || *end != '\0' || end == optarg) { warn(gettext("invalid pid %s"), optarg); errflg = 1; break; } break; case 'P': arg_parseable_mode = 1; break; default: warn(gettext("unknown option")); errflg = 1; break; } } argc -= optind; argv += optind; if (argc < 1) { warn(gettext("no arguments specified")); errflg = 1; goto done_parse; } /* if -v is specified without -r, -x, -d, or -e, -s is implied */ if (arg_valuestring && (!(arg_operation & (ACTION_REPLACE | ACTION_DELETE | ACTION_DISABLE | ACTION_ENABLE)))) { arg_operation |= ACTION_SET; } /* operations require -n */ if (arg_operation && (arg_name == NULL)) { warn(gettext("-n is required with -s, -r, -x, -e, or -d")); errflg = 1; goto done_parse; } /* enable and disable are exclusive */ if ((arg_operation & ACTION_ENABLE) && (arg_operation & ACTION_DISABLE)) { warn(gettext("options -d and -e are exclusive")); errflg = 1; goto done_parse; } /* -s, -r, and -x are exclusive */ flags = arg_operation & (ACTION_REPLACE | ACTION_SET | ACTION_DELETE); if (flags & (flags - 1)) { warn(gettext("options -s, -r, and -x are exclusive")); errflg = 1; goto done_parse; } /* -e or -d makes no sense with -x */ if ((arg_operation & ACTION_DELETE) & (arg_operation & (ACTION_ENABLE | ACTION_DISABLE))) { warn(gettext("options -e or -d not allowed with -x")); errflg = 1; goto done_parse; } /* if -r is specified -v must be as well */ if ((arg_operation & ACTION_REPLACE) && (!arg_valuestring)) { warn(gettext("option -r requires use of option -v")); errflg = 1; goto done_parse; } /* if -s is specified -v must be as well */ if ((arg_operation & ACTION_SET) && (!arg_valuestring)) { warn(gettext("option -s requires use of option -v")); errflg = 1; goto done_parse; } /* Specifying a recipient pid on a non-basic rctl makes no sense */ if (arg_pid != -1 && arg_priv > RCPRIV_BASIC) { warn(gettext("option -p not allowed on non-basic rctl")); errflg = 1; goto done_parse; } /* Specifying a recipient pid on a privileged rctl makes no sense */ if (arg_pid != -1 && arg_priv == RCPRIV_PRIVILEGED) { warn(gettext("option -p not allowed with privileged rctl")); errflg = 1; goto done_parse; } if (arg_operation) { /* do additional checks if there is an operation */ if (arg_parseable_mode == 1) { warn(gettext("-P not valid when manipulating " "resource control values")); errflg = 1; goto done_parse; } /* get rctl global flags to determine if actions are valid */ if ((rctlblkA = calloc(1, rctlblk_size())) == NULL) { warn(gettext("malloc failed: %s"), strerror(errno)); errflg = 1; goto done_parse; } if ((rctlblkB = calloc(1, rctlblk_size())) == NULL) { warn(gettext("malloc failed: %s"), strerror(errno)); errflg = 1; goto done_parse; } /* get system rctl to get global flags and max value */ if (getrctl(arg_name, NULL, rctlblkA, RCTL_FIRST)) { warn(gettext("failed to get resource control " "for %s: %s"), arg_name, strerror(errno)); errflg = 1; goto done_parse; } while (getrctl(arg_name, rctlblkA, rctlblkB, RCTL_NEXT) == 0) { /* allow user interrupt */ if (interrupt) { errflg = 1; goto done_parse; } tmp = rctlblkB; rctlblkB = rctlblkA; rctlblkA = tmp; if (rctlblk_get_privilege(rctlblkA) == RCPRIV_SYSTEM) { break; } } if (rctlblk_get_privilege(rctlblkA) != RCPRIV_SYSTEM) { warn(gettext("failed to get system resource control " "for %s: %s"), arg_name, strerror(errno)); errflg = 1; goto done_parse; } /* figure out the correct scale and unit for this rctl */ arg_global_flags = rctlblk_get_global_flags(rctlblkA); arg_global_max = rctlblk_get_value(rctlblkA); if (arg_global_flags & RCTL_GLOBAL_BYTES) { arg_unit = SCALED_UNIT_BYTES; arg_scale = scale_binary; } else if (arg_global_flags & RCTL_GLOBAL_SECONDS) { arg_unit = SCALED_UNIT_SECONDS; arg_scale = scale_metric; } else { arg_unit = SCALED_UNIT_NONE; arg_scale = scale_metric; } /* parse -v value string */ if (arg_valuestring) { if (scaledtouint64(arg_valuestring, &arg_value, NULL, &arg_modifier, NULL, arg_scale, arg_unit, SCALED_ALL_FLAGS)) { warn(gettext("invalid -v value %s"), arg_valuestring); errflg = 1; goto done_parse; } if (arg_value > arg_global_max) { warn(gettext("-v value %s exceeds system " "limit for resource control: %s"), arg_valuestring, arg_name); errflg = 1; goto done_parse; } } /* parse action */ if (arg_action_string) { char *sigchr; char *iter; if ((strcmp(arg_action_string, "signal") == 0) || (strcmp(arg_action_string, "sig") == 0)) { if (arg_operation & ACTION_ENABLE) { warn(gettext( "signal name or number must be " "specified with -e")); errflg = 1; goto done_parse; } arg_action = RCTL_LOCAL_SIGNAL; arg_signal = -1; } else if ((strncmp(arg_action_string, "signal=", strlen("signal=")) == 0) || (strncmp(arg_action_string, "sig=", strlen("sig=")) == 0)) { arg_action = RCTL_LOCAL_SIGNAL; sigchr = strrchr(arg_action_string, '='); sigchr++; iter = sigchr; while (*iter) { *iter = toupper(*iter); iter++; } if (strncmp("SIG", sigchr, 3) == 0) sigchr += 3; if (str2sig(sigchr, &arg_signal) != 0) { warn(gettext("signal invalid")); errflg = 1; goto done_parse; } } else if (strcmp(arg_action_string, "deny") == 0) { arg_action = RCTL_LOCAL_DENY; } else if (strcmp(arg_action_string, "all") == 0) { if (arg_operation & ACTION_ENABLE) { warn(gettext( "cannot use action 'all' with -e")); errflg = 1; goto done_parse; } arg_action = RCTL_LOCAL_DENY | RCTL_LOCAL_SIGNAL; arg_signal = -1; goto done_parse; } else { warn(gettext("action invalid")); errflg = 1; goto done_parse; } } /* cannot manipulate system rctls */ if (arg_priv == RCPRIV_SYSTEM) { warn(gettext("cannot modify system values")); errflg = 1; goto done_parse; } /* validate that the privilege is allowed */ if ((arg_priv == RCPRIV_BASIC) && (arg_global_flags & RCTL_GLOBAL_NOBASIC)) { warn(gettext("basic values not allowed on rctl %s"), arg_name); errflg = 1; goto done_parse; } /* validate that actions are appropriate for given rctl */ if ((arg_operation & ACTION_ENABLE) && (arg_action & RCTL_LOCAL_DENY) && (arg_global_flags & RCTL_GLOBAL_DENY_NEVER)) { warn(gettext("unable to enable deny on rctl with " "global flag 'no-deny'")); errflg = 1; goto done_parse; } if ((arg_operation & ACTION_DISABLE) && (arg_action & RCTL_LOCAL_DENY) && (arg_global_flags & RCTL_GLOBAL_DENY_ALWAYS)) { warn(gettext("unable to disable deny on rctl with " "global flag 'deny'")); errflg = 1; goto done_parse; } if ((arg_operation & ACTION_ENABLE) && (arg_action & RCTL_LOCAL_SIGNAL) && (arg_global_flags & RCTL_GLOBAL_SIGNAL_NEVER)) { warn(gettext("unable to enable signal on rctl with " "global flag 'no-signal'")); errflg = 1; goto done_parse; } /* now set defaults for options not supplied */ /* * default privilege to basic if this is a seting an rctl * operation */ if (arg_operation & ACTION_SET) { if (arg_priv == 0) { arg_priv = RCPRIV_BASIC; } } /* * -p is required when set a basic task, * project or zone rctl */ if ((arg_pid == -1) && (arg_priv == RCPRIV_BASIC) && (arg_entity_type != RCENTITY_PROCESS) && (arg_operation & ACTION_SET) && (arg_name) && (arg_name_entity == RCENTITY_TASK || arg_name_entity == RCENTITY_PROJECT || arg_name_entity == RCENTITY_ZONE)) { warn(gettext("-p pid required when setting or " "replacing task or project rctl")); errflg = 1; goto done_parse; } } else { /* validate for list mode */ /* -p is not valid in list mode */ if (arg_pid != -1) { warn(gettext("-p pid requires -s, -r, -x, -e, or -d")); errflg = 1; goto done_parse; } } /* getting/setting process rctl on task or project is error */ if ((arg_name && (arg_name_entity == RCENTITY_PROCESS)) && ((arg_entity_type == RCENTITY_TASK) || (arg_entity_type == RCENTITY_PROJECT))) { warn(gettext("cannot get/set process rctl on task " "or project")); errflg = 1; goto done_parse; } /* getting/setting task rctl on project is error */ if ((arg_name && (arg_name_entity == RCENTITY_TASK)) && (arg_entity_type == RCENTITY_PROJECT)) { warn(gettext("cannot get/set task rctl on project")); errflg = 1; goto done_parse; } done_parse: /* free any rctlblk's that we may have allocated */ if (rctlblkA) { free(rctlblkA); rctlblkA = NULL; } if (rctlblkB) { free(rctlblkB); rctlblkB = NULL; } if (errflg) usage(); /* catch signals from terminal */ if (sigset(SIGHUP, SIG_IGN) == SIG_DFL) (void) sigset(SIGHUP, intr); if (sigset(SIGINT, SIG_IGN) == SIG_DFL) (void) sigset(SIGINT, intr); if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL) (void) sigset(SIGQUIT, intr); (void) sigset(SIGTERM, intr); while (--argc >= 0 && !interrupt) { pr_info_handle_t p; char *arg = *argv++; int intarg; char *end; errflg = 0; gret = 0; /* Store int version of arg */ errno = 0; intarg = strtoul(arg, &end, 10); if (errno || *end != '\0' || end == arg) { intarg = -1; } /* * -p defaults to arg if basic and collective rctl * and -i process is specified */ if ((arg_pid == -1) && (arg_priv == RCPRIV_BASIC) && (arg_entity_type == RCENTITY_PROCESS) && (arg_name) && (arg_name_entity == RCENTITY_TASK || arg_name_entity == RCENTITY_PROJECT)) { arg_pid_string = arg; errno = 0; arg_pid = intarg; } /* Specifying a recipient pid and -i pid is redundent */ if (arg_pid != -1 && arg_entity_type == RCENTITY_PROCESS && arg_pid != intarg) { warn(gettext("option -p pid must match -i process")); errflg = 1; continue; } /* use recipient pid if we have one */ if (arg_pid_string != NULL) { target_id = arg_pid_string; search_type = RCENTITY_PROCESS; } else { target_id = arg; search_type = arg_entity_type; } (void) fflush(stdout); /* process-at-a-time */ if (arg_operation != 0) { if ((pid = grab_process_by_id(target_id, search_type, &p, arg_priv, &gret)) < 0) { /* * Mark that an error occurred so that the * return value can be set, but continue * on with other processes */ errflg = 1; continue; } /* * At this point, the victim process is held. * Do not call any Pgrab-unsafe functions until * the process is released via release_process(). */ errflg = get_rctls(p.pr); if (arg_operation & ACTION_DELETE) { /* match by privilege, value, and pid */ if (match_rctl(p.pr, &rctlblkA, arg_name, arg_valuestring, arg_value, arg_priv, arg_pid) != 0 || rctlblkA == NULL) { if (interrupt) goto out; preserve_error(gettext("no matching " "resource control found for " "deletion")); errflg = 1; goto out; } /* * grab correct process. This is neccessary * if the recipient pid does not match the * one we grabbed */ pid = regrab_process( rctlblk_get_recipient_pid(rctlblkA), &p, arg_priv, &gret); if (pid < 0) { errflg = 1; goto out; } if (prctl_setrctl(p.pr, arg_name, NULL, rctlblkA, RCTL_DELETE) != 0) { errflg = 1; goto out; } } else if (arg_operation & ACTION_SET) { /* match by privilege, value, and pid */ if (match_rctl(p.pr, &rctlblkA, arg_name, arg_valuestring, arg_value, arg_priv, arg_pid) == 0) { if (interrupt) goto out; preserve_error(gettext("resource " "control already exists")); errflg = 1; goto out; } rctlblkB = calloc(1, rctlblk_size()); if (rctlblkB == NULL) { preserve_error(gettext( "malloc failed"), strerror(errno)); errflg = 1; goto out; } rctlblk_set_value(rctlblkB, arg_value); rctlblk_set_privilege(rctlblkB, arg_priv); if (change_action(rctlblkB)) { errflg = 1; goto out; } if (prctl_setrctl(p.pr, arg_name, NULL, rctlblkB, RCTL_INSERT) != 0) { errflg = 1; goto out; } } else if (arg_operation & ACTION_REPLACE) { /* * match rctl for deletion by privilege and * pid only */ if (match_rctl(p.pr, &rctlblkA, arg_name, NULL, 0, arg_priv, arg_pid) != 0 || rctlblkA == NULL) { if (interrupt) goto out; preserve_error(gettext("no matching " "resource control to replace")); errflg = 1; goto out; } /* * grab correct process. This is neccessary * if the recipient pid does not match the * one we grabbed */ pid = regrab_process( rctlblk_get_recipient_pid(rctlblkA), &p, arg_priv, &gret); if (pid < 0) { errflg = 1; goto out; } pid = rctlblk_get_recipient_pid(rctlblkA); /* * match by privilege, value and pid to * check if new rctl already exists */ if (match_rctl(p.pr, &rctlblkB, arg_name, arg_valuestring, arg_value, arg_priv, pid) < 0) { if (interrupt) goto out; preserve_error(gettext( "Internal Error")); errflg = 1; goto out; } /* * If rctl already exists, and it does not * match the one that we will delete, than * the replace will fail. */ if (rctlblkB != NULL && arg_value != rctlblk_get_value(rctlblkA)) { preserve_error(gettext("replacement " "resource control already " "exists")); errflg = 1; goto out; } /* create new rctl */ rctlblkB = calloc(1, rctlblk_size()); if (rctlblkB == NULL) { preserve_error(gettext( "malloc failed"), strerror(errno)); errflg = 1; goto out; } localaction = rctlblk_get_local_action(rctlblkA, &signal); rctlblk_set_local_action(rctlblkB, localaction, signal); rctlblk_set_value(rctlblkB, arg_value); rctlblk_set_privilege(rctlblkB, rctlblk_get_privilege(rctlblkA)); if (change_action(rctlblkB)) { errflg = 1; goto out; } /* do replacement */ if (prctl_setrctl(p.pr, arg_name, rctlblkA, rctlblkB, RCTL_REPLACE) != 0) { errflg = 1; goto out; } } else if (arg_operation & (ACTION_ENABLE | ACTION_DISABLE)) { rctlblkB = calloc(1, rctlblk_size()); if (rctlblkB == NULL) { preserve_error(gettext( "malloc failed"), strerror(errno)); errflg = 1; goto out; } /* match by privilege, value, and pid */ if (match_rctl(p.pr, &rctlblkA, arg_name, arg_valuestring, arg_value, arg_priv, arg_pid) != 0) { if (interrupt) goto out; /* if no match, just set new rctl */ if (arg_priv == 0) arg_priv = RCPRIV_BASIC; if ((arg_priv == RCPRIV_BASIC) && (arg_entity_type != RCENTITY_PROCESS) && (arg_pid_string == NULL)) { preserve_error(gettext( "-p required when setting " "basic rctls")); errflg = 1; goto out; } rctlblk_set_value(rctlblkB, arg_value); rctlblk_set_privilege( rctlblkB, arg_priv); if (change_action(rctlblkB)) { errflg = 1; goto out; } if (prctl_setrctl(p.pr, arg_name, NULL, rctlblkB, RCTL_INSERT) != 0) { errflg = 1; goto out; } goto out; } if (rctlblkA == NULL) { preserve_error(gettext("no matching " "resource control found")); errflg = 1; goto out; } /* * grab correct process. This is neccessary * if the recipient pid does not match the * one we grabbed */ pid = regrab_process( rctlblk_get_recipient_pid(rctlblkA), &p, arg_priv, &gret); if (pid < 0) { errflg = 1; goto out; } localaction = rctlblk_get_local_action(rctlblkA, &signal); rctlblk_set_local_action(rctlblkB, localaction, signal); rctlblk_set_privilege(rctlblkB, rctlblk_get_privilege(rctlblkA)); rctlblk_set_value(rctlblkB, rctlblk_get_value(rctlblkA)); if (change_action(rctlblkB)) { errflg = 1; goto out; } if (prctl_setrctl(p.pr, arg_name, rctlblkA, rctlblkB, RCTL_REPLACE) != 0) { errflg = 1; goto out; } } out: release_process(p.pr); if (rctlblkA) free(rctlblkA); if (rctlblkB) free(rctlblkB); /* Print any errors that occurred */ if (errflg && *global_error != '\0') { proc_unctrl_psinfo(&(p.psinfo)); (void) fprintf(stderr, "%d:\t%.70s\n", (int)p.pid, p.psinfo.pr_psargs); warn("%s\n", global_error); break; } } else { struct project projent; char buf[PROJECT_BUFSZ]; char zonename[ZONENAME_MAX]; /* * Hack to allow the user to specify a system * process. */ gret = G_SYS; pid = grab_process_by_id( target_id, search_type, &p, RCPRIV_BASIC, &gret); /* * Print system process if user chose specifically * to inspect a system process. */ if (arg_entity_type == RCENTITY_PROCESS && pid < 0 && gret == G_SYS) { /* * Add blank lines between output for * operands. */ if (printed) { (void) fprintf(stdout, "\n"); } proc_unctrl_psinfo(&(p.psinfo)); (void) printf( "process: %d: %s [ system process ]\n", (int)p.pid, p.psinfo.pr_psargs); printed = 1; continue; } else if (pid < 0) { /* * Mark that an error occurred so that the * return value can be set, but continue * on with other processes */ errflg = 1; continue; } errflg = get_rctls(p.pr); release_process(p.pr); /* handle user interrupt of getting rctls */ if (interrupt) break; /* add blank lines between output for operands */ if (printed) { (void) fprintf(stdout, "\n"); } /* First print any errors */ if (errflg) { warn("%s\n", global_error); free_lists(); break; } if (getprojbyid(p.projid, &projent, buf, sizeof (buf))) { p.projname = projent.pj_name; } else { p.projname = ""; } if (getzonenamebyid(p.zoneid, zonename, sizeof (zonename)) > 0) { p.zonename = zonename; } else { p.zonename = ""; } print_rctls(&p); printed = 1; /* Free the resource control lists */ free_lists(); } } if (interrupt) errflg = 1; /* * return error if one occurred */ return (errflg); } static void intr(int sig) { interrupt = sig; } /* * get_rctls(struct ps_prochandle *, const char *) * * If controlname is given, store only controls for that named * resource. If controlname is NULL, store all controls for all * resources. * * This function is Pgrab-safe. */ static int get_rctls(struct ps_prochandle *Pr) { int ret = 0; if (arg_name == NULL) { if (rctl_walk(store_rctls, Pr) != 0) ret = 1; } else { ret = store_rctls(arg_name, Pr); } return (ret); } /* * store_rctls(const char *, void *) * * Store resource controls for the given name in a linked list. * Honor the user's options, and store only the ones they are * interested in. If priv is not 0, show only controls that match * the given privilege. * * This function is Pgrab-safe */ static int store_rctls(const char *rctlname, void *walk_data) { struct ps_prochandle *Pr = walk_data; rctlblk_t *rblk2, *rblk_tmp, *rblk1 = NULL; prctl_list_t *list = NULL; rctl_priv_t rblk_priv; rctl_entity_t rblk_entity; if (((rblk1 = calloc(1, rctlblk_size())) == NULL) || ((rblk2 = calloc(1, rctlblk_size())) == NULL)) { if (rblk1 != NULL) free(rblk1); preserve_error(gettext("malloc failed: %s"), strerror(errno)); return (1); } if (pr_getrctl(Pr, rctlname, NULL, rblk1, RCTL_FIRST)) { preserve_error(gettext("failed to get resource control " "for %s: %s"), rctlname, strerror(errno)); free(rblk1); free(rblk2); return (1); } /* Store control if it matches privilege and enity type criteria */ rblk_priv = rctlblk_get_privilege(rblk1); rblk_entity = 0; if (strncmp(rctlname, "process.", strlen("process.")) == 0) rblk_entity = RCENTITY_PROCESS; else if (strncmp(rctlname, "project.", strlen("project.")) == 0) rblk_entity = RCENTITY_PROJECT; else if (strncmp(rctlname, "task.", strlen("task.")) == 0) rblk_entity = RCENTITY_TASK; else if (strncmp(rctlname, "zone.", strlen("zone.")) == 0) rblk_entity = RCENTITY_ZONE; if (((arg_priv == 0) || (rblk_priv == arg_priv)) && ((arg_name == NULL) || strncmp(rctlname, arg_name, strlen(arg_name)) == 0) && (arg_entity_string == NULL || rblk_entity >= arg_entity_type)) { /* Once we know we have some controls, store the name */ if ((list = store_list_entry(rctlname)) == NULL) { free(rblk1); free(rblk2); return (1); } if (store_value_entry(rblk1, list) == NULL) { free(rblk1); free(rblk2); return (1); } } while (pr_getrctl(Pr, rctlname, rblk1, rblk2, RCTL_NEXT) == 0) { /* * in case this is stuck for some reason, allow manual * interrupt */ if (interrupt) { free(rblk1); free(rblk2); return (1); } rblk_priv = rctlblk_get_privilege(rblk2); /* * Store control if it matches privilege and entity type * criteria */ if (((arg_priv == 0) || (rblk_priv == arg_priv)) && ((arg_name == NULL) || strncmp(rctlname, arg_name, strlen(arg_name)) == 0) && (arg_entity_string == NULL || rblk_entity == arg_entity_type)) { /* May not have created the list yet. */ if (list == NULL) { if ((list = store_list_entry(rctlname)) == NULL) { free(rblk1); free(rblk2); return (1); } } if (store_value_entry(rblk2, list) == NULL) { free(rblk1); free(rblk2); return (1); } } rblk_tmp = rblk1; rblk1 = rblk2; rblk2 = rblk_tmp; } /* * Get the current usage for the resource control if it matched the * privilege and entity type criteria. */ if (list != NULL) { if (pr_getrctl(Pr, rctlname, NULL, rblk2, RCTL_USAGE) == 0) { list->usage = (rctl_qty_t *)malloc(sizeof (rctl_qty_t)); if (list->usage == NULL) { preserve_error(gettext("malloc failed: %s"), strerror(errno)); free(rblk1); free(rblk2); return (1); } *list->usage = rctlblk_get_value(rblk2); } else { list->usage = NULL; if (errno != ENOTSUP) { preserve_error(gettext("failed to get " "resource control usage for %s: %s"), rctlname, strerror(errno)); free(rblk1); free(rblk2); return (1); } } } free(rblk1); free(rblk2); return (0); } /* * store_value_entry(rctlblk_t *, prctl_list_t *) * * Store an rblk for a given resource control into the global list. * * This function is Pgrab-safe. */ prctl_value_t * store_value_entry(rctlblk_t *rblk, prctl_list_t *list) { prctl_value_t *e = calloc(1, sizeof (prctl_value_t)); rctlblk_t *store_blk = calloc(1, rctlblk_size()); prctl_value_t *iter = list->val_list; if (e == NULL || store_blk == NULL) { preserve_error(gettext("malloc failed %s"), strerror(errno)); if (e != NULL) free(e); if (store_blk != NULL) free(store_blk); return (NULL); } if (iter == NULL) list->val_list = e; else { while (iter->next != NULL) { iter = iter->next; } iter->next = e; } bcopy(rblk, store_blk, rctlblk_size()); e->rblk = store_blk; e->next = NULL; return (e); } /* * store_list_entry(const char *) * * Store a new resource control value in the global list. No checking * for duplicates done. * * This function is Pgrab-safe. */ prctl_list_t * store_list_entry(const char *name) { prctl_list_t *e = calloc(1, sizeof (prctl_list_t)); if (e == NULL) { preserve_error(gettext("malloc failed %s"), strerror(errno)); return (NULL); } if ((e->name = calloc(1, strlen(name) + 1)) == NULL) { preserve_error(gettext("malloc failed %s"), strerror(errno)); free(e); return (NULL); } (void) strcpy(e->name, name); e->val_list = NULL; if (global_rctl_list_head == NULL) { global_rctl_list_head = e; global_rctl_list_tail = e; } else { global_rctl_list_tail->next = e; global_rctl_list_tail = e; } e->next = NULL; return (e); } /* * free_lists() * * Free all resource control blocks and values from the global lists. * * This function is Pgrab-safe. */ void free_lists() { prctl_list_t *new_list, *old_list = global_rctl_list_head; prctl_value_t *old_val, *new_val; while (old_list != NULL) { old_val = old_list->val_list; while (old_val != NULL) { free(old_val->rblk); new_val = old_val->next; free(old_val); old_val = new_val; } free(old_list->name); free(old_list->usage); new_list = old_list->next; free(old_list); old_list = new_list; } global_rctl_list_head = NULL; global_rctl_list_tail = NULL; } void print_heading() { /* print headings */ (void) fprintf(stdout, "%-8s%-16s%-9s%-7s%-28s%10s\n", "NAME", "PRIVILEGE", "VALUE", "FLAG", "ACTION", "RECIPIENT"); } /* * print_rctls() * * Print all resource controls from the global list that was * previously populated by store_rctls. */ void print_rctls(pr_info_handle_t *p) { prctl_list_t *iter_list = global_rctl_list_head; prctl_value_t *iter_val; rctl_qty_t rblk_value; rctl_priv_t rblk_priv; uint_t local_action; int signal, local_flags, global_flags; pid_t pid; char rctl_valuestring[SCALED_STRLEN]; char *unit = NULL; scale_t *scale; char *string; int doneheading = 0; if (iter_list == NULL) return; while (iter_list != NULL) { if (doneheading == 0 && arg_entity_type == RCENTITY_PROCESS) { proc_unctrl_psinfo(&(p->psinfo)); doneheading = 1; (void) fprintf(stdout, "process: %d: %.70s\n", (int)p->pid, p->psinfo.pr_psargs); if (!arg_parseable_mode) print_heading(); } if (doneheading == 0 && arg_entity_type == RCENTITY_TASK) { doneheading = 1; (void) fprintf(stdout, "task: %d\n", (int)p->taskid); if (!arg_parseable_mode) print_heading(); } if (doneheading == 0 && arg_entity_type == RCENTITY_PROJECT) { if (!arg_parseable_mode && doneheading) (void) fprintf(stdout, "\n"); doneheading = 1; (void) fprintf(stdout, "project: %d: %.70s\n", (int)p->projid, p->projname); if (!arg_parseable_mode) print_heading(); } if (doneheading == 0 && arg_entity_type == RCENTITY_ZONE) { doneheading = 1; (void) fprintf(stdout, "zone: %d: %.70s\n", (int)p->zoneid, p->zonename); if (!arg_parseable_mode) print_heading(); } /* only print name once in normal output */ if (!arg_parseable_mode) (void) fprintf(stdout, "%s\n", iter_list->name); iter_val = iter_list->val_list; /* if for some reason there are no values, skip */ if (iter_val == 0) continue; /* get the global flags the first rctl only */ global_flags = rctlblk_get_global_flags(iter_val->rblk); if (global_flags & RCTL_GLOBAL_BYTES) { unit = SCALED_UNIT_BYTES; scale = scale_binary; } else if (global_flags & RCTL_GLOBAL_SECONDS) { unit = SCALED_UNIT_SECONDS; scale = scale_metric; } else { unit = SCALED_UNIT_NONE; scale = scale_metric; } /* print the current usage for the rctl if available */ if (iter_list->usage != NULL) { rblk_value = *(iter_list->usage); if (!arg_parseable_mode) { (void) uint64toscaled(rblk_value, 4, "E", rctl_valuestring, NULL, NULL, scale, NULL, 0); (void) fprintf(stdout, "%8s%-16s%5s%-4s\n", "", "usage", rctl_valuestring, unit); } else { (void) fprintf(stdout, "%s %s %llu - - -\n", iter_list->name, "usage", rblk_value); } } /* iterate over an print all control values */ while (iter_val != NULL) { /* print name or empty name field */ if (!arg_parseable_mode) (void) fprintf(stdout, "%8s", ""); else (void) fprintf(stdout, "%s ", iter_list->name); rblk_priv = rctlblk_get_privilege(iter_val->rblk); if (!arg_parseable_mode) print_priv(rblk_priv, "%-16s"); else print_priv(rblk_priv, "%s "); rblk_value = rctlblk_get_value(iter_val->rblk); if (arg_parseable_mode) { (void) fprintf(stdout, "%llu ", rblk_value); } else { (void) uint64toscaled(rblk_value, 4, "E", rctl_valuestring, NULL, NULL, scale, NULL, 0); (void) fprintf(stdout, "%5s", rctl_valuestring); (void) fprintf(stdout, "%-4s", unit); } local_flags = rctlblk_get_local_flags(iter_val->rblk); if (local_flags & RCTL_LOCAL_MAXIMAL) { if (global_flags & RCTL_GLOBAL_INFINITE) { string = "inf"; } else { string = "max"; } } else { string = "-"; } if (arg_parseable_mode) (void) fprintf(stdout, "%s ", string); else (void) fprintf(stdout, "%4s%3s", string, ""); local_action = rctlblk_get_local_action(iter_val->rblk, &signal); if (arg_parseable_mode) print_local_action(local_action, &signal, "%s "); else print_local_action(local_action, &signal, "%-28s"); pid = rctlblk_get_recipient_pid(iter_val->rblk); if (arg_parseable_mode) { if (pid < 0) { (void) fprintf(stdout, "%s\n", "-"); } else { (void) fprintf(stdout, "%d\n", (int)pid); } } else { if (pid < 0) { (void) fprintf(stdout, "%10s\n", "-"); } else { (void) fprintf(stdout, "%10d\n", (int)pid); } } iter_val = iter_val->next; } iter_list = iter_list->next; } } /* * * match_rctl * * find the first rctl with matching name, value, priv, and recipient pid */ int match_rctl(struct ps_prochandle *Pr, rctlblk_t **rctl, char *name, char *valuestringin, int valuein, rctl_priv_t privin, int pidin) { rctlblk_t *next; rctlblk_t *last; rctlblk_t *tmp; *rctl = NULL; next = calloc(1, rctlblk_size()); last = calloc(1, rctlblk_size()); if ((last == NULL) || (next == NULL)) { preserve_error(gettext("malloc failed"), strerror(errno)); return (-1); } /* * For this resource name, now iterate through all * the controls, looking for a match to the * user-specified input. */ if (pr_getrctl(Pr, name, NULL, next, RCTL_FIRST)) { preserve_error(gettext("failed to get resource control " "for %s: %s"), name, strerror(errno)); return (-1); } if (match_rctl_blk(next, valuestringin, valuein, privin, pidin) == 1) { free(last); *rctl = next; return (0); } tmp = next; next = last; last = tmp; while (pr_getrctl(Pr, name, last, next, RCTL_NEXT) == 0) { /* allow user interrupt */ if (interrupt) break; if (match_rctl_blk(next, valuestringin, valuein, privin, pidin) == 1) { free(last); *rctl = next; return (0); } tmp = next; next = last; last = tmp; } free(next); free(last); return (1); } /* * int match_rctl_blk(rctlblk_t *, char *, uint64, rctl_priv_t, int pid) * * Input * Must supply a valid rctl, value, privilege, and pid to match on. * If valuestring is NULL, then valuestring and valuein will not be used * If privilege type is 0 it will not be used. * If pid is -1 it will not be used. * * Return values * Returns 1 if a matching rctl given matches the parameters specified, and * 0 if they do not. * * This function is Pgrab-safe. */ int match_rctl_blk(rctlblk_t *rctl, char *valuestringin, uint64_t valuein, rctl_priv_t privin, int pidin) { rctl_qty_t value; rctl_priv_t priv; pid_t pid; int valuematch = 1; int privmatch = 1; int pidmatch = 1; value = rctlblk_get_value(rctl); priv = rctlblk_get_privilege(rctl); pid = rctlblk_get_recipient_pid(rctl); if (valuestringin) { if (arg_modifier == NULL) { valuematch = (valuein == value); } else { valuematch = scaledequint64(valuestringin, value, PRCTL_VALUE_WIDTH, arg_scale, arg_unit, SCALED_ALL_FLAGS); } } if (privin != 0) { privmatch = (privin == priv); } if (pidin != -1) { pidmatch = (pidin == pid); } return (valuematch && privmatch && pidmatch); } static int change_action(rctlblk_t *blk) { int signal = 0; int action; action = rctlblk_get_local_action(blk, &signal); if (arg_operation & ACTION_ENABLE) { if (arg_action & RCTL_LOCAL_SIGNAL) { signal = arg_signal; } action = action | arg_action; /* add local action */ rctlblk_set_local_action(blk, action, signal); } else if (arg_operation & ACTION_DISABLE) { /* * if deleting signal and signal number is specified, * then signal number must match */ if ((arg_action & RCTL_LOCAL_SIGNAL) && (arg_signal != -1)) { if (arg_signal != signal) { preserve_error(gettext("signal name or number " "does not match existing action")); return (-1); } } /* remove local action */ action = action & (~arg_action); rctlblk_set_local_action(blk, RCTL_LOCAL_NOACTION, 0); rctlblk_set_local_action(blk, action, signal); } /* enable deny if it must be enabled */ if (arg_global_flags & RCTL_GLOBAL_DENY_ALWAYS) { rctlblk_set_local_action(blk, RCTL_LOCAL_DENY | action, signal); } return (0); } /* * prctl_setrctl * * Input * This function expects that input has been validated. In the * case of a replace operation, both old_rblk and new_rblk must * be valid resource controls. If a resource control is being * created, only new_rblk must be supplied. If a resource control * is being deleted, only new_rblk must be supplied. * * If the privilege is a priviliged type, at this time, the process * tries to take on superuser privileges. */ int prctl_setrctl(struct ps_prochandle *Pr, const char *name, rctlblk_t *old_rblk, rctlblk_t *new_rblk, uint_t flags) { int ret = 0; rctl_priv_t rblk_priv; psinfo_t psinfo; zoneid_t oldzoneid = GLOBAL_ZONEID; prpriv_t *old_prpriv = NULL, *new_prpriv = NULL; priv_set_t *eset, *pset; boolean_t relinquish_failed = B_FALSE; rblk_priv = rctlblk_get_privilege(new_rblk); if (rblk_priv == RCPRIV_SYSTEM) { preserve_error(gettext("cannot modify system values")); return (1); } if (rblk_priv == RCPRIV_PRIVILEGED) { new_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid); if (new_prpriv == NULL) { preserve_error(gettext("cannot get process privileges " "for pid %d: %s"), Pstatus(Pr)->pr_pid, strerror(errno)); return (1); } /* * We only have to change the process privileges if it doesn't * already have PRIV_SYS_RESOURCE. In addition, we want to make * sure that we don't leave a process with elevated privileges, * so we make sure the process dies if we exit unexpectedly. */ eset = (priv_set_t *) &new_prpriv->pr_sets[new_prpriv->pr_setsize * priv_getsetbyname(PRIV_EFFECTIVE)]; pset = (priv_set_t *) &new_prpriv->pr_sets[new_prpriv->pr_setsize * priv_getsetbyname(PRIV_PERMITTED)]; if (!priv_ismember(eset, PRIV_SYS_RESOURCE)) { /* Keep track of original privileges */ old_prpriv = proc_get_priv(Pstatus(Pr)->pr_pid); if (old_prpriv == NULL) { preserve_error(gettext("cannot get process " "privileges for pid %d: %s"), Pstatus(Pr)->pr_pid, strerror(errno)); proc_free_priv(new_prpriv); return (1); } (void) priv_addset(eset, PRIV_SYS_RESOURCE); (void) priv_addset(pset, PRIV_SYS_RESOURCE); if (Psetflags(Pr, PR_KLC) != 0 || Psetpriv(Pr, new_prpriv) != 0) { preserve_error(gettext("cannot set process " "privileges for pid %d: %s"), Pstatus(Pr)->pr_pid, strerror(errno)); (void) Punsetflags(Pr, PR_KLC); proc_free_priv(new_prpriv); proc_free_priv(old_prpriv); return (1); } } /* * If this is a zone.* rctl, it requires more than * PRIV_SYS_RESOURCE: it wants the process to have global-zone * credentials. We temporarily grant non-global zone processes * these credentials, and make sure the process dies if we exit * unexpectedly. */ if (arg_name && arg_name_entity == RCENTITY_ZONE && getzoneid() == GLOBAL_ZONEID && proc_get_psinfo(Pstatus(Pr)->pr_pid, &psinfo) == 0 && (oldzoneid = psinfo.pr_zoneid) != GLOBAL_ZONEID) { /* * We need to give this process superuser * ("super-zone") privileges. * * Must never return without setting this back! */ if (Psetflags(Pr, PR_KLC) != 0 || Psetzoneid(Pr, GLOBAL_ZONEID) < 0) { preserve_error(gettext( "cannot set global-zone " "privileges for pid %d: %s"), Pstatus(Pr)->pr_pid, strerror(errno)); /* * We couldn't set the zoneid to begin with, so * there's no point in warning the user about * trying to un-set it. */ oldzoneid = GLOBAL_ZONEID; ret = 1; goto bail; } } } /* Now, actually populate the rctlblk in the kernel */ if (flags == RCTL_REPLACE) { /* * Replace should be a delete followed by an insert. This * allows us to replace rctl value blocks which match in * privilege and value, but have updated actions, etc. * setrctl() doesn't allow a direct replace, but we * should do the right thing for the user in the command. */ if (pr_setrctl(Pr, name, NULL, old_rblk, RCTL_DELETE)) { preserve_error(gettext("failed to delete resource " "control %s for pid %d: %s"), name, Pstatus(Pr)->pr_pid, strerror(errno)); ret = 1; goto bail; } if (pr_setrctl(Pr, name, NULL, new_rblk, RCTL_INSERT)) { preserve_error(gettext("failed to insert resource " "control %s for pid %d: %s"), name, Pstatus(Pr)->pr_pid, strerror(errno)); ret = 1; goto bail; } } else if (flags == RCTL_INSERT) { if (pr_setrctl(Pr, name, NULL, new_rblk, RCTL_INSERT)) { preserve_error(gettext("failed to create resource " "control %s for pid %d: %s"), name, Pstatus(Pr)->pr_pid, strerror(errno)); ret = 1; goto bail; } } else if (flags == RCTL_DELETE) { if (pr_setrctl(Pr, name, NULL, new_rblk, RCTL_DELETE)) { preserve_error(gettext("failed to delete resource " "control %s for pid %d: %s"), name, Pstatus(Pr)->pr_pid, strerror(errno)); ret = 1; goto bail; } } bail: if (oldzoneid != GLOBAL_ZONEID) { if (Psetzoneid(Pr, oldzoneid) != 0) relinquish_failed = B_TRUE; } if (old_prpriv != NULL) { if (Psetpriv(Pr, old_prpriv) != 0) relinquish_failed = B_TRUE; proc_free_priv(old_prpriv); } if (relinquish_failed) { /* * If this failed, we can't leave a process hanging * around with elevated privileges, so we'll have to * release the process from libproc, knowing that it * will be killed (since we set PR_KLC). */ Pdestroy_agent(Pr); preserve_error(gettext("cannot relinquish privileges " "for pid %d. The process was killed."), Pstatus(Pr)->pr_pid); } else { if (Punsetflags(Pr, PR_KLC) != 0) preserve_error(gettext("cannot relinquish privileges " "for pid %d. The process was killed."), Pstatus(Pr)->pr_pid); } if (new_prpriv != NULL) proc_free_priv(new_prpriv); return (ret); } void print_priv(rctl_priv_t local_priv, char *format) { char pstring[11]; switch (local_priv) { case RCPRIV_BASIC: (void) strcpy(pstring, "basic"); break; case RCPRIV_PRIVILEGED: (void) strcpy(pstring, "privileged"); break; case RCPRIV_SYSTEM: (void) strcpy(pstring, "system"); break; default: (void) sprintf(pstring, "%d", local_priv); break; } /* LINTED */ (void) fprintf(stdout, format, pstring); } void print_local_action(int action, int *signalp, char *format) { char sig[SIG2STR_MAX]; char sigstring[SIG2STR_MAX + 7]; char astring[5 + SIG2STR_MAX + 7]; int set = 0; astring[0] = '\0'; if (action == RCTL_LOCAL_NOACTION) { (void) strcat(astring, "none"); set++; } if (action & RCTL_LOCAL_DENY) { (void) strcat(astring, "deny"); set++; } if ((action & RCTL_LOCAL_DENY) && (action & RCTL_LOCAL_SIGNAL)) { (void) strcat(astring, ","); } if (action & RCTL_LOCAL_SIGNAL) { if (sig2str(*signalp, sig)) (void) snprintf(sigstring, sizeof (astring), "signal=%d", *signalp); else (void) snprintf(sigstring, sizeof (astring), "signal=%s", sig); (void) strcat(astring, sigstring); set++; } if (set) /* LINTED */ (void) fprintf(stdout, format, astring); else /* LINTED */ (void) fprintf(stdout, format, action); } /* * This function is used to grab the process matching the recipient pid */ pid_t regrab_process(pid_t pid, pr_info_handle_t *p, int priv, int *gret) { char pidstring[24]; gret = 0; if (pid == -1) return (p->pid); if (p->pid == pid) return (p->pid); release_process(p->pr); (void) memset(p, 0, sizeof (*p)); (void) snprintf(pidstring, 24, "%d", pid); return (grab_process_by_id( pidstring, RCENTITY_PROCESS, p, priv, gret)); } /* * int grab_process_by_id(char *, rctl_entity_t, pr_info_handle_t *, int, int *) * * Input * Supply a non-NULL string containing: * - logical project/zone name or project/zone number if type is * RCENTITY_PROJECT or RCENTITY_ZONE * - task number if type is RCENTITY_TYPE * - a pid if type is RCENTITY_PID * Also supply an un-allocated prochandle, and an allocated info_handle. * This function assumes that the type is set. * If priv is not RCPRIV_BASIC, the grabbed process is required to have * PRIV_SYS_RESOURCE in it's limit set. * * Return Values * Returns 0 on success and 1 on failure. If there is a process * running under the specified id, success is returned, and * Pr is pointed to the process. Success will be returned and Pr * set to NULL if the matching process is our own. * If success is returned, psinfo will be valid, and pid will * be the process number. The process will also be held at the * end, so release_process should be used by the caller. * * This function assumes that signals are caught already so that libproc * can be safely used. * * Return Values * pid - Process found and grabbed * -1 - Error */ pid_t grab_process_by_id(char *idname, rctl_entity_t type, pr_info_handle_t *p, int priv, int *gret) { char prbuf[PROJECT_BUFSZ]; projid_t projid; taskid_t taskid; zoneid_t zoneid; zoneid_t zone_self; struct project proj; DIR *dirp; struct dirent *dentp; int found = 0; int pid_self; int ret; int gret_in; int intidname; char *end; prpriv_t *prpriv; priv_set_t *prset; gret_in = *gret; /* get our pid se we do not try to operate on self */ pid_self = getpid(); /* Store integer version of id */ intidname = strtoul(idname, &end, 10); if (errno || *end != '\0' || end == idname) { intidname = -1; } /* * get our zoneid so we don't try to operate on a project in * another zone */ zone_self = getzoneid(); if (idname == NULL || strcmp(idname, "") == 0) { warn(gettext("id name cannot be nuint64\n")); return (-1); } /* * Set up zoneid, projid or taskid, as appropriate, so that comparisons * can be done later with the input. */ if (type == RCENTITY_ZONE) { if (zone_get_id(idname, &zoneid) != 0) { warn(gettext("%s: unknown zone\n"), idname); return (-1); } } else if (type == RCENTITY_PROJECT) { if (getprojbyname(idname, &proj, prbuf, PROJECT_BUFSZ) == NULL) { if (getprojbyid(intidname, &proj, prbuf, PROJECT_BUFSZ) == NULL) { warn(gettext("%s: cannot find project\n"), idname); return (-1); } } projid = proj.pj_projid; } else if (type == RCENTITY_TASK) { taskid = (taskid_t)atol(idname); } /* * Projects and tasks need to search through /proc for * a parent process. */ if (type == RCENTITY_ZONE || type == RCENTITY_PROJECT || type == RCENTITY_TASK) { if ((dirp = opendir("/proc")) == NULL) { warn(gettext("%s: cannot open /proc directory\n"), idname); return (-1); } /* * Look through all processes in /proc. For each process, * check if the pr_projid in their psinfo matches the * specified id. */ while (dentp = readdir(dirp)) { p->pid = atoi(dentp->d_name); /* Skip self */ if (p->pid == pid_self) continue; if (proc_get_psinfo(p->pid, &(p->psinfo)) != 0) continue; /* Skip process if it is not what we are looking for */ if (type == RCENTITY_ZONE && (p->psinfo).pr_zoneid != zoneid) { continue; } else if (type == RCENTITY_PROJECT && ((p->psinfo).pr_projid != projid || (p->psinfo).pr_zoneid != zone_self)) { continue; } else if (type == RCENTITY_TASK && (p->psinfo).pr_taskid != taskid) { continue; } /* attempt to grab process */ if (grab_process(p, gret) != 0) continue; /* * Re-confirm that this process is still running as * part of the specified project or task. If it * doesn't match, release the process and return an * error. This should only be done if the Pr struct is * not NULL. */ if (type == RCENTITY_PROJECT) { if (pr_getprojid(p->pr) != projid || pr_getzoneid(p->pr) != zone_self) { release_process(p->pr); continue; } } else if (type == RCENTITY_TASK) { if (pr_gettaskid(p->pr) != taskid) { release_process(p->pr); continue; } } else if (type == RCENTITY_ZONE) { if (pr_getzoneid(p->pr) != zoneid) { release_process(p->pr); continue; } } /* * If we are setting a privileged resource control, * verify that process has PRIV_SYS_RESOURCE in it's * limit set. If it does not, then we will not be * able to give this process the privilege it needs * to set the resource control. */ if (priv != RCPRIV_BASIC) { prpriv = proc_get_priv(p->pid); if (prpriv == NULL) { release_process(p->pr); continue; } prset = (priv_set_t *) &prpriv->pr_sets[prpriv->pr_setsize * priv_getsetbyname(PRIV_LIMIT)]; if (!priv_ismember(prset, PRIV_SYS_RESOURCE)) { proc_free_priv(prpriv); release_process(p->pr); continue; } proc_free_priv(prpriv); } found = 1; p->taskid = pr_gettaskid(p->pr); p->projid = pr_getprojid(p->pr); p->zoneid = pr_getzoneid(p->pr); break; } (void) closedir(dirp); if (found == 0) { warn(gettext("%s: No controllable process found in " "task, project, or zone.\n"), idname); return (-1); } return (p->pid); } else if (type == RCENTITY_PROCESS) { /* fail if self */ if (p->pid == pid_self) { warn(gettext("%s: cannot control self"), idname); return (-1); } /* * Process types need to be set up with the correct pid * and psinfo structure. */ if ((p->pid = proc_arg_psinfo(idname, PR_ARG_PIDS, &(p->psinfo), gret)) == -1) { warn(gettext("%s: cannot examine: %s"), idname, Pgrab_error(*gret)); return (-1); } /* grab process */ ret = grab_process(p, gret); if (ret == 1) { /* Don't print error if G_SYS is allowed */ if (gret_in == G_SYS && *gret == G_SYS) { return (-1); } else { warn(gettext("%s: cannot control: %s"), idname, Pgrab_error(*gret)); return (-1); } } else if (ret == 2) { ret = errno; warn(gettext("%s: cannot control: %s"), idname, strerror(ret)); return (-1); } p->taskid = pr_gettaskid(p->pr); p->projid = pr_getprojid(p->pr); p->zoneid = pr_getzoneid(p->pr); return (p->pid); } else { warn(gettext("%s: unknown resource entity type %d\n"), idname, type); return (-1); } } /* * Do the work required to manipulate a process through libproc. * If grab_process() returns no errors (0), then release_process() * must eventually be called. * * Return values: * 0 Successful creation of agent thread * 1 Error grabbing * 2 Error creating agent */ int grab_process(pr_info_handle_t *p, int *gret) { if ((p->pr = Pgrab(p->pid, arg_force, gret)) != NULL) { if (Psetflags(p->pr, PR_RLC) != 0) { Prelease(p->pr, 0); return (1); } if (Pcreate_agent(p->pr) == 0) { return (0); } else { Prelease(p->pr, 0); return (2); } } else { return (1); } } /* * Release the specified process. This destroys the agent * and releases the process. If the process is NULL, nothing * is done. This function should only be called if grab_process() * has previously been called and returned success. * * This function is Pgrab-safe. */ void release_process(struct ps_prochandle *Pr) { if (Pr == NULL) return; Pdestroy_agent(Pr); Prelease(Pr, 0); } /* * preserve_error(char *, ...) * * preserve_error() should be called rather than warn() by any * function that is called while the victim process is held by Pgrab. * It will save the error until the process has been un-controlled * and output is reasonable again. * * Note that multiple errors are not stored. Any error in these * sections should be critical and return immediately. * * This function is Pgrab-safe. * * Since this function may copy untrusted command line arguments to * global_error, security practices require that global_error never be * printed directly. Use printf("%s\n", global_error) or equivalent. */ /*PRINTFLIKE1*/ void preserve_error(char *format, ...) { va_list alist; va_start(alist, format); /* * GLOBAL_ERR_SZ is pretty big. If the error is longer * than that, just truncate it, rather than chance missing * the error altogether. */ (void) vsnprintf(global_error, GLOBAL_ERR_SZ-1, format, alist); va_end(alist); }