/src/net-snmp/snmplib/transports/snmpSTDDomain.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include <net-snmp/net-snmp-config.h> |
2 | | |
3 | | #include <net-snmp/library/snmpSTDDomain.h> |
4 | | |
5 | | #include <stdio.h> |
6 | | #include <sys/types.h> |
7 | | #include <signal.h> |
8 | | #include <errno.h> |
9 | | |
10 | | #ifdef HAVE_STRING_H |
11 | | #include <string.h> |
12 | | #else |
13 | | #include <strings.h> |
14 | | #endif |
15 | | #ifdef HAVE_STDLIB_H |
16 | | #include <stdlib.h> |
17 | | #endif |
18 | | #ifdef HAVE_UNISTD_H |
19 | | #include <unistd.h> |
20 | | #endif |
21 | | |
22 | | #include <net-snmp/types.h> |
23 | | #include <net-snmp/output_api.h> |
24 | | #include <net-snmp/library/snmp.h> |
25 | | #include <net-snmp/library/snmp_transport.h> |
26 | | #include <net-snmp/library/tools.h> |
27 | | |
28 | | const oid netsnmp_snmpSTDDomain[] = { TRANSPORT_DOMAIN_STD_IP }; |
29 | | static netsnmp_tdomain stdDomain; |
30 | | |
31 | | /* |
32 | | * Return a string representing the address in data, or else the "far end" |
33 | | * address if data is NULL. |
34 | | */ |
35 | | |
36 | | static char * |
37 | | netsnmp_std_fmtaddr(netsnmp_transport *t, const void *data, int len) |
38 | 0 | { |
39 | 0 | DEBUGMSGTL(("domain:std","formatting addr. data=%p\n",t->data)); |
40 | 0 | if (t->data) { |
41 | 0 | netsnmp_std_data *data = (netsnmp_std_data*)t->data; |
42 | 0 | char *buf; |
43 | |
|
44 | 0 | if (asprintf(&buf, "STD:%s", data->prog) < 0) |
45 | 0 | buf = NULL; |
46 | 0 | DEBUGMSGTL(("domain:std"," formatted:=%s\n",buf)); |
47 | 0 | return buf; |
48 | 0 | } |
49 | 0 | return strdup("STDInOut"); |
50 | 0 | } |
51 | | |
52 | | static void |
53 | | netsnmp_std_get_taddr(netsnmp_transport *t, void **addr, size_t *addr_len) |
54 | 0 | { |
55 | 0 | *addr_len = t->remote_length; |
56 | 0 | *addr = netsnmp_memdup(t->remote, *addr_len); |
57 | 0 | } |
58 | | |
59 | | /* |
60 | | * You can write something into opaque that will subsequently get passed back |
61 | | * to your send function if you like. For instance, you might want to |
62 | | * remember where a PDU came from, so that you can send a reply there... |
63 | | */ |
64 | | |
65 | | static int |
66 | | netsnmp_std_recv(netsnmp_transport *t, void *buf, int size, |
67 | | void **opaque, int *olength) |
68 | 0 | { |
69 | 0 | int rc = -1; |
70 | |
|
71 | 0 | DEBUGMSGTL(("domain:std","recv on sock %d. data=%p\n",t->sock, t->data)); |
72 | 0 | while (rc < 0) { |
73 | 0 | rc = read(t->sock, buf, size); |
74 | 0 | DEBUGMSGTL(("domain:std"," bytes: %d.\n", rc)); |
75 | 0 | if (rc < 0 && errno != EINTR) { |
76 | 0 | DEBUGMSGTL(("netsnmp_std", " read on fd %d failed: %d (\"%s\")\n", |
77 | 0 | t->sock, errno, strerror(errno))); |
78 | 0 | break; |
79 | 0 | } |
80 | 0 | if (rc == 0) { |
81 | | /* 0 input is probably bad since we selected on it */ |
82 | 0 | return -1; |
83 | 0 | } |
84 | 0 | DEBUGMSGTL(("netsnmp_std", "read on stdin got %d bytes\n", rc)); |
85 | 0 | } |
86 | | |
87 | 0 | return rc; |
88 | 0 | } |
89 | | |
90 | | |
91 | | |
92 | | static int |
93 | | netsnmp_std_send(netsnmp_transport *t, const void *buf, int size, |
94 | | void **opaque, int *olength) |
95 | 0 | { |
96 | 0 | int rc = -1; |
97 | |
|
98 | 0 | DEBUGMSGTL(("domain:std","send on sock. data=%p\n", t->data)); |
99 | 0 | while (rc < 0) { |
100 | 0 | if (t->data) { |
101 | 0 | netsnmp_std_data *data = (netsnmp_std_data*)t->data; |
102 | 0 | rc = write(data->outfd, buf, size); |
103 | 0 | } else { |
104 | | /* straight to stdout */ |
105 | 0 | rc = write(1, buf, size); |
106 | 0 | } |
107 | 0 | if (rc < 0 && errno != EINTR) { |
108 | 0 | break; |
109 | 0 | } |
110 | 0 | } |
111 | 0 | return rc; |
112 | 0 | } |
113 | | |
114 | | static int |
115 | | netsnmp_std_close(netsnmp_transport *t) |
116 | 0 | { |
117 | 0 | DEBUGMSGTL(("domain:std","close. data=%p\n", t->data)); |
118 | 0 | if (t->data) { |
119 | 0 | netsnmp_std_data *data = (netsnmp_std_data*)t->data; |
120 | 0 | close(data->outfd); |
121 | 0 | close(t->sock); |
122 | | |
123 | | /* kill the child too */ |
124 | 0 | DEBUGMSGTL(("domain:std"," killing %d\n", data->childpid)); |
125 | 0 | kill(data->childpid, SIGTERM); |
126 | 0 | sleep(1); |
127 | 0 | kill(data->childpid, SIGKILL); |
128 | | /* XXX: set an alarm to kill harder the child */ |
129 | 0 | } else { |
130 | | /* close stdout/in */ |
131 | 0 | close(STDOUT_FILENO); |
132 | 0 | close(STDIN_FILENO); |
133 | 0 | } |
134 | 0 | return 0; |
135 | 0 | } |
136 | | |
137 | | |
138 | | |
139 | | static int |
140 | | netsnmp_std_accept(netsnmp_transport *t) |
141 | 0 | { |
142 | 0 | DEBUGMSGTL(("domain:std"," accept data=%p\n", t->data)); |
143 | | /* nothing to do here */ |
144 | 0 | return 0; |
145 | 0 | } |
146 | | |
147 | | /* |
148 | | * Open a STDIN/STDOUT -based transport for SNMP. |
149 | | */ |
150 | | |
151 | | netsnmp_transport * |
152 | | netsnmp_std_transport(const char *instring, size_t instring_len, |
153 | | const char *default_target) |
154 | 0 | { |
155 | 0 | netsnmp_transport *t; |
156 | |
|
157 | 0 | t = SNMP_MALLOC_TYPEDEF(netsnmp_transport); |
158 | 0 | if (t == NULL) { |
159 | 0 | return NULL; |
160 | 0 | } |
161 | | |
162 | 0 | t->domain = netsnmp_snmpSTDDomain; |
163 | 0 | t->domain_length = |
164 | 0 | sizeof(netsnmp_snmpSTDDomain) / sizeof(netsnmp_snmpSTDDomain[0]); |
165 | |
|
166 | 0 | t->sock = -1; |
167 | 0 | t->flags = NETSNMP_TRANSPORT_FLAG_STREAM | NETSNMP_TRANSPORT_FLAG_TUNNELED; |
168 | | |
169 | | /* |
170 | | * Message size is not limited by this transport (hence msgMaxSize |
171 | | * is equal to the maximum legal size of an SNMP message). |
172 | | */ |
173 | |
|
174 | 0 | t->msgMaxSize = SNMP_MAX_PACKET_LEN; |
175 | 0 | t->f_recv = netsnmp_std_recv; |
176 | 0 | t->f_send = netsnmp_std_send; |
177 | 0 | t->f_close = netsnmp_std_close; |
178 | 0 | t->f_accept = netsnmp_std_accept; |
179 | 0 | t->f_fmtaddr = netsnmp_std_fmtaddr; |
180 | 0 | t->f_get_taddr = netsnmp_std_get_taddr; |
181 | | |
182 | | /* |
183 | | * if instring is not null length, it specifies a path to a prog |
184 | | * XXX: plus args |
185 | | */ |
186 | 0 | if (instring_len == 0 && default_target != NULL) { |
187 | 0 | instring = default_target; |
188 | 0 | instring_len = strlen(default_target); |
189 | 0 | } |
190 | |
|
191 | 0 | if (instring_len != 0) { |
192 | 0 | int infd[2], outfd[2]; /* sockets to and from the client */ |
193 | 0 | int childpid; |
194 | |
|
195 | 0 | if (pipe(infd) || pipe(outfd)) { |
196 | 0 | snmp_log(LOG_ERR, |
197 | 0 | "Failed to create needed pipes for a STD transport"); |
198 | 0 | netsnmp_transport_free(t); |
199 | 0 | return NULL; |
200 | 0 | } |
201 | | |
202 | 0 | childpid = fork(); |
203 | | /* parentpid => childpid */ |
204 | | /* infd[1] => infd[0] */ |
205 | | /* outfd[0] <= outfd[1] */ |
206 | |
|
207 | 0 | if (childpid) { |
208 | 0 | netsnmp_std_data *data; |
209 | | |
210 | | /* we're in the parent */ |
211 | 0 | close(infd[0]); |
212 | 0 | close(outfd[1]); |
213 | |
|
214 | 0 | data = SNMP_MALLOC_TYPEDEF(netsnmp_std_data); |
215 | 0 | if (!data) { |
216 | 0 | snmp_log(LOG_ERR, "snmpSTDDomain: malloc failed"); |
217 | 0 | netsnmp_transport_free(t); |
218 | 0 | return NULL; |
219 | 0 | } |
220 | 0 | t->data = data; |
221 | 0 | t->data_length = sizeof(netsnmp_std_data); |
222 | 0 | t->sock = outfd[0]; |
223 | 0 | data->prog = strdup(instring); |
224 | 0 | data->outfd = infd[1]; |
225 | 0 | data->childpid = childpid; |
226 | 0 | DEBUGMSGTL(("domain:std","parent. data=%p\n", t->data)); |
227 | 0 | } else { |
228 | | /* we're in the child */ |
229 | |
|
230 | 0 | dup2(infd[0], STDIN_FILENO); |
231 | 0 | dup2(outfd[1], STDOUT_FILENO); |
232 | | |
233 | | /* close all the pipes themselves */ |
234 | 0 | close(infd[0]); |
235 | 0 | close(infd[1]); |
236 | 0 | close(outfd[0]); |
237 | 0 | close(outfd[1]); |
238 | | |
239 | | /* call exec */ |
240 | 0 | NETSNMP_IGNORE_RESULT(system(instring)); |
241 | | /* XXX: TODO: use exec form instead; needs args */ |
242 | | /* execv(instring, NULL); */ |
243 | 0 | exit(0); |
244 | | |
245 | | /* ack... we should never ever get here */ |
246 | 0 | snmp_log(LOG_ERR, "STD transport returned after execv()\n"); |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | 0 | return t; |
251 | 0 | } |
252 | | |
253 | | netsnmp_transport * |
254 | | netsnmp_std_create_tstring(const char *instring, int local, |
255 | | const char *default_target) |
256 | 0 | { |
257 | 0 | return netsnmp_std_transport(instring, strlen(instring), default_target); |
258 | 0 | } |
259 | | |
260 | | netsnmp_transport * |
261 | | netsnmp_std_create_ostring(const void *o, size_t o_len, int local) |
262 | 0 | { |
263 | 0 | return netsnmp_std_transport(o, o_len, NULL); |
264 | 0 | } |
265 | | |
266 | | void |
267 | | netsnmp_std_ctor(void) |
268 | 3.39k | { |
269 | 3.39k | stdDomain.name = netsnmp_snmpSTDDomain; |
270 | 3.39k | stdDomain.name_length = OID_LENGTH(netsnmp_snmpSTDDomain); |
271 | 3.39k | stdDomain.prefix = calloc(2, sizeof(char *)); |
272 | 3.39k | if (!stdDomain.prefix) { |
273 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
274 | 0 | return; |
275 | 0 | } |
276 | 3.39k | stdDomain.prefix[0] = "std"; |
277 | | |
278 | 3.39k | stdDomain.f_create_from_tstring_new = netsnmp_std_create_tstring; |
279 | 3.39k | stdDomain.f_create_from_ostring = netsnmp_std_create_ostring; |
280 | | |
281 | 3.39k | netsnmp_tdomain_register(&stdDomain); |
282 | 3.39k | } |