IPv6 dual-stack support
[umurmur.git] / src / main.c
1 /* Copyright (C) 2009-2014, Martin Johansson <martin@fatbob.nu>
2    Copyright (C) 2005-2014, Thorvald Natvig <thorvald@natvig.com>
3
4    All rights reserved.
5
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9
10    - Redistributions of source code must retain the above copyright notice,
11      this list of conditions and the following disclaimer.
12    - Redistributions in binary form must reproduce the above copyright notice,
13      this list of conditions and the following disclaimer in the documentation
14      and/or other materials provided with the distribution.
15    - Neither the name of the Developers nor the names of its contributors may
16      be used to endorse or promote products derived from this software without
17      specific prior written permission.
18
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/utsname.h>
37 #include <fcntl.h>
38 #include <pwd.h>
39 #include <grp.h>
40 #include <signal.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #ifdef _POSIX_PRIORITY_SCHEDULING
45 #if (_POSIX_PRIORITY_SCHEDULING > 0)
46 #define POSIX_PRIORITY_SCHEDULING
47 #include <sched.h>
48 #endif
49 #endif
50 #include "server.h"
51 #include "ssl.h"
52 #include "channel.h"
53 #include "log.h"
54 #include "client.h"
55 #include "conf.h"
56 #include "version.h"
57 #include "config.h"
58
59 char system_string[64], version_string[64];
60 int bindport;
61 int bindport6;
62 char *bindaddr;
63 char *bindaddr6;
64
65 void lockfile(const char *pidfile)
66 {
67         int lfp, flags;
68         char str[16];
69
70         /* Don't use O_TRUNC here -- we want to leave the PID file
71          * unmodified if we cannot lock it.
72          */
73         lfp = open(pidfile, O_WRONLY|O_CREAT, 0640);
74
75         if (lfp < 0)
76                 Log_fatal("Cannot open PID-file %s for writing", pidfile);
77
78         /* Try to lock the file. */
79         if (lockf(lfp, F_TLOCK, 0) < 0) {
80                 close(lfp);
81
82                 if (errno == EACCES || errno == EAGAIN)
83                         Log_fatal("PID file is locked -- uMurmur already running?");
84
85                 Log_fatal("Cannot lock PID file: %s", strerror(errno));
86         }
87
88         /* Now that we locked the file, erase its contents. */
89         if (ftruncate(lfp, 0) < 0) {
90                 close(lfp);
91                 Log_fatal("Cannot truncate PID file: %s", strerror(errno));
92         }
93
94         snprintf(str,16,"%d\n", getpid());
95         (void)write(lfp, str, strlen(str)); /* record pid to lockfile */
96         Log_info("PID-file: %s", pidfile);
97
98         /* If uMurmur ever starts to fork()+exec(), we don't want it to
99          * leak the fd to the forked process though. Set the close-on-exec
100          * flag to prevent leakage.
101          */
102         flags = fcntl(lfp, F_GETFD, 0);
103         flags |= FD_CLOEXEC;
104         fcntl(lfp, F_SETFD, (long) flags);
105
106         /* Don't close(lfp) here!
107          * We want the fd to remain opened so the lock is held until the
108          * process exits.
109          */
110         lfp = -1;
111 }
112
113 /* Drops privileges (if configured to do so). */
114 static void switch_user(void)
115 {
116         struct passwd *pwd;
117         struct group *grp = NULL;
118         const char *username, *groupname;
119         gid_t gid;
120
121         username = getStrConf(USERNAME);
122         groupname = getStrConf(GROUPNAME);
123
124         if (!*username) {
125                 /* It's an error to specify groupname
126                  * but leave username empty.
127                  */
128                 if (*groupname)
129                         Log_fatal("username missing");
130
131                 /* Nothing to do. */
132                 return;
133         }
134
135         pwd = getpwnam(username);
136         if (!pwd)
137                 Log_fatal("Unknown user '%s'", username);
138
139         if (!*groupname)
140                 gid = pwd->pw_gid;
141         else {
142                 grp = getgrnam(groupname);
143
144                 if (!grp)
145                         Log_fatal("Unknown group '%s'", groupname);
146
147                 gid = grp->gr_gid;
148         }
149
150         if (initgroups(pwd->pw_name, gid))
151                 Log_fatal("initgroups() failed: %s", strerror(errno));
152
153         if (setgid(gid))
154                 Log_fatal("setgid() failed: %s", strerror(errno));
155
156         if (setuid(pwd->pw_uid))
157                 Log_fatal("setuid() failed: %s", strerror(errno));
158
159         if (!grp)
160                 grp = getgrgid(gid);
161         if (!grp)
162                 Log_fatal("getgrgid() failed: %s", strerror(errno));
163
164         Log_info("Switch to user '%s' group '%s'", pwd->pw_name, grp->gr_name);
165 }
166
167 void signal_handler(int sig)
168 {
169         switch(sig) {
170                 case SIGHUP:
171                         Log_info("HUP signal received.");
172                         Log_reset();
173                         break;
174                 case SIGTERM:
175                         Log_info("TERM signal. Shutting down.");
176                         Server_shutdown();
177                         break;
178         }
179 }
180
181 void daemonize()
182 {
183         int i;
184
185         if (getppid() == 1)
186                 return; /* already a daemon */
187         i = fork();
188         if ( i < 0) {
189                 fprintf(stderr, "Fork error. Exiting\n");
190                 exit(1); /* fork error */
191         }
192         if ( i > 0)
193                 exit(0); /* parent exits */
194
195         /* child (daemon) continues */
196         setsid(); /* obtain a new process group */
197         for (i = getdtablesize(); i >= 0; --i)
198                 close(i); /* close all descriptors */
199
200         i = open("/dev/null",O_RDWR);
201         (void)dup(i);
202         (void)dup(i);
203
204         umask(027); /* set newly created file permissions */
205         (void)chdir("/");
206
207 }
208
209 #ifdef POSIX_PRIORITY_SCHEDULING
210 void setscheduler()
211 {
212         int rc;
213         struct sched_param sp;
214
215         sp.sched_priority = sched_get_priority_min(SCHED_RR); /* Should suffice */
216         Log_info("Setting SCHED_RR prio %d", sp.sched_priority);
217         rc = sched_setscheduler(0, SCHED_RR, &sp);
218         if (rc < 0)
219                 Log_warn("Failed to set scheduler: %s", strerror(errno));
220 }
221 #endif
222
223 void printhelp()
224 {
225         printf("uMurmur version %s ('%s'). Mumble protocol %d.%d.%d\n", UMURMUR_VERSION,
226                 UMURMUR_CODENAME, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
227         printf("Usage: umurmurd [-d] [-r] [-h] [-p <pidfile>] [-t] [-c <conf file>] [-a <addr>] [-b <port>]\n");
228         printf("       -d             - Do not daemonize - run in foreground.\n");
229 #ifdef POSIX_PRIORITY_SCHEDULING
230         printf("       -r             - Run with realtime priority\n");
231 #endif
232         printf("       -p <pidfile>   - Write PID to this file\n");
233         printf("       -c <conf file> - Specify configuration file (default %s)\n", DEFAULT_CONFIG);
234         printf("       -t             - Test config. Error message to stderr + non-zero exit code on error\n");
235         printf("       -a <address>   - Bind to IP address\n");
236         printf("       -A <address>   - Bind to IPv6 address\n");
237         printf("       -b <port>      - Bind to port\n");
238         printf("       -B <port>      - Bind to port (IPv6)\n");
239         printf("       -h             - Print this help\n");
240         exit(0);
241 }
242
243 int main(int argc, char **argv)
244 {
245         bool_t nodaemon = false;
246 #ifdef POSIX_PRIORITY_SCHEDULING
247         bool_t realtime = false;
248 #endif
249         bool_t testconfig = false;
250         char *conffile = NULL, *pidfile = NULL;
251         int c;
252         struct utsname utsbuf;
253
254         /* Arguments */
255 #ifdef POSIX_PRIORITY_SCHEDULING
256         while ((c = getopt(argc, argv, "drp:c:a:A:b:B:ht")) != EOF) {
257 #else
258                 while ((c = getopt(argc, argv, "dp:c:a:A:b:B:ht")) != EOF) {
259 #endif
260                         switch(c) {
261                                 case 'c':
262                                         conffile = optarg;
263                                         break;
264                                 case 'p':
265                                         pidfile = optarg;
266                                         break;
267                                 case 'a':
268                                         bindaddr = optarg;
269                                         break;
270                                 case 'A':
271                                         bindaddr6 = optarg;
272                                         break;
273                                 case 'b':
274                                         bindport = atoi(optarg);
275                                         break;
276                                 case 'B':
277                                         bindport6 = atoi(optarg);
278                                         break;
279                                 case 'd':
280                                         nodaemon = true;
281                                         break;
282                                 case 'h':
283                                         printhelp();
284                                         break;
285                                 case 't':
286                                         testconfig = true;
287                                         break;
288 #ifdef POSIX_PRIORITY_SCHEDULING
289                                 case 'r':
290                                         realtime = true;
291                                         break;
292 #endif
293                                 default:
294                                         fprintf(stderr, "Unrecognized option\n");
295                                         printhelp();
296                                         break;
297                         }
298                 }
299
300                 if (testconfig) {
301                         if (!Conf_ok(conffile))
302                                 exit(1);
303                         else
304                                 exit(0);
305                 }
306
307                 /* Initialize the config subsystem early;
308                  * switch_user() will need to read some config variables as well as logging.
309                  */
310                 Conf_init(conffile);
311
312                 /* Logging to terminal if not daemonizing, otherwise to syslog or log file.
313                 */
314                 if (!nodaemon) {
315                         daemonize();
316                         Log_init(false);
317                         if (pidfile != NULL)
318                                 lockfile(pidfile);
319
320                         switch_user();
321
322                         /* Reopen log file. If user switch results in access denied, we catch
323                          * it early.
324                          */
325                         Log_reset();
326                 }
327                 else Log_init(true);
328
329                 signal(SIGCHLD, SIG_IGN); /* ignore child */
330                 signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
331                 signal(SIGTTOU, SIG_IGN);
332                 signal(SIGTTIN, SIG_IGN);
333                 signal(SIGPIPE, SIG_IGN);
334                 signal(SIGHUP, signal_handler); /* catch hangup signal */
335                 signal(SIGTERM, signal_handler); /* catch kill signal */
336
337                 /* Build system string */
338                 if (uname(&utsbuf) == 0) {
339                         snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
340                         snprintf(version_string, 64, "%s", utsbuf.release);
341                 }
342                 else {
343                         snprintf(system_string, 64, "unknown unknown");
344                         snprintf(version_string, 64, "unknown");
345                 }
346
347                 /* Initializing */
348                 SSLi_init();
349                 Chan_init();
350                 Client_init();
351                 Ban_init();
352
353 #ifdef POSIX_PRIORITY_SCHEDULING
354                 if (realtime)
355                         setscheduler();
356 #endif
357
358                 Server_run();
359
360                 Ban_deinit();
361                 SSLi_deinit();
362                 Chan_free();
363                 Log_free();
364                 Conf_deinit();
365
366                 if (pidfile != NULL)
367                         unlink(pidfile);
368
369                 return 0;
370         }