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