blob: 37a5eeb3dfb1121056fa4f8c479feae68f68e6f1 [file] [log] [blame]
#include <net-snmp/net-snmp-config.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.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 <signal.h>
#if HAVE_MACHINE_PARAM_H
#include <machine/param.h>
#endif
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#if HAVE_SYS_VMMETER_H
#if !(defined(bsdi2) || defined(netbsd1))
#include <sys/vmmeter.h>
#endif
#endif
#if HAVE_SYS_CONF_H
#include <sys/conf.h>
#endif
#if HAVE_ASM_PAGE_H
#include <asm/page.h>
#endif
#if HAVE_SYS_SWAP_H
#include <sys/swap.h>
#endif
#if HAVE_SYS_FS_H
#include <sys/fs.h>
#else
#if HAVE_UFS_FS_H
#include <ufs/fs.h>
#else
#if HAVE_UFS_UFS_DINODE_H
#include <ufs/ufs/dinode.h>
#endif
#if HAVE_UFS_FFS_FS_H
#include <ufs/ffs/fs.h>
#endif
#endif
#endif
#if HAVE_MTAB_H
#include <mtab.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#if HAVE_FSTAB_H
#include <fstab.h>
#endif
#if HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
#if HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#if HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#if (!defined(HAVE_STATVFS)) && defined(HAVE_STATFS)
#if HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif
#if HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif
#define statvfs statfs
#endif
#if HAVE_VM_VM_H
#include <vm/vm.h>
#endif
#if HAVE_VM_SWAP_PAGER_H
#include <vm/swap_pager.h>
#endif
#if HAVE_SYS_FIXPOINT_H
#include <sys/fixpoint.h>
#endif
#if HAVE_MALLOC_H
#include <malloc.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#endif
#include <ctype.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/agent/auto_nlist.h>
#include <net-snmp/agent/agent_callbacks.h>
#include <net-snmp/library/system.h>
#include "struct.h"
#include "extensible.h"
#include "mibgroup/util_funcs.h"
#include "utilities/execute.h"
#include "util_funcs/header_simple_table.h"
extern struct myproc *procwatch; /* moved to proc.c */
extern int numprocs; /* ditto */
extern struct extensible *extens; /* In exec.c */
extern struct extensible *relocs; /* In exec.c */
extern int numextens; /* ditto */
extern int numrelocs; /* ditto */
extern struct extensible *passthrus; /* In pass.c */
extern int numpassthrus; /* ditto */
extern netsnmp_subtree *subtrees;
extern struct variable2 extensible_relocatable_variables[];
extern struct variable2 extensible_passthru_variables[];
/*
* the relocatable extensible commands variables
*/
struct variable2 extensible_relocatable_variables[] = {
{MIBINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_extensible_relocatable, 1, {MIBINDEX}},
{ERRORNAME, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_relocatable, 1, {ERRORNAME}},
{SHELLCOMMAND, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_relocatable, 1, {SHELLCOMMAND}},
{ERRORFLAG, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_extensible_relocatable, 1, {ERRORFLAG}},
{ERRORMSG, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_relocatable, 1, {ERRORMSG}},
{ERRORFIX, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE,
var_extensible_relocatable, 1, {ERRORFIX}},
{ERRORFIXCMD, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_relocatable, 1, {ERRORFIXCMD}}
};
void
init_extensible(void)
{
struct variable2 extensible_extensible_variables[] = {
{MIBINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_extensible_shell, 1, {MIBINDEX}},
{ERRORNAME, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_shell, 1, {ERRORNAME}},
{SHELLCOMMAND, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_shell, 1, {SHELLCOMMAND}},
{ERRORFLAG, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
var_extensible_shell, 1, {ERRORFLAG}},
{ERRORMSG, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_shell, 1, {ERRORMSG}},
{ERRORFIX, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE,
var_extensible_shell, 1, {ERRORFIX}},
{ERRORFIXCMD, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
var_extensible_shell, 1, {ERRORFIXCMD}}
};
/*
* Define the OID pointer to the top of the mib tree that we're
* registering underneath
*/
oid extensible_variables_oid[] =
{ NETSNMP_UCDAVIS_MIB, NETSNMP_SHELLMIBNUM, 1 };
/*
* register ourselves with the agent to handle our mib tree
*/
REGISTER_MIB("ucd-snmp/extensible", extensible_extensible_variables,
variable2, extensible_variables_oid);
snmpd_register_config_handler("exec", extensible_parse_config,
extensible_free_config,
"[miboid] name program arguments");
snmpd_register_config_handler("sh", extensible_parse_config,
extensible_free_config,
"[miboid] name program-or-script arguments");
snmpd_register_config_handler("execfix", execfix_parse_config, NULL,
"exec-or-sh-name program [arguments...]");
snmp_register_callback(SNMP_CALLBACK_APPLICATION,
SNMPD_CALLBACK_PRE_UPDATE_CONFIG,
extensible_unregister, NULL);
}
extern int pass_compare(const void *a, const void *b);
void
extensible_parse_config(const char *token, char *cptr)
{
struct extensible *ptmp, **pp;
char *tcptr;
int scount;
/*
* allocate and clear memory structure
*/
ptmp = (struct extensible *) calloc(1, sizeof(struct extensible));
if (ptmp == NULL)
return; /* XXX memory alloc error */
if (*cptr == '.')
cptr++;
if (isdigit((unsigned char) *cptr)) {
/*
* its a relocatable extensible mib
*/
config_perror("WARNING: This output format is not valid, and is only retained for backward compatibility - Please consider using the 'extend' directive instead" );
for (pp = &relocs, numrelocs++; *pp; pp = &((*pp)->next));
(*pp) = ptmp;
pp = &relocs; scount = numrelocs;
} else {
/*
* it goes in with the general extensible table
*/
for (pp = &extens, numextens++; *pp; pp = &((*pp)->next));
(*pp) = ptmp;
pp = &extens; scount = numextens;
}
/*
* the rest is pretty much handled the same
*/
if (!strncasecmp(token, "sh", 2))
ptmp->type = SHPROC;
else
ptmp->type = EXECPROC;
if (isdigit((unsigned char) *cptr)) {
ptmp->miblen = parse_miboid(cptr, ptmp->miboid);
while (isdigit((unsigned char) *cptr) || *cptr == '.')
cptr++;
}
/*
* name
*/
cptr = skip_white(cptr);
copy_nword(cptr, ptmp->name, sizeof(ptmp->name));
cptr = skip_not_white(cptr);
cptr = skip_white(cptr);
/*
* command
*/
if (cptr == NULL) {
config_perror("No command specified on line");
} else {
/*
* Support multi-element commands in shell configuration
* lines, but truncate after the first command for 'exec'
*/
for (tcptr = cptr; *tcptr != 0 && *tcptr != '#'; tcptr++)
if (*tcptr == ';' && ptmp->type == EXECPROC)
break;
sprintf(ptmp->command, "%.*s", (int) (tcptr - cptr), cptr);
}
#ifdef NETSNMP_EXECFIXCMD
sprintf(ptmp->fixcmd, NETSNMP_EXECFIXCMD, ptmp->name);
#endif
if (ptmp->miblen > 0) {
/*
* For relocatable "exec" entries,
* register the new (not-strictly-valid) MIB subtree...
*/
register_mib(token,
(struct variable *) extensible_relocatable_variables,
sizeof(struct variable2),
sizeof(extensible_relocatable_variables) /
sizeof(*extensible_relocatable_variables),
ptmp->miboid, ptmp->miblen);
/*
* ... and ensure the entries are sorted by OID.
* This isn't needed for entries in the main extTable (which
* don't have MIB OIDs explicitly associated with them anyway)
*/
if (scount > 1 && pp != &extens) {
int i;
struct extensible **etmp = (struct extensible **)
malloc(((sizeof(struct extensible *)) * scount));
if (etmp == NULL)
return; /* XXX memory alloc error */
for (i = 0, ptmp = *pp;
i < scount && ptmp != 0; i++, ptmp = ptmp->next)
etmp[i] = ptmp;
qsort(etmp, scount, sizeof(struct extensible *),
pass_compare);
*pp = (struct extensible *) etmp[0];
ptmp = (struct extensible *) etmp[0];
for (i = 0; i < scount - 1; i++) {
ptmp->next = etmp[i + 1];
ptmp = ptmp->next;
}
ptmp->next = NULL;
free(etmp);
}
}
}
int
extensible_unregister(int major, int minor,
void *serverarg, void *clientarg)
{
extensible_free_config();
return 0;
}
void
extensible_free_config(void)
{
struct extensible *etmp, *etmp2;
oid tname[MAX_OID_LEN];
int i;
for (etmp = extens; etmp != NULL;) {
etmp2 = etmp;
etmp = etmp->next;
free(etmp2);
}
for (etmp = relocs; etmp != NULL;) {
etmp2 = etmp;
etmp = etmp->next;
/*
* The new modular API results in the column
* objects being registered individually, so
* they need to be unregistered individually too!
*/
memset(tname, 0, MAX_OID_LEN*sizeof(oid));
memcpy(tname, etmp2->miboid, etmp2->miblen*sizeof(oid));
for (i=1; i<4; i++) {
tname[etmp2->miblen] = i;
unregister_mib(tname, etmp2->miblen+1);
}
for (i=100; i<=103; i++) {
tname[etmp2->miblen] = i;
unregister_mib(tname, etmp2->miblen+1);
}
free(etmp2);
}
relocs = NULL;
extens = NULL;
numextens = 0;
numrelocs = 0;
}
#define MAXMSGLINES 1000
struct extensible *extens = NULL; /* In exec.c */
struct extensible *relocs = NULL; /* In exec.c */
int numextens = 0, numrelocs = 0; /* ditto */
/*
* var_extensible_shell(...
* 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
*
*/
/*
* find a give entry in the linked list associated with a proc name
*/
struct extensible *
get_exec_by_name(char *name)
{
struct extensible *etmp;
if (name == NULL)
return NULL;
for (etmp = extens; etmp != NULL && strcmp(etmp->name, name) != 0;
etmp = etmp->next);
if(NULL == etmp)
for (etmp = relocs; etmp != NULL && strcmp(etmp->name, name) != 0;
etmp = etmp->next);
return etmp;
}
void
execfix_parse_config(const char *token, char *cptr)
{
char tmpname[STRMAX];
struct extensible *execp;
/*
* don't allow two entries with the same name
*/
cptr = copy_nword(cptr, tmpname, sizeof(tmpname));
if ((execp = get_exec_by_name(tmpname)) == NULL) {
config_perror("No exec entry registered for this exec name yet.");
return;
}
if (strlen(cptr) > sizeof(execp->fixcmd)) {
config_perror("fix command too long.");
return;
}
strlcpy(execp->fixcmd, cptr, sizeof(execp->fixcmd));
}
u_char *
var_extensible_shell(struct variable * vp,
oid * name,
size_t * length,
int exact,
size_t * var_len, WriteMethod ** write_method)
{
static struct extensible *exten = 0;
static long long_ret;
int len;
if (header_simple_table
(vp, name, length, exact, var_len, write_method, numextens))
return (NULL);
if ((exten = get_exten_instance(extens, name[*length - 1]))) {
switch (vp->magic) {
case MIBINDEX:
long_ret = name[*length - 1];
return ((u_char *) (&long_ret));
case ERRORNAME: /* name defined in config file */
*var_len = strlen(exten->name);
return ((u_char *) (exten->name));
case SHELLCOMMAND:
*var_len = strlen(exten->command);
return ((u_char *) (exten->command));
case ERRORFLAG: /* return code from the process */
len = sizeof(exten->output);
if (exten->type == EXECPROC) {
exten->result = run_exec_command( exten->command, NULL,
exten->output, &len);
} else {
exten->result = run_shell_command(exten->command, NULL,
exten->output, &len);
}
long_ret = exten->result;
return ((u_char *) (&long_ret));
case ERRORMSG: /* first line of text returned from the process */
len = sizeof(exten->output);
if (exten->type == EXECPROC) {
exten->result = run_exec_command( exten->command, NULL,
exten->output, &len);
} else {
exten->result = run_shell_command(exten->command, NULL,
exten->output, &len);
}
*var_len = strlen(exten->output);
if (exten->output[*var_len - 1] == '\n')
exten->output[--(*var_len)] = '\0';
return ((u_char *) (exten->output));
case ERRORFIX:
*write_method = fixExecError;
long_return = 0;
return ((u_char *) & long_return);
case ERRORFIXCMD:
*var_len = strlen(exten->fixcmd);
return ((u_char *) exten->fixcmd);
}
return NULL;
}
return NULL;
}
int
fixExecError(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 extensible *exten;
long tmp = 0;
int fd;
static struct extensible ex;
FILE *file;
if ((exten = get_exten_instance(extens, name[10]))) {
if (var_val_type != ASN_INTEGER) {
snmp_log(LOG_ERR, "Wrong type != int\n");
return SNMP_ERR_WRONGTYPE;
}
tmp = *((long *) var_val);
if ((tmp == 1) && (action == COMMIT) && (exten->fixcmd[0] != 0)) {
strlcpy(ex.command, exten->fixcmd, sizeof(ex.command));
if ((fd = get_exec_output(&ex)) != -1) {
file = fdopen(fd, "r");
while (fgets(ex.output, sizeof(ex.output), file) != NULL);
fclose(file);
wait_on_exec(&ex);
}
}
return SNMP_ERR_NOERROR;
}
return SNMP_ERR_WRONGTYPE;
}
u_char *
var_extensible_relocatable(struct variable *vp,
oid * name,
size_t * length,
int exact,
size_t * var_len, WriteMethod ** write_method)
{
int i;
int len;
struct extensible *exten = 0;
static long long_ret;
static char errmsg[STRMAX];
char *cp, *cp1;
struct variable myvp;
oid tname[MAX_OID_LEN];
memcpy(&myvp, vp, sizeof(struct variable));
long_ret = *length;
for (i = 1; i <= (int) numrelocs; i++) {
exten = get_exten_instance(relocs, i);
if (!exten)
continue;
if ((int) exten->miblen == (int) vp->namelen - 1) {
memcpy(myvp.name, exten->miboid, exten->miblen * sizeof(oid));
myvp.namelen = exten->miblen;
*length = vp->namelen;
memcpy(tname, vp->name, vp->namelen * sizeof(oid));
if (!header_simple_table
(&myvp, tname, length, -1, var_len, write_method, -1))
break;
else
exten = NULL;
}
}
if (i > (int) numrelocs || exten == NULL) {
*length = long_ret;
*var_len = 0;
*write_method = NULL;
return (NULL);
}
*length = long_ret;
if (header_simple_table(vp, name, length, exact, var_len, write_method,
((vp->magic == ERRORMSG) ? MAXMSGLINES : 1)))
return (NULL);
switch (vp->magic) {
case MIBINDEX:
long_ret = name[*length - 1];
return ((u_char *) (&long_ret));
case ERRORNAME: /* name defined in config file */
*var_len = strlen(exten->name);
return ((u_char *) (exten->name));
case SHELLCOMMAND:
*var_len = strlen(exten->command);
return ((u_char *) (exten->command));
case ERRORFLAG: /* return code from the process */
len = sizeof(exten->output);
if (exten->type == EXECPROC)
exten->result = run_exec_command( exten->command, NULL,
exten->output, &len);
else
exten->result = run_shell_command(exten->command, NULL,
exten->output, &len);
long_ret = exten->result;
return ((u_char *) (&long_ret));
case ERRORMSG: /* first line of text returned from the process */
len = sizeof(exten->output);
if (exten->type == EXECPROC)
exten->result = run_exec_command( exten->command, NULL,
exten->output, &len);
else
exten->result = run_shell_command(exten->command, NULL,
exten->output, &len);
/*
* Pick the output string apart into individual lines,
* and extract the one being asked for....
*/
cp1 = exten->output;
for (i = 1; i != (int) name[*length - 1]; i++) {
cp = strchr(cp1, '\n');
if (!cp) {
*var_len = 0;
/* wait_on_exec(exten); ??? */
return NULL;
}
cp1 = ++cp;
}
/*
* ... and quit if we've run off the end of the output
*/
if (!*cp1) {
*var_len = 0;
return NULL;
}
cp = strchr(cp1, '\n');
if (cp)
*cp = 0;
strlcpy(errmsg, cp1, sizeof(errmsg));
*var_len = strlen(errmsg);
if (errmsg[*var_len - 1] == '\n')
errmsg[--(*var_len)] = '\0';
return ((u_char *) (errmsg));
case ERRORFIX:
*write_method = fixExecError;
long_return = 0;
return ((u_char *) & long_return);
case ERRORFIXCMD:
*var_len = strlen(exten->fixcmd);
return ((u_char *) exten->fixcmd);
}
return NULL;
}
netsnmp_subtree *
find_extensible(netsnmp_subtree *tp, oid *tname, size_t tnamelen, int exact)
{
size_t tmp;
int i;
struct extensible *exten = 0;
struct variable myvp;
oid name[MAX_OID_LEN];
static netsnmp_subtree mysubtree[2] =
{ { NULL, 0, NULL, 0, NULL, 0, NULL, 0, 0, NULL, NULL, 0, 0, 0,
NULL, NULL, NULL, 0, 0, NULL, 0, 0 },
{ NULL, 0, NULL, 0, NULL, 0, NULL, 0, 0, NULL, NULL, 0, 0, 0,
NULL, NULL, NULL, 0, 0, NULL, 0, 0 } };
for (i = 1; i <= (int) numrelocs; i++) {
exten = get_exten_instance(relocs, i);
if (!exten)
continue;
if (exten->miblen != 0) {
memcpy(myvp.name, exten->miboid, exten->miblen * sizeof(oid));
memcpy(name, tname, tnamelen * sizeof(oid));
myvp.name[exten->miblen] = name[exten->miblen];
myvp.namelen = exten->miblen + 1;
tmp = exten->miblen + 1;
if (!header_simple_table(&myvp, name, &tmp, -1,
NULL, NULL, numrelocs)) {
break;
}
}
}
if (i > (int)numrelocs || exten == NULL) {
return (tp);
}
if (mysubtree[0].name_a != NULL) {
free(mysubtree[0].name_a);
mysubtree[0].name_a = NULL;
}
mysubtree[0].name_a = snmp_duplicate_objid(exten->miboid, exten->miblen);
mysubtree[0].namelen = exten->miblen;
mysubtree[0].variables = (struct variable *)extensible_relocatable_variables;
mysubtree[0].variables_len = sizeof(extensible_relocatable_variables) /
sizeof(*extensible_relocatable_variables);
mysubtree[0].variables_width = sizeof(*extensible_relocatable_variables);
mysubtree[1].namelen = 0;
return (mysubtree);
}