xref: /illumos-gate/usr/src/lib/libproc/common/proc_arg.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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