/src/net-snmp/snmplib/transports/snmpSTDDomain.c
Line | Count | Source |
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 %" NETSNMP_FMT_SKT ". data=%p\n", |
72 | 0 | t->sock, t->data)); |
73 | 0 | while (rc < 0) { |
74 | 0 | rc = read(t->sock, buf, size); |
75 | 0 | DEBUGMSGTL(("domain:std"," bytes: %d.\n", rc)); |
76 | 0 | if (rc < 0 && errno != EINTR) { |
77 | 0 | DEBUGMSGTL(("netsnmp_std", |
78 | 0 | " read on fd %" NETSNMP_FMT_SKT " failed: %d (\"%s\")\n", |
79 | 0 | t->sock, errno, strerror(errno))); |
80 | 0 | break; |
81 | 0 | } |
82 | 0 | if (rc == 0) { |
83 | | /* 0 input is probably bad since we selected on it */ |
84 | 0 | return -1; |
85 | 0 | } |
86 | 0 | DEBUGMSGTL(("netsnmp_std", "read on stdin got %d bytes\n", rc)); |
87 | 0 | } |
88 | | |
89 | 0 | return rc; |
90 | 0 | } |
91 | | |
92 | | |
93 | | |
94 | | static int |
95 | | netsnmp_std_send(netsnmp_transport *t, const void *buf, int size, |
96 | | void **opaque, int *olength) |
97 | 0 | { |
98 | 0 | int rc = -1; |
99 | |
|
100 | 0 | DEBUGMSGTL(("domain:std","send on sock. data=%p\n", t->data)); |
101 | 0 | while (rc < 0) { |
102 | 0 | if (t->data) { |
103 | 0 | netsnmp_std_data *data = (netsnmp_std_data*)t->data; |
104 | 0 | rc = write(data->outfd, buf, size); |
105 | 0 | } else { |
106 | | /* straight to stdout */ |
107 | 0 | rc = write(1, buf, size); |
108 | 0 | } |
109 | 0 | if (rc < 0 && errno != EINTR) { |
110 | 0 | break; |
111 | 0 | } |
112 | 0 | } |
113 | 0 | return rc; |
114 | 0 | } |
115 | | |
116 | | static int |
117 | | netsnmp_std_close(netsnmp_transport *t) |
118 | 0 | { |
119 | 0 | DEBUGMSGTL(("domain:std","close. data=%p\n", t->data)); |
120 | 0 | if (t->data) { |
121 | 0 | netsnmp_std_data *data = (netsnmp_std_data*)t->data; |
122 | |
|
123 | 0 | close(data->outfd); |
124 | 0 | close(t->sock); |
125 | | |
126 | | /* kill the child too */ |
127 | 0 | DEBUGMSGTL(("domain:std"," killing %d\n", data->childpid)); |
128 | 0 | kill(data->childpid, SIGTERM); |
129 | 0 | sleep(1); |
130 | 0 | kill(data->childpid, SIGKILL); |
131 | | /* XXX: set an alarm to kill harder the child */ |
132 | 0 | } else { |
133 | | /* close stdout/in */ |
134 | 0 | close(STDOUT_FILENO); |
135 | 0 | close(STDIN_FILENO); |
136 | 0 | } |
137 | 0 | return 0; |
138 | 0 | } |
139 | | |
140 | | |
141 | | |
142 | | static NETSNMP_SOCKET |
143 | | netsnmp_std_accept(netsnmp_transport *t) |
144 | 0 | { |
145 | 0 | DEBUGMSGTL(("domain:std"," accept data=%p\n", t->data)); |
146 | | /* nothing to do here */ |
147 | 0 | return NETSNMP_INVALID_SOCKET; |
148 | 0 | } |
149 | | |
150 | | /* |
151 | | * Open a STDIN/STDOUT -based transport for SNMP. |
152 | | */ |
153 | | |
154 | | netsnmp_transport * |
155 | | netsnmp_std_transport(const char *instring, size_t instring_len, |
156 | | const char *default_target) |
157 | 0 | { |
158 | 0 | netsnmp_transport *t; |
159 | |
|
160 | 0 | t = netsnmp_transport_alloc(); |
161 | 0 | if (t == NULL) { |
162 | 0 | return NULL; |
163 | 0 | } |
164 | | |
165 | 0 | t->domain = netsnmp_snmpSTDDomain; |
166 | 0 | t->domain_length = |
167 | 0 | sizeof(netsnmp_snmpSTDDomain) / sizeof(netsnmp_snmpSTDDomain[0]); |
168 | 0 | t->flags = NETSNMP_TRANSPORT_FLAG_STREAM | NETSNMP_TRANSPORT_FLAG_TUNNELED; |
169 | | |
170 | | /* |
171 | | * Message size is not limited by this transport (hence msgMaxSize |
172 | | * is equal to the maximum legal size of an SNMP message). |
173 | | */ |
174 | |
|
175 | 0 | t->msgMaxSize = SNMP_MAX_PACKET_LEN; |
176 | 0 | t->f_recv = netsnmp_std_recv; |
177 | 0 | t->f_send = netsnmp_std_send; |
178 | 0 | t->f_close = netsnmp_std_close; |
179 | 0 | t->f_accept = netsnmp_std_accept; |
180 | 0 | t->f_fmtaddr = netsnmp_std_fmtaddr; |
181 | 0 | t->f_get_taddr = netsnmp_std_get_taddr; |
182 | | |
183 | | /* |
184 | | * if instring is not null length, it specifies a path to a prog |
185 | | * XXX: plus args |
186 | | */ |
187 | 0 | if (instring_len == 0 && default_target != NULL) { |
188 | 0 | instring = default_target; |
189 | 0 | instring_len = strlen(default_target); |
190 | 0 | } |
191 | |
|
192 | 0 | if (instring_len != 0) { |
193 | 0 | int infd[2], outfd[2]; /* sockets to and from the client */ |
194 | 0 | int childpid; |
195 | |
|
196 | 0 | if (pipe(infd) || pipe(outfd)) { |
197 | 0 | snmp_log(LOG_ERR, |
198 | 0 | "Failed to create needed pipes for a STD transport"); |
199 | 0 | netsnmp_transport_free(t); |
200 | 0 | return NULL; |
201 | 0 | } |
202 | | |
203 | 0 | childpid = fork(); |
204 | | /* parentpid => childpid */ |
205 | | /* infd[1] => infd[0] */ |
206 | | /* outfd[0] <= outfd[1] */ |
207 | |
|
208 | 0 | if (childpid) { |
209 | 0 | netsnmp_std_data *data; |
210 | | |
211 | | /* we're in the parent */ |
212 | 0 | close(infd[0]); |
213 | 0 | close(outfd[1]); |
214 | |
|
215 | 0 | data = SNMP_MALLOC_TYPEDEF(netsnmp_std_data); |
216 | 0 | if (!data) { |
217 | 0 | snmp_log(LOG_ERR, "snmpSTDDomain: malloc failed"); |
218 | 0 | netsnmp_transport_free(t); |
219 | 0 | return NULL; |
220 | 0 | } |
221 | 0 | t->data = data; |
222 | 0 | t->data_length = sizeof(netsnmp_std_data); |
223 | 0 | t->sock = outfd[0]; |
224 | 0 | data->prog = strdup(instring); |
225 | 0 | data->outfd = infd[1]; |
226 | 0 | data->childpid = childpid; |
227 | 0 | DEBUGMSGTL(("domain:std","parent. data=%p\n", t->data)); |
228 | 0 | } else { |
229 | | /* we're in the child */ |
230 | |
|
231 | 0 | dup2(infd[0], STDIN_FILENO); |
232 | 0 | dup2(outfd[1], STDOUT_FILENO); |
233 | | |
234 | | /* close all the pipes themselves */ |
235 | 0 | close(infd[0]); |
236 | 0 | close(infd[1]); |
237 | 0 | close(outfd[0]); |
238 | 0 | close(outfd[1]); |
239 | | |
240 | | /* call exec */ |
241 | 0 | NETSNMP_IGNORE_RESULT(system(instring)); |
242 | | /* XXX: TODO: use exec form instead; needs args */ |
243 | | /* execv(instring, NULL); */ |
244 | 0 | exit(0); |
245 | | |
246 | | /* ack... we should never ever get here */ |
247 | 0 | snmp_log(LOG_ERR, "STD transport returned after execv()\n"); |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | 0 | return t; |
252 | 0 | } |
253 | | |
254 | | netsnmp_transport * |
255 | | netsnmp_std_create_tstring(const char *instring, int local, |
256 | | const char *default_target) |
257 | 0 | { |
258 | 0 | return netsnmp_std_transport(instring, strlen(instring), default_target); |
259 | 0 | } |
260 | | |
261 | | netsnmp_transport * |
262 | | netsnmp_std_create_ostring(const void *o, size_t o_len, int local) |
263 | 0 | { |
264 | 0 | return netsnmp_std_transport(o, o_len, NULL); |
265 | 0 | } |
266 | | |
267 | | void |
268 | | netsnmp_std_ctor(void) |
269 | 4.25k | { |
270 | 4.25k | stdDomain.name = netsnmp_snmpSTDDomain; |
271 | 4.25k | stdDomain.name_length = OID_LENGTH(netsnmp_snmpSTDDomain); |
272 | 4.25k | stdDomain.prefix = calloc(2, sizeof(char *)); |
273 | 4.25k | if (!stdDomain.prefix) { |
274 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
275 | 0 | return; |
276 | 0 | } |
277 | 4.25k | stdDomain.prefix[0] = "std"; |
278 | | |
279 | 4.25k | stdDomain.f_create_from_tstring_new = netsnmp_std_create_tstring; |
280 | 4.25k | stdDomain.f_create_from_ostring = netsnmp_std_create_ostring; |
281 | | |
282 | 4.25k | netsnmp_tdomain_register(&stdDomain); |
283 | 4.25k | } |