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