Remove dead file.
[beanstalkd.git] / beanstalkd.c
bloba6a077ee78a513dec80bf36ca2270a452c0cf85a
1 /* beanstalk - fast, general-purpose work queue */
3 /* Copyright (C) 2007 Keith Rarick and Philotic Inc.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <signal.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <sys/resource.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <pwd.h>
31 #include <event.h>
33 #include "net.h"
34 #include "util.h"
35 #include "prot.h"
37 static char *user = NULL;
38 static int detach = 0;
39 static int port = 11300;
40 static struct in_addr host_addr = { INADDR_ANY };
42 static void
43 nullfd(int fd, int flags)
45 int r;
47 close(fd);
48 r = open("/dev/null", flags);
49 if (r != fd) twarn("open(\"/dev/null\")"), exit(1);
52 static void
53 dfork()
55 pid_t p;
57 p = fork();
58 if (p == -1) exit(1);
59 if (p) exit(0);
62 static void
63 daemonize()
65 chdir("/");
66 nullfd(0, O_RDONLY);
67 nullfd(1, O_WRONLY);
68 nullfd(2, O_WRONLY);
69 umask(0);
70 dfork();
71 setsid();
72 dfork();
75 static void
76 su(const char *user) {
77 int r;
78 struct passwd *pwent;
80 errno = 0;
81 pwent = getpwnam(user);
82 if (errno) twarn("getpwnam(\"%s\")", user), exit(32);
83 if (!pwent) twarnx("getpwnam(\"%s\"): no such user", user), exit(33);
85 r = setgid(pwent->pw_gid);
86 if (r == -1) twarn("setgid(%d \"%s\")", pwent->pw_gid, user), exit(34);
88 r = setuid(pwent->pw_uid);
89 if (r == -1) twarn("setuid(%d \"%s\")", pwent->pw_uid, user), exit(34);
92 void
93 exit_cleanly(int sig)
95 exit(0);
99 static void
100 set_sig_handlers()
102 int r;
103 struct sigaction sa;
105 sa.sa_handler = SIG_IGN;
106 sa.sa_flags = 0;
107 r = sigemptyset(&sa.sa_mask);
108 if (r == -1) twarn("sigemptyset()"), exit(111);
110 r = sigaction(SIGPIPE, &sa, 0);
111 if (r == -1) twarn("sigaction(SIGPIPE)"), exit(111);
113 sa.sa_handler = enter_drain_mode;
114 r = sigaction(SIGUSR1, &sa, 0);
115 if (r == -1) twarn("sigaction(SIGUSR1)"), exit(111);
117 sa.sa_handler = exit_cleanly;
118 r = sigaction(SIGINT, &sa, 0);
119 if (r == -1) twarn("sigaction(SIGINT)"), exit(111);
122 /* This is a workaround for a mystifying workaround in libevent's epoll
123 * implementation. The epoll_init() function creates an epoll fd with space to
124 * handle RLIMIT_NOFILE - 1 fds, accompanied by the following puzzling comment:
125 * "Solaris is somewhat retarded - it's important to drop backwards
126 * compatibility when making changes. So, don't dare to put rl.rlim_cur here."
127 * This is presumably to work around a bug in Solaris, but it has the
128 * unfortunate side-effect of causing epoll_ctl() (and, therefore, event_add())
129 * to fail for a valid fd if we have hit the limit of open fds. That makes it
130 * hard to provide reasonable behavior in that situation. So, let's reduce the
131 * real value of RLIMIT_NOFILE by one, after epoll_init() has run. */
132 static void
133 nudge_fd_limit()
135 int r;
136 struct rlimit rl;
138 r = getrlimit(RLIMIT_NOFILE, &rl);
139 if (r != 0) twarn("getrlimit(RLIMIT_NOFILE)"), exit(2);
141 rl.rlim_cur--;
143 r = setrlimit(RLIMIT_NOFILE, &rl);
144 if (r != 0) twarn("setrlimit(RLIMIT_NOFILE)"), exit(2);
147 static void
148 usage(char *msg, char *arg)
150 if (arg) warnx("%s: %s", msg, arg);
151 fprintf(stderr, "Use: %s [OPTIONS]\n"
152 "\n"
153 "Options:\n"
154 " -d detach\n"
155 " -l ADDR listen on address (default is 0.0.0.0)\n"
156 " -p PORT listen on port (default is 11300)\n"
157 " -u USER become user and group\n"
158 " -z SIZE set the maximum job size\n"
159 " -h show this help\n",
160 progname);
161 exit(arg ? 5 : 0);
164 static size_t
165 parse_size_t(char *str)
167 char r, x;
168 size_t size;
170 r = sscanf(str, "%zu%c", &size, &x);
171 if (1 != r) usage("invalid size", str);
172 return size;
175 static int
176 parse_port(char *portstr)
178 int port;
179 char *end;
181 errno = 0;
182 port = strtol(portstr, &end, 10);
183 if (end == portstr) usage("invalid port", portstr);
184 if (end[0] != 0) usage("invalid port", portstr);
185 if (errno) usage("invalid port", portstr);
187 return port;
190 static struct in_addr
191 parse_host(char *hoststr)
193 int r;
194 struct in_addr addr;
196 r = inet_aton(hoststr, &addr);
197 if (!r) usage("invalid address", hoststr);
199 return addr;
202 static void
203 opts(int argc, char **argv)
205 int i;
207 for (i = 1; i < argc; ++i) {
208 if (argv[i][0] != '-') usage("unknown option", argv[i]);
209 if (argv[i][1] == 0 || argv[i][2] != 0) usage("unknown option",argv[i]);
210 switch (argv[i][1]) {
211 case 'd':
212 detach = 1;
213 break;
214 case 'p':
215 port = parse_port(argv[++i]);
216 break;
217 case 'l':
218 host_addr = parse_host(argv[++i]);
219 break;
220 case 'z':
221 job_data_size_limit = parse_size_t(argv[++i]);
222 break;
223 case 'u':
224 user = argv[++i];
225 break;
226 case 'h':
227 usage(NULL, NULL);
228 default:
229 usage("unknown option", argv[i]);
235 main(int argc, char **argv)
237 int r;
239 progname = argv[0];
240 opts(argc, argv);
242 job_init();
243 prot_init();
245 r = make_server_socket(host_addr, port);
246 if (r == -1) twarnx("make_server_socket()"), exit(111);
248 if (user) su(user);
249 if (detach) daemonize();
250 event_init();
251 set_sig_handlers();
252 nudge_fd_limit();
254 unbrake((evh) h_accept);
256 event_dispatch();
257 twarnx("got here for some reason");
258 return 0;