/* * 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 #include #include #include "sunProcesses.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }