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