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 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 static int 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 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