proc_arg.c revision 2712:f74a135872bc
1284990Scy/*
2284990Scy * CDDL HEADER START
3284990Scy *
4284990Scy * The contents of this file are subject to the terms of the
5284990Scy * Common Development and Distribution License (the "License").
6284990Scy * You may not use this file except in compliance with the License.
7284990Scy *
8284990Scy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9284990Scy * or http://www.opensolaris.org/os/licensing.
10284990Scy * See the License for the specific language governing permissions
11284990Scy * and limitations under the License.
12284990Scy *
13284990Scy * When distributing Covered Code, include this CDDL HEADER in each
14284990Scy * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15284990Scy * If applicable, add the following below this CDDL HEADER, with the
16284990Scy * fields enclosed by brackets "[]" replaced with your own identifying
17284990Scy * information: Portions Copyright [yyyy] [name of copyright owner]
18284990Scy *
19284990Scy * CDDL HEADER END
20284990Scy */
21284990Scy/*
22284990Scy * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23284990Scy * Use is subject to license terms.
24284990Scy */
25290000Sglebius
26290000Sglebius#pragma ident	"%Z%%M%	%I%	%E% SMI"
27290000Sglebius
28290000Sglebius#include <sys/types.h>
29284990Scy#include <sys/stat.h>
30284990Scy
31284990Scy#include <libgen.h>
32284990Scy#include <limits.h>
33284990Scy#include <alloca.h>
34284990Scy#include <unistd.h>
35284990Scy#include <string.h>
36284990Scy#include <fcntl.h>
37284990Scy#include <ctype.h>
38284990Scy#include <errno.h>
39290000Sglebius#include <dirent.h>
40290000Sglebius
41284990Scy#include "Pcontrol.h"
42284990Scy
43284990Scystatic int
44284990Scyopen_psinfo(const char *arg, int *perr)
45284990Scy{
46290000Sglebius	/*
47284990Scy	 * Allocate enough space for procfs_path + arg + "/psinfo"
48284990Scy	 */
49284990Scy	char *path = alloca(strlen(arg) + strlen(procfs_path) + 9);
50284990Scy
51284990Scy	struct stat64 st;
52284990Scy	int fd;
53284990Scy
54284990Scy	if (strchr(arg, '/') == NULL) {
55290000Sglebius		(void) strcpy(path, procfs_path);
56290000Sglebius		(void) strcat(path, "/");
57284990Scy		(void) strcat(path, arg);
58284990Scy	} else
59284990Scy		(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
79static int
80open_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 */
118static int
119open_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
130static void *
131proc_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
245struct ps_prochandle *
246proc_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
252struct ps_prochandle *
253proc_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
258pid_t
259proc_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
272pid_t
273proc_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 */
291void
292proc_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
307static int
308proc_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 */
356int
357proc_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
392int
393proc_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 */
427int
428proc_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