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 2015 Ryan Zezeski 24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* 29 * pbind - bind a process to a processor (non-exclusively) 30 */ 31 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> 45 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 49 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 */ 53 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; 62 63 #define MAX_PROCFS_PATH 80 64 65 /*PRINTFLIKE1*/ 66 static void 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 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 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 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 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 * 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 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 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 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 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 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 /* 349 * Execute the cmd with args while bound to cpu. Does not return: 350 * either executes cmd successfully or dies trying. 351 */ 352 static void 353 exec_cmd(processorid_t cpu, char *cmd, char **args) 354 { 355 if (processor_bind(P_PID, P_MYID, cpu, NULL) == -1) { 356 bind_err(cpu, getpid(), -1, errno); 357 exit(ERR_FAIL); 358 } 359 360 if (execvp(cmd, args) == -1) 361 die(gettext("failed to exec %s\n"), cmd); 362 } 363 364 /* 365 * Attempt to parse str as a CPU identifier. Return the identifier or 366 * die. 367 */ 368 static processorid_t 369 parse_cpu(char *str) 370 { 371 processorid_t cpu; 372 char *endstr; 373 374 cpu = strtol(str, &endstr, 10); 375 if (endstr != NULL && *endstr != '\0' || cpu < 0) 376 die(gettext("invalid processor ID %s\n"), optarg); 377 378 return (cpu); 379 } 380 381 static int 382 usage(void) 383 { 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); 393 } 394 395 int 396 main(int argc, char *argv[]) 397 { 398 int c; 399 int ret; 400 id_t pid; 401 processorid_t cpu, old_cpu; 402 char *endstr; 403 404 progname = argv[0]; /* put actual command name in messages */ 405 406 (void) setlocale(LC_ALL, ""); /* setup localization */ 407 (void) textdomain(TEXT_DOMAIN); 408 409 while ((c = getopt(argc, argv, "b:e:qQuU")) != EOF) { 410 switch (c) { 411 412 case 'b': 413 bflag = 1; 414 cpu = parse_cpu(optarg); 415 break; 416 417 case 'e': 418 eflag = 1; 419 cpu = parse_cpu(optarg); 420 break; 421 422 case 'q': 423 qflag = 1; 424 cpu = PBIND_QUERY; 425 break; 426 427 case 'Q': 428 Qflag = 1; 429 cpu = PBIND_QUERY; 430 break; 431 432 case 'u': 433 uflag = 1; 434 cpu = PBIND_NONE; 435 break; 436 437 case 'U': 438 Uflag = 1; 439 break; 440 441 default: 442 return (usage()); 443 } 444 } 445 446 447 /* 448 * Make sure that at most one of the options b, e, q, Q, u, or 449 * U was specified. 450 */ 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()); 459 } 460 461 errors = 0; 462 argc -= optind; 463 argv += optind; 464 465 /* 466 * Handle query of all processes. 467 */ 468 if (argc == 0) { 469 if (bflag || uflag) { 470 warn(gettext("must specify at least one pid\n")); 471 return (usage()); 472 } 473 if (eflag) { 474 warn(gettext("must specify command\n")); 475 return (usage()); 476 } 477 if (Uflag) { 478 if (processor_bind(P_ALL, 0, PBIND_NONE, &old_cpu) != 0) 479 die(gettext("failed to unbind some LWPs")); 480 } 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); 487 } 488 } 489 490 if (eflag) 491 exec_cmd(cpu, argv[0], argv); 492 493 if (Qflag || Uflag) { 494 /* 495 * Go through listed processor IDs. 496 */ 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; 504 } 505 if (Qflag) { 506 (void) proc_walk(query_all_lwp, 507 &cpu, PR_WALK_LWP); 508 continue; 509 } 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; 516 } 517 continue; 518 } 519 } 520 return (errors); 521 } 522 523 /* 524 * Go through listed process[/lwp_ranges]. 525 */ 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; 533 } 534 if (endstr != NULL && *endstr == '/') { 535 /* 536 * Handle lwp range case 537 */ 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; 545 } 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 { 554 /* 555 * Handle whole process case. 556 */ 557 if (processor_bind(P_PID, pid, cpu, &old_cpu) < 0) { 558 bind_err(cpu, pid, -1, errno); 559 errors = ERR_FAIL; 560 continue; 561 } 562 if (qflag) 563 query_out(pid, -1, old_cpu); 564 else 565 bind_out(pid, -1, old_cpu, cpu); 566 } 567 } 568 return (errors); 569 } 570