36c5f88db63232928ba607e91518c4292a21aacc
[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;
63         char str[16];
64         
65         lfp = open(pidfile, O_RDWR|O_CREAT|O_EXCL, 0640);
66         
67         if (lfp < 0)
68                 Log_fatal("Cannot open PID-file %s for writing", pidfile);
69         snprintf(str,16,"%d\n", getpid());
70         write(lfp, str, strlen(str)); /* record pid to lockfile */
71         close(lfp);
72         Log_info("PID-file: %s", pidfile);
73 }
74
75 /* Drops privileges (if configured to do so). */
76 static void switch_user(void)
77 {
78         struct passwd *pwd;
79         struct group *grp = NULL;
80         const char *username, *groupname;
81         gid_t gid;
82
83         username = getStrConf(USERNAME);
84         groupname = getStrConf(GROUPNAME);
85
86         if (!*username) {
87                 /* It's an error to specify groupname
88                  * but leave username empty.
89                  */
90                 if (*groupname)
91                         Log_fatal("username missing");
92
93                 /* Nothing to do. */
94                 return;
95         }
96
97         pwd = getpwnam(username);
98         if (!pwd)
99                 Log_fatal("Unknown user '%s'", username);
100
101         if (!*groupname)
102                 gid = pwd->pw_gid;
103         else {
104                 grp = getgrnam(groupname);
105
106                 if (!grp)
107                         Log_fatal("Unknown group '%s'", groupname);
108
109                 gid = grp->gr_gid;
110         }
111
112         if (initgroups(pwd->pw_name, gid))
113                 Log_fatal("initgroups() failed: %s", strerror(errno));
114
115         if (setgid(gid))
116                 Log_fatal("setgid() failed: %s", strerror(errno));
117
118         if (setuid(pwd->pw_uid))
119                 Log_fatal("setuid() failed: %s", strerror(errno));
120         
121         if (!grp)
122                 grp = getgrgid(gid);
123         if (!grp)
124                 Log_fatal("getgrgid() failed: %s", strerror(errno));
125         
126         Log_info("Switch to user '%s' group '%s'", pwd->pw_name, grp->gr_name);
127 }
128
129 void signal_handler(int sig)
130 {
131         switch(sig) {
132         case SIGHUP:
133                 /* XXX - do stuff? */
134                 Log_info("HUP signal");
135                 break;
136         case SIGTERM:
137                 Log_info("TERM signal. Shutting down.");
138                 Server_shutdown();
139                 break;
140         }
141 }
142
143 void daemonize()
144 {
145         int i;
146         
147         if (getppid() == 1)
148                 return; /* already a daemon */
149         i = fork();
150         if ( i < 0) {
151                 fprintf(stderr, "Fork error. Exiting\n");
152                 exit(1); /* fork error */
153         }
154         if ( i > 0)
155                 exit(0); /* parent exits */
156         
157         /* child (daemon) continues */
158         setsid(); /* obtain a new process group */
159         for (i = getdtablesize(); i >= 0; --i)
160                 close(i); /* close all descriptors */
161         
162         i = open("/dev/null",O_RDWR);
163         dup(i);
164         dup(i);
165         
166         umask(027); /* set newly created file permissions */
167         chdir("/");
168                 
169 }
170
171 #ifdef _POSIX_PRIORITY_SCHEDULING
172 void setscheduler()
173 {
174         int rc;
175         struct sched_param sp;
176
177         sp.sched_priority = sched_get_priority_min(SCHED_RR); /* Should suffice */
178         Log_info("Setting SCHED_RR prio %d", sp.sched_priority);
179         rc = sched_setscheduler(0, SCHED_RR, &sp);
180         if (rc < 0)
181                 Log_warn("Failed to set scheduler: %s", strerror(errno));
182 }
183 #endif
184
185 void printhelp()
186 {
187         printf("uMurmur version %s. Mumble protocol %d.%d.%d\n", UMURMUR_VERSION, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
188         printf("Usage: umurmurd [-d] [-p <pidfile>] [-c <conf file>] [-h]\n");
189         printf("       -d             - Do not daemonize\n");
190         printf("       -p <pidfile>   - Write PID to this file\n");
191         printf("       -c <conf file> - Specify configuration file\n");
192 #ifdef _POSIX_PRIORITY_SCHEDULING
193         printf("       -r             - Run with realtime priority\n");
194 #endif
195         printf("       -a <address>   - Bind to IP address\n");
196         printf("       -b <port>      - Bind to port\n");
197         printf("       -h             - Print this help\n");
198         exit(0);
199 }
200
201 int main(int argc, char **argv)
202 {
203         bool_t nodaemon = false;
204 #ifdef _POSIX_PRIORITY_SCHEDULING
205         bool_t realtime = false;
206 #endif
207         char *conffile = NULL, *pidfile = NULL;
208         int c;
209         struct utsname utsbuf;
210         
211         /* Arguments */
212 #ifdef _POSIX_PRIORITY_SCHEDULING
213         while ((c = getopt(argc, argv, "drp:c:a:b:h")) != EOF) {
214 #else
215         while ((c = getopt(argc, argv, "dp:c:a:b:h")) != EOF) {
216 #endif
217                 switch(c) {
218                 case 'c':
219                         conffile = optarg;
220                         break;
221                 case 'p':
222                         pidfile = optarg;
223                         break;
224                 case 'a':
225                         bindaddr = optarg;
226                         break;
227                 case 'b':
228                         bindport = atoi(optarg);
229                         break;
230                 case 'd':
231                         nodaemon = true;
232                         break;
233                 case 'h':
234                         printhelp();
235                         break;
236 #ifdef _POSIX_PRIORITY_SCHEDULING
237                 case 'r':
238                         realtime = true;
239                         break;
240 #endif
241                 default:
242                         fprintf(stderr, "Unrecognized option\n");
243                         printhelp();
244                         break;
245                 }
246         }
247
248         /* Logging to terminal if not daemonizing, otherwise to syslog.
249          * Need to initialize logging before calling Conf_init()
250          */
251         if (!nodaemon)
252                 Log_init(false);
253         else
254                 Log_init(true);
255         
256         /* Initialize the config subsystem early;
257          * switch_user() will need to read some config variables.
258          */
259         Conf_init(conffile);
260
261         if (!nodaemon) {
262                 daemonize();
263                 if (pidfile != NULL)
264                         lockfile(pidfile);
265
266                 switch_user();
267         }
268
269         signal(SIGCHLD, SIG_IGN); /* ignore child */
270         signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
271         signal(SIGTTOU, SIG_IGN);
272         signal(SIGTTIN, SIG_IGN);
273         signal(SIGPIPE, SIG_IGN);
274         signal(SIGHUP, signal_handler); /* catch hangup signal */
275         signal(SIGTERM, signal_handler); /* catch kill signal */
276         
277         /* Build system string */
278         if (uname(&utsbuf) == 0) {
279                 snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
280                 snprintf(version_string, 64, "%s", utsbuf.release);
281         }
282         else {
283                 snprintf(system_string, 64, "unknown unknown");
284                 snprintf(version_string, 64, "unknown");
285         }
286         
287         /* Initializing */
288         SSLi_init();
289         Chan_init();
290         Client_init();
291
292 #ifdef _POSIX_PRIORITY_SCHEDULING
293         if (realtime)
294                 setscheduler();
295 #endif
296         
297         Server_run();
298         
299         SSLi_deinit();
300         Chan_free();
301         Log_free();
302         Conf_deinit();
303         
304         if (pidfile != NULL)
305                 unlink(pidfile);
306         
307         return 0;
308 }