blob: f83eaa535fa6366a234cba235c745f1d488162a7 [file] [log] [blame]
#include <net-snmp/net-snmp-config.h>
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_DMALLOC_H
#include <dmalloc.h>
#endif
#include <net-snmp/types.h>
#include <net-snmp/output_api.h>
#include <net-snmp/library/snmp_transport.h>
#include <net-snmp/library/snmpSTDDomain.h>
#include <net-snmp/library/tools.h>
oid netsnmp_snmpSTDDomain[] = { TRANSPORT_DOMAIN_STD_IP };
static netsnmp_tdomain stdDomain;
/*
* Return a string representing the address in data, or else the "far end"
* address if data is NULL.
*/
static char *
netsnmp_std_fmtaddr(netsnmp_transport *t, void *data, int len)
{
char *buf;
DEBUGMSGTL(("domain:std","formatting addr. data=%x\n",t->data));
if (t->data) {
netsnmp_std_data *data = (netsnmp_std_data*)t->data;
buf = (char*)malloc(SNMP_MAXBUF_MEDIUM);
if (!buf)
return strdup("STDInOut");
snprintf(buf, SNMP_MAXBUF_MEDIUM, "STD:%s", data->prog);
DEBUGMSGTL(("domain:std"," formatted:=%s\n",buf));
return buf;
}
return strdup("STDInOut");
}
/*
* You can write something into opaque that will subsequently get passed back
* to your send function if you like. For instance, you might want to
* remember where a PDU came from, so that you can send a reply there...
*/
static int
netsnmp_std_recv(netsnmp_transport *t, void *buf, int size,
void **opaque, int *olength)
{
int rc = -1;
DEBUGMSGTL(("domain:std","recv on sock %d. data=%x\n",t->sock, t->data));
while (rc < 0) {
rc = read(t->sock, buf, size);
DEBUGMSGTL(("domain:std"," bytes: %d.\n", rc));
if (rc < 0 && errno != EINTR) {
DEBUGMSGTL(("netsnmp_std", " read on fd %d failed: %d (\"%s\")\n",
t->sock, errno, strerror(errno)));
break;
}
if (rc == 0) {
/* 0 input is probably bad since we selected on it */
return -1;
}
DEBUGMSGTL(("netsnmp_std", "read on stdin got %d bytes\n", rc));
}
return rc;
}
static int
netsnmp_std_send(netsnmp_transport *t, void *buf, int size,
void **opaque, int *olength)
{
int rc = -1;
DEBUGMSGTL(("domain:std","send on sock. data=%x\n", t->data));
while (rc < 0) {
if (t->data) {
netsnmp_std_data *data = (netsnmp_std_data*)t->data;
rc = write(data->outfd, buf, size);
} else {
/* straight to stdout */
rc = write(1, buf, size);
}
if (rc < 0 && errno != EINTR) {
break;
}
}
return rc;
}
static int
netsnmp_std_close(netsnmp_transport *t)
{
DEBUGMSGTL(("domain:std","close. data=%x\n", t->data));
if (t->data) {
netsnmp_std_data *data = (netsnmp_std_data*)t->data;
close(data->outfd);
close(t->sock);
/* kill the child too */
DEBUGMSGTL(("domain:std"," killing %d\n", data->childpid));
kill(data->childpid, SIGTERM);
sleep(1);
kill(data->childpid, SIGKILL);
/* XXX: set an alarm to kill harder the child */
} else {
/* close stdout/in */
close(1);
close(0);
}
return 0;
}
static int
netsnmp_std_accept(netsnmp_transport *t)
{
DEBUGMSGTL(("domain:std"," accept data=%x\n", t->data));
/* nothing to do here */
return 0;
}
/*
* Open a STDIN/STDOUT -based transport for SNMP.
*/
netsnmp_transport *
netsnmp_std_transport(const char *instring, size_t instring_len,
const char *default_target)
{
netsnmp_transport *t;
t = (netsnmp_transport *) malloc(sizeof(netsnmp_transport));
if (t == NULL) {
return NULL;
}
memset(t, 0, sizeof(netsnmp_transport));
t->domain = netsnmp_snmpSTDDomain;
t->domain_length =
sizeof(netsnmp_snmpSTDDomain) / sizeof(netsnmp_snmpSTDDomain[0]);
t->sock = 0;
t->flags = NETSNMP_TRANSPORT_FLAG_STREAM | NETSNMP_TRANSPORT_FLAG_TUNNELED;
/*
* Message size is not limited by this transport (hence msgMaxSize
* is equal to the maximum legal size of an SNMP message).
*/
t->msgMaxSize = 0x7fffffff;
t->f_recv = netsnmp_std_recv;
t->f_send = netsnmp_std_send;
t->f_close = netsnmp_std_close;
t->f_accept = netsnmp_std_accept;
t->f_fmtaddr = netsnmp_std_fmtaddr;
/*
* if instring is not null length, it specifies a path to a prog
* XXX: plus args
*/
if (instring_len == 0 && default_target != NULL) {
instring = default_target;
instring_len = strlen(default_target);
}
if (instring_len != 0) {
int infd[2], outfd[2]; /* sockets to and from the client */
int childpid;
if (pipe(infd) || pipe(outfd)) {
snmp_log(LOG_ERR,
"Failed to create needed pipes for a STD transport");
netsnmp_transport_free(t);
return NULL;
}
childpid = fork();
/* parentpid => childpid */
/* infd[1] => infd[0] */
/* outfd[0] <= outfd[1] */
if (childpid) {
netsnmp_std_data *data;
/* we're in the parent */
close(infd[0]);
close(outfd[1]);
data = SNMP_MALLOC_TYPEDEF(netsnmp_std_data);
if (!data) {
snmp_log(LOG_ERR, "snmpSTDDomain: malloc failed");
netsnmp_transport_free(t);
return NULL;
}
t->data = data;
t->data_length = sizeof(netsnmp_std_data);
t->sock = outfd[0];
data->prog = strdup(instring);
data->outfd = infd[1];
data->childpid = childpid;
DEBUGMSGTL(("domain:std","parent. data=%x\n", t->data));
} else {
/* we're in the child */
/* close stdin */
close(0);
/* copy pipe output to stdout */
dup(infd[0]);
/* close stdout */
close(1);
/* copy pipe output to stdin */
dup(outfd[1]);
/* close all the pipes themselves */
close(infd[0]);
close(infd[1]);
close(outfd[0]);
close(outfd[1]);
/* call exec */
system(instring);
/* XXX: TODO: use exec form instead; needs args */
/* execv(instring, NULL); */
exit(0);
/* ack... we should never ever get here */
snmp_log(LOG_ERR, "STD transport returned after execv()\n");
}
}
return t;
}
netsnmp_transport *
netsnmp_std_create_tstring(const char *instring, int local,
const char *default_target)
{
return netsnmp_std_transport(instring, strlen(instring), default_target);
}
netsnmp_transport *
netsnmp_std_create_ostring(const u_char * o, size_t o_len, int local)
{
return netsnmp_std_transport((const char*)o, o_len, NULL);
}
void
netsnmp_std_ctor(void)
{
stdDomain.name = netsnmp_snmpSTDDomain;
stdDomain.name_length = sizeof(netsnmp_snmpSTDDomain) / sizeof(oid);
stdDomain.prefix = (const char **)calloc(2, sizeof(char *));
stdDomain.prefix[0] = "std";
stdDomain.f_create_from_tstring = NULL;
stdDomain.f_create_from_tstring_new = netsnmp_std_create_tstring;
stdDomain.f_create_from_ostring = netsnmp_std_create_ostring;
netsnmp_tdomain_register(&stdDomain);
}