summaryrefslogblamecommitdiffstats
path: root/src/input/pvsprivinputd.cpp
blob: df3acfc0ee83d2e1217339243e680aff8cb74afb (plain) (tree)























                                                                               
                 

                  
                    



                           
                               

                                
                                      
                      







                                                     
                 
 


                                               







                                               
                                      
 
                                                                          

 





















                                                                                                                              





                                                



































                                                                       







































                                                                                               



                                                                                                       




                                                                           


                                                  
         

                                   
 
                                  


                                                                                   
 

                                                             
         
                                                                          

                                   









                                                                                                                                       
 

                                       




                                                    
 






























                                                                                                                  


                             

                                                                                                   


                                 




                                

                                             


                                                                                          

                   
/*
 # 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 <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <signal.h>
#include <unistd.h>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <QtDebug>
#include <QFileInfo>
#include <QSettings>
#include <QCoreApplication>
#include <QStringList>
#include <QSocketNotifier>
#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=<Logger>|-l<Logger>]\n"
			"\n"
			"Options:\n"
			"  --help, -h     Show this message\n"
			"  --daemon, -d   Run in background\n"
			"  --log=<Logger>,\n"
			"  -l<Logger>     Redirect all output to <Logger>\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;
}