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