Revision: 9112 http://sourceforge.net/p/playerstage/svn/9112 Author: jpgr87 Date: 2012-12-17 02:51:14 +0000 (Mon, 17 Dec 2012) Log Message: ----------- Enable player server daemonization on UNIX systems
This patch came from Taahir Ahmed on the playerstage-developers list. It lets the Player server fork and daemonize when the -s command is given on the command line. The code is only enabled on UNIX systems (as defined by CMake.) Modified Paths: -------------- code/player/trunk/cmake/internal/FindOS.cmake code/player/trunk/server/server.cc Modified: code/player/trunk/cmake/internal/FindOS.cmake =================================================================== --- code/player/trunk/cmake/internal/FindOS.cmake 2012-12-17 02:10:07 UTC (rev 9111) +++ code/player/trunk/cmake/internal/FindOS.cmake 2012-12-17 02:51:14 UTC (rev 9112) @@ -7,6 +7,11 @@ # Or Solaris. I'm seeing a trend, here STRING (REGEX MATCH "SunOS" PLAYER_OS_SOLARIS ${CMAKE_SYSTEM_NAME}) +# Export CMAKE's UNIX variable to source files +IF(UNIX) + ADD_DEFINITIONS(-DPLAYER_UNIX) +ENDIF(UNIX) + # Windows is easy (for once) IF (WIN32) SET (PLAYER_OS_WIN TRUE BOOL INTERNAL) Modified: code/player/trunk/server/server.cc =================================================================== --- code/player/trunk/server/server.cc 2012-12-17 02:10:07 UTC (rev 9111) +++ code/player/trunk/server/server.cc 2012-12-17 02:51:14 UTC (rev 9112) @@ -98,6 +98,18 @@ #include <unistd.h> #endif +// Includes for umask() and open +#ifdef PLAYER_UNIX +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#endif + +#include <stdexcept> +using std::runtime_error; +#include <string> +using std::string; + #include <libplayertcp/playertcp.h> #include <libplayertcp/playerudp.h> #include <libplayerinterface/functiontable.h> @@ -117,10 +129,19 @@ void PrintUsage(); int ParseArgs(int* port, int* debuglevel, char** cfgfilename, int* gz_serverid, char** logfilename, + bool &shoud_daemonize, int argc, char** argv); void Quit(int signum); void Cleanup(); +int lockfile_id = -1; +bool process_is_daemon = false; + +#ifdef PLAYER_UNIX +runtime_error posix_exception(const string &prefix); +bool daemonize_self(); +#endif + PlayerTCP* ptcp; PlayerUDP* pudp; ConfigFile* cf; @@ -135,10 +156,10 @@ int debuglevel = 1; int port = PLAYERTCP_DEFAULT_PORT; int gz_serverid = -1; - int* ports; - int* new_ports; - int num_ports; - char* cfgfilename; + int* ports = NULL; + int* new_ports = NULL; + int num_ports = 0; + char* cfgfilename = NULL; char * logfileName = NULL; #ifdef WIN32 @@ -184,12 +205,81 @@ assert(pudp); PrintVersion(); - if(ParseArgs(&port, &debuglevel, &cfgfilename, &gz_serverid, &logfileName, argc, argv) < 0) + + bool should_daemonize = false; + + char *logfilename_unres = NULL; + char *cfgfilename_unres = NULL; + + if(ParseArgs(&port, &debuglevel, &cfgfilename_unres, &gz_serverid, + &logfilename_unres, should_daemonize, argc, argv) < 0) { PrintUsage(); exit(-1); } +#ifdef PLAYER_UNIX + // Adjust logfileName and cfgfilename to be absolute paths + try + { + if(logfilename_unres != NULL + && (logfileName = realpath(logfilename_unres, logfileName)) == NULL) + { + throw posix_exception("Call to realpath on supplied" + " log file name failed: "); + } + + if((cfgfilename = realpath(cfgfilename_unres, cfgfilename)) == NULL) + { + throw posix_exception("Call to realpath on supplied config file name" + " failed: "); + } + } + catch(runtime_error &re) + { + PLAYER_ERROR1("Error while processing arguments: %s", re.what()); + Cleanup(); + return 1; + } + + if(should_daemonize) + { + + // Alert the user that we're daemonizing + printf("Forking to daemon process...\n"); + + try + { + process_is_daemon = daemonize_self(); + } + catch(runtime_error &re) + { + PLAYER_ERROR1("Error while daemonizing: %s", re.what()); + Cleanup(); + return 1; + } + + // Die if we're not the daemon process + if(! process_is_daemon) + { + Cleanup(); + return 0; + } + } +#else + + // Don't do things for daemonization + logfileName = logfilename_unres; + cfgfilename = cfgfilename_unres; + + if(should_daemonize) + { + fprintf(stderr, "Cannot daemonize on a non-posix system"); + Cleanup(); + exit(1); + } +#endif + FILE* logfile = NULL; if (logfileName) logfile = fopen(logfileName,"a"); @@ -383,6 +473,16 @@ PLAYER_ERROR("failed to stop alwayson drivers"); } + // If we are a daemon, close all open file descriptors (this also unlocks the + // lockfile) + if(process_is_daemon) + { + for(int fd = sysconf(_SC_OPEN_MAX) - 1; fd <= 0; --fd) + { + close(fd); + } + } + player_globals_fini(); delete cf; } @@ -390,7 +490,15 @@ void Quit(int signum) { - player_quit = true; + switch(signum) + { + case SIGHUP: + break; + case SIGTERM: + default: + player_quit = true; + break; + } } void @@ -422,6 +530,7 @@ "Default: %d\n", PLAYERTCP_DEFAULT_PORT); fprintf(stderr, " -q : quiet mode: minimizes the console output on startup.\n"); fprintf(stderr, " -l <logfile> : log player output to the specified file\n"); + fprintf(stderr, " -s : fork to a daemon process as the current user.\n"); fprintf(stderr, " <configfile> : load the the indicated config file\n"); fprintf(stderr, "\nThe following %d drivers were compiled into Player:\n\n ", driverTable->Size()); @@ -441,11 +550,12 @@ int -ParseArgs(int* port, int* debuglevel, char** cfgfilename, int* gz_serverid, char **logfilename, +ParseArgs(int* port, int* debuglevel, char** cfgfilename, int* gz_serverid, + char **logfilename, bool &should_daemonize, int argc, char** argv) { int ch; - const char* optflags = "d:p:l:hq"; + const char* optflags = "d:p:l:hqs"; // Get letter options while((ch = getopt(argc, argv, optflags)) != -1) @@ -464,6 +574,9 @@ case 'p': *port = atoi(optarg); break; + case 's': + should_daemonize = true; + break; case '?': case ':': case 'h': @@ -479,3 +592,154 @@ return(0); } + +#ifdef PLAYER_UNIX +// Convenience function to issue an execption containing th output of strerror +runtime_error posix_exception(const string &prefix) +{ + int errno_save = errno; + errno = 0; + + return runtime_error(prefix + strerror(errno_save)); +} +#endif + +#ifdef PLAYER_UNIX +// Turn the server into a daemon. +// Adapted from Levent Karakas' daemon example: +// http://www.enderunix.org/docs/eng/daemon.php +// +// Returns false for the parent process, true for the daemon process +bool daemonize_self() +{ + + // Check if we're already a daemon + if(getppid() == 1) + return true; + + // fork daemon to remove ourselves from any shell to which we may be subject + { + int forkval = -1; + if((forkval = fork()) < 0) + { + // Fork error + throw posix_exception("Error in daemonize_self:fork(): "); + } + else if(forkval > 0) + { + // We are the parent process, and should die + return false; + } + } + + // Set ourselves as the process group leader + if(setsid() < 0) + { + // setsid error + throw posix_exception("Error in daemonize_self:setsid(): "); + } + + // Close all open file descriptors + for(int fd = sysconf(_SC_OPEN_MAX) - 1; fd <= 0; --fd) + { + close(fd); + } + + // Change directory to the tmp directory in case some drivers decide to drop + // files in the current directory. + if(chdir("/tmp") < 0) + { + // Chdir error + throw posix_exception("Error in daemonize(): chdir(): "); + } + + // Set permissions for newly created files + umask(027); + + // Direct stdin from /dev/null + if(open("/dev/null", O_RDWR) < 0) + { + throw posix_exception("Error in daemonize_self: open(/dev/null): "); + } + + // Direct stdout to temp file in working directory + if(open("/tmp/player.stdout", O_RDWR | O_CREAT, 0640) < 0) + { + throw posix_exception("Error in daemonize_self: open stdout: "); + } + + // Direct stderr to temp file in working directory + if(open("/tmp/player.stderr", O_RDWR | O_CREAT, 0640) < 0) + { + throw posix_exception("Error in daemonize_self: open stderr: "); + } + + // Open a lock file + lockfile_id = open("/tmp/player.lock", O_RDWR | O_CREAT, 0640); + if(lockfile_id < 0) + { + throw posix_exception("Error in daemonize_self: open lockfile: "); + } + + // Lock the lockfile + if(lockf(lockfile_id, F_TLOCK, 0) < 0) + { + // Could not lock lockfile. There is probably already a player instance + // running, and we should exit. + throw posix_exception("Error in daemonize_self: lock lockfile: "); + } + + // Write our pid to the lockfile + char str [10]; + if(snprintf(str, 10, "%d\n", getpid()) < 0) + { + // Error in snprintf + throw posix_exception("Error in daemonize: stringize pid: "); + } + if(write(lockfile_id, str, strlen(str)) < 0) + { + // Error in write + throw posix_exception("Error in daemonize: write pid: "); + } + + // Setup our signal mask + struct sigaction signal_action = {{0}}; + + // Handle SIGHUP and SIGTERM + signal_action.sa_handler = &Quit; + + if(sigaction(SIGHUP, &signal_action, NULL) < 0) + { + throw posix_exception("Error in daemonize: set SIGHUP action: "); + } + if(sigaction(SIGTERM, &signal_action, NULL) < 0) + { + throw posix_exception("Error in daemonize: set SIGTERM action: "); + } + + // Ignore SIGCHILD, SIGSTP, SIGTTOU, SIGTTIN (tty signals) + signal_action.sa_handler = SIG_IGN; + + if(sigaction(SIGCHLD, &signal_action, NULL) < 0) + { + throw posix_exception("Error in daemonize: ignore SIGCHILD: "); + } + + if(sigaction(SIGTSTP, &signal_action, NULL) < 0) + { + throw posix_exception("Error in daemonize: ignore SIGSTP: "); + } + + if(sigaction(SIGTTOU, &signal_action, NULL) < 0) + { + throw posix_exception("Error in daemonize: ignore SIGTTOU: "); + } + + if(sigaction(SIGTTIN, &signal_action, NULL) < 0) + { + throw posix_exception("Error in daemonize: ignore SIGTTIN: "); + } + + return true; +} +#endif This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ LogMeIn Rescue: Anywhere, Anytime Remote support for IT. Free Trial Remotely access PCs and mobile devices and provide instant support Improve your efficiency, and focus on delivering more value-add services Discover what IT Professionals Know. Rescue delivers http://p.sf.net/sfu/logmein_12329d2d _______________________________________________ Playerstage-commit mailing list Playerstage-commit@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/playerstage-commit