blob: 775d6c88fed8cd8d83ca9c5a46e6c9ee437bcb13 [file] [log] [blame]
#include <config.h>
#ifdef solaris2
#define _KMEMUSER /* Needed by <sys/user.h> */
#include <sys/types.h> /* helps define struct rlimit */
#endif
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <math.h>
#include <ctype.h>
#include <sys/types.h>
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#include "mibincl.h"
#include "struct.h"
#include "proc.h"
#ifdef USING_UCD_SNMP_ERRORMIB_MODULE
#include "errormib.h"
#else
#define setPerrorstatus(x) perror(x)
#endif
#include "util_funcs.h"
#include "read_config.h"
#include "agent_read_config.h"
#include "mib_module_config.h"
#include "../../../snmplib/system.h"
static struct myproc *get_proc_instance (struct myproc *, oid);
struct myproc *procwatch = NULL;
static struct extensible fixproc;
int numprocs=0;
void init_proc(void)
{
/* define the structure we're going to ask the agent to register our
information at */
struct variable2 extensible_proc_variables[] = {
{MIBINDEX, ASN_INTEGER, RONLY, var_extensible_proc, 1, {MIBINDEX}},
{ERRORNAME, ASN_OCTET_STR, RONLY, var_extensible_proc, 1, {ERRORNAME}},
{PROCMIN, ASN_INTEGER, RONLY, var_extensible_proc, 1, {PROCMIN}},
{PROCMAX, ASN_INTEGER, RONLY, var_extensible_proc, 1, {PROCMAX}},
{PROCCOUNT, ASN_INTEGER, RONLY, var_extensible_proc, 1, {PROCCOUNT}},
{ERRORFLAG, ASN_INTEGER, RONLY, var_extensible_proc, 1, {ERRORFLAG}},
{ERRORMSG, ASN_OCTET_STR, RONLY, var_extensible_proc, 1, {ERRORMSG}},
{ERRORFIX, ASN_INTEGER, RWRITE, var_extensible_proc, 1, {ERRORFIX}},
{ERRORFIXCMD, ASN_OCTET_STR, RONLY, var_extensible_proc, 1, {ERRORFIXCMD}}
};
/* Define the OID pointer to the top of the mib tree that we're
registering underneath */
oid proc_variables_oid[] = { EXTENSIBLEMIB,PROCMIBNUM,1 };
/* register ourselves with the agent to handle our mib tree */
REGISTER_MIB("ucd-snmp/proc", extensible_proc_variables, variable2, \
proc_variables_oid);
snmpd_register_config_handler("proc", proc_parse_config, proc_free_config,
"process-name [max-num] [min-num]");
snmpd_register_config_handler("procfix", procfix_parse_config, NULL,
"process-name program [arguments...]");
}
/* Define snmpd.conf reading routines first. They get called
automatically by the invocation of a macro in the proc.h file. */
void proc_free_config(void) {
struct myproc *ptmp, *ptmp2;
for (ptmp = procwatch; ptmp != NULL;) {
ptmp2 = ptmp;
ptmp = ptmp->next;
free(ptmp2);
}
procwatch = NULL;
numprocs = 0;
}
/* find a give entry in the linked list associated with a proc name */
static struct myproc *get_proc_by_name(char *name) {
struct myproc *ptmp;
if (name == NULL)
return NULL;
for(ptmp = procwatch; ptmp != NULL && strcmp(ptmp->name, name) != 0;
ptmp = ptmp->next);
return ptmp;
}
void procfix_parse_config(char *token, char* cptr)
{
char tmpname[STRMAX];
struct myproc *procp;
/* don't allow two entries with the same name */
cptr = copy_word(cptr,tmpname);
if ((procp = get_proc_by_name(tmpname)) == NULL) {
config_perror("No proc entry registered for this proc name yet.");
return;
}
if (strlen(cptr) > sizeof(procp->fixcmd)) {
config_perror("fix command too long.");
return;
}
strcpy(procp->fixcmd, cptr);
}
void proc_parse_config(char *token, char* cptr)
{
char tmpname[STRMAX];
struct myproc **procp = &procwatch;
/* don't allow two entries with the same name */
copy_word(cptr,tmpname);
if (get_proc_by_name(tmpname) != NULL) {
config_perror("Already have an entry for this process.");
return;
}
/* skip past used ones */
while (*procp != NULL)
procp = &((*procp)->next);
(*procp) = (struct myproc *) malloc(sizeof(struct myproc));
(*procp)->next = NULL;
numprocs++;
/* not blank and not a comment */
copy_word(cptr,(*procp)->name);
cptr = skip_not_white(cptr);
if ((cptr = skip_white(cptr)))
{
(*procp)->max = atoi(cptr);
cptr = skip_not_white(cptr);
if ((cptr = skip_white(cptr)))
(*procp)->min = atoi(cptr);
else
(*procp)->min = 0;
}
else
{
(*procp)->max = 0;
(*procp)->min = 0;
}
#ifdef PROCFIXCMD
sprintf((*procp)->fixcmd, PROCFIXCMD, (*procp)->name);
#endif
DEBUGMSGTL(("ucd-snmp/proc", "Read: %s (%d) (%d)\n",
(*procp)->name, (*procp)->max, (*procp)->min));
}
/* The routine that handles everything */
u_char *var_extensible_proc(struct variable *vp,
oid *name,
size_t *length,
int exact,
size_t *var_len,
WriteMethod **write_method)
{
struct myproc *proc;
static long long_ret;
static char errmsg[300];
if (header_simple_table(vp,name,length,exact,var_len,write_method,numprocs))
return(NULL);
if ((proc = get_proc_instance(procwatch,name[*length-1]))) {
switch (vp->magic) {
case MIBINDEX:
long_ret = name[*length-1];
return((u_char *) (&long_ret));
case ERRORNAME: /* process name to check for */
*var_len = strlen(proc->name);
return((u_char *) (proc->name));
case PROCMIN:
long_ret = proc->min;
return((u_char *) (&long_ret));
case PROCMAX:
long_ret = proc->max;
return ((u_char *) (&long_ret));
case PROCCOUNT:
long_ret = sh_count_procs(proc->name);
return ((u_char *) (& long_ret));
case ERRORFLAG:
long_ret = sh_count_procs(proc->name);
if (long_ret >= 0 &&
((proc->min && long_ret < proc->min) ||
(proc->max && long_ret > proc->max) ||
(proc->min == 0 && proc->max == 0 && long_ret < 1))) {
long_ret = 1;
}
else {
long_ret = 0;
}
return ((u_char *) (& long_ret));
case ERRORMSG:
long_ret = sh_count_procs(proc->name);
if (long_ret < 0) {
errmsg[0] = 0; /* catch out of mem errors return 0 count */
} else if (proc->min && long_ret < proc->min) {
sprintf(errmsg,"Too few %s running (# = %d)",
proc->name, (int) long_ret);
}
else if (proc->max && long_ret > proc->max) {
sprintf(errmsg,"Too many %s running (# = %d)",
proc->name, (int) long_ret);
}
else if (proc->min == 0 && proc->max == 0 && long_ret < 1) {
sprintf(errmsg,"No %s process running.", proc->name);
}
else {
errmsg[0] = 0;
}
*var_len = strlen(errmsg);
return((u_char *) errmsg);
case ERRORFIX:
*write_method = fixProcError;
long_return = fixproc.result;
return ((u_char *) &long_return);
case ERRORFIXCMD:
if (proc->fixcmd) {
*var_len = strlen(proc->fixcmd);
return (u_char *)proc->fixcmd;
}
errmsg[0] = 0;
*var_len = 0;
return ((u_char *) errmsg);
}
return NULL;
}
return NULL;
}
int
fixProcError(int action,
u_char *var_val,
u_char var_val_type,
size_t var_val_len,
u_char *statP,
oid *name,
size_t name_len)
{
struct myproc *proc;
long tmp=0;
if ((proc = get_proc_instance(procwatch,name[10]))) {
if (var_val_type != ASN_INTEGER) {
printf("Wrong type != int\n");
return SNMP_ERR_WRONGTYPE;
}
tmp = *((long *) var_val);
if (tmp == 1 && action == COMMIT) {
if (proc->fixcmd[0]) {
strcpy(fixproc.command, proc->fixcmd);
exec_command(&fixproc);
}
}
return SNMP_ERR_NOERROR;
}
return SNMP_ERR_WRONGTYPE;
}
static struct myproc *get_proc_instance(struct myproc *proc,
oid inst)
{
int i;
if (proc == NULL) return(NULL);
for (i=1;i != inst && proc != NULL; i++) proc = proc->next;
return(proc);
}
#ifdef bsdi2
#include <sys/param.h>
#include <sys/sysctl.h>
#define PP(pp, field) ((pp)->kp_proc . field)
#define EP(pp, field) ((pp)->kp_eproc . field)
#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
/* these are for keeping track of the proc array */
static int nproc = 0;
static int onproc = -1;
static struct kinfo_proc *pbase = 0;
int sh_count_procs(char *procname)
{
register int i,ret = 0;
register struct kinfo_proc *pp;
static int mib[] = { CTL_KERN, KERN_PROC , KERN_PROC_ALL };
if (sysctl(mib, 3, NULL, &nproc, NULL, 0) < 0) return 0;
if(nproc > onproc || !pbase) {
if((pbase = (struct kinfo_proc*) realloc(pbase,
nproc + sizeof(struct kinfo_proc))) == 0) return -1;
onproc = nproc;
memset(pbase,0,nproc + sizeof(struct kinfo_proc));
}
if (sysctl(mib, 3, pbase, &nproc, NULL, 0) < 0) return -1;
for (pp = pbase, i = 0; i < nproc / sizeof(struct kinfo_proc); pp++, i++)
{
if (PP(pp, p_stat) != 0 && (((PP(pp, p_flag) & P_SYSTEM) == 0)))
{
if (PP(pp, p_stat) != SZOMB && !strcmp(PP(pp,p_comm),procname)) ret++;
}
}
return ret;
}
#elif OSTYPE == ULTRIXID
#define NPROCS 32 /* number of proces to read at once */
extern int kmem, mem, swap;
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/vm.h>
#include <machine/pte.h>
#ifdef HAVE_NLIST_H
#include <nlist.h>
#endif
static struct user *getuser (struct proc *);
static int getword (off_t);
static int getstruct (off_t, char *, off_t, int);
static struct nlist proc_nl[] = {
{ "_nproc" },
#define X_NPROC 0
{ "_proc" },
#define X_PROC 1
{ "_proc_bitmap" },
#define X_PROC_BITMAP 2
{ NULL }
};
int
sh_count_procs(char *procname)
{
int total, proc_active, nproc;
int thisproc = 0;
int absolute_proc_number = -1;
struct user *auser;
struct proc *aproc, *procp;
unsigned bitmap;
struct proc procs[NPROCS], *procsp;
static int inited = 0;
procp = (struct proc *)getword(proc_nl[X_PROC].n_value);
nproc = getword(proc_nl[X_NPROC].n_value);
total = 0;
for (;;) {
do {
while (thisproc == 0) {
int nread;
int psize;
if (nproc == 0)
return(total);
thisproc = MIN(NPROCS, nproc);
psize = thisproc * sizeof(struct proc);
nproc -= thisproc;
if (lseek(kmem, (off_t)procp, L_SET) == -1 ||
(nread = read(kmem, (char *)procs, psize)) < 0) {
/* warn("read proc"); */
return(total);
}
else if (nread != psize) {
thisproc = nread / sizeof(struct proc);
nproc = 0;
/* warn("read proc: short read"); */
}
procsp = procs;
procp += thisproc;
}
aproc = procsp++;
thisproc--;
absolute_proc_number++;
if ((absolute_proc_number % 32) == 0)
bitmap = getword((unsigned int)proc_nl[X_PROC_BITMAP].n_value
+ ((absolute_proc_number / 32) * 4));
proc_active = (bitmap & (1 << (absolute_proc_number % 32))) != 0;
if (proc_active && aproc->p_stat != SZOMB && !(aproc->p_type & SWEXIT))
auser = getuser(aproc);
} while (!proc_active || auser == NULL);
if (strcmp(auser->u_comm, procname) == 0)
total ++;
}
}
#define SW_UADDR dtob(getword((off_t)dmap.dm_ptdaddr))
#define SW_UBYTES sizeof(struct user)
#define SKRD(file, src, dst, size) \
(lseek(file, (off_t)(src), L_SET) == -1) || \
(read(file, (char *)(dst), (size)) != (size))
static struct user *
getuser(struct proc *aproc)
{
static union {
struct user user;
char upgs[UPAGES][NBPG];
} u;
static struct pte uptes[UPAGES];
static struct dmap dmap;
int i, nbytes;
/*
* If process is not in core, we simply snarf it's user struct
* from the swap device.
*/
if ((aproc->p_sched & SLOAD) == 0) {
if (!getstruct((off_t)aproc->p_smap, "aproc->p_smap", (off_t)&dmap,
sizeof(dmap))) {
/* warnx("can't read dmap for pid %d from %s", aproc->p_pid,
_PATH_DRUM); */
return(NULL);
}
if (SKRD(swap, SW_UADDR, &u.user, SW_UBYTES)) {
/* warnx("can't read u for pid %d from %s", aproc->p_pid, _PATH_DRUM); */
return(NULL);
}
return (&u.user);
}
/*
* Process is in core. Follow p_addr to read in the page
* table entries that map the u-area and then read in the
* physical pages that comprise the u-area.
*
* If at any time, an lseek() or read() fails, print a warning
* message and return NULL.
*/
if (SKRD(kmem, aproc->p_addr, uptes, sizeof(uptes))) {
/* warnx("can't read user pt for pid %d from %s", aproc->p_pid, _PATH_DRUM); */
return(NULL);
}
nbytes = sizeof(struct user);
for (i = 0; i < UPAGES && nbytes > 0; i++) {
if (SKRD(mem, ptob(uptes[i].pg_pfnum), u.upgs[i], NBPG)) {
/* warnx("can't read user page %u for pid %d from %s",
uptes[i].pg_pfnum, aproc->p_pid, _PATH_MEM); */
return(NULL);
}
nbytes -= NBPG;
}
return(&u.user);
}
static int
getword(off_t loc)
{
int val;
if (SKRD(kmem, loc, &val, sizeof(val)))
exit(1);
return(val);
}
static int
getstruct(off_t loc,
char *name,
off_t dest,
int size)
{
if(SKRD(kmem,loc,dest,size))
return(0);
return(1);
}
#elif OSTYPE == SOLARISID
#ifdef _SLASH_PROC_METHOD_
#include <fcntl.h>
#include <dirent.h>
#include <procfs.h>
/*
* Gets process information from /proc/.../psinfo
*/
int
sh_count_procs(char *procname)
{
int fd,total = 0;
struct psinfo info;
char fbuf[32];
struct dirent *ent;
DIR *dir;
if (!(dir = opendir("/proc")))
return -1;
while ((ent = readdir(dir))) {
if (!strcmp(ent->d_name,"..") || !strcmp(ent->d_name,"."))
continue;
snprintf(fbuf,sizeof fbuf,"/proc/%s/psinfo",ent->d_name);
if ((fd = open(fbuf,O_RDONLY)) < 0) { /* Continue or return error? */
closedir(dir);
return -1;
}
if (read(fd,(char*)&info,sizeof(struct psinfo)) != sizeof(struct psinfo)) {
close(fd);
closedir(dir);
return -1;
}
if (!info.pr_nlwp && !info.pr_lwp.pr_lwpid) {
/* Zombie process */
} else
if (!strcmp(procname,info.pr_fname))
total++;
close(fd);
}
closedir(dir);
return total;
}
#else /* _SLASH_PROC_METHOD_ */
#define _KMEMUSER /* Needed by <sys/user.h> */
#include <kvm.h>
#include <fcntl.h>
#include <sys/user.h>
#include <sys/proc.h>
int
sh_count_procs(char *procname)
{
static kvm_t *kd = NULL;
struct proc *p;
struct user *u;
int total;
if (kd == NULL) {
kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "sunps");
if( !kd ) {
return(-1);
}
/* error check! */
}
if( kvm_setproc(kd) < 0 ) {
return( -1 );
}
kvm_setproc(kd);
total = 0;
while ((p = kvm_nextproc(kd)) != NULL) {
if( !p ) {
return( -1 );
}
u = kvm_getu(kd, p);
/* Skip this entry if u or u->u_comm is a NULL pointer */
if( !u ) {
continue;
}
if (strcmp(procname, u->u_comm) == 0)
total++;
}
return(total);
}
#endif /* _SLASH_PROC_METHOD_ */
#else
int sh_count_procs(char *procname)
{
char line[STRMAX], *cptr;
int ret=0, fd;
FILE *file;
#ifndef EXCACHETIME
#endif
struct extensible ex;
if ((fd = get_ps_output(&ex)) > 0) {
if ((file = fdopen(fd,"r")) == NULL) {
setPerrorstatus("fdopen");
return (-1);
}
while(fgets(line,sizeof(line),file) != NULL)
{
if ((cptr = find_field(line,LASTFIELD)) == NULL)
continue;
copy_word(cptr,line);
if (!strcmp(line,procname)) ret++;
}
if (ftell(file) < 2) {
#ifdef USING_UCD_SNMP_ERRORMIB_MODULE
seterrorstatus("process list unreasonable short (mem?)",2);
#endif
ret = -1;
}
fclose(file);
close(fd);
wait_on_exec(&ex);
ex.pid = 0;
} else {
ret = -1;
}
return(ret);
}
#endif
int get_ps_output(struct extensible *ex)
{
int fd;
strcpy(ex->command,PSCMD);
fd = get_exec_output(ex);
return(fd);
}