1 /* $OpenBSD: snmpd.c,v 1.9 2009/06/06 05:52:01 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 imsgev *iev_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] " 88 "[-f file] [-r path]\n", __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 const char *rcsock = NULL; 106 107 smi_init(); 108 109 log_init(1); /* log to stderr until daemonized */ 110 111 while ((c = getopt(argc, argv, "dD:nNf:r:v")) != -1) { 112 switch (c) { 113 case 'd': 114 debug = 1; 115 break; 116 case 'D': 117 if (cmdline_symset(optarg) < 0) 118 log_warnx("could not parse macro definition %s", 119 optarg); 120 break; 121 case 'n': 122 noaction++; 123 break; 124 case 'N': 125 flags |= SNMPD_F_NONAMES; 126 break; 127 case 'f': 128 conffile = optarg; 129 break; 130 case 'r': 131 rcsock = optarg; 132 break; 133 case 'v': 134 flags |= SNMPD_F_VERBOSE; 135 break; 136 default: 137 usage(); 138 } 139 } 140 141 argc -= optind; 142 argv += optind; 143 if (argc > 0) 144 usage(); 145 146 if ((env = parse_config(conffile, flags)) == NULL) 147 exit(1); 148 snmpd_env = env; 149 150 if (noaction) { 151 fprintf(stderr, "configuration ok\n"); 152 exit(0); 153 } 154 155 if (geteuid()) 156 errx(1, "need root privileges"); 157 158 if (getpwnam(SNMPD_USER) == NULL) 159 errx(1, "unknown user %s", SNMPD_USER); 160 161 /* Configure the control sockets */ 162 env->sc_csock.cs_name = SNMPD_SOCKET; 163 env->sc_rcsock.cs_name = rcsock; 164 env->sc_rcsock.cs_restricted = 1; 165 166 log_init(debug); 167 168 if (!debug) { 169 if (daemon(1, 0) == -1) 170 err(1, "failed to daemonize"); 171 } 172 173 gettimeofday(&env->sc_starttime, NULL); 174 175 log_info("startup"); 176 177 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, 178 pipe_parent2snmpe) == -1) 179 fatal("socketpair"); 180 181 session_socket_blockmode(pipe_parent2snmpe[0], BM_NONBLOCK); 182 session_socket_blockmode(pipe_parent2snmpe[1], BM_NONBLOCK); 183 184 snmpe_pid = snmpe(env, pipe_parent2snmpe); 185 setproctitle("parent"); 186 187 event_init(); 188 189 signal_set(&ev_sigint, SIGINT, snmpd_sig_handler, env); 190 signal_set(&ev_sigterm, SIGTERM, snmpd_sig_handler, env); 191 signal_set(&ev_sigchld, SIGCHLD, snmpd_sig_handler, env); 192 signal_set(&ev_sighup, SIGHUP, snmpd_sig_handler, env); 193 signal_add(&ev_sigint, NULL); 194 signal_add(&ev_sigterm, NULL); 195 signal_add(&ev_sigchld, NULL); 196 signal_add(&ev_sighup, NULL); 197 signal(SIGPIPE, SIG_IGN); 198 199 close(pipe_parent2snmpe[1]); 200 201 if ((iev_snmpe = calloc(1, sizeof(struct imsgev))) == NULL) 202 fatal(NULL); 203 204 imsg_init(&iev_snmpe->ibuf, pipe_parent2snmpe[0]); 205 iev_snmpe->handler = snmpd_dispatch_snmpe; 206 207 iev_snmpe->events = EV_READ; 208 event_set(&iev_snmpe->ev, iev_snmpe->ibuf.fd, iev_snmpe->events, 209 iev_snmpe->handler, iev_snmpe); 210 event_add(&iev_snmpe->ev, NULL); 211 212 event_dispatch(); 213 214 return (0); 215 } 216 217 void 218 snmpd_shutdown(struct snmpd *env) 219 { 220 pid_t pid; 221 222 if (snmpe_pid) 223 kill(snmpe_pid, SIGTERM); 224 225 do { 226 if ((pid = wait(NULL)) == -1 && 227 errno != EINTR && errno != ECHILD) 228 fatal("wait"); 229 } while (pid != -1 || (pid == -1 && errno == EINTR)); 230 231 control_cleanup(&env->sc_csock); 232 control_cleanup(&env->sc_rcsock); 233 log_info("terminating"); 234 exit(0); 235 } 236 237 int 238 check_child(pid_t pid, const char *pname) 239 { 240 int status; 241 242 if (waitpid(pid, &status, WNOHANG) > 0) { 243 if (WIFEXITED(status)) { 244 log_warnx("check_child: lost child: %s exited", pname); 245 return (1); 246 } 247 if (WIFSIGNALED(status)) { 248 log_warnx("check_child: lost child: %s terminated; " 249 "signal %d", pname, WTERMSIG(status)); 250 return (1); 251 } 252 } 253 254 return (0); 255 } 256 257 void 258 imsg_event_add(struct imsgev *iev) 259 { 260 iev->events = EV_READ; 261 if (iev->ibuf.w.queued) 262 iev->events |= EV_WRITE; 263 264 event_del(&iev->ev); 265 event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); 266 event_add(&iev->ev, NULL); 267 } 268 269 void 270 snmpd_dispatch_snmpe(int fd, short event, void * ptr) 271 { 272 struct imsgev *iev; 273 struct imsgbuf *ibuf; 274 struct imsg imsg; 275 ssize_t n; 276 277 iev = ptr; 278 ibuf = &iev->ibuf; 279 switch (event) { 280 case EV_READ: 281 if ((n = imsg_read(ibuf)) == -1) 282 fatal("imsg_read error"); 283 if (n == 0) { 284 /* this pipe is dead, so remove the event handler */ 285 event_del(&iev->ev); 286 event_loopexit(NULL); 287 return; 288 } 289 break; 290 case EV_WRITE: 291 if (msgbuf_write(&ibuf->w) == -1) 292 fatal("msgbuf_write"); 293 imsg_event_add(iev); 294 return; 295 default: 296 fatalx("unknown event"); 297 } 298 299 for (;;) { 300 if ((n = imsg_get(ibuf, &imsg)) == -1) 301 fatal("snmpd_dispatch_relay: imsg_read error"); 302 if (n == 0) 303 break; 304 305 switch (imsg.hdr.type) { 306 default: 307 log_debug("snmpd_dispatch_relay: unexpected imsg %d", 308 imsg.hdr.type); 309 break; 310 } 311 imsg_free(&imsg); 312 } 313 imsg_event_add(iev); 314 } 315 316 int 317 snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port) 318 { 319 int s; 320 321 switch (ss->ss_family) { 322 case AF_INET: 323 ((struct sockaddr_in *)ss)->sin_port = port; 324 ((struct sockaddr_in *)ss)->sin_len = 325 sizeof(struct sockaddr_in); 326 break; 327 case AF_INET6: 328 ((struct sockaddr_in6 *)ss)->sin6_port = port; 329 ((struct sockaddr_in6 *)ss)->sin6_len = 330 sizeof(struct sockaddr_in6); 331 break; 332 default: 333 return (-1); 334 } 335 336 s = socket(ss->ss_family, SOCK_DGRAM, IPPROTO_UDP); 337 return (s); 338 } 339 340