/* Portions of this file are subject to the following copyright(s).  See
 * the Net-SNMP's COPYING file for more details and other copyrights
 * that may apply:
 */
/*
 * Portions of this file are copyrighted by:
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms specified in the COPYING file
 * distributed with the Net-SNMP package.
 */

/*
 *  Host Resources MIB - Running Software group implementation - hr_swrun.c
 *      (also includes Running Software Performance group )
 *
 */

#include <net-snmp/net-snmp-config.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <fcntl.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <sys/param.h>
#include <ctype.h>
#if HAVE_SYS_PSTAT_H
#include <sys/pstat.h>
#endif
#if HAVE_SYS_USER_H
#ifdef solaris2
#include <libgen.h>
#define _KMEMUSER
#endif
#include <sys/user.h>
#endif
#if HAVE_SYS_PROC_H
#include <sys/proc.h>
#endif
#if HAVE_KVM_H
#include <kvm.h>
#endif
#if HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif
#if HAVE_DIRENT_H && !defined(cygwin)
#include <dirent.h>
#else
# define dirent direct
# if HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# if HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif
#ifdef cygwin
#include <windows.h>
#include <sys/cygwin.h>
#include <tlhelp32.h>
#include <psapi.h>
#endif

#if _SLASH_PROC_METHOD_
#include <procfs.h>
#endif

#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif

#include <stdio.h>

#include <net-snmp/output_api.h>
#include "host_res.h"
#include "hr_swrun.h"
#include <net-snmp/agent/auto_nlist.h>
#include "kernel.h"
#ifdef solaris2
#include "kernel_sunos5.h"
#endif
#if defined(aix4) || defined(aix5)
#include <procinfo.h>
#include <sys/types.h>
#endif

        /*********************
	 *
	 *  Initialisation & common implementation functions
	 *
	 *********************/
void            Init_HR_SWRun(void);
int             Get_Next_HR_SWRun(void);
void            End_HR_SWRun(void);
int             header_hrswrun(struct variable *, oid *, size_t *, int,
                               size_t *, WriteMethod **);
int             header_hrswrunEntry(struct variable *, oid *, size_t *,
                                    int, size_t *, WriteMethod **);

#ifdef dynix
pid_t           nextproc;
static prpsinfo_t lowpsinfo, mypsinfo;
#endif
#ifdef cygwin
static struct external_pinfo *curproc;
static struct external_pinfo lowproc;
#elif !defined(linux)
static int      LowProcIndex;
#endif
#if defined(hpux10) || defined(hpux11)
struct pst_status *proc_table;
struct pst_dynamic pst_dyn;
#elif HAVE_KVM_GETPROCS
struct kinfo_proc *proc_table;
#elif defined(solaris2)
int            *proc_table;
#elif defined(aix4) || defined(aix5)
struct procsinfo *proc_table;
#else
struct proc    *proc_table;
#endif
#ifndef dynix
int             current_proc_entry;
#endif


#define	HRSWRUN_OSINDEX		1

#define	HRSWRUN_INDEX		2
#define	HRSWRUN_NAME		3
#define	HRSWRUN_ID		4
#define	HRSWRUN_PATH		5
#define	HRSWRUN_PARAMS		6
#define	HRSWRUN_TYPE		7
#define	HRSWRUN_STATUS		8

#define	HRSWRUNPERF_CPU		9
#define	HRSWRUNPERF_MEM		10

struct variable4 hrswrun_variables[] = {
    {HRSWRUN_OSINDEX, ASN_INTEGER, RONLY, var_hrswrun, 1, {1}},
    {HRSWRUN_INDEX, ASN_INTEGER, RONLY, var_hrswrun, 3, {2, 1, 1}},
    {HRSWRUN_NAME, ASN_OCTET_STR, RONLY, var_hrswrun, 3, {2, 1, 2}},
    {HRSWRUN_ID, ASN_OBJECT_ID, RONLY, var_hrswrun, 3, {2, 1, 3}},
    {HRSWRUN_PATH, ASN_OCTET_STR, RONLY, var_hrswrun, 3, {2, 1, 4}},
    {HRSWRUN_PARAMS, ASN_OCTET_STR, RONLY, var_hrswrun, 3, {2, 1, 5}},
    {HRSWRUN_TYPE, ASN_INTEGER, RONLY, var_hrswrun, 3, {2, 1, 6}},
    {HRSWRUN_STATUS, ASN_INTEGER, RONLY, var_hrswrun, 3, {2, 1, 7}}
};

struct variable4 hrswrunperf_variables[] = {
    {HRSWRUNPERF_CPU, ASN_INTEGER, RONLY, var_hrswrun, 3, {1, 1, 1}},
    {HRSWRUNPERF_MEM, ASN_INTEGER, RONLY, var_hrswrun, 3, {1, 1, 2}}
};

oid             hrswrun_variables_oid[] = { 1, 3, 6, 1, 2, 1, 25, 4 };
oid             hrswrunperf_variables_oid[] = { 1, 3, 6, 1, 2, 1, 25, 5 };

#ifdef cygwin

/*
 * a lot of this is "stolen" from cygwin ps.cc
 */

typedef         BOOL(WINAPI * ENUMPROCESSMODULES) (HANDLE hProcess,
                                                   HMODULE * lphModule,
                                                   DWORD cb,
                                                   LPDWORD lpcbNeeded);

typedef         DWORD(WINAPI * GETMODULEFILENAME) (HANDLE hProcess,
                                                   HMODULE hModule,
                                                   LPTSTR lpstrFIleName,
                                                   DWORD nSize);

typedef         DWORD(WINAPI * GETPROCESSMEMORYINFO) (HANDLE hProcess,
                                                      PPROCESS_MEMORY_COUNTERS
                                                      pmc, DWORD nSize);

typedef         HANDLE(WINAPI * CREATESNAPSHOT) (DWORD dwFlags,
                                                 DWORD th32ProcessID);

typedef         BOOL(WINAPI * PROCESSWALK) (HANDLE hSnapshot,
                                            LPPROCESSENTRY32 lppe);

ENUMPROCESSMODULES myEnumProcessModules;
GETMODULEFILENAME myGetModuleFileNameEx;
CREATESNAPSHOT  myCreateToolhelp32Snapshot;
PROCESSWALK     myProcess32First;
PROCESSWALK     myProcess32Next;
GETPROCESSMEMORYINFO myGetProcessMemoryInfo = NULL;
cygwin_getinfo_types query = CW_GETPINFO;

static BOOL WINAPI
dummyprocessmodules(HANDLE hProcess,
                    HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded)
{
    lphModule[0] = (HMODULE) * lpcbNeeded;
    *lpcbNeeded = 1;
    return 1;
}

static DWORD WINAPI
GetModuleFileNameEx95(HANDLE hProcess,
                      HMODULE hModule, LPTSTR lpstrFileName, DWORD n)
{
    HANDLE          h;
    DWORD           pid = (DWORD) hModule;
    PROCESSENTRY32  proc;

    h = myCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (!h)
        return 0;
    proc.dwSize = sizeof(proc);
    if (myProcess32First(h, &proc))
        do
            if (proc.th32ProcessID == pid) {
                CloseHandle(h);
                strcpy(lpstrFileName, proc.szExeFile);
                return 1;
            }
        while (myProcess32Next(h, &proc));
    CloseHandle(h);
    return 0;
}

#define FACTOR (0x19db1ded53ea710LL)
#define NSPERSEC 10000000LL
#define NSPERMSEC 10000LL

static time_t   __stdcall
to_time_t(PFILETIME ptr)
{
    long            rem;
    long long       x =
        ((long long) ptr->dwHighDateTime << 32) +
        ((unsigned) ptr->dwLowDateTime);
    x -= FACTOR;
    rem = x % NSPERSEC;
    rem += NSPERSEC / 2;
    x /= NSPERSEC;
    x += rem / NSPERSEC;
    return x;
}

static long
to_msec(PFILETIME ptr)
{
    long long       x =
        ((long long) ptr->dwHighDateTime << 32) +
        (unsigned) ptr->dwLowDateTime;
    x /= NSPERMSEC;
    return x;
}

#endif                          /* cygwin */


void
init_hr_swrun(void)
{
#ifdef cygwin
    OSVERSIONINFO   ver;
    HMODULE         h;

    memset(&ver, 0, sizeof ver);
    ver.dwOSVersionInfoSize = sizeof ver;
    GetVersionEx(&ver);

    if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
        h = LoadLibrary("psapi.dll");
        if (h) {
            myEnumProcessModules =
                (ENUMPROCESSMODULES) GetProcAddress(h,
                                                    "EnumProcessModules");
            myGetModuleFileNameEx =
                (GETMODULEFILENAME) GetProcAddress(h,
                                                   "GetModuleFileNameExA");
            myGetProcessMemoryInfo =
                (GETPROCESSMEMORYINFO) GetProcAddress(h,
                                                      "GetProcessMemoryInfo");
            if (myEnumProcessModules && myGetModuleFileNameEx)
                query = CW_GETPINFO_FULL;
            else
                snmp_log(LOG_ERR, "hr_swrun failed NT init\n");
        } else
            snmp_log(LOG_ERR, "hr_swrun failed to load psapi.dll\n");
    } else {
        h = GetModuleHandle("KERNEL32.DLL");
        myCreateToolhelp32Snapshot =
            (CREATESNAPSHOT) GetProcAddress(h, "CreateToolhelp32Snapshot");
        myProcess32First =
            (PROCESSWALK) GetProcAddress(h, "Process32First");
        myProcess32Next = (PROCESSWALK) GetProcAddress(h, "Process32Next");
        myEnumProcessModules = dummyprocessmodules;
        myGetModuleFileNameEx = GetModuleFileNameEx95;
        if (myCreateToolhelp32Snapshot && myProcess32First
            && myProcess32Next)
#if 0
            /*
             * This doesn't work after all on Win98 SE 
             */
            query = CW_GETPINFO_FULL;
#else
            query = CW_GETPINFO;
#endif
        else
            snmp_log(LOG_ERR, "hr_swrun failed non-NT init\n");
    }
#endif                          /* cygwin */
#ifdef PROC_SYMBOL
    auto_nlist(PROC_SYMBOL, 0, 0);
#endif
#ifdef NPROC_SYMBOL
    auto_nlist(NPROC_SYMBOL, 0, 0);
#endif

    proc_table = 0;

    REGISTER_MIB("host/hr_swrun", hrswrun_variables, variable4,
                 hrswrun_variables_oid);
    REGISTER_MIB("host/hr_swrun", hrswrunperf_variables, variable4,
                 hrswrunperf_variables_oid);
}

/*
 * header_hrswrun(...
 * Arguments:
 * vp     IN      - pointer to variable entry that points here
 * name    IN/OUT  - IN/name requested, OUT/name found
 * length  IN/OUT  - length of IN/OUT oid's 
 * exact   IN      - TRUE if an exact match was requested
 * var_len OUT     - length of variable or 0 if function returned
 * write_method
 * 
 */

int
header_hrswrun(struct variable *vp,
               oid * name,
               size_t * length,
               int exact, size_t * var_len, WriteMethod ** write_method)
{
#define HRSWRUN_NAME_LENGTH	9
    oid             newname[MAX_OID_LEN];
    int             result;

    DEBUGMSGTL(("host/hr_swrun", "var_hrswrun: "));
    DEBUGMSGOID(("host/hr_swrun", name, *length));
    DEBUGMSG(("host/hr_swrun", " %d\n", exact));

    memcpy((char *) newname, (char *) vp->name, vp->namelen * sizeof(oid));
    newname[HRSWRUN_NAME_LENGTH] = 0;
    result = snmp_oid_compare(name, *length, newname, vp->namelen + 1);
    if ((exact && (result != 0)) || (!exact && (result >= 0)))
        return (MATCH_FAILED);
    memcpy((char *) name, (char *) newname,
           (vp->namelen + 1) * sizeof(oid));
    *length = vp->namelen + 1;

    *write_method = 0;
    *var_len = sizeof(long);    /* default to 'long' results */
    return (MATCH_SUCCEEDED);
}

int
header_hrswrunEntry(struct variable *vp,
                    oid * name,
                    size_t * length,
                    int exact,
                    size_t * var_len, WriteMethod ** write_method)
{
#define HRSWRUN_ENTRY_NAME_LENGTH	11
    oid             newname[MAX_OID_LEN];
    int             pid, LowPid = -1;
    int             result;

    DEBUGMSGTL(("host/hr_swrun", "var_hrswrunEntry: "));
    DEBUGMSGOID(("host/hr_swrun", name, *length));
    DEBUGMSG(("host/hr_swrun", " %d\n", exact));

    memcpy((char *) newname, (char *) vp->name, vp->namelen * sizeof(oid));

    /*
     *  Find the "next" running process
     */
    Init_HR_SWRun();
    for (;;) {
        pid = Get_Next_HR_SWRun();
#ifndef linux
#ifndef dynix
        DEBUGMSG(("host/hr_swrun",
                  "(index %d (entry #%d) ....", pid, current_proc_entry));
#else
        DEBUGMSG(("host/hr_swrun", "pid %d; nextproc %d ....", pid,
                  nextproc));
#endif
#endif
        if (pid == -1)
            break;
        newname[HRSWRUN_ENTRY_NAME_LENGTH] = pid;
        DEBUGMSGOID(("host/hr_swrun", newname, *length));
        DEBUGMSG(("host/hr_swrun", "\n"));
        result = snmp_oid_compare(name, *length, newname, vp->namelen + 1);
        if (exact && (result == 0)) {
            LowPid = pid;
#ifdef cygwin
            lowproc = *curproc;
#elif  dynix
            memcpy(&lowpsinfo, &mypsinfo, sizeof(prpsinfo_t));
#elif !defined(linux)
            LowProcIndex = current_proc_entry - 1;
#endif
            DEBUGMSGTL(("host/hr_swrun", " saved\n"));
            /*
             * Save process status information 
             */
            break;
        }
        if ((!exact && (result < 0)) && (LowPid == -1 || pid < LowPid)) {
            LowPid = pid;
#ifdef cygwin
            lowproc = *curproc;
#elif !defined(linux)
            LowProcIndex = current_proc_entry - 1;
#endif
            /*
             * Save process status information 
             */
            DEBUGMSG(("host/hr_swrun", " saved"));
        }
        DEBUGMSG(("host/hr_swrun", "\n"));
    }
    End_HR_SWRun();

    if (LowPid == -1) {
        DEBUGMSGTL(("host/hr_swrun", "... index out of range\n"));
        return (MATCH_FAILED);
    }

    newname[HRSWRUN_ENTRY_NAME_LENGTH] = LowPid;
    memcpy((char *) name, (char *) newname,
           (vp->namelen + 1) * sizeof(oid));
    *length = vp->namelen + 1;
    *write_method = 0;
    *var_len = sizeof(long);    /* default to 'long' results */

    DEBUGMSGTL(("host/hr_swrun", "... get process stats "));
    DEBUGMSGOID(("host/hr_swrun", name, *length));
    DEBUGMSG(("host/hr_swrun", "\n"));
    return LowPid;
}

        /*********************
	 *
	 *  System specific implementation functions
	 *
	 *********************/


u_char         *
var_hrswrun(struct variable * vp,
            oid * name,
            size_t * length,
            int exact, size_t * var_len, WriteMethod ** write_method)
{
    int             pid = 0;
    static char     string[1024];
#ifdef HAVE_SYS_PSTAT_H
    struct pst_status proc_buf;
#elif defined(solaris2)
#if _SLASH_PROC_METHOD_
    static psinfo_t psinfo;
    static psinfo_t *proc_buf;
    int             procfd;
    int             ret;
    char            procfn[sizeof "/proc/00000/psinfo"];
#else
    static struct proc *proc_buf;
    char           *cp1;
#endif                          /* _SLASH_PROC_METHOD_ */
    static time_t   when = 0;
    time_t          now;
    static int      oldpid = -1;
#endif
#if HAVE_KVM_GETPROCS
    char          **argv;
#endif
#ifdef linux
    FILE           *fp;
    char            buf[1024];
    int             i;
#endif
    char           *cp;

    if (vp->magic == HRSWRUN_OSINDEX) {
        if (header_hrswrun(vp, name, length, exact, var_len, write_method)
            == MATCH_FAILED)
            return NULL;
    } else {

        pid =
            header_hrswrunEntry(vp, name, length, exact, var_len,
                                write_method);
        if (pid == MATCH_FAILED)
            return NULL;
    }

#ifdef HAVE_SYS_PSTAT_H
    if (pstat_getproc(&proc_buf, sizeof(struct pst_status), 0, pid) == -1)
        return NULL;
#elif defined(solaris2)
    time(&now);
    if (pid == oldpid) {
        if (now != when)
            oldpid = -1;
    }
    if (oldpid != pid || proc_buf == NULL) {
#if _SLASH_PROC_METHOD_
        proc_buf = &psinfo;
        sprintf(procfn, "/proc/%.5d/psinfo", pid);
        if ((procfd = open(procfn, O_RDONLY)) != -1) {
            ret =  read(procfd, proc_buf, sizeof(*proc_buf));
            close(procfd);
            if (ret != sizeof(*proc_buf))
                return NULL;
        } else
            return NULL;
#else
        if (kd == NULL)
            return NULL;
        if ((proc_buf = kvm_getproc(kd, pid)) == NULL)
            return NULL;
#endif
        oldpid = pid;
        when = now;
    }
#endif

    switch (vp->magic) {
    case HRSWRUN_OSINDEX:
#if NETSNMP_NO_DUMMY_VALUES
        return NULL;
#else
        /* 
         * per dts, on coders:
         * cos (in general) we won't know which process should
         * be regarded as "the primary O/S process".
         * The most obvious candidate on a Unix box is probably 'init'
         * which is typically (always?) process #1.
         */
        long_return = 1;        /* Probably! */
        return (u_char *) & long_return;
#endif

    case HRSWRUN_INDEX:
        long_return = pid;
        return (u_char *) & long_return;
    case HRSWRUN_NAME:
#ifdef HAVE_SYS_PSTAT_H
        snprintf(string, sizeof(string), "%s", proc_buf.pst_cmd);
        string[ sizeof(string)-1 ] = 0;
        cp = strchr(string, ' ');
        if (cp != NULL)
            *cp = '\0';
#elif defined(dynix)
        snprintf(string, sizeof(string), "%s", lowpsinfo.pr_fname);
        string[ sizeof(string)-1 ] = 0;
        cp = strchr(string, ' ');
        if (cp != NULL)
            *cp = '\0';
#elif defined(solaris2)
#if _SLASH_PROC_METHOD_
        if (proc_buf) { 
            char *pos=strchr(proc_buf->pr_psargs,' ');
            if (pos != NULL) *pos = '\0';
            strlcpy(string, basename(proc_buf->pr_psargs),sizeof(string));
            if (pos != NULL) *pos=' ';
        } else {
            strcpy(string, "<exited>");
        }
        string[ sizeof(string)-1 ] = 0;
#else
        strncpy(string, proc_buf->p_user.u_comm, sizeof(string));
        string[ sizeof(string)-1 ] = 0;
#endif
#elif defined(aix4) || defined(aix5)
        strncpy(string, proc_table[LowProcIndex].pi_comm, sizeof(string));
        string[ sizeof(string)-1 ] = 0;
        cp = strchr(string, ' ');
        if (cp != NULL)
            *cp = '\0';
#elif HAVE_KVM_GETPROCS
    #if defined(freebsd5) && __FreeBSD_version >= 500014
        strcpy(string, proc_table[LowProcIndex].ki_comm);
    #else
        strcpy(string, proc_table[LowProcIndex].kp_proc.p_comm);
    #endif
#elif defined(linux)
        sprintf(string, "/proc/%d/status", pid);
        if ((fp = fopen(string, "r")) == NULL) {
            strcpy(string, "<exited>");
            *var_len = strlen(string);
            return (u_char *) string;
        }
        fgets(buf, sizeof(buf), fp);    /* Name: process name */
        cp = buf;
        while (*cp != ':')
            ++cp;
        ++cp;
        while (isspace(*cp))
            ++cp;
        strcpy(string, cp);
        fclose(fp);
#elif defined(cygwin)
        /* if (lowproc.process_state & (PID_ZOMBIE | PID_EXITED)) */
        if (lowproc.process_state & PID_EXITED || (lowproc.exitcode & ~0xffff))
            strcpy(string, "<defunct>");
        else if (lowproc.ppid) {
            cygwin_conv_to_posix_path(lowproc.progname, string);
            cp = strrchr(string, '/');
            if (cp)
                strcpy(string, cp + 1);
        } else if (query == CW_GETPINFO_FULL) {
            DWORD           n = lowproc.dwProcessId & 0xffff;
            HANDLE          h =
                OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                            FALSE, n);

            if (h) {
                HMODULE         hm[1000];
                if (!myEnumProcessModules(h, hm, sizeof hm, &n)) {
                    snmp_log(LOG_DEBUG, "no module handle for %lu\n", n);
                    n = 0;
                }
                if (n
                    && myGetModuleFileNameEx(h, hm[0], string,
                                             sizeof string)) {
                    cp = strrchr(string, '\\');
                    if (cp)
                        strcpy(string, cp + 1);
                } else
                    strcpy(string, "*** unknown");
                CloseHandle(h);
            } else {
                snmp_log(LOG_INFO, "no process handle for %lu\n", n);
                strcpy(string, "** unknown");
            }
        } else
            strcpy(string, "* unknown");
        cp = strchr(string, '\0') - 4;
        if (cp > string && strcasecmp(cp, ".exe") == 0)
            *cp = '\0';
#else
#if NETSNMP_NO_DUMMY_VALUES
        return NULL;
#endif
        sprintf(string, "process name");
#endif
        *var_len = strlen(string);
        /*
         * remove trailing newline 
         */
        if (*var_len) {
            cp = string + *var_len - 1;
            if (*cp == '\n')
                --(*var_len);
        }
        if (*var_len > 64) { /* MIB limit */
            *var_len = 64;
            string[64] = '\0';
        }
        return (u_char *) string;
    case HRSWRUN_ID:
        *var_len = nullOidLen;
        return (u_char *) nullOid;
    case HRSWRUN_PATH:
#ifdef HAVE_SYS_PSTAT_H
        /*
         * Path not available - use argv[0] 
         */
        sprintf(string, "%s", proc_buf.pst_cmd);
        cp = strchr(string, ' ');
        if (cp != NULL)
            *cp = '\0';
#elif defined(dynix)
        /*
         * Path not available - use argv[0] 
         */
        sprintf(string, "%s", lowpsinfo.pr_psargs);
        cp = strchr(string, ' ');
        if (cp != NULL)
            *cp = '\0';
#elif defined(solaris2)
#ifdef _SLASH_PROC_METHOD_
        if (proc_buf)
            strcpy(string, proc_buf->pr_psargs);
        else
            sprintf(string, "<exited>");
        cp = strchr(string, ' ');
        if (cp)
            *cp = 0;
#else
        cp = proc_buf->p_user.u_psargs;
        cp1 = string;
        while (*cp && *cp != ' ')
            *cp1++ = *cp++;
        *cp1 = 0;
#endif
#elif defined(aix4) || defined(aix5)
        strncpy(string, proc_table[LowProcIndex].pi_comm, sizeof(string));
        string[ sizeof(string)-1 ] = 0;
        cp = strchr(string, ' ');
        if (cp != NULL)
            *cp = '\0';
#elif HAVE_KVM_GETPROCS
    #if defined(freebsd5) && __FreeBSD_version >= 500014
        strcpy(string, proc_table[LowProcIndex].ki_comm);
    #else
        strcpy(string, proc_table[LowProcIndex].kp_proc.p_comm);
    #endif
#elif defined(linux)
        sprintf(string, "/proc/%d/cmdline", pid);
        if ((fp = fopen(string, "r")) == NULL) {
            strcpy(string, "<exited>");
            *var_len = strlen(string);
            return (u_char *) string;
        }
        if (fgets(buf, sizeof(buf) - 1, fp))    /* argv[0] '\0' argv[1] '\0' .... */
            strcpy(string, buf);
        else {
            /*
             * swapped out - no cmdline 
             */
            fclose(fp);
            sprintf(string, "/proc/%d/status", pid);
            if ((fp = fopen(string, "r")) == NULL)
                return NULL;
            fgets(buf, sizeof(buf), fp);        /* Name: process name */
            cp = strchr(buf, ':');
            ++cp;
            while (isspace(*cp))
                ++cp;
            strcpy(string, cp);
            cp = strchr(string, '\n');
            if (cp)
                *cp = 0;
        }
        fclose(fp);
#elif defined(cygwin)
        /* if (lowproc.process_state & (PID_ZOMBIE | PID_EXITED)) */
        if (lowproc.process_state & PID_EXITED || (lowproc.exitcode & ~0xffff))
            strcpy(string, "<defunct>");
        else if (lowproc.ppid)
            cygwin_conv_to_posix_path(lowproc.progname, string);
        else if (query == CW_GETPINFO_FULL) {
            DWORD           n = lowproc.dwProcessId & 0xFFFF;
            HANDLE          h =
                OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                            FALSE, n);
            if (h) {
                HMODULE         hm[1000];
                if (!myEnumProcessModules(h, hm, sizeof hm, &n))
                    n = 0;
                if (!n
                    || !myGetModuleFileNameEx(h, hm[0], string,
                                              sizeof string))
                    strcpy(string, "*** unknown");
                CloseHandle(h);
            } else
                strcpy(string, "** unknown");
        } else
            strcpy(string, "* unknown");
#else
#if NETSNMP_NO_DUMMY_VALUES
        return NULL;
#endif
        sprintf(string, "/bin/wombat");
#endif
        *var_len = strlen(string);
        if (*var_len > 128) { /* MIB limit */
            *var_len = 128;
            string[128] = '\0';
        }
        return (u_char *) string;
    case HRSWRUN_PARAMS:
#ifdef HAVE_SYS_PSTAT_H
        cp = strchr(proc_buf.pst_cmd, ' ');
        if (cp != NULL) {
            cp++;
            sprintf(string, "%s", cp);
        } else
            string[0] = '\0';
#elif defined(dynix)
        cp = strchr(lowpsinfo.pr_psargs, ' ');
        if (cp != NULL) {
            cp++;
            sprintf(string, "%s", cp);
        } else
            string[0] = '\0';
#elif defined(solaris2)
#ifdef _SLASH_PROC_METHOD_
        if (proc_buf) {
            cp = strchr(proc_buf->pr_psargs, ' ');
            if (cp)
                strcpy(string, cp + 1);
            else
                string[0] = 0;
        } else
            string[0] = 0;
#else
        cp = proc_buf->p_user.u_psargs;
        while (*cp && *cp != ' ')
            cp++;
        if (*cp == ' ')
            cp++;
        strcpy(string, cp);
#endif
#elif defined(aix4) || defined(aix5)
        cp = strchr(proc_table[LowProcIndex].pi_comm, ' ');
        if (cp != NULL) {
            cp++;
            sprintf(string, "%s", cp);
        } else
            string[0] = '\0';
#elif HAVE_KVM_GETPROCS
        string[0] = 0;
        argv = kvm_getargv(kd, proc_table + LowProcIndex, sizeof(string));
        if (argv)
            argv++;
        while (argv && *argv) {
            if (string[0] != 0)
                strcat(string, " ");
            strcat(string, *argv);
            argv++;
        }
#elif defined(linux)
        sprintf(string, "/proc/%d/cmdline", pid);
        if ((fp = fopen(string, "r")) == NULL) {
            strcpy(string, "");
            *var_len = 0;
            return (u_char *) string;
        }
        memset(buf, 0, sizeof(buf));

        /*
         * argv[0] '\0' argv[1] '\0' .... 
         */
        if (!fgets(buf, sizeof(buf) - 2, fp)) {
            /*
             * maybe be empty (even argv[0] is missing) 
             */
            string[0] = '\0';
            *var_len = 0;
            fclose(fp);
            return string;
        }

        /*
         * Skip over argv[0] 
         */
        cp = buf;
        while (*cp)
            ++cp;
        ++cp;
        /*
         * Now join together separate arguments. 
         */
        while (1) {
            while (*cp)
                ++cp;
            if (*(cp + 1) == '\0')
                break;          /* '\0''\0' => End of command line */
            *cp = ' ';
        }

        cp = buf;
        while (*cp)
            ++cp;
        ++cp;
        strcpy(string, cp);
        fclose(fp);
#elif defined(cygwin)
        string[0] = 0;
#else
#if NETSNMP_NO_DUMMY_VALUES
        return NULL;
#endif
        sprintf(string, "-h -q -v");
#endif
        *var_len = strlen(string);
        if (*var_len > 128) { /* MIB limit */
            *var_len = 128;
            string[128] = '\0';
        }
        return (u_char *) string;
    case HRSWRUN_TYPE:
#ifdef PID_MAXSYS
        if (pid < PID_MAXSYS)
            long_return = 2;    /* operatingSystem */
        else
            long_return = 4;    /* application */
#elif defined(aix4) || defined(aix5)
		if (proc_table[LowProcIndex].pi_flags & SKPROC) {
			long_return = 2;	/* kernel process */
		} else
			long_return = 4;	/* application */
#elif HAVE_KVM_GETPROCS
    #if defined(freebsd5) && __FreeBSD_version >= 500014
	if (proc_table[LowProcIndex].ki_flag & P_SYSTEM) {
	    if (proc_table[LowProcIndex].ki_pri.pri_class == PRI_ITHD)
		long_return = 3;/* deviceDriver */
	    else
		long_return = 2;/* operatingSystem */
	} else
	    long_return = 4;	/* application */
    #else
        if (proc_table[LowProcIndex].kp_proc.p_flag & P_SYSTEM)
	    long_return = 2;	/* operatingSystem */
	else
	    long_return = 4;	/* application */
    #endif
#else
	long_return = 4;	/* application */
#endif
        return (u_char *) & long_return;
    case HRSWRUN_STATUS:
#if defined(cygwin)
        if (lowproc.process_state & PID_STOPPED)
            long_return = 3;    /* notRunnable */
        /* else if (lowproc.process_state & PID_ZOMBIE) */
        else if (lowproc.exitcode & ~0xffff)
            long_return = 4;    /* invalid */
        else
            long_return = 1;    /* running */
#elif !defined(linux)
#if defined(hpux10) || defined(hpux11)
        switch (proc_table[LowProcIndex].pst_stat) {
        case PS_STOP:
            long_return = 3;    /* notRunnable */
            break;
        case PS_SLEEP:
            long_return = 2;    /* runnable */
            break;
        case PS_RUN:
            long_return = 1;    /* running */
            break;
        case PS_ZOMBIE:
        case PS_IDLE:
        case PS_OTHER:
        default:
            long_return = 4;    /* invalid */
            break;
        }
#else
#if HAVE_KVM_GETPROCS
    #if defined(freebsd5) && __FreeBSD_version >= 500014
        switch (proc_table[LowProcIndex].ki_stat) {
    #else
        switch (proc_table[LowProcIndex].kp_proc.p_stat) {
    #endif
#elif defined(dynix)
        switch (lowpsinfo.pr_state) {
#elif defined(solaris2)
#if _SLASH_PROC_METHOD_
        switch (proc_buf ? proc_buf->pr_lwp.pr_state : SIDL) {
#else
        switch (proc_buf->p_stat) {
#endif
#elif defined(aix4) || defined(aix5)
        switch (proc_table[LowProcIndex].pi_state) {
#else
        switch (proc_table[LowProcIndex].p_stat) {
#endif
        case SSTOP:
            long_return = 3;    /* notRunnable */
            break;
        case 0:
#ifdef SSWAP
        case SSWAP:
#endif
#ifdef SSLEEP
        case SSLEEP:
#endif
#ifdef SWAIT
        case SWAIT:
#endif
            long_return = 2;    /* runnable */
            break;
#ifdef SACTIVE
        case SACTIVE:
#endif
#ifdef SRUN
        case SRUN:
#endif
#ifdef SONPROC
        case SONPROC:
#endif
            long_return = 1;    /* running */
            break;
        case SIDL:
        case SZOMB:
        default:
            long_return = 4;    /* invalid */
            break;
        }
#endif
#else
        sprintf(string, "/proc/%d/stat", pid);
        if ((fp = fopen(string, "r")) != NULL) {
            fgets(buf, sizeof(buf), fp);
            cp = buf;
            for (i = 0; i < 2; ++i) {   /* skip two fields */
                while (*cp != ' ')
                    ++cp;
                ++cp;
            }

            switch (*cp) {
            case 'R':
                long_return = 1;        /* running */
                break;
            case 'S':
                long_return = 2;        /* runnable */
                break;
            case 'D':
            case 'T':
                long_return = 3;        /* notRunnable */
                break;
            case 'Z':
            default:
                long_return = 4;        /* invalid */
                break;
            }
            fclose(fp);
        } else
            long_return = 4;    /* invalid */
#endif
        return (u_char *) & long_return;

    case HRSWRUNPERF_CPU:
#ifdef HAVE_SYS_PSTAT_H
        long_return = proc_buf.pst_cptickstotal;
        /*
         * Not convinced this is right, but....
         */
#elif defined(dynix)
        long_return = lowpsinfo.pr_time.tv_sec * 100 +
            lowpsinfo.pr_time.tv_nsec / 10000000;
#elif defined(solaris2)
#if _SLASH_PROC_METHOD_
        long_return = proc_buf ? proc_buf->pr_time.tv_sec * 100 +
            proc_buf->pr_time.tv_nsec / 10000000 : 0;
#else
        long_return = proc_buf->p_utime * 100 + proc_buf->p_stime * 100;
#endif
#elif HAVE_KVM_GETPROCS
    #if defined(freebsd5) && __FreeBSD_version >= 500014
        /* XXX: Accessing ki_paddr causes sig10 ...
        long_return = proc_table[LowProcIndex].ki_paddr->p_uticks +
            proc_table[LowProcIndex].ki_paddr->p_sticks +
            proc_table[LowProcIndex].ki_paddr->p_iticks; */
        long_return = 0;
    #elif defined(freebsd5)
        long_return = proc_table[LowProcIndex].ki_runtime / 100000;
    #else
        long_return = proc_table[LowProcIndex].kp_proc.p_uticks +
            proc_table[LowProcIndex].kp_proc.p_sticks +
            proc_table[LowProcIndex].kp_proc.p_iticks;
    #endif
#elif defined(linux)
        sprintf(string, "/proc/%d/stat", pid);
        if ((fp = fopen(string, "r")) == NULL) {
            long_return = 0;
            return (u_char *) & long_return;
        }
        fgets(buf, sizeof(buf), fp);
        cp = buf;
        for (i = 0; i < 13; ++i) {      /* skip 13 fields */
            while (*cp != ' ')
                ++cp;
            ++cp;
        }

        long_return = atoi(cp); /* utime */

        while (*cp != ' ')
            ++cp;
        ++cp;
        long_return += atoi(cp);        /* + stime */
        fclose(fp);
#elif defined(sunos4)
        long_return = proc_table[LowProcIndex].p_time;
#elif defined(cygwin)
        {
            DWORD           n = lowproc.dwProcessId;
            HANDLE          h =
                OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                            FALSE, n);
            FILETIME        ct, et, kt, ut;

            if (h) {
                if (GetProcessTimes(h, &ct, &et, &kt, &ut))
                    long_return = (to_msec(&kt) + to_msec(&ut)) / 10;
                else {
                    snmp_log(LOG_INFO, "no process times for %lu (%lu)\n",
                             lowproc.pid, n);
                    long_return = 0;
                }
                CloseHandle(h);
            } else {
                snmp_log(LOG_INFO, "no process handle for %lu (%lu)\n",
                         lowproc.pid, n);
                long_return = 0;
            }
        }
#elif defined(aix4) || defined(aix5)
        long_return = proc_table[LowProcIndex].pi_ru.ru_utime.tv_sec * 100 +
            proc_table[LowProcIndex].pi_ru.ru_utime.tv_usec / 10000000 + /* nanoseconds */
            proc_table[LowProcIndex].pi_ru.ru_stime.tv_sec * 100 +
            proc_table[LowProcIndex].pi_ru.ru_stime.tv_usec / 10000000; /* nanoseconds */
#else
        long_return = proc_table[LowProcIndex].p_utime.tv_sec * 100 +
            proc_table[LowProcIndex].p_utime.tv_usec / 10000 +
            proc_table[LowProcIndex].p_stime.tv_sec * 100 +
            proc_table[LowProcIndex].p_stime.tv_usec / 10000;
#endif
        return (u_char *) & long_return;
    case HRSWRUNPERF_MEM:
#ifdef HAVE_SYS_PSTAT_H
        long_return = (proc_buf.pst_rssize << PGSHIFT) / 1024;
#elif defined(dynix)
        long_return = (lowpsinfo.pr_rssize * MMU_PAGESIZE) / 1024;
#elif defined(solaris2)
#if _SLASH_PROC_METHOD_
        long_return = proc_buf ? proc_buf->pr_rssize : 0;
#else
        long_return = proc_buf->p_swrss;
#endif
#elif defined(aix4) || defined(aix5)
        long_return = proc_table[LowProcIndex].pi_size * getpagesize() / 1024;
#elif HAVE_KVM_GETPROCS && !defined(darwin8)
  #if defined(freebsd5) && __FreeBSD_version >= 500014
	    /* XXX
	    long_return = proc_table[LowProcIndex].ki_vmspace->vm_tsize +
			  proc_table[LowProcIndex].ki_vmspace->vm_ssize +
			  proc_table[LowProcIndex].ki_vmspace->vm_dsize;
	    long_return = long_return * (getpagesize() / 1024); */
	    long_return = 0;
  #elif defined(freebsd3) && !defined(darwin)
        long_return =
    #if defined(freebsd5)
            proc_table[LowProcIndex].ki_size / 1024;
    #else
            proc_table[LowProcIndex].kp_eproc.e_vm.vm_map.size / 1024;
    #endif
  #else
        long_return = proc_table[LowProcIndex].kp_eproc.e_vm.vm_tsize +
            proc_table[LowProcIndex].kp_eproc.e_vm.vm_ssize +
            proc_table[LowProcIndex].kp_eproc.e_vm.vm_dsize;
        long_return = long_return * (getpagesize() / 1024);
  #endif
#elif defined(linux)
        sprintf(string, "/proc/%d/stat", pid);
        if ((fp = fopen(string, "r")) == NULL) {
            long_return = 0;
            return (u_char *) & long_return;
        }
        fgets(buf, sizeof(buf), fp);
        cp = buf;
        for (i = 0; i < 23; ++i) {      /* skip 23 fields */
            while (*cp != ' ')
                ++cp;
            ++cp;
        }
        long_return = atoi(cp) * (getpagesize() / 1024);        /* rss */
        fclose(fp);
#elif defined(cygwin)
        {
            DWORD           n = lowproc.dwProcessId;
            HANDLE          h =
                OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                            FALSE, n);
            PROCESS_MEMORY_COUNTERS pmc;

            if (h) {
                if (myGetProcessMemoryInfo
                    && myGetProcessMemoryInfo(h, &pmc, sizeof pmc))
                    long_return = pmc.WorkingSetSize / 1024;
                else {
                    snmp_log(LOG_INFO, "no process times for %lu (%lu)\n",
                             lowproc.pid, n);
                    long_return = 0;
                }
                CloseHandle(h);
            } else {
                snmp_log(LOG_INFO, "no process handle for %lu (%lu)\n",
                         lowproc.pid, n);
                long_return = 0;
            }
        }
#else
#if NETSNMP_NO_DUMMY_VALUES
        return NULL;
#endif
        long_return = 16 * 1024;        /* XXX - 16M! */
#endif
        return (u_char *) & long_return;
    default:
        DEBUGMSGTL(("snmpd", "unknown sub-id %d in var_hrswrun\n",
                    vp->magic));
    }
    return NULL;
}


        /*********************
	 *
	 *  Internal implementation functions
	 *
	 *********************/

#if defined(linux)

DIR            *procdir = NULL;
struct dirent  *procentry_p;

void
Init_HR_SWRun(void)
{
    if (procdir != NULL)
        closedir(procdir);
    procdir = opendir("/proc");
}

int
Get_Next_HR_SWRun(void)
{
    int             pid;
    if (procdir == NULL)
      return -1;
    procentry_p = readdir(procdir);

    if (procentry_p == NULL)
        return -1;

    pid = atoi(procentry_p->d_name);
    if (pid == 0)
        return (Get_Next_HR_SWRun());
    return pid;
}

void
End_HR_SWRun(void)
{
    if (procdir)
        closedir(procdir);
    procdir = NULL;
}

#elif defined(cygwin)

static pid_t    curpid;

void
Init_HR_SWRun(void)
{
    cygwin_internal(CW_LOCK_PINFO, 1000);
    curpid = 0;
}

int
Get_Next_HR_SWRun(void)
{
    curproc =
        (struct external_pinfo *) cygwin_internal(query,
                                                  curpid | CW_NEXTPID);
    if (curproc)
        curpid = curproc->pid;
    else {
        curpid = -1;
    }
    return curpid;
}

void
End_HR_SWRun(void)
{
    cygwin_internal(CW_UNLOCK_PINFO);
}

#elif defined(dynix)

void
Init_HR_SWRun(void)
{
    nextproc = 0;
}

int
Get_Next_HR_SWRun(void)
{
    getprpsinfo_t  *select = 0;

    DEBUGMSGTL(("host/hr_swrun::GetNextHR_SWRun",
                "nextproc == %d... &nextproc = %u\n", nextproc,
                &nextproc));
    if ((nextproc = getprpsinfo(nextproc, select, &mypsinfo)) < 0) {
        return -1;
    } else {
        DEBUGMSGTL(("host/hr_swrun::GetNextHR_SWRun",
                    "getprpsinfo returned %d\n", nextproc));
        return mypsinfo.pr_pid;
    }

}

void
End_HR_SWRun(void)
{
    /*
     * just a stub... because it's declared 
     */
}

#else                           /* linux */

static int      nproc;

void
Init_HR_SWRun(void)
{
    size_t          bytes;
    static time_t   iwhen = 0;
    time_t          now;

    time(&now);
    if (now == iwhen) {
        current_proc_entry = 0;
        return;
    }
    iwhen = now;

#if defined(hpux10) || defined(hpux11)
    pstat_getdynamic(&pst_dyn, sizeof(struct pst_dynamic), 1, 0);
    nproc = pst_dyn.psd_activeprocs;
    bytes = nproc * sizeof(struct pst_status);
    if ((proc_table =
         (struct pst_status *) realloc(proc_table, bytes)) == NULL) {
        current_proc_entry = nproc + 1;
        return;
    }
    pstat_getproc(proc_table, sizeof(struct pst_status), nproc, 0);

#elif defined(solaris2)
    if (getKstatInt("unix", "system_misc", "nproc", &nproc)) {
        current_proc_entry = nproc + 1;
        return;
    }
    bytes = nproc * sizeof(int);
    if ((proc_table = (int *) realloc(proc_table, bytes)) == NULL) {
        current_proc_entry = nproc + 1;
        return;
    }
    {
        DIR            *f;
        struct dirent  *dp;
#if _SLASH_PROC_METHOD_ == 0
        if (kd == NULL) {
            current_proc_entry = nproc + 1;
            return;
        }
#endif
        f = opendir("/proc");
        current_proc_entry = 0;
        while ((dp = readdir(f)) != NULL && current_proc_entry < nproc)
            if (dp->d_name[0] != '.')
                proc_table[current_proc_entry++] = atoi(dp->d_name);
        closedir(f);
    }
#elif defined(aix4) || defined(aix5)
    {
		pid_t proc_index = 0;
		int avail = 1024;
		if (proc_table) {
			free(proc_table);
		}
		nproc = 0;
		proc_table = malloc(sizeof(proc_table[0]) * avail);
		for (;;) {
			int got;
			if (!proc_table) {
				nproc = 0;
				snmp_log_perror("Init_HR_SWRun-malloc");
				return;
			}
			got = getprocs(proc_table + nproc, sizeof(proc_table[0]),
						   0, sizeof(struct fdsinfo),
						   &proc_index, avail - nproc);
			nproc += got;
			if (nproc < avail) {
				break;
			}
			avail += 1024;
			proc_table = realloc(proc_table, avail * sizeof(proc_table[0]));
		}
    }
#elif HAVE_KVM_GETPROCS
    {
        if (kd == NULL) {
            nproc = 0;
            return;
        }
        proc_table = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
    }
#else

    current_proc_entry = 1;
#ifndef bsdi2
    nproc = 0;

    if (auto_nlist(NPROC_SYMBOL, (char *) &nproc, sizeof(int)) == 0) {
        snmp_log_perror("Init_HR_SWRun-auto_nlist NPROC");
        return;
    }
#endif
    bytes = nproc * sizeof(struct proc);

    if (proc_table)
        free((char *) proc_table);
    if ((proc_table = (struct proc *) malloc(bytes)) == NULL) {
        nproc = 0;
        snmp_log_perror("Init_HR_SWRun-malloc");
        return;
    }

    {
        int             proc_table_base;
        if (auto_nlist
            (PROC_SYMBOL, (char *) &proc_table_base,
             sizeof(proc_table_base)) == 0) {
            nproc = 0;
            snmp_log_perror("Init_HR_SWRun-auto_nlist PROC");
            return;
        }
        if (NETSNMP_KLOOKUP(proc_table_base, (char *) proc_table, bytes) == 0) {
            nproc = 0;
            snmp_log_perror("Init_HR_SWRun-klookup");
            return;
        }
    }
#endif
    current_proc_entry = 0;
}

int
Get_Next_HR_SWRun(void)
{
    while (current_proc_entry < nproc) {
#if defined(hpux10) || defined(hpux11)
        return proc_table[current_proc_entry++].pst_pid;
#elif defined(solaris2)
        return proc_table[current_proc_entry++];
#elif HAVE_KVM_GETPROCS
    #if defined(freebsd5) && __FreeBSD_version >= 500014
        if (proc_table[current_proc_entry].ki_stat != 0)
            return proc_table[current_proc_entry++].ki_pid;
    #else
        if (proc_table[current_proc_entry].kp_proc.p_stat != 0)
            return proc_table[current_proc_entry++].kp_proc.p_pid;
    #endif
#elif defined(aix4) || defined(aix5)
        if (proc_table[current_proc_entry].pi_state != 0)
            return proc_table[current_proc_entry++].pi_pid;
        else
            ++current_proc_entry;
#else
        if (proc_table[current_proc_entry].p_stat != 0)
            return proc_table[current_proc_entry++].p_pid;
        else
            ++current_proc_entry;
#endif

    }
    return -1;
}

void
End_HR_SWRun(void)
{
    current_proc_entry = nproc + 1;
}
#endif

int
count_processes(void)
{
#if !(defined(linux) || defined(cygwin) || defined(hpux10) || defined(hpux11) || defined(solaris2) || HAVE_KVM_GETPROCS || defined(dynix))
    int             i;
#endif
    int             total = 0;

    Init_HR_SWRun();
#if defined(hpux10) || defined(hpux11) || HAVE_KVM_GETPROCS || defined(solaris2)
    total = nproc;
#else
#if defined(aix4) || defined(aix5)
    for (i = 0; i < nproc; ++i) {
        if (proc_table[i].pi_state != 0)
#elif !defined(linux) && !defined(cygwin) && !defined(dynix)
    for (i = 0; i < nproc; ++i) {
        if (proc_table[i].p_stat != 0)
#else
    while (Get_Next_HR_SWRun() != -1) {
#endif
        ++total;
    }
#endif                          /* !hpux10 && !hpux11 && !HAVE_KVM_GETPROCS && !solaris2 */
    End_HR_SWRun();
    return total;
}
