summaryrefslogtreecommitdiff
path: root/usr/src/lib/libproc/common/proc_arg.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libproc/common/proc_arg.c
downloadillumos-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.c509
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);
+}