File: | src/usr.sbin/snmpd/snmpd.c |
Warning: | line 198, column 2 Value stored to 'argv' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: snmpd.c,v 1.46 2021/08/10 06:52:03 martijn Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org> |
5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any |
7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. |
9 | * |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ |
18 | |
19 | #include <sys/types.h> |
20 | #include <sys/queue.h> |
21 | #include <sys/socket.h> |
22 | #include <sys/wait.h> |
23 | #include <sys/tree.h> |
24 | |
25 | #include <net/if.h> |
26 | |
27 | #include <string.h> |
28 | #include <stdio.h> |
29 | #include <stdlib.h> |
30 | #include <getopt.h> |
31 | #include <err.h> |
32 | #include <errno(*__errno()).h> |
33 | #include <event.h> |
34 | #include <signal.h> |
35 | #include <syslog.h> |
36 | #include <unistd.h> |
37 | #include <fcntl.h> |
38 | #include <pwd.h> |
39 | |
40 | #include "snmpd.h" |
41 | #include "mib.h" |
42 | |
43 | __dead__attribute__((__noreturn__)) void usage(void); |
44 | |
45 | void snmpd_shutdown(struct snmpd *); |
46 | void snmpd_sig_handler(int, short, void *); |
47 | int snmpd_dispatch_snmpe(int, struct privsep_proc *, struct imsg *); |
48 | int check_child(pid_t, const char *); |
49 | |
50 | struct snmpd *snmpd_env; |
51 | |
52 | static struct privsep_proc procs[] = { |
53 | { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown }, |
54 | }; |
55 | |
56 | enum privsep_procid privsep_process; |
57 | |
58 | void |
59 | snmpd_sig_handler(int sig, short event, void *arg) |
60 | { |
61 | struct privsep *ps = arg; |
62 | struct snmpd *env = ps->ps_env; |
63 | int die = 0, status, fail, id; |
64 | pid_t pid; |
65 | char *cause; |
66 | |
67 | switch (sig) { |
68 | case SIGTERM15: |
69 | case SIGINT2: |
70 | die = 1; |
71 | /* FALLTHROUGH */ |
72 | case SIGCHLD20: |
73 | do { |
74 | int len; |
75 | |
76 | pid = waitpid(WAIT_ANY(-1), &status, WNOHANG1); |
77 | if (pid <= 0) |
78 | continue; |
79 | |
80 | fail = 0; |
81 | if (WIFSIGNALED(status)(((status) & 0177) != 0177 && ((status) & 0177 ) != 0)) { |
82 | fail = 1; |
83 | len = asprintf(&cause, "terminated; signal %d", |
84 | WTERMSIG(status)(((status) & 0177))); |
85 | } else if (WIFEXITED(status)(((status) & 0177) == 0)) { |
86 | if (WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff) != 0) { |
87 | fail = 1; |
88 | len = asprintf(&cause, |
89 | "exited abnormally"); |
90 | } else |
91 | len = asprintf(&cause, "exited okay"); |
92 | } else |
93 | fatalx("unexpected cause of SIGCHLD"); |
94 | |
95 | if (len == -1) |
96 | fatal("asprintf"); |
97 | |
98 | for (id = 0; id < PROC_MAX; id++) { |
99 | if (pid == ps->ps_pid[id] && |
100 | check_child(ps->ps_pid[id], |
101 | ps->ps_title[id])) { |
102 | die = 1; |
103 | if (fail) |
104 | log_warnx("lost child: %s %s", |
105 | ps->ps_title[id], cause); |
106 | break; |
107 | } |
108 | } |
109 | free(cause); |
110 | } while (pid > 0 || (pid == -1 && errno(*__errno()) == EINTR4)); |
111 | |
112 | if (die) |
113 | snmpd_shutdown(env); |
114 | break; |
115 | case SIGHUP1: |
116 | /* reconfigure */ |
117 | break; |
118 | case SIGUSR130: |
119 | /* ignore */ |
120 | break; |
121 | default: |
122 | fatalx("unexpected signal"); |
123 | } |
124 | } |
125 | |
126 | __dead__attribute__((__noreturn__)) void |
127 | usage(void) |
128 | { |
129 | extern char *__progname; |
130 | |
131 | fprintf(stderr(&__sF[2]), "usage: %s [-dNnv] [-D macro=value] " |
132 | "[-f file]\n", __progname); |
133 | exit(1); |
134 | } |
135 | |
136 | int |
137 | main(int argc, char *argv[]) |
138 | { |
139 | int c; |
140 | struct snmpd *env; |
141 | int debug = 0, verbose = 0; |
142 | u_int flags = 0; |
143 | int noaction = 0; |
144 | const char *conffile = CONF_FILE"/etc/snmpd.conf"; |
145 | struct privsep *ps; |
146 | int proc_id = PROC_PARENT, proc_instance = 0; |
147 | int argc0 = argc; |
148 | char **argv0 = argv; |
149 | const char *errp, *title = NULL((void *)0); |
150 | |
151 | smi_init(); |
152 | |
153 | /* log to stderr until daemonized */ |
154 | log_init(1, LOG_DAEMON(3<<3)); |
155 | |
156 | while ((c = getopt(argc, argv, "dD:nNf:I:P:v")) != -1) { |
157 | switch (c) { |
158 | case 'd': |
159 | debug++; |
160 | flags |= SNMPD_F_DEBUG0x02; |
161 | break; |
162 | case 'D': |
163 | if (cmdline_symset(optarg) < 0) |
164 | log_warnx("could not parse macro definition %s", |
165 | optarg); |
166 | break; |
167 | case 'n': |
168 | noaction = 1; |
169 | break; |
170 | case 'N': |
171 | flags |= SNMPD_F_NONAMES0x04; |
172 | break; |
173 | case 'f': |
174 | conffile = optarg; |
175 | break; |
176 | case 'I': |
177 | proc_instance = strtonum(optarg, 0, |
178 | PROC_MAX_INSTANCES32, &errp); |
179 | if (errp) |
180 | fatalx("invalid process instance"); |
181 | break; |
182 | case 'P': |
183 | title = optarg; |
184 | proc_id = proc_getid(procs, nitems(procs)(sizeof((procs)) / sizeof((procs)[0])), title); |
185 | if (proc_id == PROC_MAX) |
186 | fatalx("invalid process name"); |
187 | break; |
188 | case 'v': |
189 | verbose++; |
190 | flags |= SNMPD_F_VERBOSE0x01; |
191 | break; |
192 | default: |
193 | usage(); |
194 | } |
195 | } |
196 | |
197 | argc -= optind; |
198 | argv += optind; |
Value stored to 'argv' is never read | |
199 | if (argc > 0) |
200 | usage(); |
201 | |
202 | if ((env = parse_config(conffile, flags)) == NULL((void *)0)) |
203 | exit(1); |
204 | |
205 | ps = &env->sc_ps; |
206 | ps->ps_env = env; |
207 | snmpd_env = env; |
208 | ps->ps_instance = proc_instance; |
209 | if (title) |
210 | ps->ps_title[proc_id] = title; |
211 | |
212 | if (noaction) { |
213 | fprintf(stderr(&__sF[2]), "configuration ok\n"); |
214 | exit(0); |
215 | } |
216 | |
217 | if (geteuid()) |
218 | errx(1, "need root privileges"); |
219 | |
220 | if ((ps->ps_pw = getpwnam(SNMPD_USER"_snmpd")) == NULL((void *)0)) |
221 | errx(1, "unknown user %s", SNMPD_USER"_snmpd"); |
222 | |
223 | log_init(debug, LOG_DAEMON(3<<3)); |
224 | log_setverbose(verbose); |
225 | |
226 | gettimeofday(&env->sc_starttime, NULL((void *)0)); |
227 | env->sc_engine_boots = 0; |
228 | |
229 | pf_init(); |
230 | |
231 | proc_init(ps, procs, nitems(procs)(sizeof((procs)) / sizeof((procs)[0])), debug, argc0, argv0, proc_id); |
232 | if (!debug && daemon(0, 0) == -1) |
233 | err(1, "failed to daemonize"); |
234 | |
235 | log_procinit("parent"); |
236 | log_info("startup"); |
237 | |
238 | event_init(); |
239 | |
240 | signal_set(&ps->ps_evsigint, SIGINT, snmpd_sig_handler, ps)event_set(&ps->ps_evsigint, 2, 0x08|0x10, snmpd_sig_handler , ps); |
241 | signal_set(&ps->ps_evsigterm, SIGTERM, snmpd_sig_handler, ps)event_set(&ps->ps_evsigterm, 15, 0x08|0x10, snmpd_sig_handler , ps); |
242 | signal_set(&ps->ps_evsigchld, SIGCHLD, snmpd_sig_handler, ps)event_set(&ps->ps_evsigchld, 20, 0x08|0x10, snmpd_sig_handler , ps); |
243 | signal_set(&ps->ps_evsighup, SIGHUP, snmpd_sig_handler, ps)event_set(&ps->ps_evsighup, 1, 0x08|0x10, snmpd_sig_handler , ps); |
244 | signal_set(&ps->ps_evsigpipe, SIGPIPE, snmpd_sig_handler, ps)event_set(&ps->ps_evsigpipe, 13, 0x08|0x10, snmpd_sig_handler , ps); |
245 | signal_set(&ps->ps_evsigusr1, SIGUSR1, snmpd_sig_handler, ps)event_set(&ps->ps_evsigusr1, 30, 0x08|0x10, snmpd_sig_handler , ps); |
246 | |
247 | signal_add(&ps->ps_evsigint, NULL)event_add(&ps->ps_evsigint, ((void *)0)); |
248 | signal_add(&ps->ps_evsigterm, NULL)event_add(&ps->ps_evsigterm, ((void *)0)); |
249 | signal_add(&ps->ps_evsigchld, NULL)event_add(&ps->ps_evsigchld, ((void *)0)); |
250 | signal_add(&ps->ps_evsighup, NULL)event_add(&ps->ps_evsighup, ((void *)0)); |
251 | signal_add(&ps->ps_evsigpipe, NULL)event_add(&ps->ps_evsigpipe, ((void *)0)); |
252 | signal_add(&ps->ps_evsigusr1, NULL)event_add(&ps->ps_evsigusr1, ((void *)0)); |
253 | |
254 | proc_connect(ps); |
255 | |
256 | if (pledge("stdio dns sendfd proc exec id", NULL((void *)0)) == -1) |
257 | fatal("pledge"); |
258 | |
259 | event_dispatch(); |
260 | |
261 | log_debug("%d parent exiting", getpid()); |
262 | |
263 | return (0); |
264 | } |
265 | |
266 | void |
267 | snmpd_shutdown(struct snmpd *env) |
268 | { |
269 | proc_kill(&env->sc_ps); |
270 | |
271 | free(env); |
272 | |
273 | log_info("terminating"); |
274 | exit(0); |
275 | } |
276 | |
277 | int |
278 | check_child(pid_t pid, const char *pname) |
279 | { |
280 | int status; |
281 | |
282 | if (waitpid(pid, &status, WNOHANG1) > 0) { |
283 | if (WIFEXITED(status)(((status) & 0177) == 0)) { |
284 | log_warnx("check_child: lost child: %s exited", pname); |
285 | return (1); |
286 | } |
287 | if (WIFSIGNALED(status)(((status) & 0177) != 0177 && ((status) & 0177 ) != 0)) { |
288 | log_warnx("check_child: lost child: %s terminated; " |
289 | "signal %d", pname, WTERMSIG(status)(((status) & 0177))); |
290 | return (1); |
291 | } |
292 | } |
293 | |
294 | return (0); |
295 | } |
296 | |
297 | int |
298 | snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg) |
299 | { |
300 | switch (imsg->hdr.type) { |
301 | case IMSG_TRAP_EXEC: |
302 | return (traphandler_priv_recvmsg(p, imsg)); |
303 | case IMSG_CTL_RELOAD: |
304 | /* XXX notyet */ |
305 | default: |
306 | break; |
307 | } |
308 | |
309 | return (-1); |
310 | } |
311 | |
312 | int |
313 | snmpd_socket_af(struct sockaddr_storage *ss, int type) |
314 | { |
315 | int fd, serrno; |
316 | const int enable = 1; |
317 | |
318 | fd = socket(ss->ss_family, (type == SOCK_STREAM1 ? |
319 | SOCK_STREAM1 | SOCK_NONBLOCK0x4000 : SOCK_DGRAM2) | SOCK_CLOEXEC0x8000, 0); |
320 | if (fd == -1) |
321 | return -1; |
322 | |
323 | if (setsockopt(fd, SOL_SOCKET0xffff, SO_REUSEADDR0x0004, &enable, |
324 | sizeof(enable)) == -1) { |
325 | serrno = errno(*__errno()); |
326 | close(fd); |
327 | errno(*__errno()) = serrno; |
328 | return -1; |
329 | } |
330 | return fd; |
331 | } |
332 | |
333 | u_long |
334 | snmpd_engine_time(void) |
335 | { |
336 | struct timeval now; |
337 | |
338 | /* |
339 | * snmpEngineBoots should be stored in a non-volatile storage. |
340 | * snmpEngineTime is the number of seconds since snmpEngineBoots |
341 | * was last incremented. We don't rely on non-volatile storage. |
342 | * snmpEngineBoots is set to zero and snmpEngineTime to the system |
343 | * clock. Hence, the tuple (snmpEngineBoots, snmpEngineTime) is |
344 | * still unique and protects us against replay attacks. It only |
345 | * 'expires' a little bit sooner than the RFC3414 method. |
346 | */ |
347 | gettimeofday(&now, NULL((void *)0)); |
348 | return now.tv_sec; |
349 | } |