/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * psrset - create and manage processor sets */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ #endif #define MAX_PROCFS_PATH 80 #define ERR_OK 0 /* exit status for success */ #define ERR_FAIL 1 /* exit status for errors */ #define ERR_USAGE 2 /* exit status for usage errors */ static char *progname; static int errors; static char cflag; static char dflag; static char aflag; static char rflag; static char iflag; static char bflag; static char uflag; static char Uflag; static char qflag; static char Qflag; static char pflag; static char nflag; static char fflag; static char Fflag; static char eflag; extern int pset_assign_forced(psetid_t, processorid_t, psetid_t *); /*PRINTFLIKE1*/ static void warn(char *format, ...) { int err = errno; va_list alist; (void) fprintf(stderr, "%s: ", progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); if (strchr(format, '\n') == NULL) (void) fprintf(stderr, ": %s\n", strerror(err)); } /*PRINTFLIKE1*/ static void die(char *format, ...) { int err = errno; va_list alist; (void) fprintf(stderr, "%s: ", progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); if (strchr(format, '\n') == NULL) (void) fprintf(stderr, ": %s\n", strerror(err)); exit(ERR_FAIL); } static prpriv_t *orig_priv; static struct ps_prochandle * grab_proc(id_t pid) { int ret; prpriv_t *new_priv; priv_set_t *eff_set, *perm_set; struct ps_prochandle *Pr; if ((Pr = Pgrab(pid, 0, &ret)) == NULL) { warn(gettext("cannot control process %d: %s\n"), (int)pid, Pgrab_error(ret)); errors = ERR_FAIL; return (NULL); } /* * If target process does not have required PRIV_SYS_RES_CONFIG * privilege, assign it temporarily to that process' effective * and permitted sets so that it can call pset_bind(2). */ if ((new_priv = proc_get_priv(pid)) == NULL) { warn(gettext("unable to get privileges for process %d: %s\n"), pid, strerror(errno)); errors = ERR_FAIL; Prelease(Pr, 0); return (NULL); } if (Pcreate_agent(Pr) != 0) { warn(gettext("cannot control process %d\n"), (int)pid); errors = ERR_FAIL; Prelease(Pr, 0); free(new_priv); return (NULL); } eff_set = (priv_set_t *)&new_priv->pr_sets[new_priv->pr_setsize * priv_getsetbyname(PRIV_EFFECTIVE)]; perm_set = (priv_set_t *)&new_priv->pr_sets[new_priv->pr_setsize * priv_getsetbyname(PRIV_PERMITTED)]; if (!priv_ismember(eff_set, PRIV_SYS_RES_CONFIG)) { /* * Save original privileges */ if ((orig_priv = proc_get_priv(pid)) == NULL) { warn(gettext("unable to get privileges for " "process %d: %s\n"), pid, strerror(errno)); errors = ERR_FAIL; Pdestroy_agent(Pr); Prelease(Pr, 0); free(new_priv); return (NULL); } (void) priv_addset(eff_set, PRIV_SYS_RES_CONFIG); (void) priv_addset(perm_set, PRIV_SYS_RES_CONFIG); /* * We don't want to leave a process with elevated privileges, * so make sure the process dies if we exit unexpectedly. */ if (Psetflags(Pr, PR_KLC) != 0 || Psetpriv(Pr, new_priv) != 0) { warn(gettext("unable to set process privileges for " "process %d: %s\n"), pid, strerror(errno)); (void) Punsetflags(Pr, PR_KLC); free(new_priv); free(orig_priv); Pdestroy_agent(Pr); Prelease(Pr, 0); orig_priv = NULL; errors = ERR_FAIL; return (NULL); } } free(new_priv); return (Pr); } static void rele_proc(struct ps_prochandle *Pr) { if (Pr == NULL) return; if (orig_priv != NULL) { if (Psetpriv(Pr, orig_priv) != 0) { /* * If this fails, we can't leave a process with * elevated privileges, so we have to release the * process from libproc, knowing that it will * be killed (since we set the PR_KLC flag). */ Pdestroy_agent(Pr); warn(gettext("cannot relinquish privileges for " "process %d. The process was killed\n"), Ppsinfo(Pr)->pr_pid); errors = ERR_FAIL; } else { (void) Punsetflags(Pr, PR_KLC); Pdestroy_agent(Pr); } free(orig_priv); orig_priv = NULL; } else { Pdestroy_agent(Pr); } Prelease(Pr, 0); } static void bind_err(psetid_t pset, id_t pid, id_t lwpid, int err) { char *msg; switch (pset) { case PS_NONE: msg = gettext("unbind"); break; case PS_QUERY: msg = gettext("query"); break; default: msg = gettext("bind"); break; } errno = err; if (lwpid == -1) warn(gettext("cannot %s pid %d"), msg, pid); else warn(gettext("cannot %s lwpid %d/%d"), msg, pid, lwpid); } /* * Output for create. */ static void create_out(psetid_t pset) { (void) printf("%s %d\n", gettext("created processor set"), pset); } /* * Output for assign. */ static void assign_out(processorid_t cpu, psetid_t old, psetid_t new) { if (old == PS_NONE) { if (new == PS_NONE) (void) printf(gettext("processor %d: was not assigned," " now not assigned\n"), cpu); else (void) printf(gettext("processor %d: was not assigned," " now %d\n"), cpu, new); } else { if (new == PS_NONE) (void) printf(gettext("processor %d: was %d, " "now not assigned\n"), cpu, old); else (void) printf(gettext("processor %d: was %d, " "now %d\n"), cpu, old, new); } } /* * Output for query. */ static void query_out(id_t pid, id_t lwpid, psetid_t pset) { char *proclwp; char pidstr[21]; if (lwpid == -1) { (void) snprintf(pidstr, 20, "%d", pid); proclwp = "process"; } else { (void) snprintf(pidstr, 20, "%d/%d", pid, lwpid); proclwp = "lwp"; } if (pset == PS_NONE) (void) printf(gettext("%s id %s: not bound\n"), proclwp, pidstr); else (void) printf(gettext("%s id %s: %d\n"), proclwp, pidstr, pset); } /* * Output for info. */ static void info_out(psetid_t pset, int type, uint_t numcpus, processorid_t *cpus) { int i; if (type == PS_SYSTEM) (void) printf(gettext("system processor set %d:"), pset); else (void) printf(gettext("user processor set %d:"), pset); if (numcpus == 0) (void) printf(gettext(" empty")); else if (numcpus > 1) (void) printf(gettext(" processors")); else (void) printf(gettext(" processor")); for (i = 0; i < numcpus; i++) (void) printf(" %d", cpus[i]); (void) printf("\n"); } /* * Output for print. */ static void print_out(processorid_t cpu, psetid_t pset) { if (pset == PS_NONE) (void) printf(gettext("processor %d: not assigned\n"), cpu); else (void) printf(gettext("processor %d: %d\n"), cpu, pset); } /* * Output for bind. */ static void bind_out(id_t pid, id_t lwpid, psetid_t old, psetid_t new) { char *proclwp; char pidstr[21]; if (lwpid == -1) { (void) snprintf(pidstr, 20, "%d", pid); proclwp = "process"; } else { (void) snprintf(pidstr, 20, "%d/%d", pid, lwpid); proclwp = "lwp"; } if (old == PS_NONE) { if (new == PS_NONE) (void) printf(gettext("%s id %s: was not bound, " "now not bound\n"), proclwp, pidstr); else (void) printf(gettext("%s id %s: was not bound, " "now %d\n"), proclwp, pidstr, new); } else { if (new == PS_NONE) (void) printf(gettext("%s id %s: was %d, " "now not bound\n"), proclwp, pidstr, old); else (void) printf(gettext("%s id %s: was %d, " "now %d\n"), proclwp, pidstr, old, new); } } static void bind_lwp(struct ps_prochandle *Pr, id_t pid, id_t lwpid, psetid_t pset) { psetid_t old_pset; if (pr_pset_bind(Pr, pset, P_LWPID, lwpid, &old_pset) < 0) { bind_err(pset, pid, lwpid, errno); errors = ERR_FAIL; } else { if (qflag) query_out(pid, lwpid, old_pset); else bind_out(pid, lwpid, old_pset, pset); } } static int do_cpu(psetid_t pset, processorid_t cpu, int print, int mustexist) { psetid_t old_pset; int err; if ((!Fflag && pset_assign(pset, cpu, &old_pset) != 0) || (Fflag && pset_assign_forced(pset, cpu, &old_pset) != 0)) { if (errno == EINVAL && !mustexist) return (EINVAL); err = errno; switch (pset) { case PS_NONE: warn(gettext("cannot remove processor %d"), cpu); break; case PS_QUERY: warn(gettext("cannot query processor %d"), cpu); break; default: warn(gettext("cannot assign processor %d"), cpu); break; } return (err); } if (print) print_out(cpu, old_pset); else assign_out(cpu, old_pset, pset); return (0); } static int do_range(psetid_t pset, processorid_t first, processorid_t last, int print) { processorid_t cpu; int error = ERR_OK; int err; int found_one = 0; for (cpu = first; cpu <= last; cpu++) { if ((err = do_cpu(pset, cpu, print, 0)) == 0) found_one = 1; else if (err != EINVAL) error = ERR_FAIL; } if (!found_one && error == ERR_OK) { warn(gettext("no processors in range %d-%d\n"), first, last); error = ERR_FAIL; } return (error); } static int do_info(psetid_t pset) { int type; uint_t numcpus; processorid_t *cpus; numcpus = (uint_t)sysconf(_SC_NPROCESSORS_MAX); cpus = (processorid_t *) malloc(numcpus * sizeof (processorid_t)); if (cpus == NULL) { warn(gettext("memory allocation failed")); return (ERR_FAIL); } if (pset_info(pset, &type, &numcpus, cpus) != 0) { warn(gettext("cannot get info for processor set %d"), pset); free(cpus); return (ERR_FAIL); } info_out(pset, type, numcpus, cpus); free(cpus); return (ERR_OK); } static int do_destroy(psetid_t pset) { if (pset_destroy(pset) != 0) { warn(gettext("could not remove processor set %d"), pset); return (ERR_FAIL); } (void) printf(gettext("removed processor set %d\n"), pset); return (ERR_OK); } static int do_intr(psetid_t pset, int flag) { uint_t i, numcpus; processorid_t *cpus; int error = ERR_OK; numcpus = (uint_t)sysconf(_SC_NPROCESSORS_MAX); cpus = (processorid_t *) malloc(numcpus * sizeof (processorid_t)); if (cpus == NULL) { warn(gettext("memory allocation failed")); return (ERR_FAIL); } if (pset_info(pset, NULL, &numcpus, cpus) != 0) { warn(gettext( "cannot set interrupt status for processor set %d"), pset); free(cpus); return (ERR_FAIL); } for (i = 0; i < numcpus; i++) { int status = p_online(cpus[i], P_STATUS); if (status != P_OFFLINE && status != P_POWEROFF && status != flag) { if (p_online(cpus[i], flag) == -1) { warn(gettext("processor %d"), cpus[i]); error = ERR_FAIL; } } } free(cpus); return (error); } /* * Query the type and CPUs for all active processor sets in the system. */ static int info_all(void) { psetid_t *psetlist; uint_t npsets, oldnpsets; int i; int errors = ERR_OK; if (pset_list(NULL, &npsets) != 0) { warn(gettext("cannot get number of processor sets")); return (1); } for (;;) { psetlist = malloc(sizeof (psetid_t) * npsets); if (psetlist == NULL) { warn(gettext("memory allocation failed")); return (ERR_FAIL); } oldnpsets = npsets; if (pset_list(psetlist, &npsets) != 0) { warn(gettext("cannot get list of processor sets")); free(psetlist); return (ERR_FAIL); } if (npsets <= oldnpsets) break; free(psetlist); } for (i = 0; i < npsets; i++) { if (do_info(psetlist[i])) errors = ERR_FAIL; } free(psetlist); return (errors); } /* * Query the processor set assignments for all CPUs in the system. */ static int print_all(void) { psetid_t pset; processorid_t cpuid, max_cpuid; int errors = ERR_OK; max_cpuid = (processorid_t)sysconf(_SC_CPUID_MAX); for (cpuid = 0; cpuid <= max_cpuid; cpuid++) { if (pset_assign(PS_QUERY, cpuid, &pset) == 0) { if (pset != PS_NONE) print_out(cpuid, pset); } else if (errno != EINVAL) { warn(gettext("cannot query processor %d"), cpuid); errors = ERR_FAIL; } } return (errors); } /*ARGSUSED*/ static int query_all_proc(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, void *arg) { id_t pid = psinfo->pr_pid; psetid_t binding; if (pset_bind(PS_QUERY, P_PID, pid, &binding) < 0) { /* * Ignore search errors. The process may have exited * since we read the directory. */ if (errno == ESRCH) return (0); bind_err(PS_QUERY, pid, -1, errno); errors = ERR_FAIL; return (0); } if (binding != PS_NONE) query_out(pid, -1, binding); return (0); } static int query_all_lwp(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, void *arg) { id_t pid = psinfo->pr_pid; id_t lwpid = lwpsinfo->pr_lwpid; psetid_t *cpuid = arg; psetid_t binding = lwpsinfo->pr_bindpset; if (psinfo->pr_nlwp == 1) lwpid = -1; /* report process bindings if only 1 lwp */ if ((cpuid != NULL && *cpuid == binding) || (cpuid == NULL && binding != PBIND_NONE)) query_out(pid, lwpid, binding); return (0); } void exec_cmd(psetid_t pset, char **argv) { if (pset_bind(pset, P_PID, P_MYID, NULL) != 0) { warn(gettext("cannot exec in processor set %d"), pset); return; } (void) execvp(argv[0], argv); warn(gettext("cannot exec command %s"), argv[0]); } int usage(void) { (void) fprintf(stderr, gettext( "usage: \n" "\t%1$s -c [-F] [processor_id ...]\n" "\t%1$s -d processor_set_id ...\n" "\t%1$s -n processor_set_id\n" "\t%1$s -f processor_set_id\n" "\t%1$s -e processor_set_id command [argument(s)...]\n" "\t%1$s -a [-F] processor_set_id processor_id ...\n" "\t%1$s -r [-F] processor_id ...\n" "\t%1$s -p [processorid ...]\n" "\t%1$s -b processor_set_id pid[/lwpids] ...\n" "\t%1$s -u pid[/lwpids] ...\n" "\t%1$s -q [pid[/lwpids] ...]\n" "\t%1$s -U [processor_set_id] ...\n" "\t%1$s -Q [processor_set_id] ...\n" "\t%1$s [-i] [processor_set_id ...]\n"), progname); return (ERR_USAGE); } /* * Query, set, or clear bindings for the range of LWPs in the given process. */ static int do_lwps(id_t pid, const char *range, psetid_t pset) { char procfile[MAX_PROCFS_PATH]; struct ps_prochandle *Pr; struct prheader header; struct lwpsinfo *lwp; char *lpsinfo, *ptr; psetid_t binding; int nent, size; int i, fd, found; /* * Report bindings for LWPs in process 'pid'. */ (void) snprintf(procfile, MAX_PROCFS_PATH, "/proc/%d/lpsinfo", (int)pid); if ((fd = open(procfile, O_RDONLY)) < 0) { if (errno == ENOENT) errno = ESRCH; bind_err(pset, pid, -1, errno); return (ERR_FAIL); } if (pread(fd, &header, sizeof (header), 0) != sizeof (header)) { (void) close(fd); bind_err(pset, pid, -1, errno); return (ERR_FAIL); } nent = header.pr_nent; size = header.pr_entsize * nent; ptr = lpsinfo = malloc(size); if (lpsinfo == NULL) { bind_err(pset, pid, -1, errno); return (ERR_FAIL); } if (pread(fd, lpsinfo, size, sizeof (header)) != size) { bind_err(pset, pid, -1, errno); free(lpsinfo); (void) close(fd); return (ERR_FAIL); } if ((bflag || uflag) && (Pr = grab_proc(pid)) == NULL) { free(lpsinfo); (void) close(fd); return (ERR_FAIL); } found = 0; for (i = 0; i < nent; i++, ptr += header.pr_entsize) { /*LINTED ALIGNMENT*/ lwp = (lwpsinfo_t *)ptr; binding = lwp->pr_bindpset; if (!proc_lwp_in_set(range, lwp->pr_lwpid)) continue; found++; if (bflag || uflag) bind_lwp(Pr, pid, lwp->pr_lwpid, pset); else if (binding != PBIND_NONE) query_out(pid, lwp->pr_lwpid, binding); } if (bflag || uflag) rele_proc(Pr); free(lpsinfo); (void) close(fd); if (found == 0) { warn(gettext("cannot %s lwpid %d/%s: " "No matching LWPs found\n"), bflag ? "bind" : "query", pid, range); return (ERR_FAIL); } return (ERR_OK); } int main(int argc, char *argv[]) { extern int optind; int c; id_t pid; processorid_t cpu; psetid_t pset, old_pset; char *errptr; progname = argv[0]; /* put actual command name in messages */ (void) setlocale(LC_ALL, ""); /* setup localization */ (void) textdomain(TEXT_DOMAIN); while ((c = getopt(argc, argv, "cdFarpibqQuUnfe")) != EOF) { switch (c) { case 'c': cflag = 1; break; case 'd': dflag = 1; break; case 'e': eflag = 1; break; case 'a': aflag = 1; break; case 'r': rflag = 1; pset = PS_NONE; break; case 'p': pflag = 1; pset = PS_QUERY; break; case 'i': iflag = 1; break; case 'b': bflag = 1; break; case 'u': uflag = 1; pset = PS_NONE; break; case 'U': Uflag = 1; break; case 'q': qflag = 1; pset = PS_QUERY; break; case 'Q': Qflag = 1; break; case 'f': fflag = 1; break; case 'F': Fflag = 1; break; case 'n': nflag = 1; break; default: return (usage()); } } /* * Make sure that at most one of the options was specified. */ c = cflag + dflag + aflag + rflag + pflag + iflag + bflag + uflag + Uflag + qflag + Qflag + fflag + nflag + eflag; if (c < 1) { /* nothing specified */ iflag = 1; /* default is to get info */ } else if (c > 1) { warn(gettext("options are mutually exclusive\n")); return (usage()); } if (Fflag && (cflag + aflag + rflag == 0)) return (usage()); errors = 0; argc -= optind; argv += optind; if (argc == 0) { /* * Handle single option cases. */ if (qflag) { (void) proc_walk(query_all_proc, NULL, PR_WALK_PROC); return (errors); } if (Qflag) { (void) proc_walk(query_all_lwp, NULL, PR_WALK_LWP); return (errors); } if (Uflag) { if (pset_bind(PS_NONE, P_ALL, 0, &old_pset) != 0) die(gettext("failed to unbind all LWPs")); } if (pflag) return (print_all()); if (iflag) return (info_all()); } /* * Get processor set id. */ if (aflag || bflag || fflag || nflag || eflag) { if (argc < 1) { /* must specify processor set */ warn(gettext("must specify processor set\n")); return (usage()); } pset = strtol(*argv, &errptr, 10); if (errptr != NULL && *errptr != '\0' || pset < 0) { warn(gettext("invalid processor set ID %s\n"), *argv); return (ERR_FAIL); } argv++; argc--; } if (cflag) { if (pset_create(&pset) != 0) { warn(gettext("could not create processor set")); return (ERR_FAIL); } else { create_out(pset); if (argc == 0) return (ERR_OK); } } else if (iflag || dflag) { if (argc == 0) { warn(gettext("must specify at least one " "processor set\n")); return (usage()); } /* * Go through listed processor sets. */ for (; argc > 0; argv++, argc--) { pset = (psetid_t)strtol(*argv, &errptr, 10); if (errptr != NULL && *errptr != '\0') { warn(gettext("invalid processor set ID %s\n"), *argv); errors = ERR_FAIL; continue; } if (iflag) { errors = do_info(pset); } else { errors = do_destroy(pset); } } } else if (nflag) { errors = do_intr(pset, P_ONLINE); } else if (fflag) { errors = do_intr(pset, P_NOINTR); } else if (eflag) { if (argc == 0) { warn(gettext("must specify command\n")); return (usage()); } exec_cmd(pset, argv); /* if returning, must have had an error */ return (ERR_USAGE); } if (cflag || aflag || rflag || pflag) { /* * Perform function for each processor specified. */ if (argc == 0) { warn(gettext("must specify at least one processor\n")); return (usage()); } /* * Go through listed processors. */ for (; argc > 0; argv++, argc--) { if (strchr(*argv, '-') == NULL) { /* individual processor id */ cpu = (processorid_t)strtol(*argv, &errptr, 10); if (errptr != NULL && *errptr != '\0') { warn(gettext("invalid processor " "ID %s\n"), *argv); errors = ERR_FAIL; continue; } if (do_cpu(pset, cpu, pflag, 1)) errors = ERR_FAIL; } else { /* range of processors */ processorid_t first, last; first = (processorid_t) strtol(*argv, &errptr, 10); if (*errptr++ != '-') { warn(gettext( "invalid processor range %s\n"), *argv); errors = ERR_USAGE; continue; } last = (processorid_t) strtol(errptr, &errptr, 10); if ((errptr != NULL && *errptr != '\0') || last < first || first < 0) { warn(gettext( "invalid processor range %s\n"), *argv); errors = ERR_USAGE; continue; } if (do_range(pset, first, last, pflag)) errors = ERR_FAIL; } } } else if (bflag || uflag || qflag) { /* * Perform function for each pid/lwpid specified. */ if (argc == 0) { warn(gettext("must specify at least one pid\n")); return (usage()); } /* * Go through listed processes/lwp_ranges. */ for (; argc > 0; argv++, argc--) { pid = (id_t)strtol(*argv, &errptr, 10); if (errno != 0 || (errptr != NULL && *errptr != '\0' && *errptr != '/')) { warn(gettext("invalid process ID: %s\n"), *argv); continue; } if (errptr != NULL && *errptr == '/') { int ret; /* * Handle lwp range case */ const char *lwps = (const char *)(++errptr); if (*lwps == '\0' || proc_lwp_range_valid(lwps) != 0) { warn(gettext("invalid lwp range " "for pid %d\n"), (int)pid); errors = ERR_FAIL; continue; } if (!qflag) (void) proc_initstdio(); ret = do_lwps(pid, lwps, pset); if (!qflag) (void) proc_finistdio(); if (ret != ERR_OK) errors = ret; } else { /* * Handle whole process case. */ if (pset_bind(pset, P_PID, pid, &old_pset) < 0) { bind_err(pset, pid, -1, errno); errors = ERR_FAIL; continue; } if (qflag) query_out(pid, -1, old_pset); else bind_out(pid, -1, old_pset, pset); } } } if (Qflag || Uflag) { /* * Go through listed processor set IDs. */ for (; argc > 0; argv++, argc--) { errno = 0; pset = (id_t)strtol(*argv, &errptr, 10); if (errno != 0 || (errptr != NULL && *errptr != '\0')) { warn(gettext("invalid processor set ID\n")); continue; } if (Qflag) { (void) proc_walk(query_all_lwp, &pset, PR_WALK_LWP); continue; } if (Uflag) { if (pset_bind(PS_NONE, P_PSETID, pset, &old_pset) != 0) { warn(gettext("failed to unbind from " "processor set %d"), (int)pset); errors = ERR_FAIL; } continue; } } } return (errors); }