1 #include <net-snmp/net-snmp-config.h>
18 #include <sys/types.h>
20 #include <netinet/in.h>
23 # include <sys/wait.h>
36 #include <net-snmp/net-snmp-includes.h>
37 #include <net-snmp/agent/net-snmp-agent-includes.h>
40 #include "pass_persist.h"
41 #include "extensible.h"
42 #include "util_funcs.h"
44 struct extensible *persistpassthrus = NULL;
45 int numpersistpassthrus = 0;
46 struct persist_pipe_type {
50 } *persist_pipes = (struct persist_pipe_type *) NULL;
51 static int init_persist_pipes(void);
52 static void close_persist_pipe(int iindex);
53 static int open_persist_pipe(int iindex, char *command);
54 static void destruct_persist_pipes(void);
55 static int write_persist_pipe(int iindex, const char *data);
58 * These are defined in pass.c
60 extern int asc2bin(char *p);
61 extern int bin2asc(char *p, size_t n);
62 extern int snmp_oid_min_compare(const oid *, size_t, const oid *,
66 * the relocatable extensible commands variables
68 struct variable2 extensible_persist_passthru_variables[] = {
70 * bogus entry. Only some of it is actually used.
72 {MIBINDEX, ASN_INTEGER, RWRITE, var_extensible_pass_persist, 0,
77 init_pass_persist(void)
79 snmpd_register_config_handler("pass_persist",
80 pass_persist_parse_config,
81 pass_persist_free_config,
86 pass_persist_parse_config(const char *token, char *cptr)
88 struct extensible **ppass = &persistpassthrus, **etmp, *ptmp;
94 if (!isdigit(*cptr)) {
95 config_perror("second token is not a OID");
98 numpersistpassthrus++;
100 while (*ppass != NULL)
101 ppass = &((*ppass)->next);
102 (*ppass) = (struct extensible *) malloc(sizeof(struct extensible));
105 (*ppass)->type = PASSTHRU_PERSIST;
107 (*ppass)->miblen = parse_miboid(cptr, (*ppass)->miboid);
108 while (isdigit(*cptr) || *cptr == '.')
113 cptr = skip_white(cptr);
115 config_perror("No command specified on pass_persist line");
116 (*ppass)->command[0] = 0;
118 for (tcptr = cptr; *tcptr != 0 && *tcptr != '#' && *tcptr != ';';
120 strncpy((*ppass)->command, cptr, tcptr - cptr);
121 (*ppass)->command[tcptr - cptr] = 0;
123 strcpy((*ppass)->name, (*ppass)->command);
124 (*ppass)->next = NULL;
126 register_mib("pass_persist",
127 (struct variable *) extensible_persist_passthru_variables,
128 sizeof(struct variable2), 1, (*ppass)->miboid,
132 * argggg -- pasthrus must be sorted
134 if (numpersistpassthrus > 0) {
135 etmp = (struct extensible **)
136 malloc(((sizeof(struct extensible *)) * numpersistpassthrus));
139 for (i = 0, ptmp = (struct extensible *) persistpassthrus;
140 i < numpersistpassthrus && ptmp != 0; i++, ptmp = ptmp->next)
142 qsort(etmp, numpersistpassthrus, sizeof(struct extensible *),
143 pass_persist_compare);
144 persistpassthrus = (struct extensible *) etmp[0];
145 ptmp = (struct extensible *) etmp[0];
147 for (i = 0; i < numpersistpassthrus - 1; i++) {
148 ptmp->next = etmp[i + 1];
157 pass_persist_free_config(void)
159 struct extensible *etmp, *etmp2;
162 * Close any open pipes to any programs
164 destruct_persist_pipes();
166 for (etmp = persistpassthrus; etmp != NULL;) {
169 unregister_mib(etmp2->miboid, etmp2->miblen);
172 persistpassthrus = NULL;
173 numpersistpassthrus = 0;
177 var_extensible_pass_persist(struct variable *vp,
181 size_t * var_len, WriteMethod ** write_method)
183 oid newname[MAX_OID_LEN];
184 int i, rtest, newlen;
185 static long long_ret;
186 char buf[SNMP_MAXBUF];
187 static char buf2[SNMP_MAXBUF];
188 static oid objid[MAX_OID_LEN];
189 struct extensible *persistpassthru;
193 * Make sure that our basic pipe structure is malloced
195 init_persist_pipes();
198 for (i = 1; i <= numpersistpassthrus; i++) {
199 persistpassthru = get_exten_instance(persistpassthrus, i);
200 rtest = snmp_oid_min_compare(name, *length,
201 persistpassthru->miboid,
202 persistpassthru->miblen);
203 if ((exact && rtest == 0) || (!exact && rtest <= 0)) {
207 if (persistpassthru->miblen >= *length || rtest < 0)
208 sprint_mib_oid(buf, persistpassthru->miboid,
209 persistpassthru->miblen);
211 sprint_mib_oid(buf, name, *length);
214 * Open our pipe if necessary
216 if (!open_persist_pipe(i, persistpassthru->name)) {
221 snprintf(persistpassthru->command,
222 sizeof(persistpassthru->command), "get\n%s\n", buf);
224 snprintf(persistpassthru->command,
225 sizeof(persistpassthru->command), "getnext\n%s\n", buf);
226 persistpassthru->command[ sizeof(persistpassthru->command)-1 ] = 0;
228 DEBUGMSGTL(("ucd-snmp/pass_persist",
229 "persistpass-sending:\n%s",
230 persistpassthru->command));
231 if (!write_persist_pipe(i, persistpassthru->command)) {
234 * close_persist_pipes is called in write_persist_pipe
240 * valid call. Exec and get output
242 if ((file = persist_pipes[i].fIn)) {
243 if (fgets(buf, sizeof(buf), file) == NULL) {
245 close_persist_pipe(i);
249 * persistant scripts return "NONE\n" on invalid items
251 if (!strncmp(buf, "NONE", 4)) {
255 newlen = parse_miboid(buf, newname);
258 * its good, so copy onto name/length
260 memcpy((char *) name, (char *) newname,
261 (int) newlen * sizeof(oid));
265 * set up return pointer for setable stuff
267 *write_method = setPassPersist;
269 if (newlen == 0 || fgets(buf, sizeof(buf), file) == NULL
270 || fgets(buf2, sizeof(buf2), file) == NULL) {
272 close_persist_pipe(i);
277 * buf contains the return type, and buf2 contains the data
279 if (!strncasecmp(buf, "string", 6)) {
280 buf2[strlen(buf2) - 1] = 0; /* zap the linefeed */
281 *var_len = strlen(buf2);
282 vp->type = ASN_OCTET_STR;
283 return ((unsigned char *) buf2);
284 } else if (!strncasecmp(buf, "integer", 7)) {
285 *var_len = sizeof(long_ret);
286 long_ret = strtol(buf2, NULL, 10);
287 vp->type = ASN_INTEGER;
288 return ((unsigned char *) &long_ret);
289 } else if (!strncasecmp(buf, "unsigned", 7)) {
290 *var_len = sizeof(long_ret);
291 long_ret = strtoul(buf2, NULL, 10);
292 vp->type = ASN_UNSIGNED;
293 return ((unsigned char *) &long_ret);
294 } else if (!strncasecmp(buf, "counter", 7)) {
295 *var_len = sizeof(long_ret);
296 long_ret = strtoul(buf2, NULL, 10);
297 vp->type = ASN_COUNTER;
298 return ((unsigned char *) &long_ret);
299 } else if (!strncasecmp(buf, "octet", 5)) {
300 *var_len = asc2bin(buf2);
301 vp->type = ASN_OCTET_STR;
302 return ((unsigned char *) buf2);
303 } else if (!strncasecmp(buf, "opaque", 5)) {
304 *var_len = asc2bin(buf2);
305 vp->type = ASN_OPAQUE;
306 return ((unsigned char *) buf2);
307 } else if (!strncasecmp(buf, "gauge", 5)) {
308 *var_len = sizeof(long_ret);
309 long_ret = strtoul(buf2, NULL, 10);
310 vp->type = ASN_GAUGE;
311 return ((unsigned char *) &long_ret);
312 } else if (!strncasecmp(buf, "objectid", 8)) {
313 newlen = parse_miboid(buf2, objid);
314 *var_len = newlen * sizeof(oid);
315 vp->type = ASN_OBJECT_ID;
316 return ((unsigned char *) objid);
317 } else if (!strncasecmp(buf, "timetick", 8)) {
318 *var_len = sizeof(long_ret);
319 long_ret = strtoul(buf2, NULL, 10);
320 vp->type = ASN_TIMETICKS;
321 return ((unsigned char *) &long_ret);
322 } else if (!strncasecmp(buf, "ipaddress", 9)) {
323 newlen = parse_miboid(buf2, objid);
326 "invalid ipaddress returned: %s\n",
332 (objid[0] << (8 * 3)) + (objid[1] << (8 * 2)) +
333 (objid[2] << 8) + objid[3];
334 long_ret = htonl(long_ret);
335 *var_len = sizeof(long_ret);
336 vp->type = ASN_IPADDRESS;
337 return ((unsigned char *) &long_ret);
346 *write_method = NULL;
351 setPassPersist(int action,
355 u_char * statP, oid * name, size_t name_len)
358 struct extensible *persistpassthru;
360 char buf[SNMP_MAXBUF], buf2[SNMP_MAXBUF];
366 * Make sure that our basic pipe structure is malloced
368 init_persist_pipes();
370 for (i = 1; i <= numpersistpassthrus; i++) {
371 persistpassthru = get_exten_instance(persistpassthrus, i);
372 rtest = snmp_oid_min_compare(name, name_len,
373 persistpassthru->miboid,
374 persistpassthru->miblen);
376 if (action != COMMIT)
377 return SNMP_ERR_NOERROR;
381 if (persistpassthru->miblen >= name_len || rtest < 0)
382 sprint_mib_oid(buf, persistpassthru->miboid,
383 persistpassthru->miblen);
385 sprint_mib_oid(buf, name, name_len);
386 snprintf(persistpassthru->command,
387 sizeof(persistpassthru->command), "set\n%s\n", buf);
388 persistpassthru->command[ sizeof(persistpassthru->command)-1 ] = 0;
389 switch (var_val_type) {
394 tmp = *((long *) var_val);
395 switch (var_val_type) {
397 sprintf(buf, "integer %d\n", (int) tmp);
400 sprintf(buf, "counter %d\n", (int) tmp);
403 sprintf(buf, "gauge %d\n", (int) tmp);
406 sprintf(buf, "timeticks %d\n", (int) tmp);
411 utmp = *((u_long *) var_val);
413 sprintf(buf, "ipaddress %d.%d.%d.%d\n",
414 (int) ((utmp & 0xff000000) >> (8 * 3)),
415 (int) ((utmp & 0xff0000) >> (8 * 2)),
416 (int) ((utmp & 0xff00) >> (8)),
417 (int) ((utmp & 0xff)));
421 memcpy(buf2, var_val, var_val_len);
422 if (var_val_len == 0)
423 sprintf(buf, "string \"\"\n");
424 else if (bin2asc(buf2, var_val_len) == (int) var_val_len)
425 snprintf(buf, sizeof(buf), "string \"%s\"\n", buf2);
427 snprintf(buf, sizeof(buf), "octet \"%s\"\n", buf2);
428 buf[ sizeof(buf)-1 ] = 0;
431 sprint_mib_oid(buf2, (oid *) var_val, var_val_len);
432 snprintf(buf, sizeof(buf), "objectid \"%s\"\n", buf2);
433 buf[ sizeof(buf)-1 ] = 0;
436 strncat(persistpassthru->command, buf,
437 sizeof(persistpassthru->command) -
438 strlen(persistpassthru->command) - 2);
439 persistpassthru->command[ sizeof(persistpassthru->command)-2 ] = '\n';
440 persistpassthru->command[ sizeof(persistpassthru->command)-1 ] = 0;
442 if (!open_persist_pipe(i, persistpassthru->name)) {
443 return SNMP_ERR_NOTWRITABLE;
446 DEBUGMSGTL(("ucd-snmp/pass_persist",
447 "persistpass-writing: %s\n",
448 persistpassthru->command));
449 if (!write_persist_pipe(i, persistpassthru->command)) {
450 close_persist_pipe(i);
451 return SNMP_ERR_NOTWRITABLE;
454 if (fgets(buf, sizeof(buf), persist_pipes[i].fIn) == NULL) {
455 close_persist_pipe(i);
456 return SNMP_ERR_NOTWRITABLE;
459 if (!strncasecmp(buf, "not-writable", 11)) {
460 return SNMP_ERR_NOTWRITABLE;
461 } else if (!strncasecmp(buf, "wrong-type", 9)) {
462 return SNMP_ERR_WRONGTYPE;
464 return SNMP_ERR_NOERROR;
467 if (snmp_get_do_debugging()) {
468 sprint_mib_oid(buf2, name, name_len);
469 DEBUGMSGTL(("ucd-snmp/pass_persist", "persistpass-notfound: %s\n",
472 return SNMP_ERR_NOSUCHNAME;
476 pass_persist_compare(const void *a, const void *b)
478 const struct extensible *const *ap, *const *bp;
479 ap = (const struct extensible * const *) a;
480 bp = (const struct extensible * const *) b;
481 return snmp_oid_compare((*ap)->miboid, (*ap)->miblen, (*bp)->miboid,
486 * Initialize our persistant pipes
487 * - Returns 1 on success, 0 on failure.
488 * - Initializes all FILE pointers to NULL to indicate "closed"
491 init_persist_pipes(void)
496 * if we are already taken care of, just return
499 return persist_pipes ? 1 : 0;
503 * Otherwise malloc and initialize
505 persist_pipes = (struct persist_pipe_type *)
506 malloc(sizeof(struct persist_pipe_type) *
507 (numpersistpassthrus + 1));
509 for (i = 0; i <= numpersistpassthrus; i++) {
510 persist_pipes[i].fIn = persist_pipes[i].fOut = (FILE *) 0;
511 persist_pipes[i].fdIn = persist_pipes[i].fdOut = -1;
512 persist_pipes[i].pid = -1;
515 return persist_pipes ? 1 : 0;
519 * Destruct our persistant pipes
523 destruct_persist_pipes(void)
528 * Return if there are no pipes
530 if (!persist_pipes) {
534 for (i = 0; i <= numpersistpassthrus; i++) {
535 close_persist_pipe(i);
539 persist_pipes = (struct persist_pipe_type *) 0;
543 * returns 0 on failure, 1 on success
546 open_persist_pipe(int iindex, char *command)
548 static int recurse = 0; /* used to allow one level of recursion */
550 DEBUGMSGTL(("ucd-snmp/pass_persist", "open_persist_pipe(%d,'%s')\n",
553 * Open if it's not already open
555 if (persist_pipes[iindex].pid == -1) {
556 int fdIn, fdOut, pid;
557 get_exec_pipes(command, &fdIn, &fdOut, &pid);
563 DEBUGMSGTL(("ucd-snmp/pass_persist",
564 "open_persist_pipe: pid == -1\n"));
570 * If not, fill out our structure
572 persist_pipes[iindex].pid = pid;
573 persist_pipes[iindex].fdIn = fdIn;
574 persist_pipes[iindex].fdOut = fdOut;
575 persist_pipes[iindex].fIn = fdopen(fdIn, "r");
576 persist_pipes[iindex].fOut = fdopen(fdOut, "w");
579 * Setup our -non-buffered-io-
581 setbuf(persist_pipes[iindex].fOut, (char *) 0);
585 * Send test packet always so we can self-catch
588 char buf[SNMP_MAXBUF];
590 * Should catch SIGPIPE around this call!
592 if (!write_persist_pipe(iindex, "PING\n")) {
593 DEBUGMSGTL(("ucd-snmp/pass_persist",
594 "open_persist_pipe: Error writing PING\n"));
595 close_persist_pipe(iindex);
598 * Recurse one time if we get a SIGPIPE
602 return open_persist_pipe(iindex, command);
607 if (fgets(buf, sizeof(buf), persist_pipes[iindex].fIn) == NULL) {
608 DEBUGMSGTL(("ucd-snmp/pass_persist",
609 "open_persist_pipe: Error reading for PONG\n"));
610 close_persist_pipe(iindex);
614 if (strncmp(buf, "PONG", 4)) {
615 DEBUGMSGTL(("ucd-snmp/pass_persist",
616 "open_persist_pipe: PONG not received!\n"));
617 close_persist_pipe(iindex);
627 #if STRUCT_SIGACTION_HAS_SA_SIGACTION
632 sigpipe_handler(int sig, siginfo_t * sip, void *uap)
639 write_persist_pipe(int iindex, const char *data)
642 struct sigaction sa, osa;
643 int wret = 0, werrno = 0;
646 * Don't write to a non-existant process
648 if (persist_pipes[iindex].pid == -1) {
653 * Setup our signal action to catch SIGPIPEs
655 sa.sa_handler = NULL;
656 #if STRUCT_SIGACTION_HAS_SA_SIGACTION
657 sa.sa_sigaction = &sigpipe_handler;
659 sigemptyset(&sa.sa_mask);
661 if (sigaction(SIGPIPE, &sa, &osa)) {
662 DEBUGMSGTL(("ucd-snmp/pass_persist",
663 "write_persist_pipe: sigaction failed: %d", errno));
669 wret = write(persist_pipes[iindex].fdOut, data, strlen(data));
673 * Reset the signal handler
675 sigaction(SIGPIPE, &osa, (struct sigaction *) 0);
678 if (werrno != EINTR) {
679 DEBUGMSGTL(("ucd-snmp/pass_persist",
680 "write_persist_pipe: write returned unknown error %d\n",
683 close_persist_pipe(iindex);
686 #endif /* HAVE_SIGNAL */
691 close_persist_pipe(int iindex)
695 * Check and nix every item
697 if (persist_pipes[iindex].fOut) {
698 fclose(persist_pipes[iindex].fOut);
699 persist_pipes[iindex].fOut = (FILE *) 0;
701 if (persist_pipes[iindex].fdOut != -1) {
702 close(persist_pipes[iindex].fdOut);
703 persist_pipes[iindex].fdOut = -1;
705 if (persist_pipes[iindex].fIn) {
706 fclose(persist_pipes[iindex].fIn);
707 persist_pipes[iindex].fIn = (FILE *) 0;
709 if (persist_pipes[iindex].fdIn != -1) {
710 close(persist_pipes[iindex].fdIn);
711 persist_pipes[iindex].fdIn = -1;
713 if (persist_pipes[iindex].pid != -1) {
715 waitpid(persist_pipes[iindex].pid, 0, 0);
717 persist_pipes[iindex].pid = -1;