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

Reply via email to