components/net-snmp-57/sun/agent/modules/seaExtensions/sunProcesses.c
author Gowtham Thommandra <Gowtham.Thommandra@Oracle.COM>
Fri, 11 Dec 2015 03:49:26 -0800
changeset 5867 445e2cf1c845
parent 1853 components/net-snmp/sun/agent/modules/seaExtensions/sunProcesses.c@7d5d194ae029
permissions -rw-r--r--
PSARC/2014/169 Net-snmp upgrade to version 5.7.2.1 15685782 SUNBT7005330 NET-SNMP upgrade to 5.7.2.1 15705167 SUNBT7030994 find memory leaks for snmp-notify after doing libfmnotif

/*
 * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
 *
 * U.S. Government Rights - Commercial software. Government users are subject
 * to the Sun Microsystems, Inc. standard license agreement and applicable
 * provisions of the FAR and its supplements.
 *
 *
 * This distribution may include materials developed by third parties. Sun,
 * Sun Microsystems, the Sun logo and Solaris are trademarks or registered
 * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
 *
 */
/*
 * Note: this file originally auto-generated by mib2c using
 *         : mib2c.iterate.conf,v 5.5 2002/12/16 22:50:18 hardaker Exp $
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "sunProcesses.h"

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <procfs.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
#include <ftw.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <memory.h>
#include <pwd.h>
#include <syslog.h>

#include "snmpvars.h"
#include "agent.h"
#include "asn1.h"

/* The following code is borrowed from ps.c */
#define        NUID        64
#define TRUE 1
#define FALSE 0

#define NTTYS 20      /* max ttys that can be specified with the -t option  */
#define SIZ 30        /* max processes that can be specified with -p and -g */
#define ARGSIZ 30     /* size of buffer holding args for -t, -p, -u options */

#define FSTYPE_MAX 8

#ifndef MAXLOGIN
#define MAXLOGIN 8    /* max number of chars in login that will be printed */
#endif

#define UDQ 50


static struct psinfo info; /* process information structure from /proc */

char *ttyname();
static char *psfile = "/tmp/mibiisa_ps_data";


static int ndev;             /* number of devices */
static int maxdev;           /* number of devl structures allocated */

#define DNSIZE 14
static struct devl {         /* device list */
    char   dname[DNSIZE]; /* device name */
    dev_t  dev;           /* device number */
} *devl = NULL;

static char *procdir = "/proc";  /* standard /proc directory */
static int  rd_only = 0;         /* flag for remote filesystem read-only */
void usage();             /* print usage message and quit */

static time_t ps_cache_time = 0;
time_t cache_now = 0;
int cache_lifetime = 45;

static void call_ftw_for_dev(void);
static void wrdata();
static void write_tmp_file();
static int isprocdir();
static void get_ps_data(void);
static void clean_ps(ps_ldata_t *);
static char *get_usr_name(uid_t);
static ps_data_t *find_ps_data(pid_t pid);
static void pr_ps(void);

ps_data_t *pstable = PS_NULL;
int pstable_lines = 0;   /* # of items in memory block pointed    */
                                /* to by pstable.                        */

static void
clean_ps(ps_ldata_t *head)
{
    if (head != PS_LNULL) {
        ps_ldata_t *pdp;
        ps_ldata_t *nxt;
        for (pdp = head; pdp != PS_LNULL; pdp = nxt) {
            nxt = pdp->link;
            free(pdp);
        }
    }
}

static int
pscomp(ps_data_t *i, ps_data_t *j)
{
    return (i->pid - j->pid);
}

static struct ncache {
    uid_t uid;
    char  name[USRNM_SZ+1];
} nc[NUID];

/*
 * This function assumes that the password file is hashed
 * (or some such) to allow fast access based on a uid key.
 */
static char *
get_usr_name(uid_t uid)
{
    struct passwd *pw;
    int cp;

#if        (((NUID) & ((NUID) - 1)) != 0)
cp = uid % (NUID);
#else
cp = uid & ((NUID) - 1);
#endif
    if (uid >= 0 && nc[cp].uid == uid && nc[cp].name[0])
        return (nc[cp].name);
    pw = getpwuid(uid);
    if (!pw)
        return ((char *)0);
    nc[cp].uid = uid;
    strncpy(nc[cp].name, pw->pw_name, USRNM_SZ);

    return (nc[cp].name);
}

void
pr_ps(void)
{
    ps_data_t *psp;
    int lines;

    printf("%d entries\n", pstable_lines);
    printf("UID   PID   PPID   SZ   USR   WCHAN  TTY  CPU  CMD \n\n");

    for (psp = pstable, lines = 0; lines < pstable_lines; psp++, lines++) {
        printf("%d     %u     %u        %d    %s     %s    %s    %d   %s\n",
            psp->uid,
            psp->pid,
            psp->ppid,
            psp->sz,
            psp->usrname,
            psp->wchan,
            psp->tty,
            psp->cpu,
            psp->cmd);
    }
}

/*
 *  Locate a particular PID.
 *  Return a pointer to the entry or NULL if not found.
 */
static ps_data_t * find_ps_data(pid_t pid)
{
    ps_data_t *psp;
    ps_data_t key;

    key.pid = pid;

    /* Should add a cache here */

    psp = (ps_data_t *)bsearch((char *)&key, (char *)pstable,
                            pstable_lines, sizeof (ps_data_t),
                            (int (*)())pscomp);
    return (psp);
}


void
get_ps_data(void)
{
    ps_ldata_t *ps_last = PS_LNULL;
    ps_ldata_t *ps_head = PS_LNULL;
    ps_ldata_t *psp;
    ps_data_t  *pstp;
    static char *usrname;
    int i = 0;
    DIR *dirp;
    struct dirent *dentp;
    char pname[MAXNAMELEN];
    int pdlen;
    char *gettty();

    if (pstable != PS_NULL) {  /* Don't run ps unless we need to */
        if ((cache_now - ps_cache_time) <= cache_lifetime)
            return;
        free(pstable);
    }

    pstable_lines = 0;
    ps_cache_time = cache_now;
    /*
     * Determine root path for remote machine.
     */
    if (!readata()) {        /* get data from psfile */
        call_ftw_for_dev();
        wrdata();
    }

    /*
     * Determine which processes to print info about by searching
     * the /proc directory and looking at each process.
     */
    if ((dirp = opendir(procdir)) == NULL) {
        (void) SYSLOG0("Cannot open PROC directory\n");
        return;
    }

    (void) strcpy(pname, procdir);
    pdlen = strlen(pname);
    pname[pdlen++] = '/';

    /* for each active process --- */
    while (dentp = readdir(dirp)) {
        int procfd;

        if (dentp->d_name[0] == '.')                /* skip . and .. */
                continue;
        (void) strcpy(pname + pdlen, dentp->d_name);
        (void) strcat(pname + pdlen, "/psinfo");
retry:
        if ((procfd = open(pname, O_RDONLY)) == -1)
                continue;

        /*
         * Get the info structure for the process and close quickly.
         */
        if (read(procfd, &info, sizeof (info)) != sizeof (info)) {
            int saverr = errno;

            (void) close(procfd);
            if (saverr == EAGAIN)
                goto retry;
            if (saverr != ENOENT)
                (void) SYSLOG2("read of %s: %s\n",
                               pname, strerror(saverr));
            continue;
        }
        (void) close(procfd);
        if ((psp = (ps_ldata_t *)malloc(sizeof (ps_ldata_t))) == PS_LNULL)
            break;
        memset((char *)psp, 0, sizeof (ps_ldata_t));
        psp->pdata.uid = info.pr_uid;
        psp->pdata.pid = info.pr_pid;
        psp->pdata.ppid = info.pr_ppid;
        psp->pdata.sz = info.pr_size;
        if (info.pr_lwp.pr_wchan)
            sprintf(psp->pdata.wchan, "%9x", info.pr_lwp.pr_wchan);
        else
            strcpy(psp->pdata.wchan, "         ");
        memset(&psp->pdata.stat[0], 0, STAT_SZ+1);
        if (info.pr_lwp.pr_sname)
            psp->pdata.stat[0] = info.pr_lwp.pr_sname;
        i = 0;
        strcpy(psp->pdata.tty, (char *)gettty(&i));
        psp->pdata.cpu = info.pr_time.tv_sec;
        strcpy(psp->pdata.cmd, info.pr_fname);

        if ((usrname = (get_usr_name(psp->pdata.uid))) != NULL)
            strncpy(psp->pdata.usrname, usrname, USRNM_SZ);
        else {
            free(psp);
            continue;
        }

        psp->pdata.usrname[USRNM_SZ] = '\0';
        pstable_lines++;
        if (ps_last == PS_LNULL)
            ps_head = psp;
        else
            ps_last->link = psp;
        ps_last = psp;
    }

    (void) closedir(dirp);
    if ((pstable = (ps_data_t *)malloc(pstable_lines
                    * sizeof (ps_data_t))) == PS_NULL) {
        clean_ps(ps_head);
        return;
    }
    for (pstp = pstable, psp = ps_head; psp != PS_LNULL;
                                    pstp++, psp = psp->link) {
        memcpy((char *)pstp, (char *)&(psp->pdata), sizeof (ps_data_t));
    }
    clean_ps(ps_head);
    qsort(pstable, pstable_lines, sizeof (ps_data_t), (int (*)())pscomp);
}

int
readata()
{
    struct stat sbuf1, sbuf2;
    int fd;

    if ((fd = open(psfile, O_RDONLY)) == -1)
        return (0);

    if (fstat(fd, &sbuf1) < 0 || sbuf1.st_size == 0 ||
        stat("/dev", &sbuf2) == -1 || sbuf1.st_mtime <= sbuf2.st_mtime ||
        sbuf1.st_mtime <= sbuf2.st_ctime) {

        if (!rd_only) {                /* if read-only, believe old data */
            (void) close(fd);
            return (0);
        }
    }

    /* Read /dev data from psfile. */
    if (read_tmp_file(fd, (char *) &ndev, sizeof (ndev)) == 0)  {
        (void) close(fd);
        return (0);
    }

    if (devl)
        free(devl);

    if ((devl = (struct devl *)malloc(ndev * sizeof (*devl))) == NULL) {
        SYSLOG1("malloc() for device table failed, %s\n", strerror(errno));
        exit(1);
    }
    if (read_tmp_file(fd, (char *)devl, ndev * sizeof (*devl)) == 0)  {
        (void) close(fd);
        return (0);
    }

    (void) close(fd);
    return (1);
}

/*
 * call_ftw_for_dev() uses ftw() to pass pathnames under /dev to gdev()
 * along with a status buffer.
 */
static void
call_ftw_for_dev(void)
{
    int gdev();
    int rcode;

    ndev = 0;
    rcode = ftw("/dev", gdev, 17);

    switch (rcode) {
    case 0:
        return;                /* successful return, devl populated */
    case 1:
        SYSLOG0(" ftw() encountered problem\n");
        break;
    case -1:
        SYSLOG1(" ftw() failed, %s\n", strerror(errno));
        break;
    default:
        SYSLOG1(" ftw() unexpected return, rcode=%d\n", rcode);
        break;
    }
    exit(1);
}

/*
 * gdev() puts device names and ID into the devl structure for character
 * special files in /dev.  The "/dev/" string is stripped from the name
 * and if the resulting pathname exceeds DNSIZE in length then the highest
 * level directory names are stripped until the pathname is DNSIZE or less.
 */
int
gdev(objptr, statp, numb)
    char        *objptr;
    struct stat *statp;
    int        numb;
{
    int i;
    int leng, start;
    static struct devl ldevl[2];
    static int lndev, consflg;

    switch (numb) {

    case FTW_F:
        if ((statp->st_mode & S_IFMT) == S_IFCHR) {
            /* Get more and be ready for syscon & systty. */
            while (ndev + lndev >= maxdev) {
                maxdev += UDQ;
                devl = (struct devl *) ((devl == NULL) ?
                            malloc(sizeof (struct devl) * maxdev) :
                            realloc(devl, sizeof (struct devl) * maxdev));
                if (devl == NULL) {
                    SYSLOG1(" not enough memory for %d devices\n", maxdev);
                    exit(1);
                }
            }
            /*
             * Save systty & syscon entries if the console
             * entry hasn't been seen.
             */
            if (!consflg && (strcmp("/dev/systty", objptr) == 0 ||
                strcmp("/dev/syscon", objptr) == 0)) {
                (void) strncpy(ldevl[lndev].dname, &objptr[5], DNSIZE);
                ldevl[lndev].dev = statp->st_rdev;
                lndev++;
                return (0);
            }

            leng = strlen(objptr);
            /* Strip off /dev/ */
            if (leng < DNSIZE + 4)
                (void) strcpy(devl[ndev].dname, &objptr[5]);
            else {
                start = leng - DNSIZE - 1;

                for (i = start; i < leng && (objptr[i] != '/'); i++)
                            ;
                if (i == leng)
                    (void) strncpy(devl[ndev].dname,
                                    &objptr[start], DNSIZE);
                else
                    (void) strncpy(devl[ndev].dname,
                                    &objptr[i+1], DNSIZE);
            }
            devl[ndev].dev = statp->st_rdev;
            ndev++;
            /*
             * Put systty & syscon entries in devl when console
             * is found.
             */
            if (strcmp("/dev/console", objptr) == 0) {
                consflg++;
                for (i = 0; i < lndev; i++) {
                    (void) strncpy(devl[ndev].dname,
                                    ldevl[i].dname, DNSIZE);
                    devl[ndev].dev = ldevl[i].dev;
                    ndev++;
                }
                lndev = 0;
            }
        }
        return (0);

    case FTW_D:
    case FTW_DNR:
    case FTW_NS:
        return (0);

    default:
        SYSLOG1(" gdev() error, %d, encountered\n", numb);
        return (1);
    }
}


void
wrdata()
{
    char tmpname[MAXNAMELEN];
    char *tfname;
    int        fd;

    (void) umask(02);
    (void) strcpy(tmpname, "/tmp/mibiisa_ps.XXXXXX");
    if ((tfname = mktemp(tmpname)) == NULL || *tfname == '\0') {
        SYSLOG1(" mktemp(\"/tmp/mibiisa_ps.XXXXXX\") failed, %s\n",
                strerror(errno));
        return;
    }

    if ((fd = open(tfname, O_WRONLY|O_CREAT|O_EXCL, 0664)) < 0) {
        SYSLOG2(" open(\"%s\") for write failed, %s\n",
                tfname, strerror(errno));
        return;
    }

    /*
     * Make owner root, group sys.
     */
    (void) chown(tfname, (uid_t)0, (gid_t)3);

    /* write /dev data */
    write_tmp_file(fd, (char *) &ndev, sizeof (ndev));
    write_tmp_file(fd, (char *)devl, ndev * sizeof (*devl));

    (void) close(fd);

    if (rename(tfname, psfile) != 0) {
        SYSLOG2(" rename(\"%s\",\"%s\") failed\n", tfname, psfile);
        return;
    }
}

/*
 * gettty returns the user's tty number or ? if none.
 */
char *
gettty(ip)
int        *ip;        /* where the search left off last time */
{
    int        i;

    if (info.pr_ttydev != PRNODEV && *ip >= 0) {
        for (i = *ip; i < ndev; i++) {
            if (devl[i].dev == info.pr_ttydev) {
                *ip = i + 1;
                return (devl[i].dname);
            }
        }
    }
    *ip = -1;
    return ("?");
}

/*
 * Special read; unlinks psfile on read error.
 */
int
read_tmp_file(fd, bp, bs)
    int fd;
    char *bp;
    unsigned int bs;
{
    int rbs;

    if ((rbs = read(fd, bp, bs)) != bs) {
        SYSLOG2("read_tmp_file() error on read, rbs=%d, bs=%d\n",
                rbs, bs);
        (void) unlink(psfile);
        return (0);
    }
    return (1);
}

/*
 * Special write; unlinks psfile on write error.
 */
void
write_tmp_file(fd, bp, bs)
int fd;
char *bp;
unsigned bs;
{
    int wbs;

    if ((wbs = write(fd, bp, bs)) != bs) {
        SYSLOG2("write_tmp_file() error on write, wbs=%d, bs=%d\n",
                wbs, bs);
        (void) unlink(psfile);
    }
}


/*
 * Return true iff dir is a /proc directory.
 *
 * This works because of the fact that "/proc/0" and "/proc/00" are the
 * same file, namely process 0, and are not linked to each other.  Ugly.
 */
static int
isprocdir(dir)                /* return TRUE iff dir is a PROC directory */
    char *dir;
{
    struct stat stat1;        /* dir/0  */
    struct stat stat2;        /* dir/00 */
    char path[200];
    char *p;

    /*
     * Make a copy of the directory name without trailing '/'s
     */
    if (dir == NULL)
        (void) strcpy(path, ".");
    else {
        (void) strncpy(path, dir, (int) sizeof (path) - 4);
        path[sizeof (path)-4] = '\0';
        p = path + strlen(path);
        while (p > path && *--p == '/')
            *p = '\0';
        if (*path == '\0')
            (void) strcpy(path, ".");
    }

    /*
     * Append "/0" to the directory path and stat() the file.
     */
    p = path + strlen(path);
    *p++ = '/';
    *p++ = '0';
    *p = '\0';
    if (stat(path, &stat1) != 0)
        return (FALSE);

    /*
     * Append "/00" to the directory path and stat() the file.
     */
    *p++ = '0';
    *p = '\0';
    if (stat(path, &stat2) != 0)
        return (FALSE);

    /*
     * See if we ended up with the same file.
     */
    if (stat1.st_dev != stat2.st_dev || stat1.st_ino != stat2.st_ino ||
        stat1.st_mode != stat2.st_mode ||
        stat1.st_nlink != stat2.st_nlink ||
        stat1.st_uid != stat2.st_uid || stat1.st_gid != stat2.st_gid ||
        stat1.st_size != stat2.st_size)
        return (FALSE);

    /*
     * Return TRUE iff we have a regular file with a single link.
     */
    return ((stat1.st_mode & S_IFMT) == S_IFREG && stat1.st_nlink == 1);
}


/*
 * Initialize the sunProcessTable table by defining its contents and how
 * it's structured
 */
void initialize_table_sunProcessTable(void) {

    static oid sunProcessTable_oid[] = {1, 3, 6, 1, 4, 1, 42, 3, 12};
    netsnmp_table_registration_info *table_info;
    netsnmp_handler_registration *my_handler;
    netsnmp_iterator_info *iinfo;

    /* create the table structure itself */
    table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
    iinfo = SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info);

    /*
     * if your table is read only, it's easiest to change the
     * HANDLER_CAN_RWRITE definition below to HANDLER_CAN_RONLY
     */
    my_handler = netsnmp_create_handler_registration("sunProcessTable",
                                            sunProcessTable_handler,
                                            sunProcessTable_oid,
                                            OID_LENGTH(sunProcessTable_oid),
                                            HANDLER_CAN_RWRITE);

    if (!my_handler || !table_info || !iinfo)
        return; /* mallocs failed */

    /*
     * Setting up the table's definition
     */
    netsnmp_table_helper_add_indexes(table_info, ASN_INTEGER, 0);

    table_info->min_column = 1;
    table_info->max_column = 11;

    /* iterator access routines */
    iinfo->get_first_data_point = sunProcessTable_get_first_data_point;
    iinfo->get_next_data_point = sunProcessTable_get_next_data_point;

    iinfo->table_reginfo = table_info;

    /*
     * registering the table with the master agent
     */
    DEBUGMSGTL(("initialize_table_sunProcessTable",
                "Registering table sunProcessTable as a table iterator\n"));
    netsnmp_register_table_iterator(my_handler, iinfo);
}

/* Initializes the sunProcesses module */
void init_sunProcesses(void) {

    (void) time(&cache_now);

    /* here we initialize all the tables we're planning on supporting */
    initialize_table_sunProcessTable();
}

/*
 * returns the first data point within the sunProcessTable table data.
 *
 * Set the my_loop_context variable to the first data point structure
 * of your choice (from which you can find the next one). This could
 * be anything from the first node in a linked list, to an integer
 * pointer containing the beginning of an array variable.
 *
 * Set the my_data_context variable to something to be returned to
 * you later that will provide you with the data to return in a given
 * row. * This could be the same pointer as what my_loop_context is
 * set to, or something different.
 *
 * The put_index_data variable contains a list of snmp variable
 * bindings, one for each index in your table. * Set the values of
 * each appropriately according to the data matching the first row
 * and return the put_index_data variable at the end of the function.
 */
netsnmp_variable_list *
sunProcessTable_get_first_data_point(void **my_loop_context,
                                void **my_data_context,
                                netsnmp_variable_list *put_index_data,
                                netsnmp_iterator_info *mydata) {
    long long_type;
    netsnmp_variable_list *vptr;
    ps_data_t *ps_ptr;

    get_ps_data();

    ps_ptr = pstable;
    if (ps_ptr == NULL) {
        return (NULL);
    }

    *my_loop_context = ps_ptr;
    *my_data_context = ps_ptr;

    vptr = put_index_data;

    long_type = (long)ps_ptr[0].pid;
    snmp_set_var_value(vptr, (u_char *) &long_type, sizeof(long_type));

    /*    vptr = vptr->next_variable; */
/*
pr_ps();
*/
    return (put_index_data);
}

/*
 * functionally the same as sunProcessTable_get_first_data_point, but
 * my_loop_context has already been set to a previous value and should
 * be updated to the next in the list.  For example, if it was a
 * linked list, you might want to cast it and the return
 * my_loop_context->next.  The my_data_context pointer should be set
 * to something you need later and the indexes in put_index_data
 * updated again.
 */

netsnmp_variable_list *
sunProcessTable_get_next_data_point(void **my_loop_context,
                                        void **my_data_context,
                                        netsnmp_variable_list *put_index_data,
                                        netsnmp_iterator_info *mydata)
{
    long long_type;
    netsnmp_variable_list *vptr;
    ps_data_t *ps_ptr;

    get_ps_data();

    ps_ptr = (ps_data_t *) (*my_loop_context);
    ps_ptr++;

    if (ps_ptr > &(pstable[pstable_lines - 1])) {
        return (NULL);
    }

    *my_loop_context = ps_ptr;
    *my_data_context = ps_ptr;

    vptr = put_index_data;

    long_type = (long)ps_ptr[0].pid;
    snmp_set_var_value(vptr, (u_char *) &long_type, sizeof(long_type));

    /*    vptr = vptr->next_variable; */

    return (put_index_data);
}

/*
 * handles requests for the sunProcessTable table,
 * if anything else needs to be done
 */
int sunProcessTable_handler(netsnmp_mib_handler *handler,
                        netsnmp_handler_registration *reginfo,
                        netsnmp_agent_request_info *reqinfo,
                        netsnmp_request_info *requests) {

    long long_type;
    netsnmp_request_info *request;
    netsnmp_table_request_info *table_info;
    netsnmp_variable_list *var;
    ps_data_t *psp;

    /* For caching purposes, find out what the time is now */
    (void) time(&cache_now);

    for (request = requests; request; request = request->next) {
        var = request->requestvb;
        if (request->processed != 0)
            continue;

        /* perform anything here that you need to do before each */
        /* request is processed. */

        /* the following extracts the my_data_context pointer set in */
        /* the loop functions above.  You can then use the results to */
        /* help return data for the columns of the sunProcessTable */
        /* table in question */
        psp = (ps_data_t *) netsnmp_extract_iterator_context(request);
        if (psp == NULL) {
            if (reqinfo->mode == MODE_GET ||
                reqinfo->mode == MODE_SET_RESERVE1) {

                netsnmp_set_request_error(reqinfo, request,
                                            SNMP_NOSUCHINSTANCE);
                continue;
            }
            /*
             * XXX: no row existed, if you support creation and this is a
             * set, start dealing with it here, else continue
             */
        }

        /* extracts the information about the table from the request */
        table_info = netsnmp_extract_table_info(request);

        /* table_info->colnum contains the column number requested */
        /* table_info->indexes contains a linked list of snmp variable */
        /* bindings for the indexes of the table.  Values in the list */
        /* have been set corresponding to the indexes of the request */
        if (table_info == NULL) {
            continue;
        }

        switch (reqinfo->mode) {
        /*
         * the table_iterator helper should change all GETNEXTs
         * into GETs for you automatically, so you don't have to
         * worry about the GETNEXT case.  Only GETs and SETs need
         * to be dealt with here
         */
        case MODE_GET:
            switch (table_info->colnum) {
            case COLUMN_PSPROCESSID:
                long_type = (long)psp->pid;
                snmp_set_var_typed_value(var, ASN_INTEGER,
                                    (u_char *) &long_type,
                                    sizeof (long_type));
                break;

            case COLUMN_PSPARENTPROCESSID:
                long_type = (long)psp->ppid;
                snmp_set_var_typed_value(var, ASN_INTEGER,
                                    (u_char *) &long_type,
                                    sizeof (long_type));
                break;

            case COLUMN_PSPROCESSSIZE:
                long_type = (long)psp->sz;
                snmp_set_var_typed_value(var, ASN_INTEGER,
                                    (u_char *) &long_type,
                                    sizeof (long_type));
                break;

            case COLUMN_PSPROCESSCPUTIME:
                long_type = (long)psp->cpu;
                snmp_set_var_typed_value(var, ASN_INTEGER,
                                    (u_char *) &long_type,
                                    sizeof (long_type));
                break;

            case COLUMN_PSPROCESSSTATE:
                snmp_set_var_typed_value(var, ASN_OCTET_STR,
                                    (u_char *) &psp->stat,
                                    strlen(psp->stat));
                break;

            case COLUMN_PSPROCESSWAITCHANNEL:
                snmp_set_var_typed_value(var, ASN_OCTET_STR,
                                    (u_char *) &psp->wchan,
                                    strlen(psp->wchan));
                break;

            case COLUMN_PSPROCESSTTY:
                snmp_set_var_typed_value(var, ASN_OCTET_STR,
                                    (u_char *) &psp->tty,
                                    strlen(psp->tty));
                break;

            case COLUMN_PSPROCESSUSERNAME:
                snmp_set_var_typed_value(var, ASN_OCTET_STR,
                                    (u_char *) &psp->usrname,
                                    strlen(psp->usrname));
                break;

            case COLUMN_PSPROCESSUSERID:
                long_type = (long)psp->uid;
                snmp_set_var_typed_value(var, ASN_INTEGER,
                                    (u_char *) &long_type,
                                    sizeof (long_type));
                break;

            case COLUMN_PSPROCESSNAME:
                snmp_set_var_typed_value(var, ASN_OCTET_STR,
                                    (u_char *) &psp->cmd,
                                    strlen(psp->cmd));
                break;

            case COLUMN_PSPROCESSSTATUS:
                long_type = (long)psp->sz;
                snmp_set_var_typed_value(var, ASN_INTEGER,
                                    (u_char *) &long_type,
                                    sizeof (long_type));
                break;

            default:
                /* We shouldn't get here */
                snmp_log(LOG_ERR,
"problem encountered in sunProcessTable_handler: unknown column\n");
            }
            break;

        case MODE_SET_RESERVE1:
            break;

        case MODE_SET_RESERVE2:
            break;

        case MODE_SET_FREE:
            break;

        case MODE_SET_ACTION:
            /* set handling... */
/* XXX don't know about 64 bit */
            if ((int) *(requests->requestvb->val.integer) != 0) {
                (void) kill(psp->pid,
                        (int)*(requests->requestvb->val.integer));
            }
            break;

        case MODE_SET_COMMIT:
            break;

        case MODE_SET_UNDO:
            break;

        default:
            snmp_log(LOG_ERR,
    "problem encountered in sunProcessTable_handler: unsupported mode\n");
        }
    }

    return (SNMP_ERR_NOERROR);
}