net-snmp  5.4.1
snmpSTDDomain.c
00001 #include <net-snmp/net-snmp-config.h>
00002 
00003 #include <stdio.h>
00004 #include <sys/types.h>
00005 #include <signal.h>
00006 #include <errno.h>
00007 
00008 #if HAVE_STRING_H
00009 #include <string.h>
00010 #else
00011 #include <strings.h>
00012 #endif
00013 #if HAVE_STDLIB_H
00014 #include <stdlib.h>
00015 #endif
00016 #if HAVE_UNISTD_H
00017 #include <unistd.h>
00018 #endif
00019 
00020 #if HAVE_DMALLOC_H
00021 #include <dmalloc.h>
00022 #endif
00023 
00024 #include <net-snmp/types.h>
00025 #include <net-snmp/output_api.h>
00026 
00027 #include <net-snmp/library/snmp_transport.h>
00028 #include <net-snmp/library/snmpSTDDomain.h>
00029 #include <net-snmp/library/tools.h>
00030 
00031 oid netsnmp_snmpSTDDomain[] = { TRANSPORT_DOMAIN_STD_IP };
00032 static netsnmp_tdomain stdDomain;
00033 
00034 /*
00035  * Return a string representing the address in data, or else the "far end"
00036  * address if data is NULL.  
00037  */
00038 
00039 static char *
00040 netsnmp_std_fmtaddr(netsnmp_transport *t, void *data, int len)
00041 {
00042     char *buf;
00043     DEBUGMSGTL(("domain:std","formatting addr.  data=%x\n",t->data));
00044     if (t->data) {
00045         netsnmp_std_data *data = (netsnmp_std_data*)t->data;
00046         buf = (char*)malloc(SNMP_MAXBUF_MEDIUM);
00047         if (!buf)
00048             return strdup("STDInOut");
00049         snprintf(buf, SNMP_MAXBUF_MEDIUM, "STD:%s", data->prog);
00050         DEBUGMSGTL(("domain:std","  formatted:=%s\n",buf));
00051         return buf;
00052     }
00053     return strdup("STDInOut");
00054 }
00055 
00056 
00057 
00058 /*
00059  * You can write something into opaque that will subsequently get passed back 
00060  * to your send function if you like.  For instance, you might want to
00061  * remember where a PDU came from, so that you can send a reply there...  
00062  */
00063 
00064 static int
00065 netsnmp_std_recv(netsnmp_transport *t, void *buf, int size,
00066                  void **opaque, int *olength)
00067 {
00068     int rc = -1;
00069 
00070     DEBUGMSGTL(("domain:std","recv on sock %d.  data=%x\n",t->sock, t->data));
00071     while (rc < 0) {
00072         rc = read(t->sock, buf, size);
00073         DEBUGMSGTL(("domain:std","  bytes: %d.\n", rc));
00074         if (rc < 0 && errno != EINTR) {
00075             DEBUGMSGTL(("netsnmp_std", " read on fd %d failed: %d (\"%s\")\n",
00076                         t->sock, errno, strerror(errno)));
00077             break;
00078         }
00079         if (rc == 0) {
00080             /* 0 input is probably bad since we selected on it */
00081             return -1;
00082         }
00083         DEBUGMSGTL(("netsnmp_std", "read on stdin got %d bytes\n", rc));
00084     }
00085 
00086     return rc;
00087 }
00088 
00089 
00090 
00091 static int
00092 netsnmp_std_send(netsnmp_transport *t, void *buf, int size,
00093                  void **opaque, int *olength)
00094 {
00095     int rc = -1;
00096 
00097     DEBUGMSGTL(("domain:std","send on sock.  data=%x\n", t->data));
00098     while (rc < 0) {
00099         if (t->data) {
00100             netsnmp_std_data *data = (netsnmp_std_data*)t->data;
00101             rc = write(data->outfd, buf, size);
00102         } else {
00103             /* straight to stdout */
00104             rc = write(1, buf, size);
00105         }
00106         if (rc < 0 && errno != EINTR) {
00107             break;
00108         }
00109     }
00110     return rc;
00111 }
00112 
00113 static int
00114 netsnmp_std_close(netsnmp_transport *t)
00115 {
00116     DEBUGMSGTL(("domain:std","close.  data=%x\n", t->data));
00117     if (t->data) {
00118         netsnmp_std_data *data = (netsnmp_std_data*)t->data;
00119         close(data->outfd);
00120         close(t->sock);
00121 
00122         /* kill the child too */
00123         DEBUGMSGTL(("domain:std"," killing %d\n", data->childpid));
00124         kill(data->childpid, SIGTERM);
00125         sleep(1);
00126         kill(data->childpid, SIGKILL);
00127         /* XXX: set an alarm to kill harder the child */
00128     } else {
00129         /* close stdout/in */
00130         close(1);
00131         close(0);
00132     }
00133     return 0;
00134 }
00135 
00136 
00137 
00138 static int
00139 netsnmp_std_accept(netsnmp_transport *t)
00140 {
00141     DEBUGMSGTL(("domain:std"," accept data=%x\n", t->data));
00142     /* nothing to do here */
00143     return 0;
00144 }
00145 
00146 /*
00147  * Open a STDIN/STDOUT -based transport for SNMP.
00148  */
00149 
00150 netsnmp_transport *
00151 netsnmp_std_transport(const char *instring, size_t instring_len,
00152                       const char *default_target)
00153 {
00154     netsnmp_transport *t;
00155 
00156     t = (netsnmp_transport *) malloc(sizeof(netsnmp_transport));
00157     if (t == NULL) {
00158         return NULL;
00159     }
00160     memset(t, 0, sizeof(netsnmp_transport));
00161 
00162     t->domain = netsnmp_snmpSTDDomain;
00163     t->domain_length =
00164         sizeof(netsnmp_snmpSTDDomain) / sizeof(netsnmp_snmpSTDDomain[0]);
00165 
00166     t->sock = 0;
00167     t->flags = NETSNMP_TRANSPORT_FLAG_STREAM | NETSNMP_TRANSPORT_FLAG_TUNNELED;
00168 
00169     /*
00170      * Message size is not limited by this transport (hence msgMaxSize
00171      * is equal to the maximum legal size of an SNMP message).  
00172      */
00173 
00174     t->msgMaxSize = 0x7fffffff;
00175     t->f_recv     = netsnmp_std_recv;
00176     t->f_send     = netsnmp_std_send;
00177     t->f_close    = netsnmp_std_close;
00178     t->f_accept   = netsnmp_std_accept;
00179     t->f_fmtaddr  = netsnmp_std_fmtaddr;
00180 
00181     /*
00182      * if instring is not null length, it specifies a path to a prog
00183      * XXX: plus args
00184      */
00185     if (instring_len == 0 && default_target != NULL) {
00186         instring = default_target;
00187         instring_len = strlen(default_target);
00188     }
00189 
00190     if (instring_len != 0) {
00191         int infd[2], outfd[2];  /* sockets to and from the client */
00192         int childpid;
00193 
00194         if (pipe(infd) || pipe(outfd)) {
00195             snmp_log(LOG_ERR,
00196                      "Failed to create needed pipes for a STD transport");
00197             netsnmp_transport_free(t);
00198             return NULL;
00199         }
00200 
00201         childpid = fork();
00202         /* parentpid => childpid */
00203         /* infd[1]   => infd[0] */
00204         /* outfd[0]  <= outfd[1] */
00205 
00206         if (childpid) {
00207             netsnmp_std_data *data;
00208             
00209             /* we're in the parent */
00210             close(infd[0]);
00211             close(outfd[1]);
00212 
00213             data = SNMP_MALLOC_TYPEDEF(netsnmp_std_data);
00214             if (!data) {
00215                 snmp_log(LOG_ERR, "snmpSTDDomain: memdup failed");
00216                 netsnmp_transport_free(t);
00217                 return NULL;
00218             }
00219             t->data = data;
00220             t->data_length = sizeof(netsnmp_std_data);
00221             t->sock = outfd[0];
00222             data->prog = strdup(instring);
00223             data->outfd = infd[1];
00224             data->childpid = childpid;
00225             DEBUGMSGTL(("domain:std","parent.  data=%x\n", t->data));
00226         } else {
00227             /* we're in the child */
00228 
00229             /* close stdin */
00230             close(0);
00231             /* copy pipe output to stdout */
00232             dup(infd[0]);
00233 
00234             /* close stdout */
00235             close(1);
00236             /* copy pipe output to stdin */
00237             dup(outfd[1]);
00238             
00239             /* close all the pipes themselves */
00240             close(infd[0]);
00241             close(infd[1]);
00242             close(outfd[0]);
00243             close(outfd[1]);
00244 
00245             /* call exec */
00246             system(instring);
00247             /* XXX: TODO: use exec form instead; needs args */
00248             /* execv(instring, NULL); */
00249             exit(0);
00250 
00251             /* ack...  we should never ever get here */
00252             snmp_log(LOG_ERR, "STD transport returned after execv()\n");
00253         }
00254     }            
00255 
00256     return t;
00257 }
00258 
00259 netsnmp_transport *
00260 netsnmp_std_create_tstring(const char *instring, int local,
00261                            const char *default_target)
00262 {
00263     return netsnmp_std_transport(instring, strlen(instring), default_target);
00264 }
00265 
00266 netsnmp_transport *
00267 netsnmp_std_create_ostring(const u_char * o, size_t o_len, int local)
00268 {
00269     return netsnmp_std_transport((const char*)o, o_len, NULL);
00270 }
00271 
00272 void
00273 netsnmp_std_ctor(void)
00274 {
00275     stdDomain.name = netsnmp_snmpSTDDomain;
00276     stdDomain.name_length = sizeof(netsnmp_snmpSTDDomain) / sizeof(oid);
00277     stdDomain.prefix = (const char **)calloc(2, sizeof(char *));
00278     stdDomain.prefix[0] = "std";
00279 
00280     stdDomain.f_create_from_tstring_new = netsnmp_std_create_tstring;
00281     stdDomain.f_create_from_ostring = netsnmp_std_create_ostring;
00282 
00283     netsnmp_tdomain_register(&stdDomain);
00284 }