Merge branch 'gnutls' into ipv6
[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("       -b <port>      - Bind to port\n");
237         printf("       -h             - Print this help\n");
238         exit(0);
239 }
240
241 int main(int argc, char **argv)
242 {
243         bool_t nodaemon = false;
244 #ifdef POSIX_PRIORITY_SCHEDULING
245         bool_t realtime = false;
246 #endif
247         bool_t testconfig = false;
248         char *conffile = NULL, *pidfile = NULL;
249         int c;
250         struct utsname utsbuf;
251
252         /* Arguments */
253 #ifdef POSIX_PRIORITY_SCHEDULING
254         while ((c = getopt(argc, argv, "drp:c:a:A:b:B:ht")) != EOF) {
255 #else
256         while ((c = getopt(argc, argv, "dp:c:a:A:b:B:ht")) != EOF) {
257 #endif
258                 switch(c) {
259                 case 'c':
260                         conffile = optarg;
261                         break;
262                 case 'p':
263                         pidfile = optarg;
264                         break;
265                 case 'a':
266                         bindaddr = optarg;
267                         break;
268     case 'A':
269       bindaddr6 = optarg;
270       break;
271                 case 'b':
272                         bindport = atoi(optarg);
273                         break;
274     case 'B':
275       bindport6 = atoi(optarg);
276       break;
277                 case 'd':
278                         nodaemon = true;
279                         break;
280                 case 'h':
281                         printhelp();
282                         break;
283                 case 't':
284                         testconfig = true;
285                         break;
286 #ifdef POSIX_PRIORITY_SCHEDULING
287                 case 'r':
288                         realtime = true;
289                         break;
290 #endif
291                 default:
292                         fprintf(stderr, "Unrecognized option\n");
293                         printhelp();
294                         break;
295                 }
296         }
297
298         if (testconfig) {
299                 if (!Conf_ok(conffile))
300                         exit(1);
301                 else
302                         exit(0);
303         }
304
305         /* Initialize the config subsystem early;
306          * switch_user() will need to read some config variables as well as logging.
307          */
308         Conf_init(conffile);
309
310         /* Logging to terminal if not daemonizing, otherwise to syslog or log file.
311          */
312         if (!nodaemon) {
313                 daemonize();
314                 Log_init(false);
315                 if (pidfile != NULL)
316                         lockfile(pidfile);
317
318                 switch_user();
319
320                 /* Reopen log file. If user switch results in access denied, we catch
321                  * it early.
322                  */
323                 Log_reset();
324         }
325         else Log_init(true);
326
327         signal(SIGCHLD, SIG_IGN); /* ignore child */
328         signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
329         signal(SIGTTOU, SIG_IGN);
330         signal(SIGTTIN, SIG_IGN);
331         signal(SIGPIPE, SIG_IGN);
332         signal(SIGHUP, signal_handler); /* catch hangup signal */
333         signal(SIGTERM, signal_handler); /* catch kill signal */
334
335         /* Build system string */
336         if (uname(&utsbuf) == 0) {
337                 snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
338                 snprintf(version_string, 64, "%s", utsbuf.release);
339         }
340         else {
341                 snprintf(system_string, 64, "unknown unknown");
342                 snprintf(version_string, 64, "unknown");
343         }
344
345         /* Initializing */
346         SSLi_init();
347         Chan_init();
348         Client_init();
349         Ban_init();
350
351 #ifdef POSIX_PRIORITY_SCHEDULING
352         if (realtime)
353                 setscheduler();
354 #endif
355
356         Server_run();
357
358         Ban_deinit();
359         SSLi_deinit();
360         Chan_free();
361         Log_free();
362         Conf_deinit();
363
364         if (pidfile != NULL)
365                 unlink(pidfile);
366
367         return 0;
368 }