From ab9c85a11d43c276ac698fc81981ded4007e5a4f Mon Sep 17 00:00:00 2001 From: Tamas TEVESZ Date: Mon, 22 Mar 2010 15:48:47 +0100 Subject: [PATCH] Add the BSD version of GetCommandForPid() - tested on Net, Free, Open and DragonFly - fix up the linux version (terminate argv with a null ptr) - add error handling to file ops in the linux version - add a stub version for platforms not supported --- src/osdep/bsd.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++ src/osdep/linux.c | 33 ++++++--- src/osdep/stub.c | 11 +++ src/osdep/test.c | 49 +++++++++++++ 4 files changed, 255 insertions(+), 9 deletions(-) create mode 100644 src/osdep/bsd.c create mode 100644 src/osdep/stub.c create mode 100644 src/osdep/test.c diff --git a/src/osdep/bsd.c b/src/osdep/bsd.c new file mode 100644 index 00000000..0057adc8 --- /dev/null +++ b/src/osdep/bsd.c @@ -0,0 +1,171 @@ + +#if defined( FREEBSD ) || defined( DRAGONFLYBSD ) +# include +#else /* OPENBSD || NETBSD */ +# include +#endif +#include + +#include + +#if defined( OPENBSD ) +# include +# include /* _POSIX2_LINE_MAX */ +#endif + +#include +#include + +#if defined( OPENBSD ) +# include +#endif + +#include + +#include + +#include "../wconfig.h" + +/* + * copy argc and argv for an existing process identified by `pid' + * into suitable storage given in ***argv and *argc. + * + * subsequent calls use the same static area for argv and argc. + * + * returns 0 for failure, in which case argc := 0 and argv := NULL + * returns 1 for success + */ +/* + * NetBSD, FreeBSD and DragonFlyBSD supply the necessary information via + * sysctl(3). The result is a plain simple flat octet stream and its length. + * The octet stream represents argv, with members separated by a null character. + * The argv array returned by GetCommandForPid() consists of pointers into this + * stream (which is stored in a static array, `args'). Net and Free/DFly only + * differ slightly in the MIB vector given to sysctl(3). Free and DFly are + * identical. + * + * OpenBSD supplies the necessary informationvia kvm(3) in the form of a real + * argv array. This array is flattened to be in the same way as Net/Free/DFly + * returns the arguments in the first place. This is done in order for the + * storage (`args') to easily be made static, which means some memory bytes + * are sacrificed to save hoops of memory management. + */ +Bool GetCommandForPid(int pid, char ***argv, int *argc) +{ + /* + * it just returns failure if the sysctl calls fail; since there's + * apparently no harm done to the caller because of this, it seems + * more user-friendly than to bomb out. + */ + int j, mib[4]; + unsigned int i; + size_t count; + static char *args = NULL; + static int argmax = 0; +#if defined( OPENBSD ) + char kvmerr[_POSIX2_LINE_MAX]; /* for kvm*() error reporting */ + int procs; /* kvm_getprocs() */ + kvm_t *kd; + struct kinfo_proc *kp; + char **nargv; /* kvm_getarg() */ +#endif + + *argv = NULL; + *argc = 0; + + /* the system-wide limit */ + if (argmax == 0) { /* it hopefully doesn't change at runtime *g* */ + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + mib[2] = 0; + mib[4] = 0; + + count = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &count, NULL, 0) == -1) + return False; + } + + /* if argmax is still 0, something went very seriously wrong */ + assert( argmax > 0); + + /* space for args; no need to free before returning even on errors */ + if (args == NULL) + args = (char *)wmalloc(argmax); + +#if defined( OPENBSD ) + /* kvm descriptor */ + if ((kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, kvmerr)) == NULL) + return False; + + procs = 0; + /* the process we are interested in */ + if ((kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &procs)) == NULL || procs == 0) + /* if kvm_getprocs() bombs out or does not find the process */ + return False; + + /* get it's argv */ + if ((nargv = kvm_getargv(kd, kp, 0)) == NULL) + return False; + + /* flatten nargv into args */ + count = 0; + memset(args, 0, argmax); + /* + * must have this much free space in `args' in order for the current + * iteration not to overflow it: we are at `count', and will append + * the next ((*argc)+1) arg and a null (+1) + * technically, overflow (or truncation, which isn't handled) can not + * happen (should not, at least). + */ + #define ARGSPACE ( count + strlen(nargv[ (*argc) + 1 ] ) + 1 ) + while (nargv[*argc] && ARGSPACE < argmax ) { + memcpy(args + count, nargv[*argc], strlen(nargv[*argc])); + count += strlen(nargv[*argc]) + 1; + (*argc)++; + } + #undef ARGSPACE + /* by now *argc is correct as a byproduct */ + + kvm_close(kd); +#else /* FREEBSD || NETBSD || DRAGONFLYBSD */ + + mib[0] = CTL_KERN; +#if defined( NETBSD ) + mib[1] = KERN_PROC_ARGS; + mib[2] = pid; + mib[3] = KERN_PROC_ARGV; +#elif defined( FREEBSD ) || defined( DRAGONFLYBSD ) + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_ARGS; + mib[3] = pid; +#endif + + count = argmax; + /* canary */ + *args = 0; + if (sysctl(mib, 4, args, &count, NULL, 0) == -1 || *args == 0) + return False; + + /* args is a flattened series of null-terminated strings */ + for (i = 0; i < count; i++) + if (args[i] == '\0') + (*argc)++; +#endif + + *argv = (char **)wmalloc(sizeof(char *) * (*argc + 1 /* term. null ptr */)); + (*argv)[0] = args; + + /* go through args, set argv[$next] to the beginning of each string */ + for (i = 0, j = 1; i < count; i++) { + if (args[i] != '\0') + continue; + if (i < count - 1) + (*argv)[j++] = &args[i + 1]; + if (j == *argc) + break; + } + + /* the list of arguments must be terminated by a null pointer */ + (*argv)[j] = NULL; + return True; +} diff --git a/src/osdep/linux.c b/src/osdep/linux.c index 9f87a3cd..a754bb6b 100644 --- a/src/osdep/linux.c +++ b/src/osdep/linux.c @@ -17,7 +17,9 @@ * copy argc and argv for an existing process identified by `pid' * into suitable storage given in ***argv and *argc. * - * returns 0 for failure, in which case argc := 0 and arv := NULL + * subsequent calls use the same static area for argv and argc. + * + * returns 0 for failure, in which case argc := 0 and argv := NULL * returns 1 for success */ Bool GetCommandForPid(int pid, char ***argv, int *argc) @@ -31,16 +33,27 @@ Bool GetCommandForPid(int pid, char ***argv, int *argc) /* cmdline is a flattened series of null-terminated strings */ snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); - if ((fd = open(buf, O_RDONLY)) == -1) - return False; - - /* XXX: read/close errors */ - if ((count = read(fd, buf, sizeof(buf))) == -1) { - close(fd); + while (1) { + if ((fd = open(buf, O_RDONLY)) != -1) + break; + if (errno == EINTR) + continue; return False; } - close(fd); + while (1) { + if ((count = read(fd, buf, sizeof(buf))) != -1) + break; + if (errno == EINTR) + continue; + return False; + } + + do { + close(fd); + } while (errno == EINTR); + + /* count args */ for (i = 0; i < count; i++) if (buf[i] == '\0') (*argc)++; @@ -48,7 +61,7 @@ Bool GetCommandForPid(int pid, char ***argv, int *argc) if (*argc == 0) return False; - *argv = (char **)wmalloc(sizeof(char *) * *argc); + *argv = (char **)wmalloc(sizeof(char *) * (*argc + 1 /* term. null ptr */)); (*argv)[0] = buf; /* go through buf, set argv[$next] to the beginning of each string */ @@ -61,5 +74,7 @@ Bool GetCommandForPid(int pid, char ***argv, int *argc) break; } + /* the list of arguments must be terminated by a null pointer */ + (*argv)[j] = NULL; return True; } diff --git a/src/osdep/stub.c b/src/osdep/stub.c new file mode 100644 index 00000000..38b98203 --- /dev/null +++ b/src/osdep/stub.c @@ -0,0 +1,11 @@ + +#include +#include "../wconfig.h" + +Bool GetCommandForPid(int pid, char ***argv, int *argc) +{ + *argv = NULL; + *argc = 0; + + return False; +} diff --git a/src/osdep/test.c b/src/osdep/test.c new file mode 100644 index 00000000..3bba44ec --- /dev/null +++ b/src/osdep/test.c @@ -0,0 +1,49 @@ + +#include +#include + +#include + +/* + * gcc -D{$os} -I ../../WINGs ${includes...} -Wall -Wextra -g -ggdb3 -c -o ${plat}.o ${plat}.c + * gcc -I ../../WINGs ${includes...} -g -ggdb3 -c -o test.o test.c + * gcc -g -ggdb3 -o test ${plat}.o ../../WINGs/.libs/libWUtil.a test.o + * + * $ ./test 1 2 'foo bar' "`date`" `date` + * arg[0] = [./test] + * arg[1] = [1] + * arg[2] = [2] + * arg[3] = [foo bar] + * arg[4] = [Sat Mar 20 18:36:22 CET 2010] + * arg[5] = [Sat] + * arg[6] = [Mar] + * arg[7] = [20] + * arg[8] = [18:36:22] + * arg[9] = [CET] + * arg[10] = [2010] + * $ + */ + +Bool GetCommandForPid(int pid, char ***argv, int *argc); +extern char *__progname; + +int main(int argc, char **argv) { + + char **nargv; + int i, nargc; + + if (argc < 2) { + printf("Usage: %s arg arg arg ...\n", __progname); + return 0; + } + + if (GetCommandForPid(getpid(), &nargv, &nargc) == False) { + printf("GetCommandForPid() failed\n"); + } else { + printf("nargv = %d\n", nargc); + for(i = 0; i < nargc; i++) + printf("arg[%d] = [%s]\n", i, nargv[i]); + } + + return 0; +}