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 }