diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libproc/common/proc_arg.c | |
download | illumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libproc/common/proc_arg.c')
-rw-r--r-- | usr/src/lib/libproc/common/proc_arg.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/usr/src/lib/libproc/common/proc_arg.c b/usr/src/lib/libproc/common/proc_arg.c new file mode 100644 index 0000000000..2a79b016c2 --- /dev/null +++ b/usr/src/lib/libproc/common/proc_arg.c @@ -0,0 +1,509 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <libgen.h> +#include <limits.h> +#include <alloca.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <dirent.h> + +#include "libproc.h" + +static int +open_psinfo(const char *arg, int *perr) +{ + /* + * Allocate enough space for "/proc/" + arg + "/psinfo" + */ + char *path = alloca(strlen(arg) + 14); + + struct stat64 st; + int fd; + + if (strchr(arg, '/') == NULL) { + (void) strcpy(path, "/proc/"); + (void) strcat(path, arg); + } else + (void) strcpy(path, arg); + + (void) strcat(path, "/psinfo"); + + /* + * Attempt to open the psinfo file, and return the fd if we can + * confirm this is a regular file provided by /proc. + */ + if ((fd = open64(path, O_RDONLY)) >= 0) { + if (fstat64(fd, &st) != 0 || !S_ISREG(st.st_mode) || + strcmp(st.st_fstype, "proc") != 0) { + (void) close(fd); + fd = -1; + } + } else if (errno == EACCES || errno == EPERM) + *perr = G_PERM; + + return (fd); +} + +static int +open_core(const char *arg, int *perr) +{ +#ifdef _BIG_ENDIAN + uchar_t order = ELFDATA2MSB; +#else + uchar_t order = ELFDATA2LSB; +#endif + GElf_Ehdr ehdr; + int fd; + int is_noelf = -1; + + /* + * Attempt to open the core file, and return the fd if we can confirm + * this is an ELF file of type ET_CORE. + */ + if ((fd = open64(arg, O_RDONLY)) >= 0) { + if (read(fd, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) { + (void) close(fd); + fd = -1; + } else if ((is_noelf = memcmp(&ehdr.e_ident[EI_MAG0], ELFMAG, + SELFMAG)) != 0 || ehdr.e_type != ET_CORE) { + (void) close(fd); + fd = -1; + if (is_noelf == 0 && + ehdr.e_ident[EI_DATA] != order) + *perr = G_ISAINVAL; + } + } else if (errno == EACCES || errno == EPERM) + *perr = G_PERM; + + return (fd); +} + +/* + * Make the error message precisely match the type of arguments the caller + * wanted to process. This ensures that a tool which only accepts pids does + * not produce an error message saying "no such process or core file 'foo'". + */ +static int +open_error(int oflag) +{ + if ((oflag & PR_ARG_ANY) == PR_ARG_PIDS) + return (G_NOPROC); + + if ((oflag & PR_ARG_ANY) == PR_ARG_CORES) + return (G_NOCORE); + + return (G_NOPROCORCORE); +} + +static void * +proc_grab_common(const char *arg, const char *path, int oflag, int gflag, + int *perr, const char **lwps, psinfo_t *psp) +{ + psinfo_t psinfo; + char *core; + int fd; + char *slash; + struct ps_prochandle *Pr; + + *perr = 0; + if (lwps) + *lwps = NULL; + + if (lwps != NULL && (slash = strrchr(arg, '/')) != NULL) { + /* + * Check to see if the user has supplied an lwp range. First, + * try to grab it as a pid/lwp combo. + */ + *slash = '\0'; + if ((oflag & PR_ARG_PIDS) && + (fd = open_psinfo(arg, perr)) != -1) { + if (read(fd, &psinfo, + sizeof (psinfo_t)) == sizeof (psinfo_t)) { + (void) close(fd); + *lwps = slash + 1; + *slash = '/'; + if (proc_lwp_range_valid(*lwps) != 0) { + *perr = G_BADLWPS; + return (NULL); + } + if (psp) { + *psp = psinfo; + return (psp); + } else { + return (Pgrab(psinfo.pr_pid, gflag, + perr)); + } + } + (void) close(fd); + } + + /* + * Next, try grabbing it as a corefile. + */ + if ((oflag & PR_ARG_CORES) && + (fd = open_core(arg, perr)) != -1) { + *lwps = slash + 1; + *slash = '/'; + if (proc_lwp_range_valid(*lwps) != 0) { + *perr = G_BADLWPS; + return (NULL); + } + core = alloca(strlen(arg) + 1); + (void) strcpy(core, arg); + if ((Pr = Pfgrab_core(fd, path == NULL ? + dirname(core) : path, perr)) != NULL) { + if (psp) { + (void) memcpy(psp, Ppsinfo(Pr), + sizeof (psinfo_t)); + Prelease(Pr, 0); + return (psp); + } else { + return (Pr); + } + } + } + + *slash = '/'; + } + + if ((oflag & PR_ARG_PIDS) && (fd = open_psinfo(arg, perr)) != -1) { + if (read(fd, &psinfo, sizeof (psinfo_t)) == sizeof (psinfo_t)) { + (void) close(fd); + if (psp) { + *psp = psinfo; + return (psp); + } else { + return (Pgrab(psinfo.pr_pid, gflag, perr)); + } + } + /* + * If the read failed, the process may have gone away; + * we continue checking for core files or fail with G_NOPROC + */ + (void) close(fd); + } + + if ((oflag & PR_ARG_CORES) && (fd = open_core(arg, perr)) != -1) { + core = alloca(strlen(arg) + 1); + (void) strcpy(core, arg); + if ((Pr = Pfgrab_core(fd, path == NULL ? dirname(core) : path, + perr)) != NULL) { + if (psp) { + (void) memcpy(psp, Ppsinfo(Pr), + sizeof (psinfo_t)); + Prelease(Pr, 0); + return (psp); + } else { + return (Pr); + } + } + } + + /* + * We were unable to open the corefile. If we have no meaningful + * information, report the (ambiguous) error from open_error(). + */ + + if (*perr == 0) + *perr = open_error(oflag); + + return (NULL); +} + +struct ps_prochandle * +proc_arg_xgrab(const char *arg, const char *path, int oflag, int gflag, + int *perr, const char **lwps) +{ + return (proc_grab_common(arg, path, oflag, gflag, perr, lwps, NULL)); +} + +struct ps_prochandle * +proc_arg_grab(const char *arg, int oflag, int gflag, int *perr) +{ + return (proc_grab_common(arg, NULL, oflag, gflag, perr, NULL, NULL)); +} + +pid_t +proc_arg_psinfo(const char *arg, int oflag, psinfo_t *psp, int *perr) +{ + psinfo_t psinfo; + + if (psp == NULL) + psp = &psinfo; + + if (proc_grab_common(arg, NULL, oflag, 0, perr, NULL, psp) == NULL) + return (-1); + else + return (psp->pr_pid); +} + +pid_t +proc_arg_xpsinfo(const char *arg, int oflag, psinfo_t *psp, int *perr, + const char **lwps) +{ + psinfo_t psinfo; + + if (psp == NULL) + psp = &psinfo; + + if (proc_grab_common(arg, NULL, oflag, 0, perr, lwps, psp) == NULL) + return (-1); + else + return (psp->pr_pid); +} + +/* + * Convert psinfo_t.pr_psargs string into itself, replacing unprintable + * characters with space along the way. Stop on a null character. + */ +void +proc_unctrl_psinfo(psinfo_t *psp) +{ + char *s = &psp->pr_psargs[0]; + size_t n = PRARGSZ; + int c; + + while (n-- != 0 && (c = (*s & UCHAR_MAX)) != '\0') { + if (!isprint(c)) + c = ' '; + *s++ = (char)c; + } + + *s = '\0'; +} + +static int +proc_lwp_get_range(char *range, id_t *low, id_t *high) +{ + if (*range == '-') + *low = 0; + else + *low = (id_t)strtol(range, &range, 10); + + if (*range == '\0' || *range == ',') { + *high = *low; + return (0); + } + if (*range != '-') { + return (-1); + } + range++; + + if (*range == '\0') + *high = INT_MAX; + else + *high = (id_t)strtol(range, &range, 10); + + if (*range != '\0' && *range != ',') { + return (-1); + } + + if (*high < *low) { + id_t tmp = *high; + *high = *low; + *low = tmp; + } + + return (0); +} + +/* + * Determine if the specified lwpid is in the given set of lwpids. + * The set can include multiple lwpid ranges separated by commas + * and has the following syntax: + * + * lwp_range[,lwp_range]* + * + * where lwp_range is specifed as: + * + * -n lwpid <= n + * n-m n <= lwpid <= m + * n- lwpid >= n + * n lwpid == n + */ +int +proc_lwp_in_set(const char *set, lwpid_t lwpid) +{ + id_t low, high; + id_t id = (id_t)lwpid; + char *comma; + char *range = (char *)set; + + /* + * A NULL set indicates that all LWPs are valid. + */ + if (set == NULL) + return (1); + + while (range != NULL) { + comma = strchr(range, ','); + if (comma != NULL) + *comma = '\0'; + if (proc_lwp_get_range(range, &low, &high) != 0) { + if (comma != NULL) + *comma = ','; + return (0); + } + if (comma != NULL) { + *comma = ','; + range = comma + 1; + } else { + range = NULL; + } + if (id >= low && id <= high) + return (1); + } + + return (0); +} + +int +proc_lwp_range_valid(const char *set) +{ + char *comma; + char *range = (char *)set; + id_t low, high; + int ret; + + if (range == NULL || *range == '\0' || *range == ',') + return (-1); + + while (range != NULL) { + comma = strchr(range, ','); + if (comma != NULL) + *comma = '\0'; + if ((ret = proc_lwp_get_range(range, &low, &high)) != 0) { + if (comma != NULL) + *comma = ','; + return (ret); + } + if (comma != NULL) { + *comma = ','; + range = comma + 1; + } else { + range = NULL; + } + } + + return (0); +} + +/* + * Walk all processes or LWPs in /proc and call func() for each. + * Stop calling func() if it returns non 0 value and return it. + */ +int +proc_walk(proc_walk_f *func, void *arg, int flag) +{ + DIR *procdir; + struct dirent *dirent; + char *errptr; + char pidstr[80]; + psinfo_t psinfo; + lwpsinfo_t *lwpsinfo; + prheader_t prheader; + void *buf; + char *ptr; + int bufsz; + id_t pid; + int fd, i; + int ret = 0; + + if (flag != PR_WALK_PROC && flag != PR_WALK_LWP) { + errno = EINVAL; + return (-1); + } + if ((procdir = opendir("/proc")) == NULL) + return (-1); + while (dirent = readdir(procdir)) { + if (dirent->d_name[0] == '.') /* skip . and .. */ + continue; + pid = (id_t)strtol(dirent->d_name, &errptr, 10); + if (errptr != NULL && *errptr != '\0') + continue; + /* PR_WALK_PROC case */ + (void) snprintf(pidstr, sizeof (pidstr), + "/proc/%ld/psinfo", pid); + fd = open(pidstr, O_RDONLY); + if (fd < 0) + continue; + if (read(fd, &psinfo, sizeof (psinfo)) != sizeof (psinfo)) { + (void) close(fd); + continue; + } + (void) close(fd); + if (flag == PR_WALK_PROC) { + if ((ret = func(&psinfo, &psinfo.pr_lwp, arg)) != 0) + break; + continue; + } + /* PR_WALK_LWP case */ + (void) snprintf(pidstr, sizeof (pidstr), + "/proc/%ld/lpsinfo", pid); + fd = open(pidstr, O_RDONLY); + if (fd < 0) + continue; + if (read(fd, &prheader, sizeof (prheader)) != + sizeof (prheader)) { + (void) close(fd); + continue; + } + bufsz = prheader.pr_nent * prheader.pr_entsize; + if ((buf = malloc(bufsz)) == NULL) { + (void) close(fd); + ret = -1; + break; + } + ptr = buf; + if (pread(fd, buf, bufsz, sizeof (prheader)) != bufsz) { + free(buf); + (void) close(fd); + continue; + } + (void) close(fd); + for (i = 0; i < prheader.pr_nent; + i++, ptr += prheader.pr_entsize) { + /*LINTED ALIGNMENT*/ + lwpsinfo = (lwpsinfo_t *)ptr; + if ((ret = func(&psinfo, lwpsinfo, arg)) != 0) { + free(buf); + break; + } + } + free(buf); + } + (void) closedir(procdir); + return (ret); +} |