waffle.c (3519B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <sys/select.h> 3 #include <sys/signalfd.h> 4 #include <sys/socket.h> 5 #include <sys/stat.h> 6 #include <sys/types.h> 7 #include <sys/un.h> 8 #include <errno.h> 9 #include <signal.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <syslog.h> 14 #include <unistd.h> 15 #include "arg.h" 16 #include "waffle.h" 17 18 int daemonize = 1; 19 int verbose = 0; 20 char *argv0; 21 static fd_set master; 22 static int fdmax; 23 24 static int 25 serv_listen(const char *name) 26 { 27 struct sockaddr_un sun; 28 int fd, r; 29 socklen_t len; 30 31 fd = socket(AF_UNIX, SOCK_STREAM, 0); 32 if (fd < 0) 33 logerr("socket: %s\n", strerror(errno)); 34 35 unlink(name); 36 37 memset(&sun, 0, sizeof(sun)); 38 sun.sun_family = AF_UNIX; 39 strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 40 41 len = sizeof(sun); 42 r = bind(fd, (struct sockaddr *)&sun, len); 43 if (r < 0) 44 logerr("bind %s: %s\n", name, strerror(errno)); 45 46 if (chmod(name, 0666) < 0) 47 logerr("chmod %s: %s\n", name, strerror(errno)); 48 49 r = listen(fd, 5); 50 if (r < 0) 51 logerr("listen: %s\n", strerror(errno)); 52 53 return fd; 54 } 55 56 static int 57 serv_accept(int listenfd) 58 { 59 struct sockaddr_un sun; 60 int clifd; 61 socklen_t len; 62 63 len = sizeof(sun); 64 clifd = accept(listenfd, (struct sockaddr *)&sun, &len); 65 if (clifd < 0) 66 logerr("accept: %s\n", strerror(errno)); 67 return clifd; 68 } 69 70 static int 71 add_cli(int clifd) 72 { 73 struct timeval tv; 74 75 tv.tv_sec = 0; 76 tv.tv_usec = NSCDTIMEOUT * 1000; 77 if (setsockopt(clifd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { 78 close(clifd); 79 return -1; 80 } 81 FD_SET(clifd, &master); 82 if (clifd > fdmax) 83 fdmax = clifd; 84 return 0; 85 } 86 87 static void 88 del_cli(int clifd) 89 { 90 FD_CLR(clifd, &master); 91 close(clifd); 92 } 93 94 static void 95 usage(void) 96 { 97 fprintf(stderr, "usage: %s [-dv]\n", argv0); 98 exit(1); 99 } 100 101 int 102 main(int argc, char *argv[]) 103 { 104 FILE *fp; 105 sigset_t mask; 106 struct signalfd_siginfo si; 107 fd_set rfds; 108 int listenfd, clifd, sfd; 109 int i, n; 110 ssize_t r; 111 112 ARGBEGIN { 113 case 'd': 114 daemonize = 0; 115 break; 116 case 'v': 117 verbose++; 118 break; 119 default: 120 usage(); 121 } ARGEND; 122 123 signal(SIGPIPE, SIG_IGN); 124 sigemptyset(&mask); 125 sigaddset(&mask, SIGTERM); 126 sigaddset(&mask, SIGINT); 127 sigprocmask(SIG_BLOCK, &mask, NULL); 128 129 if (daemonize) { 130 openlog(argv[0], LOG_CONS | LOG_PID, LOG_DAEMON); 131 if (daemon(0, 0) < 0) 132 logerr("daemon: %s\n", strerror(errno)); 133 if ((fp = fopen(PIDFILE, "w"))) { 134 fprintf(fp, "%d\n", getpid()); 135 fclose(fp); 136 } 137 } 138 139 listenfd = serv_listen(SOCKPATH); 140 FD_ZERO(&master); 141 FD_ZERO(&rfds); 142 FD_SET(listenfd, &master); 143 fdmax = listenfd; 144 145 sfd = signalfd(-1, &mask, 0); 146 if (sfd < 0) 147 logerr("signalfd: %s\n", strerror(errno)); 148 FD_SET(sfd, &master); 149 if (sfd > fdmax) 150 fdmax = sfd; 151 152 if (backends_init() < 0) 153 logerr("no available backends\n"); 154 155 while (1) { 156 rfds = master; 157 n = select(fdmax + 1, &rfds, NULL, NULL, NULL); 158 if (n < 0) { 159 if (errno == EINTR) 160 continue; 161 logerr("select: %s\n", strerror(errno)); 162 } 163 for (i = 0; i <= fdmax; i++) { 164 if (!FD_ISSET(i, &rfds)) 165 continue; 166 if (i == sfd) { 167 r = read(sfd, &si, sizeof(si)); 168 if ((size_t)r != sizeof(si)) 169 continue; 170 switch (si.ssi_signo) { 171 case SIGTERM: 172 case SIGINT: 173 goto out; 174 } 175 } else if (i == listenfd) { 176 clifd = serv_accept(listenfd); 177 if (add_cli(clifd) < 0) 178 continue; 179 } else { 180 if (handle_req(i) < 0) 181 logwarn("failed to handle request from fd %d\n", i); 182 del_cli(i); 183 } 184 } 185 } 186 out: 187 backends_term(); 188 if (daemonize) { 189 unlink(PIDFILE); 190 closelog(); 191 } 192 exit(0); 193 }