Fix building on OSX.
[umurmur.git] / src / main.c
1 /* Copyright (C) 2009-2011, Martin Johansson <martin@fatbob.nu>
2    Copyright (C) 2005-2011, 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         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         dup(i);
199         dup(i);
200         
201         umask(027); /* set newly created file permissions */
202         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. Mumble protocol %d.%d.%d\n", UMURMUR_VERSION, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
223         printf("Usage: umurmurd [-d] [-r] [-h] [-p <pidfile>] [-t] [-c <conf file>] [-a <addr>] [-b <port>]\n");
224         printf("       -d             - Do not daemonize - run in foreground.\n");
225 #ifdef POSIX_PRIORITY_SCHEDULING
226         printf("       -r             - Run with realtime priority\n");
227 #endif
228         printf("       -p <pidfile>   - Write PID to this file\n");
229         printf("       -c <conf file> - Specify configuration file (default %s)\n", DEFAULT_CONFIG);
230         printf("       -t             - Test config. Error message to stderr + non-zero exit code on error\n");
231         printf("       -a <address>   - Bind to IP address\n");
232         printf("       -b <port>      - Bind to port\n");
233         printf("       -h             - Print this help\n");
234         exit(0);
235 }
236
237 int main(int argc, char **argv)
238 {
239         bool_t nodaemon = false;
240 #ifdef POSIX_PRIORITY_SCHEDULING
241         bool_t realtime = false;
242 #endif
243         bool_t testconfig = false;
244         char *conffile = NULL, *pidfile = NULL;
245         int c;
246         struct utsname utsbuf;
247         
248         /* Arguments */
249 #ifdef POSIX_PRIORITY_SCHEDULING
250         while ((c = getopt(argc, argv, "drp:c:a:b:ht")) != EOF) {
251 #else
252         while ((c = getopt(argc, argv, "dp:c:a:b:ht")) != EOF) {
253 #endif
254                 switch(c) {
255                 case 'c':
256                         conffile = optarg;
257                         break;
258                 case 'p':
259                         pidfile = optarg;
260                         break;
261                 case 'a':
262                         bindaddr = optarg;
263                         break;
264                 case 'b':
265                         bindport = atoi(optarg);
266                         break;
267                 case 'd':
268                         nodaemon = true;
269                         break;
270                 case 'h':
271                         printhelp();
272                         break;
273                 case 't':
274                         testconfig = true;
275                         break;
276 #ifdef POSIX_PRIORITY_SCHEDULING
277                 case 'r':
278                         realtime = true;
279                         break;
280 #endif
281                 default:
282                         fprintf(stderr, "Unrecognized option\n");
283                         printhelp();
284                         break;
285                 }
286         }
287
288         if (testconfig) {
289                 if (!Conf_ok(conffile))
290                         exit(1);
291                 else
292                         exit(0);
293         }
294                 
295         /* Initialize the config subsystem early;
296          * switch_user() will need to read some config variables as well as logging.
297          */
298         Conf_init(conffile);
299         
300         /* Logging to terminal if not daemonizing, otherwise to syslog or log file.
301          */
302         if (!nodaemon) {
303                 daemonize();
304                 Log_init(false);
305                 if (pidfile != NULL)
306                         lockfile(pidfile);
307
308                 switch_user();
309
310                 /* Reopen log file. If user switch results in access denied, we catch
311                  * it early.
312                  */
313                 Log_reset(); 
314         }
315         else Log_init(true);
316         
317         signal(SIGCHLD, SIG_IGN); /* ignore child */
318         signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
319         signal(SIGTTOU, SIG_IGN);
320         signal(SIGTTIN, SIG_IGN);
321         signal(SIGPIPE, SIG_IGN);
322         signal(SIGHUP, signal_handler); /* catch hangup signal */
323         signal(SIGTERM, signal_handler); /* catch kill signal */
324         
325         /* Build system string */
326         if (uname(&utsbuf) == 0) {
327                 snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
328                 snprintf(version_string, 64, "%s", utsbuf.release);
329         }
330         else {
331                 snprintf(system_string, 64, "unknown unknown");
332                 snprintf(version_string, 64, "unknown");
333         }
334         
335         /* Initializing */
336         SSLi_init();
337         Chan_init();
338         Client_init();
339
340 #ifdef POSIX_PRIORITY_SCHEDULING
341         if (realtime)
342                 setscheduler();
343 #endif
344         
345         Server_run();
346         
347         SSLi_deinit();
348         Chan_free();
349         Log_free();
350         Conf_deinit();
351         
352         if (pidfile != NULL)
353                 unlink(pidfile);
354         
355         return 0;
356 }