Merge branch 'cleanups'
[unleashed.git] / usr / src / cmd / pbind / pbind.c
blob1e9d5e2efb67fdde06e959b2cd8262bb8645c0d8
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2015 Ryan Zezeski
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
29 * pbind - bind a process to a processor (non-exclusively)
32 #include <sys/types.h>
33 #include <sys/procset.h>
34 #include <sys/processor.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <procfs.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 #include <dirent.h>
42 #include <locale.h>
43 #include <libproc.h>
44 #include <stdarg.h>
46 #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
47 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
48 #endif
50 #define ERR_OK 0 /* exit status for success */
51 #define ERR_FAIL 1 /* exit status for errors */
52 #define ERR_USAGE 2 /* exit status for usage errors */
54 static char *progname;
55 static char bflag;
56 static char eflag;
57 static char qflag;
58 static char Qflag;
59 static char uflag;
60 static char Uflag;
61 static int errors;
63 #define MAX_PROCFS_PATH 80
65 /*PRINTFLIKE1*/
66 static void
67 warn(char *format, ...)
69 int err = errno;
70 va_list alist;
72 (void) fprintf(stderr, "%s: ", progname);
73 va_start(alist, format);
74 (void) vfprintf(stderr, format, alist);
75 va_end(alist);
76 if (strchr(format, '\n') == NULL)
77 (void) fprintf(stderr, ": %s\n", strerror(err));
80 /*PRINTFLIKE1*/
81 static void
82 die(char *format, ...)
84 int err = errno;
85 va_list alist;
87 (void) fprintf(stderr, "%s: ", progname);
88 va_start(alist, format);
89 (void) vfprintf(stderr, format, alist);
90 va_end(alist);
91 if (strchr(format, '\n') == NULL)
92 (void) fprintf(stderr, ": %s\n", strerror(err));
93 exit(ERR_FAIL);
97 * Output for query.
99 static void
100 query_out(id_t pid, id_t lwpid, processorid_t cpu)
102 char *proclwp;
103 char pidstr[21];
105 if (lwpid == -1) {
106 (void) snprintf(pidstr, 20, "%d", (int)pid);
107 proclwp = "process";
108 } else {
109 (void) snprintf(pidstr, 20, "%d/%d", (int)pid, (int)lwpid);
110 proclwp = "lwp";
113 if (cpu == PBIND_NONE)
114 (void) printf(gettext("%s id %s: not bound\n"),
115 proclwp, pidstr);
116 else
117 (void) printf(gettext("%s id %s: %d\n"),
118 proclwp, pidstr, cpu);
122 * Binding error.
124 static void
125 bind_err(processorid_t cpu, id_t pid, id_t lwpid, int err)
127 char *msg;
129 switch (cpu) {
130 case PBIND_NONE:
131 msg = gettext("unbind");
132 break;
133 case PBIND_QUERY:
134 msg = gettext("query");
135 break;
136 default:
137 msg = gettext("bind");
138 break;
140 if (lwpid == -1)
141 warn(gettext("cannot %s pid %d: %s\n"), msg,
142 (int)pid, strerror(err));
143 else
144 warn(gettext("cannot %s lwpid %d/%d: %s\n"), msg,
145 (int)pid, (int)lwpid, strerror(err));
149 * Output for bind.
151 static void
152 bind_out(id_t pid, id_t lwpid, processorid_t old, processorid_t new)
154 char *proclwp;
155 char pidstr[21];
157 if (lwpid == -1) {
158 (void) snprintf(pidstr, 20, "%d", (int)pid);
159 proclwp = "process";
160 } else {
161 (void) snprintf(pidstr, 20, "%d/%d", (int)pid, (int)lwpid);
162 proclwp = "lwp";
165 if (old == PBIND_NONE) {
166 if (new == PBIND_NONE)
167 (void) printf(gettext("%s id %s: was not bound, "
168 "now not bound\n"), proclwp, pidstr);
169 else
170 (void) printf(gettext("%s id %s: was not bound, "
171 "now %d\n"), proclwp, pidstr, new);
172 } else {
173 if (new == PBIND_NONE)
174 (void) printf(gettext("%s id %s: was %d, "
175 "now not bound\n"), proclwp, pidstr, old);
176 else
177 (void) printf(gettext("%s id %s: was %d, "
178 "now %d\n"), proclwp, pidstr, old, new);
182 static struct ps_prochandle *
183 grab_proc(id_t pid)
185 int ret;
186 struct ps_prochandle *Pr;
188 if ((Pr = Pgrab(pid, 0, &ret)) == NULL) {
189 warn(gettext("cannot control process %d: %s\n"),
190 (int)pid, Pgrab_error(ret));
191 errors = ERR_FAIL;
192 return (NULL);
196 * Set run-on-last-close flag so the controlled process
197 * runs even if we die on a signal, and create an agent LWP.
199 if (Psetflags(Pr, PR_RLC) != 0 || Pcreate_agent(Pr) != 0) {
200 warn(gettext("cannot control process %d\n"), (int)pid);
201 errors = ERR_FAIL;
202 Prelease(Pr, 0);
203 return (NULL);
205 return (Pr);
208 static void
209 rele_proc(struct ps_prochandle *Pr)
211 if (Pr == NULL)
212 return;
213 Pdestroy_agent(Pr);
214 Prelease(Pr, 0);
217 static void
218 bind_lwp(struct ps_prochandle *Pr, id_t pid, id_t lwpid, processorid_t cpu)
220 processorid_t old_cpu;
222 if (pr_processor_bind(Pr, P_LWPID, lwpid, cpu, &old_cpu) < 0) {
223 bind_err(cpu, pid, lwpid, errno);
224 errors = ERR_FAIL;
225 } else {
226 if (qflag)
227 query_out(pid, lwpid, old_cpu);
228 else
229 bind_out(pid, lwpid, old_cpu, cpu);
234 * Query, set, or clear bindings for the range of LWPs in the given process.
236 static int
237 do_lwps(id_t pid, const char *range, processorid_t cpu)
239 char procfile[MAX_PROCFS_PATH];
240 struct ps_prochandle *Pr;
241 struct prheader header;
242 processorid_t binding;
243 struct lwpsinfo *lwp;
244 char *lpsinfo, *ptr;
245 int nent, size;
246 int i, fd, found;
249 * Report bindings for LWPs in process 'pid'.
251 (void) snprintf(procfile, MAX_PROCFS_PATH,
252 "/proc/%d/lpsinfo", (int)pid);
253 if ((fd = open(procfile, O_RDONLY)) < 0) {
254 if (errno == ENOENT)
255 errno = ESRCH;
256 bind_err(cpu, pid, -1, errno);
257 return (ERR_FAIL);
259 if (pread(fd, &header, sizeof (header), 0) != sizeof (header)) {
260 (void) close(fd);
261 bind_err(cpu, pid, -1, errno);
262 return (ERR_FAIL);
264 nent = header.pr_nent;
265 size = header.pr_entsize * nent;
266 ptr = lpsinfo = malloc(size);
267 if (lpsinfo == NULL) {
268 bind_err(cpu, pid, -1, errno);
269 return (ERR_FAIL);
271 if (pread(fd, lpsinfo, size, sizeof (header)) != size) {
272 bind_err(cpu, pid, -1, errno);
273 free(lpsinfo);
274 (void) close(fd);
275 return (ERR_FAIL);
278 if ((bflag || uflag) && (Pr = grab_proc(pid)) == NULL) {
279 free(lpsinfo);
280 (void) close(fd);
281 return (ERR_FAIL);
283 found = 0;
284 for (i = 0; i < nent; i++, ptr += header.pr_entsize) {
285 /*LINTED ALIGNMENT*/
286 lwp = (lwpsinfo_t *)ptr;
287 binding = lwp->pr_bindpro;
288 if (!proc_lwp_in_set(range, lwp->pr_lwpid))
289 continue;
290 found++;
291 if (bflag || uflag)
292 bind_lwp(Pr, pid, lwp->pr_lwpid, cpu);
293 else if (binding != PBIND_NONE)
294 query_out(pid, lwp->pr_lwpid, binding);
296 if (bflag || uflag)
297 rele_proc(Pr);
298 free(lpsinfo);
299 (void) close(fd);
300 if (found == 0) {
301 warn(gettext("cannot %s lwpid %d/%s: "
302 "No matching LWPs found\n"),
303 bflag ? "bind" : "query", pid, range);
304 return (ERR_FAIL);
306 return (ERR_OK);
309 /*ARGSUSED*/
310 static int
311 query_all_proc(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, void *arg)
313 id_t pid = psinfo->pr_pid;
314 processorid_t binding;
316 if (processor_bind(P_PID, pid, PBIND_QUERY, &binding) < 0) {
318 * Ignore search errors. The process may have exited
319 * since we read the directory.
321 if (errno == ESRCH)
322 return (0);
323 bind_err(PBIND_QUERY, pid, -1, errno);
324 errors = ERR_FAIL;
325 return (0);
327 if (binding != PBIND_NONE)
328 query_out(pid, -1, binding);
329 return (0);
332 static int
333 query_all_lwp(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, void *arg)
335 id_t pid = psinfo->pr_pid;
336 id_t lwpid = lwpsinfo->pr_lwpid;
337 processorid_t *cpuid = arg;
338 processorid_t binding = lwpsinfo->pr_bindpro;
340 if (psinfo->pr_nlwp == 1)
341 lwpid = -1; /* report process bindings if only 1 lwp */
342 if ((cpuid != NULL && *cpuid == binding) ||
343 (cpuid == NULL && binding != PBIND_NONE))
344 query_out(pid, lwpid, binding);
345 return (0);
349 * Execute the cmd with args while bound to cpu. Does not return:
350 * either executes cmd successfully or dies trying.
352 static void
353 exec_cmd(processorid_t cpu, char *cmd, char **args)
355 if (processor_bind(P_PID, P_MYID, cpu, NULL) == -1) {
356 bind_err(cpu, getpid(), -1, errno);
357 exit(ERR_FAIL);
360 if (execvp(cmd, args) == -1)
361 die(gettext("failed to exec %s\n"), cmd);
365 * Attempt to parse str as a CPU identifier. Return the identifier or
366 * die.
368 static processorid_t
369 parse_cpu(char *str)
371 processorid_t cpu;
372 char *endstr;
374 cpu = strtol(str, &endstr, 10);
375 if (endstr != NULL && *endstr != '\0' || cpu < 0)
376 die(gettext("invalid processor ID %s\n"), optarg);
378 return (cpu);
381 static int
382 usage(void)
384 (void) fprintf(stderr,
385 gettext("usage: \n\t%1$s -b processor_id pid[/lwpids] ...\n"
386 "\t%1$s -e processor_id cmd [args...]\n"
387 "\t%1$s -U [processor_id] ...\n"
388 "\t%1$s -Q [processor_id] ...\n"
389 "\t%1$s -u pid[/lwpids] ...\n"
390 "\t%1$s [-q] [pid[/lwpids] ...]\n"),
391 progname);
392 return (ERR_USAGE);
396 main(int argc, char *argv[])
398 int c;
399 int ret;
400 id_t pid;
401 processorid_t cpu, old_cpu;
402 char *endstr;
404 progname = argv[0]; /* put actual command name in messages */
406 (void) setlocale(LC_ALL, ""); /* setup localization */
407 (void) textdomain(TEXT_DOMAIN);
409 while ((c = getopt(argc, argv, "b:e:qQuU")) != EOF) {
410 switch (c) {
412 case 'b':
413 bflag = 1;
414 cpu = parse_cpu(optarg);
415 break;
417 case 'e':
418 eflag = 1;
419 cpu = parse_cpu(optarg);
420 break;
422 case 'q':
423 qflag = 1;
424 cpu = PBIND_QUERY;
425 break;
427 case 'Q':
428 Qflag = 1;
429 cpu = PBIND_QUERY;
430 break;
432 case 'u':
433 uflag = 1;
434 cpu = PBIND_NONE;
435 break;
437 case 'U':
438 Uflag = 1;
439 break;
441 default:
442 return (usage());
448 * Make sure that at most one of the options b, e, q, Q, u, or
449 * U was specified.
451 c = bflag + eflag + qflag + Qflag + uflag + Uflag;
452 if (c < 1) { /* nothing specified */
453 qflag = 1; /* default to query */
454 cpu = PBIND_QUERY;
455 } else if (c > 1) {
456 warn(gettext("options -b, -e, -q, -Q, -u and -U "
457 "are mutually exclusive\n"));
458 return (usage());
461 errors = 0;
462 argc -= optind;
463 argv += optind;
466 * Handle query of all processes.
468 if (argc == 0) {
469 if (bflag || uflag) {
470 warn(gettext("must specify at least one pid\n"));
471 return (usage());
473 if (eflag) {
474 warn(gettext("must specify command\n"));
475 return (usage());
477 if (Uflag) {
478 if (processor_bind(P_ALL, 0, PBIND_NONE, &old_cpu) != 0)
479 die(gettext("failed to unbind some LWPs"));
481 if (Qflag) {
482 (void) proc_walk(query_all_lwp, NULL, PR_WALK_LWP);
483 return (errors);
484 } else {
485 (void) proc_walk(query_all_proc, NULL, PR_WALK_PROC);
486 return (errors);
490 if (eflag)
491 exec_cmd(cpu, argv[0], argv);
493 if (Qflag || Uflag) {
495 * Go through listed processor IDs.
497 for (; argc > 0; argv++, argc--) {
498 errno = 0;
499 cpu = (id_t)strtol(*argv, &endstr, 10);
500 if (errno != 0 || (endstr != NULL && *endstr != '\0') ||
501 p_online(cpu, P_STATUS) == -1) {
502 warn(gettext("invalid processor ID\n"));
503 continue;
505 if (Qflag) {
506 (void) proc_walk(query_all_lwp,
507 &cpu, PR_WALK_LWP);
508 continue;
510 if (Uflag) {
511 if (processor_bind(P_CPUID, cpu,
512 PBIND_NONE, &old_cpu) != 0) {
513 warn(gettext("failed to unbind from "
514 "processor %d"), (int)cpu);
515 errors = ERR_FAIL;
517 continue;
520 return (errors);
524 * Go through listed process[/lwp_ranges].
526 for (; argc > 0; argv++, argc--) {
527 errno = 0;
528 pid = (id_t)strtol(*argv, &endstr, 10);
529 if (errno != 0 ||
530 (endstr != NULL && *endstr != '\0' && *endstr != '/')) {
531 warn(gettext("invalid process ID: %s\n"), *argv);
532 continue;
534 if (endstr != NULL && *endstr == '/') {
536 * Handle lwp range case
538 const char *lwps = (const char *)(++endstr);
539 if (*lwps == '\0' ||
540 proc_lwp_range_valid(lwps) != 0) {
541 warn(gettext("invalid lwp range "
542 "for pid %d\n"), (int)pid);
543 errors = ERR_FAIL;
544 continue;
546 if (!qflag)
547 (void) proc_initstdio();
548 ret = do_lwps(pid, lwps, qflag ? PBIND_QUERY : cpu);
549 if (!qflag)
550 (void) proc_finistdio();
551 if (ret != ERR_OK)
552 errors = ret;
553 } else {
555 * Handle whole process case.
557 if (processor_bind(P_PID, pid, cpu, &old_cpu) < 0) {
558 bind_err(cpu, pid, -1, errno);
559 errors = ERR_FAIL;
560 continue;
562 if (qflag)
563 query_out(pid, -1, old_cpu);
564 else
565 bind_out(pid, -1, old_cpu, cpu);
568 return (errors);