From b6bf18f6753d470d408b0bae6d06fcf4d6b02baf Mon Sep 17 00:00:00 2001 From: "Elizabeth J. Myers" Date: Wed, 4 Apr 2012 21:27:55 -0500 Subject: [PATCH] ext: add proctitle setting functions to set process titles portably --- configure | 39 ++- configure.ac | 17 +- src/libmowgli/ext/Makefile | 6 +- src/libmowgli/ext/proctitle.c | 350 +++++++++++++++++++++++++++ src/libmowgli/ext/proctitle.h | 25 ++ src/libmowgli/mowgli.h | 1 + src/libmowgli/platform/autoconf.h.in | 13 + 7 files changed, 445 insertions(+), 6 deletions(-) create mode 100644 src/libmowgli/ext/proctitle.c create mode 100644 src/libmowgli/ext/proctitle.h diff --git a/configure b/configure index 47354f7..645a6fa 100755 --- a/configure +++ b/configure @@ -3769,7 +3769,7 @@ fi done -for ac_header in poll.h winsock2.h sys/epoll.h sys/select.h +for ac_header in poll.h winsock2.h sys/epoll.h sys/select.h sys/pstat.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -3782,7 +3782,7 @@ fi done -for ac_func in fcntl kqueue mmap select dispatch_block port_create +for ac_func in fcntl kqueue mmap select dispatch_block port_create setproctitle pstat do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -3795,6 +3795,41 @@ fi done +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PS_STRINGS" >&5 +$as_echo_n "checking for PS_STRINGS... " >&6; } +if ${pgac_cv_var_PS_STRINGS+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include + +int +main () +{ +PS_STRINGS->ps_nargvstr = 1; +PS_STRINGS->ps_argvstr = "foo"; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + pgac_cv_var_PS_STRINGS=yes +else + pgac_cv_var_PS_STRINGS=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_var_PS_STRINGS" >&5 +$as_echo "$pgac_cv_var_PS_STRINGS" >&6; } +if test "$pgac_cv_var_PS_STRINGS" = yes ; then + +$as_echo "#define HAVE_PS_STRINGS /**/" >>confdefs.h + +fi + # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 diff --git a/configure.ac b/configure.ac index 730a017..93e72e2 100644 --- a/configure.ac +++ b/configure.ac @@ -24,8 +24,21 @@ AS_IF([test "x$GCC" = "xyes"], [ CFLAGS="$CFLAGS $MORECFLAGS" ]) -AC_CHECK_HEADERS([poll.h winsock2.h sys/epoll.h sys/select.h]) -AC_CHECK_FUNCS([fcntl kqueue mmap select dispatch_block port_create]) +AC_CHECK_HEADERS([poll.h winsock2.h sys/epoll.h sys/select.h sys/pstat.h]) +AC_CHECK_FUNCS([fcntl kqueue mmap select dispatch_block port_create setproctitle pstat]) + +AC_CACHE_CHECK([for PS_STRINGS], [pgac_cv_var_PS_STRINGS], +[AC_TRY_LINK( +[#include +#include +], +[PS_STRINGS->ps_nargvstr = 1; +PS_STRINGS->ps_argvstr = "foo";], +[pgac_cv_var_PS_STRINGS=yes], +[pgac_cv_var_PS_STRINGS=no])]) +if test "$pgac_cv_var_PS_STRINGS" = yes ; then + AC_DEFINE([HAVE_PS_STRINGS], [], [Define to 1 if the PS_STRINGS struct exists on your platform (likely no).]) +fi AC_PATH_PROG(AR, ar) AC_PATH_PROG(RANLIB, ranlib) diff --git a/src/libmowgli/ext/Makefile b/src/libmowgli/ext/Makefile index a6edb4e..d7466b6 100644 --- a/src/libmowgli/ext/Makefile +++ b/src/libmowgli/ext/Makefile @@ -7,13 +7,15 @@ SRCS = confparse.c \ error_backtrace.c \ getopt_long.c \ global_storage.c \ - program_opts.c + program_opts.c \ + proctitle.c INCLUDES = confparse.h \ error_backtrace.h \ getopt_long.h \ global_storage.h \ - program_opts.h + program_opts.h \ + proctitle.h include ../../../buildsys.mk diff --git a/src/libmowgli/ext/proctitle.c b/src/libmowgli/ext/proctitle.c new file mode 100644 index 0000000..128416e --- /dev/null +++ b/src/libmowgli/ext/proctitle.c @@ -0,0 +1,350 @@ +/* + * Code has been calqued from PostgreSQL 9.1 by Elizabeth J. Myers. + * Below is their copyright header + */ + +/*-------------------------------------------------------------------- + * ps_status.c + * + * Routines to support changing the ps display of PostgreSQL backends + * to contain some useful information. Mechanism differs wildly across + * platforms. + * + * src/backend/utils/misc/ps_status.c + * + * Copyright (c) 2000-2011, PostgreSQL Global Development Group + * various details abducted from various places + *-------------------------------------------------------------------- + */ + +#include "mowgli.h" + +#ifdef HAVE_SYS_PSTAT_H +#include /* for HP-UX */ +#endif +#ifdef HAVE_PS_STRINGS +#include /* for old BSD */ +#include +#endif +#if defined(__darwin__) +#include +#endif + +extern char **environ; +bool mowgli_proctitle_update = true; + + +/* + * Alternative ways of updating ps display: + * + * MOWGLI_SETPROC_USE_SETPROCTITLE + * use the function setproctitle(const char *, ...) + * (newer BSD systems) + * MOWGLI_SETPROC_USE_PSTAT + * use the pstat(PSTAT_SETCMD, ) + * (HPUX) + * MOWGLI_SETPROC_USE_PS_STRINGS + * assign PS_STRINGS->ps_argvstr = "string" + * (some BSD systems) + * MOWGLI_SETPROC_USE_CHANGE_ARGV + * assign argv[0] = "string" + * (some other BSD systems) + * MOWGLI_SETPROC_USE_CLOBBER_ARGV + * write over the argv and environment area + * (Linux and most SysV-like systems) + * MOWGLI_SETPROC_USE_WIN32 + * push the string out as the name of a Windows event + * MOWGLI_SETPROC_USE_NONE + * don't update ps display + * (This is the default, as it is safest.) + */ +#if defined(HAVE_SETPROCTITLE) +#define MOWGLI_SETPROC_USE_SETPROCTITLE +#elif defined(HAVE_PSTAT) && defined(PSTAT_SETCMD) +#define MOWGLI_SETPROC_USE_PSTAT +#elif defined(HAVE_PS_STRINGS) +#define MOWGLI_SETPROC_USE_PS_STRINGS +#elif (defined(BSD) || defined(__bsdi__) || defined(__hurd__)) && !defined(__darwin__) +#define MOWGLI_SETPROC_USE_CHANGE_ARGV +#elif defined(__linux__) || defined(_AIX) || defined(__sgi) || (defined(sun) && !defined(BSD)) || defined(ultrix) || defined(__ksr__) || defined(__osf__) || defined(__svr4__) || defined(__svr5__) || defined(__darwin__) +#define MOWGLI_SETPROC_USE_CLOBBER_ARGV +#elif defined(WIN32) +#define MOWGLI_SETPROC_USE_WIN32 +#else +#define MOWGLI_SETPROC_USE_NONE +#endif + + +/* Different systems want the buffer padded differently */ +#if defined(_AIX) || defined(__linux__) || defined(__svr4__) || defined(__darwin__) +#define PS_PADDING '\0' +#else +#define PS_PADDING ' ' +#endif + + +#ifndef MOWGLI_SETPROC_USE_CLOBBER_ARGV +/* all but one option need a buffer to write their ps line in */ +#define PS_BUFFER_SIZE 256 +static char ps_buffer[PS_BUFFER_SIZE]; +static const size_t ps_buffer_size = PS_BUFFER_SIZE; +#else /* MOWGLI_SETPROC_USE_CLOBBER_ARGV */ +static char *ps_buffer; /* will point to argv area */ +static size_t ps_buffer_size; /* space determined at run time */ +static size_t last_status_len; /* use to minimize length of clobber */ +#endif /* MOWGLI_SETPROC_USE_CLOBBER_ARGV */ + +static size_t ps_buffer_cur_len; /* nominal strlen(ps_buffer) */ + +static size_t ps_buffer_fixed_size; /* size of the constant prefix */ + +/* save the original argv[] location here */ +static int save_argc; +static char **save_argv; + + +/* + * Call this early in startup to save the original argc/argv values. + * If needed, we make a copy of the original argv[] array to preserve it + * from being clobbered by subsequent ps_display actions. + * + * (The original argv[] will not be overwritten by this routine, but may be + * overwritten during mowgli_proctitle_init. Also, the physical location of the + * environment strings may be moved, so this should be called before any code + * that might try to hang onto a getenv() result.) + */ +char ** +mowgli_proctitle_copy_args(int argc, char **argv) +{ + save_argc = argc; + save_argv = argv; + +#if defined(MOWGLI_SETPROC_USE_CLOBBER_ARGV) + + /* + * If we're going to overwrite the argv area, count the available space. + * Also move the environment to make additional room. + */ + { + char *end_of_area = NULL; + char **new_environ; + int i; + + /* + * check for contiguous argv strings + */ + for (i = 0; i < argc; i++) + { + if (i == 0 || end_of_area + 1 == argv[i]) + end_of_area = argv[i] + strlen(argv[i]); + } + + if (end_of_area == NULL) /* probably can't happen? */ + { + ps_buffer = NULL; + ps_buffer_size = 0; + return argv; + } + + /* + * check for contiguous environ strings following argv + */ + for (i = 0; environ[i] != NULL; i++) + { + if (end_of_area + 1 == environ[i]) + end_of_area = environ[i] + strlen(environ[i]); + } + + ps_buffer = argv[0]; + last_status_len = ps_buffer_size = end_of_area - argv[0]; + + /* + * move the environment out of the way + */ + new_environ = (char **) mowgli_alloc((i + 1) * sizeof(char *)); + for (i = 0; environ[i] != NULL; i++) + new_environ[i] = mowgli_strdup(environ[i]); + new_environ[i] = NULL; + environ = new_environ; + } +#endif /* MOWGLI_SETPROC_USE_CLOBBER_ARGV */ + +#if defined(MOWGLI_SETPROC_USE_CHANGE_ARGV) || defined(MOWGLI_SETPROC_USE_CLOBBER_ARGV) + + /* + * If we're going to change the original argv[] then make a copy for + * argument parsing purposes. + * + * (NB: do NOT think to remove the copying of argv[]. + * On some platforms, getopt() keeps pointers into the argv array, and will + * get horribly confused when it is re-called to analyze a subprocess' + * argument string if the argv storage has been clobbered meanwhile. Other + * platforms have other dependencies on argv[]. + */ + { + char **new_argv; + int i; + + new_argv = (char **) mowgli_alloc((argc + 1) * sizeof(char *)); + for (i = 0; i < argc; i++) + new_argv[i] = mowgli_strdup(argv[i]); + new_argv[argc] = NULL; + +#if defined(__darwin__) + /* + * Darwin (and perhaps other NeXT-derived platforms?) has a static + * copy of the argv pointer, which we may fix like so: + */ + *_NSGetArgv() = new_argv; +#endif + + argv = new_argv; + } +#endif /* MOWGLI_SETPROC_USE_CHANGE_ARGV or MOWGLI_SETPROC_USE_CLOBBER_ARGV */ + + return argv; +} + +/* + * Call this once during subprocess startup to set the identification + * values. + * + * At this point, the original argv[] array may be overwritten. + */ +void +mowgli_proctitle_init(const char *initial_str, const char *fmt, ...) +{ + va_list va; + +#ifndef MOWGLI_SETPROC_USE_NONE + /* no ps display if you didn't call mowgli_proctitle_copy_args() */ + if (!save_argv) + return; + +#ifdef MOWGLI_SETPROC_USE_CLOBBER_ARGV + /* If ps_buffer is a pointer, it might still be null */ + if (!ps_buffer) + return; +#endif + + va_start(va, fmt); + vsnprintf(ps_buffer, sizeof(ps_buffer), fmt, va); + va_end(va); + + /* + * Overwrite argv[] to point at appropriate space, if needed + */ + +#ifdef MOWGLI_SETPROC_USE_CHANGE_ARGV + save_argv[0] = ps_buffer; + save_argv[1] = NULL; +#endif /* MOWGLI_SETPROC_USE_CHANGE_ARGV */ + +#ifdef MOWGLI_SETPROC_USE_CLOBBER_ARGV + /* make extra argv slots point at end_of_area (a NUL) */ + for (int i = 1; i < save_argc; i++) + save_argv[i] = ps_buffer + ps_buffer_size; +#endif /* MOWGLI_SETPROC_USE_CLOBBER_ARGV */ + + + ps_buffer_cur_len = ps_buffer_fixed_size = strlen(ps_buffer); + + mowgli_proctitle_set(initial_str, true); +#endif /* not MOWGLI_SETPROC_USE_NONE */ +} + +/* + * Call this to update the ps status display to a fixed prefix plus an + * indication of what you're currently doing passed in the argument. + */ +void +mowgli_proctitle_set(const char *activity, bool force) +{ +#ifndef MOWGLI_SETPROC_USE_NONE + /* mowgli_proctitle_update=off disables updates, unless force = true */ + if (!force && !mowgli_proctitle_update) + return; + +#ifdef MOWGLI_SETPROC_USE_CLOBBER_ARGV + /* If ps_buffer is a pointer, it might still be null */ + if (!ps_buffer) + return; +#endif + + /* Update ps_buffer to contain both fixed part and activity */ + mowgli_strlcpy(ps_buffer + ps_buffer_fixed_size, activity, + ps_buffer_size - ps_buffer_fixed_size); + ps_buffer_cur_len = strlen(ps_buffer); + + /* Transmit new setting to kernel, if necessary */ + +#ifdef MOWGLI_SETPROC_USE_SETPROCTITLE + setproctitle("%s", ps_buffer); +#endif + +#ifdef MOWGLI_SETPROC_USE_PSTAT + { + union pstun pst; + + pst.pst_command = ps_buffer; + pstat(PSTAT_SETCMD, pst, ps_buffer_cur_len, 0, 0); + } +#endif /* MOWGLI_SETPROC_USE_PSTAT */ + +#ifdef MOWGLI_SETPROC_USE_PS_STRINGS + PS_STRINGS->ps_nargvstr = 1; + PS_STRINGS->ps_argvstr = ps_buffer; +#endif /* MOWGLI_SETPROC_USE_PS_STRINGS */ + +#ifdef MOWGLI_SETPROC_USE_CLOBBER_ARGV + /* pad unused memory; need only clobber remainder of old status string */ + if (last_status_len > ps_buffer_cur_len) + memset(ps_buffer + ps_buffer_cur_len, PS_PADDING, + last_status_len - ps_buffer_cur_len); + last_status_len = ps_buffer_cur_len; +#endif /* MOWGLI_SETPROC_USE_CLOBBER_ARGV */ + +#ifdef MOWGLI_SETPROC_USE_WIN32 + { + /* + * Win32 does not support showing any changed arguments. To make it at + * all possible to track which backend is doing what, we create a + * named object that can be viewed with for example Process Explorer. + */ + static HANDLE ident_handle = INVALID_HANDLE_VALUE; + char name[PS_BUFFER_SIZE + 32]; + + if (ident_handle != INVALID_HANDLE_VALUE) + CloseHandle(ident_handle); + + sprintf(name, "mowgli_ident(%d): %s", MyProcPid, ps_buffer); + + ident_handle = CreateEvent(NULL, TRUE, FALSE, name); + } +#endif /* MOWGLI_SETPROC_USE_WIN32 */ +#endif /* not MOWGLI_SETPROC_USE_NONE */ +} + + +/* + * Returns what's currently in the ps display, in case someone needs + * it. Note that only the activity part is returned. On some platforms + * the string will not be null-terminated, so return the effective + * length into *displen. + */ +const char * +mowgli_proctitle_get(int *displen) +{ +#ifdef MOWGLI_SETPROC_USE_CLOBBER_ARGV + /* If ps_buffer is a pointer, it might still be null */ + if (!ps_buffer) + { + *displen = 0; + return ""; + } +#endif + + *displen = (int) (ps_buffer_cur_len - ps_buffer_fixed_size); + + return ps_buffer + ps_buffer_fixed_size; +} diff --git a/src/libmowgli/ext/proctitle.h b/src/libmowgli/ext/proctitle.h new file mode 100644 index 0000000..0c43838 --- /dev/null +++ b/src/libmowgli/ext/proctitle.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * ps_status.h + * + * Declarations for backend/utils/misc/ps_status.c + * + * src/include/utils/ps_status.h + * + *------------------------------------------------------------------------- + */ + +#ifndef __PS_STATUS_H__ +#define __PS_STATUS_H__ + +extern bool mowgli_proctitle_update; + +extern char **mowgli_proctitle_copy_args(int argc, char **argv); + +extern void mowgli_proctitle_init(const char *initial_str, const char *fmt, ...); + +extern void mowgli_proctitle_set(const char *activity, bool force); + +extern const char *mowgli_proctitle_get(int *displen); + +#endif /* __PS_STATUS_H__ */ diff --git a/src/libmowgli/mowgli.h b/src/libmowgli/mowgli.h index a8b86da..7c83041 100644 --- a/src/libmowgli/mowgli.h +++ b/src/libmowgli/mowgli.h @@ -70,6 +70,7 @@ MOWGLI_DECLS_START #include "base/bitvector.h" #include "base/hook.h" #include "base/mowgli_signal.h" +#include "ext/proctitle.h" #include "ext/error_backtrace.h" #include "base/random.h" #include "base/argstack.h" diff --git a/src/libmowgli/platform/autoconf.h.in b/src/libmowgli/platform/autoconf.h.in index 39904bc..9fbdbde 100644 --- a/src/libmowgli/platform/autoconf.h.in +++ b/src/libmowgli/platform/autoconf.h.in @@ -27,12 +27,22 @@ /* Define to 1 if you have the `port_create' function. */ #undef HAVE_PORT_CREATE +/* Define to 1 if you have the `pstat' function. */ +#undef HAVE_PSTAT + +/* Define to 1 if the PS_STRINGS struct exists on your platform (likely no). + */ +#undef HAVE_PS_STRINGS + /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD /* Define to 1 if you have the `select' function. */ #undef HAVE_SELECT +/* Define to 1 if you have the `setproctitle' function. */ +#undef HAVE_SETPROCTITLE + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H @@ -48,6 +58,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_EPOLL_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PSTAT_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SELECT_H