-/* Copyright (C) 2009, Martin Johansson <martin@fatbob.nu>
- Copyright (C) 2005-2009, Thorvald Natvig <thorvald@natvig.com>
+/* Copyright (C) 2009-2012, Martin Johansson <martin@fatbob.nu>
+ Copyright (C) 2005-2012, 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
+#if (_POSIX_PRIORITY_SCHEDULING > 0)
+#define POSIX_PRIORITY_SCHEDULING
+#include <sched.h>
+#endif
+#endif
#include "server.h"
#include "ssl.h"
#include "channel.h"
#include "conf.h"
#include "version.h"
+char system_string[64], version_string[64];
+int bindport;
+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.");
}
+#ifdef POSIX_PRIORITY_SCHEDULING
void setscheduler()
{
int rc;
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(" -p <pidfile> - Write PID to this file\n");
- printf(" -c <conf file> - Specify configuration file\n");
+ printf("Usage: umurmurd [-d] [-r] [-h] [-p <pidfile>] [-t] [-c <conf file>] [-a <addr>] [-b <port>]\n");
+ printf(" -d - Do not daemonize - run in foreground.\n");
+#ifdef POSIX_PRIORITY_SCHEDULING
printf(" -r - Run with realtime priority\n");
+#endif
+ printf(" -p <pidfile> - Write PID to this file\n");
+ printf(" -c <conf file> - Specify configuration file (default %s)\n", DEFAULT_CONFIG);
+ printf(" -t - Test config. Error message to stderr + non-zero exit code on error\n");
+ printf(" -a <address> - Bind to IP address\n");
+ printf(" -b <port> - Bind to port\n");
printf(" -h - Print this help\n");
exit(0);
}
int main(int argc, char **argv)
{
bool_t nodaemon = false;
+#ifdef POSIX_PRIORITY_SCHEDULING
bool_t realtime = false;
+#endif
+ bool_t testconfig = false;
char *conffile = NULL, *pidfile = NULL;
int c;
+ struct utsname utsbuf;
/* Arguments */
- while ((c = getopt(argc, argv, "drp:c:h")) != EOF) {
+#ifdef POSIX_PRIORITY_SCHEDULING
+ while ((c = getopt(argc, argv, "drp:c:a:b:ht")) != EOF) {
+#else
+ while ((c = getopt(argc, argv, "dp:c:a:b:ht")) != EOF) {
+#endif
switch(c) {
case 'c':
conffile = optarg;
case 'p':
pidfile = optarg;
break;
+ case 'a':
+ bindaddr = optarg;
+ break;
+ case 'b':
+ bindport = atoi(optarg);
+ break;
case 'd':
nodaemon = true;
break;
case 'h':
printhelp();
break;
+ case 't':
+ testconfig = true;
+ break;
+#ifdef POSIX_PRIORITY_SCHEDULING
case 'r':
realtime = true;
break;
+#endif
default:
fprintf(stderr, "Unrecognized option\n");
printhelp();
break;
}
}
-
- if (Conf_init(conffile) != 0) {
- fprintf(stderr, "Configuration error\n");
- exit(1);
+
+ if (testconfig) {
+ if (!Conf_ok(conffile))
+ exit(1);
+ else
+ exit(0);
}
+ /* 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 */
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, signal_handler); /* catch hangup signal */
signal(SIGTERM, signal_handler); /* catch kill signal */
-
+
+ /* Build system string */
+ if (uname(&utsbuf) == 0) {
+ snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
+ snprintf(version_string, 64, "%s", utsbuf.release);
+ }
+ else {
+ snprintf(system_string, 64, "unknown unknown");
+ snprintf(version_string, 64, "unknown");
+ }
+
/* Initializing */
- SSL_init();
+ SSLi_init();
Chan_init();
Client_init();
+#ifdef POSIX_PRIORITY_SCHEDULING
if (realtime)
setscheduler();
+#endif
Server_run();
- SSL_deinit();
+ SSLi_deinit();
Chan_free();
Log_free();
Conf_deinit();