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