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