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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 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 = alloca(strlen(arg) + 1); 184 (void) strcpy(core, arg); 185 if ((Pr = Pfgrab_core(fd, path == NULL ? 186 dirname(core) : path, perr)) != NULL) { 187 if (psp) { 188 (void) memcpy(psp, Ppsinfo(Pr), 189 sizeof (psinfo_t)); 190 Prelease(Pr, 0); 191 return (psp); 192 } else { 193 return (Pr); 194 } 195 } 196 } 197 198 *slash = '/'; 199 } 200 201 if ((oflag & PR_ARG_PIDS) && (fd = open_psinfo(arg, perr)) != -1) { 202 if (read(fd, &psinfo, sizeof (psinfo_t)) == sizeof (psinfo_t)) { 203 (void) close(fd); 204 if (psp) { 205 *psp = psinfo; 206 return (psp); 207 } else { 208 return (Pgrab(psinfo.pr_pid, gflag, perr)); 209 } 210 } 211 /* 212 * If the read failed, the process may have gone away; 213 * we continue checking for core files or fail with G_NOPROC 214 */ 215 (void) close(fd); 216 } 217 218 if ((oflag & PR_ARG_CORES) && (fd = open_core(arg, perr)) != -1) { 219 core = alloca(strlen(arg) + 1); 220 (void) strcpy(core, arg); 221 if ((Pr = Pfgrab_core(fd, path == NULL ? dirname(core) : path, 222 perr)) != NULL) { 223 if (psp) { 224 (void) memcpy(psp, Ppsinfo(Pr), 225 sizeof (psinfo_t)); 226 Prelease(Pr, 0); 227 return (psp); 228 } else { 229 return (Pr); 230 } 231 } 232 } 233 234 /* 235 * We were unable to open the corefile. If we have no meaningful 236 * information, report the (ambiguous) error from open_error(). 237 */ 238 239 if (*perr == 0) 240 *perr = open_error(oflag); 241 242 return (NULL); 243 } 244 245 struct ps_prochandle * 246 proc_arg_xgrab(const char *arg, const char *path, int oflag, int gflag, 247 int *perr, const char **lwps) 248 { 249 return (proc_grab_common(arg, path, oflag, gflag, perr, lwps, NULL)); 250 } 251 252 struct ps_prochandle * 253 proc_arg_grab(const char *arg, int oflag, int gflag, int *perr) 254 { 255 return (proc_grab_common(arg, NULL, oflag, gflag, perr, NULL, NULL)); 256 } 257 258 pid_t 259 proc_arg_psinfo(const char *arg, int oflag, psinfo_t *psp, int *perr) 260 { 261 psinfo_t psinfo; 262 263 if (psp == NULL) 264 psp = &psinfo; 265 266 if (proc_grab_common(arg, NULL, oflag, 0, perr, NULL, psp) == NULL) 267 return (-1); 268 else 269 return (psp->pr_pid); 270 } 271 272 pid_t 273 proc_arg_xpsinfo(const char *arg, int oflag, psinfo_t *psp, int *perr, 274 const char **lwps) 275 { 276 psinfo_t psinfo; 277 278 if (psp == NULL) 279 psp = &psinfo; 280 281 if (proc_grab_common(arg, NULL, oflag, 0, perr, lwps, psp) == NULL) 282 return (-1); 283 else 284 return (psp->pr_pid); 285 } 286 287 /* 288 * Convert psinfo_t.pr_psargs string into itself, replacing unprintable 289 * characters with space along the way. Stop on a null character. 290 */ 291 void 292 proc_unctrl_psinfo(psinfo_t *psp) 293 { 294 char *s = &psp->pr_psargs[0]; 295 size_t n = PRARGSZ; 296 int c; 297 298 while (n-- != 0 && (c = (*s & UCHAR_MAX)) != '\0') { 299 if (!isprint(c)) 300 c = ' '; 301 *s++ = (char)c; 302 } 303 304 *s = '\0'; 305 } 306 307 static int 308 proc_lwp_get_range(char *range, id_t *low, id_t *high) 309 { 310 if (*range == '-') 311 *low = 0; 312 else 313 *low = (id_t)strtol(range, &range, 10); 314 315 if (*range == '\0' || *range == ',') { 316 *high = *low; 317 return (0); 318 } 319 if (*range != '-') { 320 return (-1); 321 } 322 range++; 323 324 if (*range == '\0') 325 *high = INT_MAX; 326 else 327 *high = (id_t)strtol(range, &range, 10); 328 329 if (*range != '\0' && *range != ',') { 330 return (-1); 331 } 332 333 if (*high < *low) { 334 id_t tmp = *high; 335 *high = *low; 336 *low = tmp; 337 } 338 339 return (0); 340 } 341 342 /* 343 * Determine if the specified lwpid is in the given set of lwpids. 344 * The set can include multiple lwpid ranges separated by commas 345 * and has the following syntax: 346 * 347 * lwp_range[,lwp_range]* 348 * 349 * where lwp_range is specifed as: 350 * 351 * -n lwpid <= n 352 * n-m n <= lwpid <= m 353 * n- lwpid >= n 354 * n lwpid == n 355 */ 356 int 357 proc_lwp_in_set(const char *set, lwpid_t lwpid) 358 { 359 id_t low, high; 360 id_t id = (id_t)lwpid; 361 char *comma; 362 char *range = (char *)set; 363 364 /* 365 * A NULL set indicates that all LWPs are valid. 366 */ 367 if (set == NULL) 368 return (1); 369 370 while (range != NULL) { 371 comma = strchr(range, ','); 372 if (comma != NULL) 373 *comma = '\0'; 374 if (proc_lwp_get_range(range, &low, &high) != 0) { 375 if (comma != NULL) 376 *comma = ','; 377 return (0); 378 } 379 if (comma != NULL) { 380 *comma = ','; 381 range = comma + 1; 382 } else { 383 range = NULL; 384 } 385 if (id >= low && id <= high) 386 return (1); 387 } 388 389 return (0); 390 } 391 392 int 393 proc_lwp_range_valid(const char *set) 394 { 395 char *comma; 396 char *range = (char *)set; 397 id_t low, high; 398 int ret; 399 400 if (range == NULL || *range == '\0' || *range == ',') 401 return (-1); 402 403 while (range != NULL) { 404 comma = strchr(range, ','); 405 if (comma != NULL) 406 *comma = '\0'; 407 if ((ret = proc_lwp_get_range(range, &low, &high)) != 0) { 408 if (comma != NULL) 409 *comma = ','; 410 return (ret); 411 } 412 if (comma != NULL) { 413 *comma = ','; 414 range = comma + 1; 415 } else { 416 range = NULL; 417 } 418 } 419 420 return (0); 421 } 422 423 /* 424 * Walk all processes or LWPs in /proc and call func() for each. 425 * Omit system processes (like process-IDs 0, 2, and 3). 426 * Stop calling func() if it returns non 0 value and return it. 427 */ 428 int 429 proc_walk(proc_walk_f *func, void *arg, int flag) 430 { 431 DIR *procdir; 432 struct dirent *dirent; 433 char *errptr; 434 char pidstr[PATH_MAX]; 435 psinfo_t psinfo; 436 lwpsinfo_t *lwpsinfo; 437 prheader_t prheader; 438 void *buf; 439 char *ptr; 440 int bufsz; 441 id_t pid; 442 int fd, i; 443 int ret = 0; 444 445 if (flag != PR_WALK_PROC && flag != PR_WALK_LWP) { 446 errno = EINVAL; 447 return (-1); 448 } 449 if ((procdir = opendir(procfs_path)) == NULL) 450 return (-1); 451 while (dirent = readdir(procdir)) { 452 if (dirent->d_name[0] == '.') /* skip . and .. */ 453 continue; 454 pid = (id_t)strtol(dirent->d_name, &errptr, 10); 455 if (errptr != NULL && *errptr != '\0') 456 continue; 457 /* PR_WALK_PROC case */ 458 (void) snprintf(pidstr, sizeof (pidstr), 459 "%s/%ld/psinfo", procfs_path, pid); 460 fd = open(pidstr, O_RDONLY); 461 if (fd < 0) 462 continue; 463 if (read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo) || 464 (psinfo.pr_flag & SSYS)) { 465 (void) close(fd); 466 continue; 467 } 468 (void) close(fd); 469 if (flag == PR_WALK_PROC) { 470 if ((ret = func(&psinfo, &psinfo.pr_lwp, arg)) != 0) 471 break; 472 continue; 473 } 474 /* PR_WALK_LWP case */ 475 (void) snprintf(pidstr, sizeof (pidstr), 476 "%s/%ld/lpsinfo", procfs_path, pid); 477 fd = open(pidstr, O_RDONLY); 478 if (fd < 0) 479 continue; 480 if (read(fd, &prheader, sizeof (prheader)) != 481 sizeof (prheader)) { 482 (void) close(fd); 483 continue; 484 } 485 bufsz = prheader.pr_nent * prheader.pr_entsize; 486 if ((buf = malloc(bufsz)) == NULL) { 487 (void) close(fd); 488 ret = -1; 489 break; 490 } 491 ptr = buf; 492 if (pread(fd, buf, bufsz, sizeof (prheader)) != bufsz) { 493 free(buf); 494 (void) close(fd); 495 continue; 496 } 497 (void) close(fd); 498 for (i = 0; i < prheader.pr_nent; 499 i++, ptr += prheader.pr_entsize) { 500 /*LINTED ALIGNMENT*/ 501 lwpsinfo = (lwpsinfo_t *)ptr; 502 if ((ret = func(&psinfo, lwpsinfo, arg)) != 0) { 503 free(buf); 504 break; 505 } 506 } 507 free(buf); 508 } 509 (void) closedir(procdir); 510 return (ret); 511 } 512