Merge branch 'autotools'
[umurmur.git] / src / main.c
index d1351ddc080f9c76a761023f1f43fb2e288975b6..fce997a77a471f4201d3706e107f0e51ac1fee58 100644 (file)
@@ -1,5 +1,5 @@
-/* Copyright (C) 2009-2010, Martin Johansson <martin@fatbob.nu>
-   Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
+/* Copyright (C) 2009-2011, Martin Johansson <martin@fatbob.nu>
+   Copyright (C) 2005-2011, Thorvald Natvig <thorvald@natvig.com>
 
    All rights reserved.
 
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/utsname.h>
 #include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
 #include <signal.h>
-#include <sched.h>
 #include <errno.h>
 #include <string.h>
 #include <stdlib.h>
-
+#ifdef _POSIX_PRIORITY_SCHEDULING
+#include <sched.h>
+#endif
 #include "server.h"
 #include "ssl.h"
 #include "channel.h"
@@ -56,25 +58,112 @@ char *bindaddr;
 
 void lockfile(const char *pidfile)
 {
-       int lfp;
+       int lfp, flags;
        char str[16];
-       
-       lfp = open(pidfile, O_RDWR|O_CREAT, 0640);
+
+       /* Don't use O_TRUNC here -- we want to leave the PID file
+        * unmodified if we cannot lock it.
+        */
+       lfp = open(pidfile, O_WRONLY|O_CREAT, 0640);
        
        if (lfp < 0)
                Log_fatal("Cannot open PID-file %s for writing", pidfile);
-       sprintf(str,"%d\n", getpid());
+
+       /* Try to lock the file. */
+       if (lockf(lfp, F_TLOCK, 0) < 0) {
+               close(lfp);
+
+               if (errno == EACCES || errno == EAGAIN)
+                       Log_fatal("PID file is locked -- uMurmur already running?");
+
+               Log_fatal("Cannot lock PID file: %s", strerror(errno));
+       }
+
+       /* Now that we locked the file, erase its contents. */
+       if (ftruncate(lfp, 0) < 0) {
+               close(lfp);
+               Log_fatal("Cannot truncate PID file: %s", strerror(errno));
+       }
+
+       snprintf(str,16,"%d\n", getpid());
        write(lfp, str, strlen(str)); /* record pid to lockfile */
        Log_info("PID-file: %s", pidfile);
+
+       /* If uMurmur ever starts to fork()+exec(), we don't want it to
+        * leak the fd to the forked process though. Set the close-on-exec
+        * flag to prevent leakage.
+        */
+       flags = fcntl(lfp, F_GETFD, 0);
+       flags |= FD_CLOEXEC;
+       fcntl(lfp, F_SETFD, (long) flags);
+
+       /* Don't close(lfp) here!
+        * We want the fd to remain opened so the lock is held until the
+        * process exits.
+        */
+       lfp = -1;
 }
 
+/* Drops privileges (if configured to do so). */
+static void switch_user(void)
+{
+       struct passwd *pwd;
+       struct group *grp = NULL;
+       const char *username, *groupname;
+       gid_t gid;
+
+       username = getStrConf(USERNAME);
+       groupname = getStrConf(GROUPNAME);
+
+       if (!*username) {
+               /* It's an error to specify groupname
+                * but leave username empty.
+                */
+               if (*groupname)
+                       Log_fatal("username missing");
+
+               /* Nothing to do. */
+               return;
+       }
+
+       pwd = getpwnam(username);
+       if (!pwd)
+               Log_fatal("Unknown user '%s'", username);
+
+       if (!*groupname)
+               gid = pwd->pw_gid;
+       else {
+               grp = getgrnam(groupname);
+
+               if (!grp)
+                       Log_fatal("Unknown group '%s'", groupname);
+
+               gid = grp->gr_gid;
+       }
+
+       if (initgroups(pwd->pw_name, gid))
+               Log_fatal("initgroups() failed: %s", strerror(errno));
+
+       if (setgid(gid))
+               Log_fatal("setgid() failed: %s", strerror(errno));
+
+       if (setuid(pwd->pw_uid))
+               Log_fatal("setuid() failed: %s", strerror(errno));
+       
+       if (!grp)
+               grp = getgrgid(gid);
+       if (!grp)
+               Log_fatal("getgrgid() failed: %s", strerror(errno));
+       
+       Log_info("Switch to user '%s' group '%s'", pwd->pw_name, grp->gr_name);
+}
 
 void signal_handler(int sig)
 {
        switch(sig) {
        case SIGHUP:
-               /* XXX - do stuff? */
-               Log_info("HUP signal");
+               Log_info("HUP signal received.");
+               Log_reset();
                break;
        case SIGTERM:
                Log_info("TERM signal. Shutting down.");
@@ -111,6 +200,7 @@ void daemonize()
                
 }
 
+#ifdef _POSIX_PRIORITY_SCHEDULING
 void setscheduler()
 {
        int rc;
@@ -122,15 +212,18 @@ void setscheduler()
        if (rc < 0)
                Log_warn("Failed to set scheduler: %s", strerror(errno));
 }
+#endif
 
 void printhelp()
 {
        printf("uMurmur version %s. Mumble protocol %d.%d.%d\n", UMURMUR_VERSION, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
        printf("Usage: umurmurd [-d] [-p <pidfile>] [-c <conf file>] [-h]\n");
-       printf("       -d             - Do not deamonize\n");
+       printf("       -d             - Do not daemonize\n");
        printf("       -p <pidfile>   - Write PID to this file\n");
        printf("       -c <conf file> - Specify configuration file\n");
+#ifdef _POSIX_PRIORITY_SCHEDULING
        printf("       -r             - Run with realtime priority\n");
+#endif
        printf("       -a <address>   - Bind to IP address\n");
        printf("       -b <port>      - Bind to port\n");
        printf("       -h             - Print this help\n");
@@ -140,13 +233,19 @@ void printhelp()
 int main(int argc, char **argv)
 {
        bool_t nodaemon = false;
+#ifdef _POSIX_PRIORITY_SCHEDULING
        bool_t realtime = false;
+#endif
        char *conffile = NULL, *pidfile = NULL;
        int c;
        struct utsname utsbuf;
        
        /* Arguments */
+#ifdef _POSIX_PRIORITY_SCHEDULING
        while ((c = getopt(argc, argv, "drp:c:a:b:h")) != EOF) {
+#else
+       while ((c = getopt(argc, argv, "dp:c:a:b:h")) != EOF) {
+#endif
                switch(c) {
                case 'c':
                        conffile = optarg;
@@ -166,9 +265,11 @@ int main(int argc, char **argv)
                case 'h':
                        printhelp();
                        break;
+#ifdef _POSIX_PRIORITY_SCHEDULING
                case 'r':
                        realtime = true;
                        break;
+#endif
                default:
                        fprintf(stderr, "Unrecognized option\n");
                        printhelp();
@@ -176,19 +277,27 @@ int main(int argc, char **argv)
                }
        }
        
-       if (Conf_init(conffile) != 0) {
-               fprintf(stderr, "Configuration error\n");
-               exit(1);
-       }
-               
+       /* Initialize the config subsystem early;
+        * switch_user() will need to read some config variables as well as logging.
+        */
+       Conf_init(conffile);
+       
+       /* Logging to terminal if not daemonizing, otherwise to syslog or log file.
+        */
        if (!nodaemon) {
-               Log_init(false);
                daemonize();
+               Log_init(false);
                if (pidfile != NULL)
                        lockfile(pidfile);
+
+               switch_user();
+
+               /* Reopen log file. If user switch results in access denied, we catch
+                * it early.
+                */
+               Log_reset(); 
        }
-       else
-               Log_init(true);
+       else Log_init(true);
        
        signal(SIGCHLD, SIG_IGN); /* ignore child */
        signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
@@ -213,8 +322,10 @@ int main(int argc, char **argv)
        Chan_init();
        Client_init();
 
+#ifdef _POSIX_PRIORITY_SCHEDULING
        if (realtime)
                setscheduler();
+#endif
        
        Server_run();