1 /* $OpenBSD: snmpd.c,v 1.7 2008/05/12 19:15:02 pyr Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008 Reyk Floeter <reyk@vantronix.net> 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/param.h> 23 #include <sys/wait.h> 24 #include <sys/tree.h> 25 26 #include <net/if.h> 27 28 #include <string.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <getopt.h> 32 #include <err.h> 33 #include <errno.h> 34 #include <event.h> 35 #include <signal.h> 36 #include <unistd.h> 37 #include <pwd.h> 38 39 #include "snmpd.h" 40 41 __dead void usage(void); 42 43 void snmpd_sig_handler(int, short, void *); 44 void snmpd_shutdown(struct snmpd *); 45 void snmpd_dispatch_snmpe(int, short, void *); 46 int check_child(pid_t, const char *); 47 48 struct snmpd *snmpd_env; 49 50 int pipe_parent2snmpe[2]; 51 struct imsgbuf *ibuf_snmpe; 52 pid_t snmpe_pid = 0; 53 54 void 55 snmpd_sig_handler(int sig, short event, void *arg) 56 { 57 struct snmpd *env = arg; 58 int die = 0; 59 60 switch (sig) { 61 case SIGTERM: 62 case SIGINT: 63 die = 1; 64 /* FALLTHROUGH */ 65 case SIGCHLD: 66 if (check_child(snmpe_pid, "snmp engine")) { 67 snmpe_pid = 0; 68 die = 1; 69 } 70 if (die) 71 snmpd_shutdown(env); 72 break; 73 case SIGHUP: 74 /* reconfigure */ 75 break; 76 default: 77 fatalx("unexpected signal"); 78 } 79 } 80 81 /* __dead is for lint */ 82 __dead void 83 usage(void) 84 { 85 extern char *__progname; 86 87 fprintf(stderr, "usage: %s [-dNnv] [-D macro=value] [-f file]\n", 88 __progname); 89 exit(1); 90 } 91 92 int 93 main(int argc, char *argv[]) 94 { 95 int c; 96 struct snmpd *env; 97 struct event ev_sigint; 98 struct event ev_sigterm; 99 struct event ev_sigchld; 100 struct event ev_sighup; 101 int debug = 0; 102 u_int flags = 0; 103 int noaction = 0; 104 const char *conffile = CONF_FILE; 105 106 smi_init(); 107 108 log_init(1); /* log to stderr until daemonized */ 109 110 while ((c = getopt(argc, argv, "dD:nNf:v")) != -1) { 111 switch (c) { 112 case 'd': 113 debug = 1; 114 break; 115 case 'D': 116 if (cmdline_symset(optarg) < 0) 117 log_warnx("could not parse macro definition %s", 118 optarg); 119 break; 120 case 'n': 121 noaction++; 122 break; 123 case 'N': 124 flags |= SNMPD_F_NONAMES; 125 break; 126 case 'f': 127 conffile = optarg; 128 break; 129 case 'v': 130 flags |= SNMPD_F_VERBOSE; 131 break; 132 default: 133 usage(); 134 } 135 } 136 137 argc -= optind; 138 argv += optind; 139 if (argc > 0) 140 usage(); 141 142 if ((env = parse_config(conffile, flags)) == NULL) 143 exit(1); 144 snmpd_env = env; 145 146 if (noaction) { 147 fprintf(stderr, "configuration ok\n"); 148 exit(0); 149 } 150 151 if (geteuid()) 152 errx(1, "need root privileges"); 153 154 if (getpwnam(SNMPD_USER) == NULL) 155 errx(1, "unknown user %s", SNMPD_USER); 156 157 log_init(debug); 158 159 if (!debug) { 160 if (daemon(1, 0) == -1) 161 err(1, "failed to daemonize"); 162 } 163 164 gettimeofday(&env->sc_starttime, NULL); 165 166 log_info("startup"); 167 168 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, 169 pipe_parent2snmpe) == -1) 170 fatal("socketpair"); 171 172 session_socket_blockmode(pipe_parent2snmpe[0], BM_NONBLOCK); 173 session_socket_blockmode(pipe_parent2snmpe[1], BM_NONBLOCK); 174 175 snmpe_pid = snmpe(env, pipe_parent2snmpe); 176 setproctitle("parent"); 177 178 event_init(); 179 180 signal_set(&ev_sigint, SIGINT, snmpd_sig_handler, env); 181 signal_set(&ev_sigterm, SIGTERM, snmpd_sig_handler, env); 182 signal_set(&ev_sigchld, SIGCHLD, snmpd_sig_handler, env); 183 signal_set(&ev_sighup, SIGHUP, snmpd_sig_handler, env); 184 signal_add(&ev_sigint, NULL); 185 signal_add(&ev_sigterm, NULL); 186 signal_add(&ev_sigchld, NULL); 187 signal_add(&ev_sighup, NULL); 188 signal(SIGPIPE, SIG_IGN); 189 190 close(pipe_parent2snmpe[1]); 191 192 if ((ibuf_snmpe = calloc(1, sizeof(struct imsgbuf))) == NULL) 193 fatal(NULL); 194 195 imsg_init(ibuf_snmpe, pipe_parent2snmpe[0], snmpd_dispatch_snmpe); 196 197 ibuf_snmpe->events = EV_READ; 198 event_set(&ibuf_snmpe->ev, ibuf_snmpe->fd, ibuf_snmpe->events, 199 ibuf_snmpe->handler, ibuf_snmpe); 200 event_add(&ibuf_snmpe->ev, NULL); 201 202 event_dispatch(); 203 204 return (0); 205 } 206 207 void 208 snmpd_shutdown(struct snmpd *env) 209 { 210 pid_t pid; 211 212 if (snmpe_pid) 213 kill(snmpe_pid, SIGTERM); 214 215 do { 216 if ((pid = wait(NULL)) == -1 && 217 errno != EINTR && errno != ECHILD) 218 fatal("wait"); 219 } while (pid != -1 || (pid == -1 && errno == EINTR)); 220 221 control_cleanup(); 222 log_info("terminating"); 223 exit(0); 224 } 225 226 int 227 check_child(pid_t pid, const char *pname) 228 { 229 int status; 230 231 if (waitpid(pid, &status, WNOHANG) > 0) { 232 if (WIFEXITED(status)) { 233 log_warnx("check_child: lost child: %s exited", pname); 234 return (1); 235 } 236 if (WIFSIGNALED(status)) { 237 log_warnx("check_child: lost child: %s terminated; " 238 "signal %d", pname, WTERMSIG(status)); 239 return (1); 240 } 241 } 242 243 return (0); 244 } 245 246 void 247 imsg_event_add(struct imsgbuf *ibuf) 248 { 249 ibuf->events = EV_READ; 250 if (ibuf->w.queued) 251 ibuf->events |= EV_WRITE; 252 253 event_del(&ibuf->ev); 254 event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf); 255 event_add(&ibuf->ev, NULL); 256 } 257 258 void 259 snmpd_dispatch_snmpe(int fd, short event, void * ptr) 260 { 261 struct imsgbuf *ibuf; 262 struct imsg imsg; 263 ssize_t n; 264 265 ibuf = ptr; 266 switch (event) { 267 case EV_READ: 268 if ((n = imsg_read(ibuf)) == -1) 269 fatal("imsg_read error"); 270 if (n == 0) { 271 /* this pipe is dead, so remove the event handler */ 272 event_del(&ibuf->ev); 273 event_loopexit(NULL); 274 return; 275 } 276 break; 277 case EV_WRITE: 278 if (msgbuf_write(&ibuf->w) == -1) 279 fatal("msgbuf_write"); 280 imsg_event_add(ibuf); 281 return; 282 default: 283 fatalx("unknown event"); 284 } 285 286 for (;;) { 287 if ((n = imsg_get(ibuf, &imsg)) == -1) 288 fatal("snmpd_dispatch_relay: imsg_read error"); 289 if (n == 0) 290 break; 291 292 switch (imsg.hdr.type) { 293 default: 294 log_debug("snmpd_dispatch_relay: unexpected imsg %d", 295 imsg.hdr.type); 296 break; 297 } 298 imsg_free(&imsg); 299 } 300 imsg_event_add(ibuf); 301 } 302 303 int 304 snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port) 305 { 306 int s; 307 308 switch (ss->ss_family) { 309 case AF_INET: 310 ((struct sockaddr_in *)ss)->sin_port = port; 311 ((struct sockaddr_in *)ss)->sin_len = 312 sizeof(struct sockaddr_in); 313 break; 314 case AF_INET6: 315 ((struct sockaddr_in6 *)ss)->sin6_port = port; 316 ((struct sockaddr_in6 *)ss)->sin6_len = 317 sizeof(struct sockaddr_in6); 318 break; 319 default: 320 return (-1); 321 } 322 323 s = socket(ss->ss_family, SOCK_DGRAM, IPPROTO_UDP); 324 return (s); 325 } 326 327