Home | History | Annotate | Download | only in common
      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