xref: /illumos-gate/usr/src/lib/libproc/common/proc_arg.c (revision b51e021de072d76292addb44467f777617c6a8c1)
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   * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23   * Use is subject to license terms.
24   */
25  
26  #pragma ident	"%Z%%M%	%I%	%E% SMI"
27  
28  #include <sys/types.h>
29  #include <sys/stat.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   * Stop calling func() if it returns non 0 value and return it.
426   */
427  int
428  proc_walk(proc_walk_f *func, void *arg, int flag)
429  {
430  	DIR *procdir;
431  	struct dirent *dirent;
432  	char *errptr;
433  	char pidstr[PATH_MAX];
434  	psinfo_t psinfo;
435  	lwpsinfo_t *lwpsinfo;
436  	prheader_t prheader;
437  	void *buf;
438  	char *ptr;
439  	int bufsz;
440  	id_t pid;
441  	int fd, i;
442  	int ret = 0;
443  
444  	if (flag != PR_WALK_PROC && flag != PR_WALK_LWP) {
445  		errno = EINVAL;
446  		return (-1);
447  	}
448  	if ((procdir = opendir(procfs_path)) == NULL)
449  		return (-1);
450  	while (dirent = readdir(procdir)) {
451  		if (dirent->d_name[0] == '.')	/* skip . and .. */
452  			continue;
453  		pid = (id_t)strtol(dirent->d_name, &errptr, 10);
454  		if (errptr != NULL && *errptr != '\0')
455  			continue;
456  		/* PR_WALK_PROC case */
457  		(void) snprintf(pidstr, sizeof (pidstr),
458  		    "%s/%ld/psinfo", procfs_path, pid);
459  		fd = open(pidstr, O_RDONLY);
460  		if (fd < 0)
461  			continue;
462  		if (read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) {
463  			(void) close(fd);
464  			continue;
465  		}
466  		(void) close(fd);
467  		if (flag == PR_WALK_PROC) {
468  			if ((ret = func(&psinfo, &psinfo.pr_lwp, arg)) != 0)
469  				break;
470  			continue;
471  		}
472  		/* PR_WALK_LWP case */
473  		(void) snprintf(pidstr, sizeof (pidstr),
474  		    "%s/%ld/lpsinfo", procfs_path, pid);
475  		fd = open(pidstr, O_RDONLY);
476  		if (fd < 0)
477  			continue;
478  		if (read(fd, &prheader, sizeof (prheader)) !=
479  		    sizeof (prheader)) {
480  			(void) close(fd);
481  			continue;
482  		}
483  		bufsz = prheader.pr_nent * prheader.pr_entsize;
484  		if ((buf = malloc(bufsz)) == NULL) {
485  			(void) close(fd);
486  			ret = -1;
487  			break;
488  		}
489  		ptr = buf;
490  		if (pread(fd, buf, bufsz, sizeof (prheader)) != bufsz) {
491  			free(buf);
492  			(void) close(fd);
493  			continue;
494  		}
495  		(void) close(fd);
496  		for (i = 0; i < prheader.pr_nent;
497  		    i++, ptr += prheader.pr_entsize) {
498  			/*LINTED ALIGNMENT*/
499  			lwpsinfo = (lwpsinfo_t *)ptr;
500  			if ((ret = func(&psinfo, lwpsinfo, arg)) != 0) {
501  				free(buf);
502  				break;
503  			}
504  		}
505  		free(buf);
506  	}
507  	(void) closedir(procdir);
508  	return (ret);
509  }
510