/* # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # -------------------------------------------------------------------------- # pvsprivinputd.cpp: # - Handle privileged input events - daemon # -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pvsCheckPrivileges.h" #include "pvsPrivInputSocket.h" #include "pvsPrivInputHandler.h" #include "pvsPrivInputSignalHandler.h" #include "pvsSyslog.h" using namespace std; #ifndef UNIX_PATH_MAX # define UNIX_PATH_MAX 108 /* according to unix(7) */ #endif QByteArray socketPath; int signalFds[2]; QTextStream qout(stdout, QIODevice::WriteOnly); QTextStream qerr(stderr, QIODevice::WriteOnly); static void unlinkSocket() { if(!socketPath.isNull()) { unlink(socketPath.constData()); } } static void onCaughtSignal(int signum) { pvsPrivInputSendMessage(signalFds[0], &signum, sizeof(signum), 0); } static void usage() { qout << QObject::tr( "Usage: %1 [--help|-h] [--daemon|-d] [--log=|-l]\n" "\n" "Options:\n" " --help, -h Show this message\n" " --daemon, -d Run in background\n" " --log=,\n" " -l Redirect all output to \n" " valid values are:\n" " - any file name\n" " - `syslog' to redirect output to the system log\n" " - `null' to discard output (default)\n" " (without quotes)\n" "\n" "Signals:\n" " SIGINT, SIGTERM (or press Ctrl+C when run in foreground)\n" " Quit\n" " SIGHUP Reload configuration and cached user data\n").arg(qApp->arguments().at(0)) << flush; } int main(int argc, char** argv) { QCoreApplication app(argc, argv); app.setApplicationName("pvsprivinputd"); app.setOrganizationName("openslx"); bool no_fork = true; QString logTarget = "null"; foreach(QString arg, app.arguments().mid(1)) { if(arg == "--daemon" || arg == "-d") { no_fork = false; } else if(arg.startsWith("-l")) { logTarget = arg.mid(2); } else if(arg.startsWith("--log=")) { logTarget = arg.mid(6); } else if(arg == "--help" || arg == "-h") { usage(); return EXIT_SUCCESS; } else { qout << "Unexpected argument: " << arg << endl; usage(); return EXIT_FAILURE; } } if(logTarget != "null" && logTarget != "syslog") { logTarget = QFileInfo(logTarget).absoluteFilePath(); qout << "Writing log to " << logTarget << endl; } if(!no_fork) { pid_t pid; pid_t sid; pid = fork(); if(pid < 0) { qCritical("Could not fork: %s", strerror(errno)); exit(EXIT_FAILURE); } if(pid > 0) { exit(EXIT_SUCCESS); } // We are now inside a child process. // Detach from parent: sid = setsid(); if(sid < 0) { qCritical("Could not detach from parent process: %s", strerror(errno)); exit(EXIT_FAILURE); } // Change to some benign directory: if(chdir("/") < 0) { qCritical("Could not change directory: %s", strerror(errno)); exit(EXIT_FAILURE); } } // Okay, we are running as a daemon. Defer reopening standard file descriptors // so we can report errors. // Set the file creation mask to allow all processes to connect to // the socket (access control is handled differently): umask(0); // Store the socket path before the signal handler is installed so it does not risk segfaulting // due to bad timing. socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); // Ignore SIGPIPE. Connection errors are handled internally. // According to signal(2), the only error is that the signal number // is invalid. This cannot happen. signal(SIGPIPE, SIG_IGN); // Create our socket: int sock = pvsPrivInputMakeServerSocket(); if(sock < 0) { exit(EXIT_FAILURE); } // Install our main object PVSPrivInputHandler* handler = new PVSPrivInputHandler(sock, &app); // When the handler gets deleted, we want to quit the application QObject::connect(handler, SIGNAL(destroyed(QObject*)), &app, SLOT(quit())); // set up signal handling: if(socketpair(AF_UNIX, SOCK_DGRAM, 0, signalFds) < 0) { qCritical("Could not set up signal handling. Giving up."); exit(EXIT_FAILURE); } PVSPrivInputSignalHandler sigHandler; QSocketNotifier sigintWatcher(signalFds[1], QSocketNotifier::Read); sigHandler.allowUnauthenticatedKilling(!pvsPrivInputEnableReceiveCredentials(signalFds[1])); QObject::connect(&sigintWatcher, SIGNAL(activated(int)), &sigHandler, SLOT(signalReceived(int))); QObject::connect(&sigHandler, SIGNAL(terminate()), &app, SLOT(quit())); QObject::connect(&sigHandler, SIGNAL(reloadConfiguration()), PVSCheckPrivileges::instance(), SLOT(updateCachedUserDatabase())); QObject::connect(&sigHandler, SIGNAL(reloadConfiguration()), PVSCheckPrivileges::instance(), SLOT(rereadConfiguration())); signal(SIGINT, onCaughtSignal); signal(SIGTERM, onCaughtSignal); signal(SIGHUP, onCaughtSignal); PVSLogRedirector* logRedir = 0; QSocketNotifier* logNotif = 0; // Our setup is complete. if(!no_fork) { // Reopen standard file descriptors: freopen("/dev/null", "r", stdin); if(logTarget == "null") { freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stderr); } else if(logTarget == "syslog") { logRedir = new PVSSyslogRedirector(); } else { logRedir = new PVSLogfileRedirector(logTarget); } if(logRedir) { int logFds[2]; if(socketpair(AF_UNIX, SOCK_STREAM, 0, logFds) < 0) { qWarning("Could not open a socket pair: %s", strerror(errno)); return EXIT_FAILURE; } logNotif = new QSocketNotifier(logFds[1], QSocketNotifier::Read); QObject::connect(logNotif, SIGNAL(activated(int)), logRedir, SLOT(inputAvailableOn(int))); // redirect stdout and stderr: dup2(logFds[0], 1); dup2(logFds[0], 2); } } atexit(unlinkSocket); qout << "PVS Privileged Input Daemon initialization complete. Entering main loop." << endl; // and run the main loop. int ret = app.exec(); if(logRedir) { delete logNotif; delete logRedir; } PVSCheckPrivileges::deleteInstance(); qout << "PVS Privileged Input Daemon deinitialization complete. Exiting." << endl; return ret; }