Clean up 'help'.
[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>] [-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("       -h             - Print this help\n");
226         printf("       -p <pidfile>   - Write PID to this file\n");
227         printf("       -c <conf file> - Specify configuration file (default %s)\n", DEFAULT_CONFIG);
228         printf("       -a <address>   - Bind to IP address\n");
229         printf("       -b <port>      - Bind to port\n");
230         exit(0);
231 }
232
233 int main(int argc, char **argv)
234 {
235         bool_t nodaemon = false;
236 #ifdef _POSIX_PRIORITY_SCHEDULING
237         bool_t realtime = false;
238 #endif
239         char *conffile = NULL, *pidfile = NULL;
240         int c;
241         struct utsname utsbuf;
242         
243         /* Arguments */
244 #ifdef _POSIX_PRIORITY_SCHEDULING
245         while ((c = getopt(argc, argv, "drp:c:a:b:h")) != EOF) {
246 #else
247         while ((c = getopt(argc, argv, "dp:c:a:b:h")) != EOF) {
248 #endif
249                 switch(c) {
250                 case 'c':
251                         conffile = optarg;
252                         break;
253                 case 'p':
254                         pidfile = optarg;
255                         break;
256                 case 'a':
257                         bindaddr = optarg;
258                         break;
259                 case 'b':
260                         bindport = atoi(optarg);
261                         break;
262                 case 'd':
263                         nodaemon = true;
264                         break;
265                 case 'h':
266                         printhelp();
267                         break;
268 #ifdef _POSIX_PRIORITY_SCHEDULING
269                 case 'r':
270                         realtime = true;
271                         break;
272 #endif
273                 default:
274                         fprintf(stderr, "Unrecognized option\n");
275                         printhelp();
276                         break;
277                 }
278         }
279         
280         /* Initialize the config subsystem early;
281          * switch_user() will need to read some config variables as well as logging.
282          */
283         Conf_init(conffile);
284         
285         /* Logging to terminal if not daemonizing, otherwise to syslog or log file.
286          */
287         if (!nodaemon) {
288                 daemonize();
289                 Log_init(false);
290                 if (pidfile != NULL)
291                         lockfile(pidfile);
292
293                 switch_user();
294
295                 /* Reopen log file. If user switch results in access denied, we catch
296                  * it early.
297                  */
298                 Log_reset(); 
299         }
300         else Log_init(true);
301         
302         signal(SIGCHLD, SIG_IGN); /* ignore child */
303         signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
304         signal(SIGTTOU, SIG_IGN);
305         signal(SIGTTIN, SIG_IGN);
306         signal(SIGPIPE, SIG_IGN);
307         signal(SIGHUP, signal_handler); /* catch hangup signal */
308         signal(SIGTERM, signal_handler); /* catch kill signal */
309         
310         /* Build system string */
311         if (uname(&utsbuf) == 0) {
312                 snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
313                 snprintf(version_string, 64, "%s", utsbuf.release);
314         }
315         else {
316                 snprintf(system_string, 64, "unknown unknown");
317                 snprintf(version_string, 64, "unknown");
318         }
319         
320         /* Initializing */
321         SSLi_init();
322         Chan_init();
323         Client_init();
324
325 #ifdef _POSIX_PRIORITY_SCHEDULING
326         if (realtime)
327                 setscheduler();
328 #endif
329         
330         Server_run();
331         
332         SSLi_deinit();
333         Chan_free();
334         Log_free();
335         Conf_deinit();
336         
337         if (pidfile != NULL)
338                 unlink(pidfile);
339         
340         return 0;
341 }