/* $Cambridge: hermes/src/prayer/lib/setproctitle.c,v 1.3 2008/09/16 09:59:57 dpc22 Exp $ */
/************************************************
 *    Prayer - a Webmail Interface              *
 ************************************************/

/* Copyright (c) University of Cambridge 2000 - 2008 */
/* See the file NOTICE for conditions of use and distribution. */

/* Setproctitle is only supported on some architectures. Default: disable */

#include "lib.h"

/* Following code has been stolen from sendmail 8.9.3: src/conf.[ch] */

/*
**  SETPROCTITLE -- set process title for ps
**
**	Parameters:
**		fmt -- a printf style format string.
**		a, b, c -- possible parameters to fmt.
**
**	Returns:
**		none.
**
**	Side Effects:
**		Clobbers argv of our main procedure so ps(1) will
**		display the title.
*/

#define SPT_NONE	0       /* don't use it at all */
#define SPT_REUSEARGV	1       /* cover argv with title information */
#define SPT_BUILTIN	2       /* use libc builtin */
#define SPT_PSTAT	3       /* use pstat(PSTAT_SETCMD, ...) */
#define SPT_PSSTRINGS	4       /* use PS_STRINGS->... */
#define SPT_SYSMIPS	5       /* use sysmips() supported by NEWS-OS 6 */
#define SPT_SCO		6       /* write kernel u. area */
#define SPT_CHANGEARGV	7       /* write our own strings into argv[] */

#ifndef SPT_TYPE
# define SPT_TYPE	SPT_REUSEARGV
#endif

#if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN

# if SPT_TYPE == SPT_PSTAT
#  include <sys/pstat.h>
# endif
# if SPT_TYPE == SPT_PSSTRINGS
#  include <machine/vmparam.h>
#  include <sys/exec.h>
#  ifndef PS_STRINGS            /* hmmmm....  apparently not available after all */
#   undef SPT_TYPE
#   define SPT_TYPE	SPT_REUSEARGV
#  else
#   ifndef NKPDE                /* FreeBSD 2.0 */
#    define NKPDE 63
typedef unsigned int *pt_entry_t;
#   endif
#  endif
# endif

# if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV
#  define SETPROC_STATIC	static
# else
#  define SETPROC_STATIC
# endif

# if SPT_TYPE == SPT_SYSMIPS
#  include <sys/sysmips.h>
#  include <sys/sysnews.h>
# endif

# if SPT_TYPE == SPT_SCO
#  include <sys/immu.h>
#  include <sys/dir.h>
#  include <sys/user.h>
#  include <sys/fs/s5param.h>
#  if PSARGSZ > MAXLINE
#   define SPT_BUFSIZE	PSARGSZ
#  endif
# endif

# ifndef SPT_PADCHAR
#  define SPT_PADCHAR	' '
# endif

#endif                          /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */

# ifndef SPT_BUFSIZE
#  define SPT_BUFSIZE	MAXLINE
# endif

/*
**  Pointers for setproctitle.
**	This allows "ps" listings to give more useful information.
*/

static char *progname = NULL;
static char **Argv = NULL;      /* pointer to argument vector */
static char *LastArgv = NULL;   /* end of argv */

void initsetproctitle(const char *name, int argc, char **argv, char **envp)
{
    register int i, envpsize = 0;
    extern char **environ;
    char *s;

    if ((s = strrchr(name, '/')))
        progname = strdup(s + 1);
    else
        progname = strdup(name);

    /*
     **  Move the environment so setproctitle can use the space at
     **  the top of memory.
     */

    for (i = 0; envp[i] != NULL; i++)
        envpsize += strlen(envp[i]) + 1;
    environ = (char **) malloc(sizeof(char *) * (i + 1));
    for (i = 0; envp[i] != NULL; i++)
        environ[i] = strdup(envp[i]);
    environ[i] = NULL;

    /*
     **  Save start and extent of argv for setproctitle.
     */

    Argv = argv;

    /*
     **  Determine how much space we can use for setproctitle.  
     **  Use all contiguous argv and envp pointers starting at argv[0]
     */
    for (i = 0; i < argc; i++) {
        if (i == 0 || LastArgv + 1 == argv[i])
            LastArgv = argv[i] + strlen(argv[i]);
        else
            continue;
    }
    for (i = 0; envp[i] != NULL; i++) {
        if (LastArgv + 1 == envp[i])
            LastArgv = envp[i] + strlen(envp[i]);
        else
            continue;
    }
}

#if SPT_TYPE != SPT_BUILTIN


/*VARARGS1*/
void
# ifdef __STDC__
setproctitle(const char *fmt, ...)
# else
setproctitle(fmt, va_alist)
const char *fmt;
va_dcl
# endif
{
# if SPT_TYPE != SPT_NONE
    register char *p;
    register int i;
    SETPROC_STATIC char buf[SPT_BUFSIZE];
    VA_LOCAL_DECL
#  if SPT_TYPE == SPT_PSTAT
    union pstun pst;
#  endif
#  if SPT_TYPE == SPT_SCO
    off_t seek_off;
    static int kmem = -1;
    static int kmempid = -1;
    struct user u;
#  endif

    p = buf;

    /* print heading for grep */
    (void) strcpy(p, progname);
    p += strlen(p);
    *p++ = ':';
    *p++ = ' ';

    /* print the argument string */
    VA_START(fmt);
    (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
    VA_END;

    i = strlen(buf);

#  if SPT_TYPE == SPT_PSTAT
    pst.pst_command = buf;
    pstat(PSTAT_SETCMD, pst, i, 0, 0);
#  endif
#  if SPT_TYPE == SPT_PSSTRINGS
    PS_STRINGS->ps_nargvstr = 1;
    PS_STRINGS->ps_argvstr = buf;
#  endif
#  if SPT_TYPE == SPT_SYSMIPS
    sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
#  endif
#  if SPT_TYPE == SPT_SCO
    if (kmem < 0 || kmempid != getpid()) {
        if (kmem >= 0)
            close(kmem);
        kmem = open(_PATH_KMEM, O_RDWR, 0);
        if (kmem < 0)
            return;
        (void) fcntl(kmem, F_SETFD, 1);
        kmempid = getpid();
    }
    buf[PSARGSZ - 1] = '\0';
    seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) & u;
    if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off)
        (void) write(kmem, buf, PSARGSZ);
#  endif
#  if SPT_TYPE == SPT_REUSEARGV
    if (i > LastArgv - Argv[0] - 2) {
        i = LastArgv - Argv[0] - 2;
        buf[i] = '\0';
    }
    (void) strcpy(Argv[0], buf);
    p = &Argv[0][i];
    while (p < LastArgv)
        *p++ = SPT_PADCHAR;
    Argv[1] = NULL;
#  endif
#  if SPT_TYPE == SPT_CHANGEARGV
    Argv[0] = buf;
    Argv[1] = 0;
#  endif
# endif                         /* SPT_TYPE != SPT_NONE */
}

#endif                          /* SPT_TYPE != SPT_BUILTIN */
