Merge branch 'master' of github.com:fmorgner/umurmur
[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
58 char system_string[64], version_string[64];
59 int bindport;
60 char *bindaddr;
61
62 void lockfile(const char *pidfile)
63 {
64         int lfp, flags;
65         char str[16];
66
67         /* Don't use O_TRUNC here -- we want to leave the PID file
68          * unmodified if we cannot lock it.
69          */
70         lfp = open(pidfile, O_WRONLY|O_CREAT, 0640);
71
72         if (lfp < 0)
73                 Log_fatal("Cannot open PID-file %s for writing", pidfile);
74
75         /* Try to lock the file. */
76         if (lockf(lfp, F_TLOCK, 0) < 0) {
77                 close(lfp);
78
79                 if (errno == EACCES || errno == EAGAIN)
80                         Log_fatal("PID file is locked -- uMurmur already running?");
81
82                 Log_fatal("Cannot lock PID file: %s", strerror(errno));
83         }
84
85         /* Now that we locked the file, erase its contents. */
86         if (ftruncate(lfp, 0) < 0) {
87                 close(lfp);
88                 Log_fatal("Cannot truncate PID file: %s", strerror(errno));
89         }
90
91         snprintf(str,16,"%d\n", getpid());
92         (void)write(lfp, str, strlen(str)); /* record pid to lockfile */
93         Log_info("PID-file: %s", pidfile);
94
95         /* If uMurmur ever starts to fork()+exec(), we don't want it to
96          * leak the fd to the forked process though. Set the close-on-exec
97          * flag to prevent leakage.
98          */
99         flags = fcntl(lfp, F_GETFD, 0);
100         flags |= FD_CLOEXEC;
101         fcntl(lfp, F_SETFD, (long) flags);
102
103         /* Don't close(lfp) here!
104          * We want the fd to remain opened so the lock is held until the
105          * process exits.
106          */
107         lfp = -1;
108 }
109
110 /* Drops privileges (if configured to do so). */
111 static void switch_user(void)
112 {
113         struct passwd *pwd;
114         struct group *grp = NULL;
115         const char *username, *groupname;
116         gid_t gid;
117
118         username = getStrConf(USERNAME);
119         groupname = getStrConf(GROUPNAME);
120
121         if (!*username) {
122                 /* It's an error to specify groupname
123                  * but leave username empty.
124                  */
125                 if (*groupname)
126                         Log_fatal("username missing");
127
128                 /* Nothing to do. */
129                 return;
130         }
131
132         pwd = getpwnam(username);
133         if (!pwd)
134                 Log_fatal("Unknown user '%s'", username);
135
136         if (!*groupname)
137                 gid = pwd->pw_gid;
138         else {
139                 grp = getgrnam(groupname);
140
141                 if (!grp)
142                         Log_fatal("Unknown group '%s'", groupname);
143
144                 gid = grp->gr_gid;
145         }
146
147         if (initgroups(pwd->pw_name, gid))
148                 Log_fatal("initgroups() failed: %s", strerror(errno));
149
150         if (setgid(gid))
151                 Log_fatal("setgid() failed: %s", strerror(errno));
152
153         if (setuid(pwd->pw_uid))
154                 Log_fatal("setuid() failed: %s", strerror(errno));
155
156         if (!grp)
157                 grp = getgrgid(gid);
158         if (!grp)
159                 Log_fatal("getgrgid() failed: %s", strerror(errno));
160
161         Log_info("Switch to user '%s' group '%s'", pwd->pw_name, grp->gr_name);
162 }
163
164 void signal_handler(int sig)
165 {
166         switch(sig) {
167         case SIGHUP:
168                 Log_info("HUP signal received.");
169                 Log_reset();
170                 break;
171         case SIGTERM:
172                 Log_info("TERM signal. Shutting down.");
173                 Server_shutdown();
174                 break;
175         }
176 }
177
178 void daemonize()
179 {
180         int i;
181
182         if (getppid() == 1)
183                 return; /* already a daemon */
184         i = fork();
185         if ( i < 0) {
186                 fprintf(stderr, "Fork error. Exiting\n");
187                 exit(1); /* fork error */
188         }
189         if ( i > 0)
190                 exit(0); /* parent exits */
191
192         /* child (daemon) continues */
193         setsid(); /* obtain a new process group */
194         for (i = getdtablesize(); i >= 0; --i)
195                 close(i); /* close all descriptors */
196
197         i = open("/dev/null",O_RDWR);
198         (void)dup(i);
199         (void)dup(i);
200
201         umask(027); /* set newly created file permissions */
202         (void)chdir("/");
203
204 }
205
206 #ifdef POSIX_PRIORITY_SCHEDULING
207 void setscheduler()
208 {
209         int rc;
210         struct sched_param sp;
211
212         sp.sched_priority = sched_get_priority_min(SCHED_RR); /* Should suffice */
213         Log_info("Setting SCHED_RR prio %d", sp.sched_priority);
214         rc = sched_setscheduler(0, SCHED_RR, &sp);
215         if (rc < 0)
216                 Log_warn("Failed to set scheduler: %s", strerror(errno));
217 }
218 #endif
219
220 void printhelp()
221 {
222         printf("uMurmur version %s ('%s'). Mumble protocol %d.%d.%d\n", UMURMUR_VERSION,
223                UMURMUR_CODENAME, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
224         printf("Usage: umurmurd [-d] [-r] [-h] [-p <pidfile>] [-t] [-c <conf file>] [-a <addr>] [-b <port>]\n");
225         printf("       -d             - Do not daemonize - run in foreground.\n");
226 #ifdef POSIX_PRIORITY_SCHEDULING
227         printf("       -r             - Run with realtime priority\n");
228 #endif
229         printf("       -p <pidfile>   - Write PID to this file\n");
230         printf("       -c <conf file> - Specify configuration file (default %s)\n", DEFAULT_CONFIG);
231         printf("       -t             - Test config. Error message to stderr + non-zero exit code on error\n");
232         printf("       -a <address>   - Bind to IP address\n");
233         printf("       -b <port>      - Bind to port\n");
234         printf("       -h             - Print this help\n");
235         exit(0);
236 }
237
238 int main(int argc, char **argv)
239 {
240         bool_t nodaemon = false;
241 #ifdef POSIX_PRIORITY_SCHEDULING
242         bool_t realtime = false;
243 #endif
244         bool_t testconfig = false;
245         char *conffile = NULL, *pidfile = NULL;
246         int c;
247         struct utsname utsbuf;
248
249         /* Arguments */
250 #ifdef POSIX_PRIORITY_SCHEDULING
251         while ((c = getopt(argc, argv, "drp:c:a:b:ht")) != EOF) {
252 #else
253         while ((c = getopt(argc, argv, "dp:c:a:b:ht")) != EOF) {
254 #endif
255                 switch(c) {
256                 case 'c':
257                         conffile = optarg;
258                         break;
259                 case 'p':
260                         pidfile = optarg;
261                         break;
262                 case 'a':
263                         bindaddr = optarg;
264                         break;
265                 case 'b':
266                         bindport = atoi(optarg);
267                         break;
268                 case 'd':
269                         nodaemon = true;
270                         break;
271                 case 'h':
272                         printhelp();
273                         break;
274                 case 't':
275                         testconfig = true;
276                         break;
277 #ifdef POSIX_PRIORITY_SCHEDULING
278                 case 'r':
279                         realtime = true;
280                         break;
281 #endif
282                 default:
283                         fprintf(stderr, "Unrecognized option\n");
284                         printhelp();
285                         break;
286                 }
287         }
288
289         if (testconfig) {
290                 if (!Conf_ok(conffile))
291                         exit(1);
292                 else
293                         exit(0);
294         }
295
296         /* Initialize the config subsystem early;
297          * switch_user() will need to read some config variables as well as logging.
298          */
299         Conf_init(conffile);
300
301         /* Logging to terminal if not daemonizing, otherwise to syslog or log file.
302          */
303         if (!nodaemon) {
304                 daemonize();
305                 Log_init(false);
306                 if (pidfile != NULL)
307                         lockfile(pidfile);
308
309                 switch_user();
310
311                 /* Reopen log file. If user switch results in access denied, we catch
312                  * it early.
313                  */
314                 Log_reset();
315         }
316         else Log_init(true);
317
318         signal(SIGCHLD, SIG_IGN); /* ignore child */
319         signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
320         signal(SIGTTOU, SIG_IGN);
321         signal(SIGTTIN, SIG_IGN);
322         signal(SIGPIPE, SIG_IGN);
323         signal(SIGHUP, signal_handler); /* catch hangup signal */
324         signal(SIGTERM, signal_handler); /* catch kill signal */
325
326         /* Build system string */
327         if (uname(&utsbuf) == 0) {
328                 snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
329                 snprintf(version_string, 64, "%s", utsbuf.release);
330         }
331         else {
332                 snprintf(system_string, 64, "unknown unknown");
333                 snprintf(version_string, 64, "unknown");
334         }
335
336         /* Initializing */
337         SSLi_init();
338         Chan_init();
339         Client_init();
340         Ban_init();
341
342 #ifdef POSIX_PRIORITY_SCHEDULING
343         if (realtime)
344                 setscheduler();
345 #endif
346
347         Server_run();
348
349         Ban_deinit();
350         SSLi_deinit();
351         Chan_free();
352         Log_free();
353         Conf_deinit();
354
355         if (pidfile != NULL)
356                 unlink(pidfile);
357
358         return 0;
359 }