1 /*
2 * CDDL HEADER START
3 *
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.
8 *
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.
13 *
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]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * pbind - bind a process to a processor (non-exclusively)
31 */
32
33 #include <sys/types.h>
34 #include <sys/procset.h>
35 #include <sys/processor.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <procfs.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <dirent.h>
43 #include <locale.h>
44 #include <libproc.h>
45 #include <stdarg.h>
46
47 #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
48 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
49 #endif
50
51 #define ERR_OK 0 /* exit status for success */
52 #define ERR_FAIL 1 /* exit status for errors */
53 #define ERR_USAGE 2 /* exit status for usage errors */
54
55 static char *progname;
56 static char bflag;
57 static char qflag;
58 static char Qflag;
59 static char uflag;
60 static char Uflag;
61 static int errors;
62
63 #define MAX_PROCFS_PATH 80
64
65 /*PRINTFLIKE1*/
66 static void
warn(char * format,...)67 warn(char *format, ...)
68 {
69 int err = errno;
70 va_list alist;
71
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));
78 }
79
80 /*PRINTFLIKE1*/
81 static void
die(char * format,...)82 die(char *format, ...)
83 {
84 int err = errno;
85 va_list alist;
86
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);
94 }
95
96 /*
97 * Output for query.
98 */
99 static void
query_out(id_t pid,id_t lwpid,processorid_t cpu)100 query_out(id_t pid, id_t lwpid, processorid_t cpu)
101 {
102 char *proclwp;
103 char pidstr[21];
104
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";
111 }
112
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);
119 }
120
121 /*
122 * Binding error.
123 */
124 static void
bind_err(processorid_t cpu,id_t pid,id_t lwpid,int err)125 bind_err(processorid_t cpu, id_t pid, id_t lwpid, int err)
126 {
127 char *msg;
128
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;
139 }
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));
146 }
147
148 /*
149 * Output for bind.
150 */
151 static void
bind_out(id_t pid,id_t lwpid,processorid_t old,processorid_t new)152 bind_out(id_t pid, id_t lwpid, processorid_t old, processorid_t new)
153 {
154 char *proclwp;
155 char pidstr[21];
156
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";
163 }
164
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);
179 }
180 }
181
182 static struct ps_prochandle *
grab_proc(id_t pid)183 grab_proc(id_t pid)
184 {
185 int ret;
186 struct ps_prochandle *Pr;
187
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);
193 }
194
195 /*
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.
198 */
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);
204 }
205 return (Pr);
206 }
207
208 static void
rele_proc(struct ps_prochandle * Pr)209 rele_proc(struct ps_prochandle *Pr)
210 {
211 if (Pr == NULL)
212 return;
213 Pdestroy_agent(Pr);
214 Prelease(Pr, 0);
215 }
216
217 static void
bind_lwp(struct ps_prochandle * Pr,id_t pid,id_t lwpid,processorid_t cpu)218 bind_lwp(struct ps_prochandle *Pr, id_t pid, id_t lwpid, processorid_t cpu)
219 {
220 processorid_t old_cpu;
221
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);
230 }
231 }
232
233 /*
234 * Query, set, or clear bindings for the range of LWPs in the given process.
235 */
236 static int
do_lwps(id_t pid,const char * range,processorid_t cpu)237 do_lwps(id_t pid, const char *range, processorid_t cpu)
238 {
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;
247
248 /*
249 * Report bindings for LWPs in process 'pid'.
250 */
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);
258 }
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);
263 }
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);
270 }
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);
276 }
277
278 if ((bflag || uflag) && (Pr = grab_proc(pid)) == NULL) {
279 free(lpsinfo);
280 (void) close(fd);
281 return (ERR_FAIL);
282 }
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);
295 }
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);
305 }
306 return (ERR_OK);
307 }
308
309 /*ARGSUSED*/
310 static int
query_all_proc(psinfo_t * psinfo,lwpsinfo_t * lwpsinfo,void * arg)311 query_all_proc(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, void *arg)
312 {
313 id_t pid = psinfo->pr_pid;
314 processorid_t binding;
315
316 if (processor_bind(P_PID, pid, PBIND_QUERY, &binding) < 0) {
317 /*
318 * Ignore search errors. The process may have exited
319 * since we read the directory.
320 */
321 if (errno == ESRCH)
322 return (0);
323 bind_err(PBIND_QUERY, pid, -1, errno);
324 errors = ERR_FAIL;
325 return (0);
326 }
327 if (binding != PBIND_NONE)
328 query_out(pid, -1, binding);
329 return (0);
330 }
331
332 static int
query_all_lwp(psinfo_t * psinfo,lwpsinfo_t * lwpsinfo,void * arg)333 query_all_lwp(psinfo_t *psinfo, lwpsinfo_t *lwpsinfo, void *arg)
334 {
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;
339
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);
346 }
347
348 static int
usage(void)349 usage(void)
350 {
351 (void) fprintf(stderr,
352 gettext("usage: \n\t%1$s -b processor_id pid[/lwpids] ...\n"
353 "\t%1$s -U [processor_id] ...\n"
354 "\t%1$s -Q [processor_id] ...\n"
355 "\t%1$s -u pid[/lwpids] ...\n"
356 "\t%1$s [-q] [pid[/lwpids] ...]\n"),
357 progname);
358 return (ERR_USAGE);
359 }
360
361 int
main(int argc,char * argv[])362 main(int argc, char *argv[])
363 {
364 int c;
365 int ret;
366 id_t pid;
367 processorid_t cpu, old_cpu;
368 char *endstr;
369
370 progname = argv[0]; /* put actual command name in messages */
371
372 (void) setlocale(LC_ALL, ""); /* setup localization */
373 (void) textdomain(TEXT_DOMAIN);
374
375 while ((c = getopt(argc, argv, "b:qQuU")) != EOF) {
376 switch (c) {
377
378 case 'b':
379 bflag = 1;
380 cpu = strtol(optarg, &endstr, 10);
381 if (endstr != NULL && *endstr != '\0' || cpu < 0)
382 die(gettext("invalid processor ID %s\n"),
383 optarg);
384 break;
385
386 case 'q':
387 qflag = 1;
388 cpu = PBIND_QUERY;
389 break;
390
391 case 'Q':
392 Qflag = 1;
393 cpu = PBIND_QUERY;
394 break;
395
396 case 'u':
397 uflag = 1;
398 cpu = PBIND_NONE;
399 break;
400
401 case 'U':
402 Uflag = 1;
403 break;
404
405 default:
406 return (usage());
407 }
408 }
409
410
411 /*
412 * Make sure that at most one of the options b, q, Q, u, or U
413 * was specified.
414 */
415 c = bflag + qflag + Qflag + uflag + Uflag;
416 if (c < 1) { /* nothing specified */
417 qflag = 1; /* default to query */
418 cpu = PBIND_QUERY;
419 } else if (c > 1) {
420 warn(gettext("options -b, -q, -Q, -u and -U "
421 "are mutually exclusive\n"));
422 return (usage());
423 }
424
425 errors = 0;
426 argc -= optind;
427 argv += optind;
428
429 /*
430 * Handle query of all processes.
431 */
432 if (argc == 0) {
433 if (bflag || uflag) {
434 warn(gettext("must specify at least one pid\n"));
435 return (usage());
436 }
437 if (Uflag) {
438 if (processor_bind(P_ALL, 0, PBIND_NONE, &old_cpu) != 0)
439 die(gettext("failed to unbind some LWPs"));
440 }
441 if (Qflag) {
442 (void) proc_walk(query_all_lwp, NULL, PR_WALK_LWP);
443 return (errors);
444 } else {
445 (void) proc_walk(query_all_proc, NULL, PR_WALK_PROC);
446 return (errors);
447 }
448 }
449
450 if (Qflag || Uflag) {
451 /*
452 * Go through listed processor IDs.
453 */
454 for (; argc > 0; argv++, argc--) {
455 errno = 0;
456 cpu = (id_t)strtol(*argv, &endstr, 10);
457 if (errno != 0 || (endstr != NULL && *endstr != '\0') ||
458 p_online(cpu, P_STATUS) == -1) {
459 warn(gettext("invalid processor ID\n"));
460 continue;
461 }
462 if (Qflag) {
463 (void) proc_walk(query_all_lwp,
464 &cpu, PR_WALK_LWP);
465 continue;
466 }
467 if (Uflag) {
468 if (processor_bind(P_CPUID, cpu,
469 PBIND_NONE, &old_cpu) != 0) {
470 warn(gettext("failed to unbind from "
471 "processor %d"), (int)cpu);
472 errors = ERR_FAIL;
473 }
474 continue;
475 }
476 }
477 return (errors);
478 }
479
480 /*
481 * Go through listed process[/lwp_ranges].
482 */
483 for (; argc > 0; argv++, argc--) {
484 errno = 0;
485 pid = (id_t)strtol(*argv, &endstr, 10);
486 if (errno != 0 ||
487 (endstr != NULL && *endstr != '\0' && *endstr != '/')) {
488 warn(gettext("invalid process ID: %s\n"), *argv);
489 continue;
490 }
491 if (endstr != NULL && *endstr == '/') {
492 /*
493 * Handle lwp range case
494 */
495 const char *lwps = (const char *)(++endstr);
496 if (*lwps == '\0' ||
497 proc_lwp_range_valid(lwps) != 0) {
498 warn(gettext("invalid lwp range "
499 "for pid %d\n"), (int)pid);
500 errors = ERR_FAIL;
501 continue;
502 }
503 if (!qflag)
504 (void) proc_initstdio();
505 ret = do_lwps(pid, lwps, qflag ? PBIND_QUERY : cpu);
506 if (!qflag)
507 (void) proc_finistdio();
508 if (ret != ERR_OK)
509 errors = ret;
510 } else {
511 /*
512 * Handle whole process case.
513 */
514 if (processor_bind(P_PID, pid, cpu, &old_cpu) < 0) {
515 bind_err(cpu, pid, -1, errno);
516 errors = ERR_FAIL;
517 continue;
518 }
519 if (qflag)
520 query_out(pid, -1, old_cpu);
521 else
522 bind_out(pid, -1, old_cpu, cpu);
523 }
524 }
525 return (errors);
526 }
527