Blame snmplib/transports/snmpSTDDomain.c

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