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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2019 Joyent, Inc. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <sys/proc.h> 30 31 #include <libgen.h> 32 #include <limits.h> 33 #include <alloca.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <fcntl.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <dirent.h> 40 41 #include "Pcontrol.h" 42 43 static int 44 open_psinfo(const char *arg, int *perr) 45 { 46 /* 47 * Allocate enough space for procfs_path + arg + "/psinfo" 48 */ 49 char *path = alloca(strlen(arg) + strlen(procfs_path) + 9); 50 51 struct stat64 st; 52 int fd; 53 54 if (strchr(arg, '/') == NULL) { 55 (void) strcpy(path, procfs_path); 56 (void) strcat(path, "/"); 57 (void) strcat(path, arg); 58 } else 59 (void) strcpy(path, arg); 60 61 (void) strcat(path, "/psinfo"); 62 63 /* 64 * Attempt to open the psinfo file, and return the fd if we can 65 * confirm this is a regular file provided by /proc. 66 */ 67 if ((fd = open64(path, O_RDONLY)) >= 0) { 68 if (fstat64(fd, &st) != 0 || !S_ISREG(st.st_mode) || 69 strcmp(st.st_fstype, "proc") != 0) { 70 (void) close(fd); 71 fd = -1; 72 } 73 } else if (errno == EACCES || errno == EPERM) 74 *perr = G_PERM; 75 76 return (fd); 77 } 78 79 static int 80 open_core(const char *arg, int *perr) 81 { 82 #ifdef _BIG_ENDIAN 83 uchar_t order = ELFDATA2MSB; 84 #else 85 uchar_t order = ELFDATA2LSB; 86 #endif 87 GElf_Ehdr ehdr; 88 int fd; 89 int is_noelf = -1; 90 91 /* 92 * Attempt to open the core file, and return the fd if we can confirm 93 * this is an ELF file of type ET_CORE. 94 */ 95 if ((fd = open64(arg, O_RDONLY)) >= 0) { 96 if (read(fd, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) { 97 (void) close(fd); 98 fd = -1; 99 } else if ((is_noelf = memcmp(&ehdr.e_ident[EI_MAG0], ELFMAG, 100 SELFMAG)) != 0 || ehdr.e_type != ET_CORE) { 101 (void) close(fd); 102 fd = -1; 103 if (is_noelf == 0 && 104 ehdr.e_ident[EI_DATA] != order) 105 *perr = G_ISAINVAL; 106 } 107 } else if (errno == EACCES || errno == EPERM) 108 *perr = G_PERM; 109 110 return (fd); 111 } 112 113 /* 114 * Make the error message precisely match the type of arguments the caller 115 * wanted to process. This ensures that a tool which only accepts pids does 116 * not produce an error message saying "no such process or core file 'foo'". 117 */ 118 static int 119 open_error(int oflag) 120 { 121 if ((oflag & PR_ARG_ANY) == PR_ARG_PIDS) 122 return (G_NOPROC); 123 124 if ((oflag & PR_ARG_ANY) == PR_ARG_CORES) 125 return (G_NOCORE); 126 127 return (G_NOPROCORCORE); 128 } 129 130 static void * 131 proc_grab_common(const char *arg, const char *path, int oflag, int gflag, 132 int *perr, const char **lwps, psinfo_t *psp) 133 { 134 psinfo_t psinfo; 135 char *core; 136 int fd; 137 char *slash; 138 struct ps_prochandle *Pr; 139 140 *perr = 0; 141 if (lwps) 142 *lwps = NULL; 143 144 if (lwps != NULL && (slash = strrchr(arg, '/')) != NULL) { 145 /* 146 * Check to see if the user has supplied an lwp range. First, 147 * try to grab it as a pid/lwp combo. 148 */ 149 *slash = '\0'; 150 if ((oflag & PR_ARG_PIDS) && 151 (fd = open_psinfo(arg, perr)) != -1) { 152 if (read(fd, &psinfo, 153 sizeof (psinfo_t)) == sizeof (psinfo_t)) { 154 (void) close(fd); 155 *lwps = slash + 1; 156 *slash = '/'; 157 if (proc_lwp_range_valid(*lwps) != 0) { 158 *perr = G_BADLWPS; 159 return (NULL); 160 } 161 if (psp) { 162 *psp = psinfo; 163 return (psp); 164 } else { 165 return (Pgrab(psinfo.pr_pid, gflag, 166 perr)); 167 } 168 } 169 (void) close(fd); 170 } 171 172 /* 173 * Next, try grabbing it as a corefile. 174 */ 175 if ((oflag & PR_ARG_CORES) && 176 (fd = open_core(arg, perr)) != -1) { 177 *lwps = slash + 1; 178 *slash = '/'; 179 if (proc_lwp_range_valid(*lwps) != 0) { 180 *perr = G_BADLWPS; 181 return (NULL); 182 } 183 core = strdupa(arg); 184 if ((Pr = Pfgrab_core(fd, path == NULL ? 185 dirname(core) : path, perr)) != NULL) { 186 if (psp) { 187 (void) memcpy(psp, Ppsinfo(Pr), 188 sizeof (psinfo_t)); 189 Prelease(Pr, 0); 190 return (psp); 191 } else { 192 return (Pr); 193 } 194 } 195 } 196 197 *slash = '/'; 198 } 199 200 if ((oflag & PR_ARG_PIDS) && (fd = open_psinfo(arg, perr)) != -1) { 201 if (read(fd, &psinfo, sizeof (psinfo_t)) == sizeof (psinfo_t)) { 202 (void) close(fd); 203 if (psp) { 204 *psp = psinfo; 205 return (psp); 206 } else { 207 return (Pgrab(psinfo.pr_pid, gflag, perr)); 208 } 209 } 210 /* 211 * If the read failed, the process may have gone away; 212 * we continue checking for core files or fail with G_NOPROC 213 */ 214 (void) close(fd); 215 } 216 217 if ((oflag & PR_ARG_CORES) && (fd = open_core(arg, perr)) != -1) { 218 core = strdupa(arg); 219 if ((Pr = Pfgrab_core(fd, path == NULL ? dirname(core) : path, 220 perr)) != NULL) { 221 if (psp) { 222 (void) memcpy(psp, Ppsinfo(Pr), 223 sizeof (psinfo_t)); 224 Prelease(Pr, 0); 225 return (psp); 226 } else { 227 return (Pr); 228 } 229 } 230 } 231 232 /* 233 * We were unable to open the corefile. If we have no meaningful 234 * information, report the (ambiguous) error from open_error(). 235 */ 236 237 if (*perr == 0) 238 *perr = open_error(oflag); 239 240 return (NULL); 241 } 242 243 struct ps_prochandle * 244 proc_arg_xgrab(const char *arg, const char *path, int oflag, int gflag, 245 int *perr, const char **lwps) 246 { 247 return (proc_grab_common(arg, path, oflag, gflag, perr, lwps, NULL)); 248 } 249 250 struct ps_prochandle * 251 proc_arg_grab(const char *arg, int oflag, int gflag, int *perr) 252 { 253 return (proc_grab_common(arg, NULL, oflag, gflag, perr, NULL, NULL)); 254 } 255 256 pid_t 257 proc_arg_psinfo(const char *arg, int oflag, psinfo_t *psp, int *perr) 258 { 259 psinfo_t psinfo; 260 261 if (psp == NULL) 262 psp = &psinfo; 263 264 if (proc_grab_common(arg, NULL, oflag, 0, perr, NULL, psp) == NULL) 265 return (-1); 266 else 267 return (psp->pr_pid); 268 } 269 270 pid_t 271 proc_arg_xpsinfo(const char *arg, int oflag, psinfo_t *psp, int *perr, 272 const char **lwps) 273 { 274 psinfo_t psinfo; 275 276 if (psp == NULL) 277 psp = &psinfo; 278 279 if (proc_grab_common(arg, NULL, oflag, 0, perr, lwps, psp) == NULL) 280 return (-1); 281 else 282 return (psp->pr_pid); 283 } 284 285 /* 286 * Convert psinfo_t.pr_psargs string into itself, replacing unprintable 287 * characters with space along the way. Stop on a null character. 288 */ 289 void 290 proc_unctrl_psinfo(psinfo_t *psp) 291 { 292 char *s = &psp->pr_psargs[0]; 293 size_t n = PRARGSZ; 294 int c; 295 296 while (n-- != 0 && (c = (*s & UCHAR_MAX)) != '\0') { 297 if (!isprint(c)) 298 c = ' '; 299 *s++ = (char)c; 300 } 301 302 *s = '\0'; 303 } 304 305 static int 306 proc_lwp_get_range(char *range, id_t *low, id_t *high) 307 { 308 if (*range == '-') 309 *low = 0; 310 else 311 *low = (id_t)strtol(range, &range, 10); 312 313 if (*range == '\0' || *range == ',') { 314 *high = *low; 315 return (0); 316 } 317 if (*range != '-') { 318 return (-1); 319 } 320 range++; 321 322 if (*range == '\0') 323 *high = INT_MAX; 324 else 325 *high = (id_t)strtol(range, &range, 10); 326 327 if (*range != '\0' && *range != ',') { 328 return (-1); 329 } 330 331 if (*high < *low) { 332 id_t tmp = *high; 333 *high = *low; 334 *low = tmp; 335 } 336 337 return (0); 338 } 339 340 /* 341 * Determine if the specified lwpid is in the given set of lwpids. 342 * The set can include multiple lwpid ranges separated by commas 343 * and has the following syntax: 344 * 345 * lwp_range[,lwp_range]* 346 * 347 * where lwp_range is specifed as: 348 * 349 * -n lwpid <= n 350 * n-m n <= lwpid <= m 351 * n- lwpid >= n 352 * n lwpid == n 353 */ 354 int 355 proc_lwp_in_set(const char *set, lwpid_t lwpid) 356 { 357 id_t low, high; 358 id_t id = (id_t)lwpid; 359 char *comma; 360 char *range = (char *)set; 361 362 /* 363 * A NULL set indicates that all LWPs are valid. 364 */ 365 if (set == NULL) 366 return (1); 367 368 while (range != NULL) { 369 comma = strchr(range, ','); 370 if (comma != NULL) 371 *comma = '\0'; 372 if (proc_lwp_get_range(range, &low, &high) != 0) { 373 if (comma != NULL) 374 *comma = ','; 375 return (0); 376 } 377 if (comma != NULL) { 378 *comma = ','; 379 range = comma + 1; 380 } else { 381 range = NULL; 382 } 383 if (id >= low && id <= high) 384 return (1); 385 } 386 387 return (0); 388 } 389 390 int 391 proc_lwp_range_valid(const char *set) 392 { 393 char *comma; 394 char *range = (char *)set; 395 id_t low, high; 396 int ret; 397 398 if (range == NULL || *range == '\0' || *range == ',') 399 return (-1); 400 401 while (range != NULL) { 402 comma = strchr(range, ','); 403 if (comma != NULL) 404 *comma = '\0'; 405 if ((ret = proc_lwp_get_range(range, &low, &high)) != 0) { 406 if (comma != NULL) 407 *comma = ','; 408 return (ret); 409 } 410 if (comma != NULL) { 411 *comma = ','; 412 range = comma + 1; 413 } else { 414 range = NULL; 415 } 416 } 417 418 return (0); 419 } 420 421 /* 422 * Walk all processes or LWPs in /proc and call func() for each. 423 * Omit system processes (like process-IDs 0, 2, and 3). 424 * Stop calling func() if it returns non 0 value and return it. 425 */ 426 int 427 proc_walk(proc_walk_f *func, void *arg, int flag) 428 { 429 DIR *procdir; 430 struct dirent *dirent; 431 char *errptr; 432 char pidstr[PATH_MAX]; 433 psinfo_t psinfo; 434 lwpsinfo_t *lwpsinfo; 435 prheader_t prheader; 436 void *buf; 437 char *ptr; 438 int bufsz; 439 id_t pid; 440 int fd, i; 441 int ret = 0; 442 boolean_t walk_sys = B_FALSE; 443 444 if ((flag & PR_WALK_INCLUDE_SYS) != 0) 445 walk_sys = B_TRUE; 446 flag &= ~PR_WALK_INCLUDE_SYS; 447 448 if (flag != PR_WALK_PROC && flag != PR_WALK_LWP) { 449 errno = EINVAL; 450 return (-1); 451 } 452 if ((procdir = opendir(procfs_path)) == NULL) 453 return (-1); 454 while (dirent = readdir(procdir)) { 455 if (dirent->d_name[0] == '.') /* skip . and .. */ 456 continue; 457 pid = (id_t)strtol(dirent->d_name, &errptr, 10); 458 if (errptr != NULL && *errptr != '\0') 459 continue; 460 /* PR_WALK_PROC case */ 461 (void) snprintf(pidstr, sizeof (pidstr), 462 "%s/%ld/psinfo", procfs_path, pid); 463 fd = open(pidstr, O_RDONLY); 464 if (fd < 0) 465 continue; 466 if (read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo) || 467 ((psinfo.pr_flag & SSYS) != 0 && !walk_sys)) { 468 (void) close(fd); 469 continue; 470 } 471 (void) close(fd); 472 if (flag == PR_WALK_PROC) { 473 if ((ret = func(&psinfo, &psinfo.pr_lwp, arg)) != 0) 474 break; 475 continue; 476 } 477 /* PR_WALK_LWP case */ 478 (void) snprintf(pidstr, sizeof (pidstr), 479 "%s/%ld/lpsinfo", procfs_path, pid); 480 fd = open(pidstr, O_RDONLY); 481 if (fd < 0) 482 continue; 483 if (read(fd, &prheader, sizeof (prheader)) != 484 sizeof (prheader)) { 485 (void) close(fd); 486 continue; 487 } 488 bufsz = prheader.pr_nent * prheader.pr_entsize; 489 if ((buf = malloc(bufsz)) == NULL) { 490 (void) close(fd); 491 ret = -1; 492 break; 493 } 494 ptr = buf; 495 if (pread(fd, buf, bufsz, sizeof (prheader)) != bufsz) { 496 free(buf); 497 (void) close(fd); 498 continue; 499 } 500 (void) close(fd); 501 for (i = 0; i < prheader.pr_nent; 502 i++, ptr += prheader.pr_entsize) { 503 /*LINTED ALIGNMENT*/ 504 lwpsinfo = (lwpsinfo_t *)ptr; 505 if ((ret = func(&psinfo, lwpsinfo, arg)) != 0) { 506 free(buf); 507 break; 508 } 509 } 510 free(buf); 511 } 512 (void) closedir(procdir); 513 return (ret); 514 } 515