Răsfoiți Sursa

autorestart

David Rose 23 ani în urmă
părinte
comite
bb6fd11fab
2 a modificat fișierele cu 296 adăugiri și 0 ștergeri
  1. 7 0
      direct/src/autorestart/Sources.pp
  2. 289 0
      direct/src/autorestart/autorestart.c

+ 7 - 0
direct/src/autorestart/Sources.pp

@@ -0,0 +1,7 @@
+#begin bin_target
+  // This program only compiles on Unix.
+  #define BUILD_TARGET $[UNIX_PLATFORM]
+
+  #define TARGET autorestart
+  #define SOURCES autorestart.c
+#end bin_target

+ 289 - 0
direct/src/autorestart/autorestart.c

@@ -0,0 +1,289 @@
+/* Filename: autorestart.c
+ * Created by:  drose (05Sep02)
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * PANDA 3D SOFTWARE
+ * Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+ *
+ * All use of this software is subject to the terms of the Panda 3d
+ * Software license.  You should have received a copy of this license
+ * along with this source code; you will also find a current copy of
+ * the license at http://www.panda3d.org/license.txt .
+ *
+ * To contact the maintainers of this program write to
+ * [email protected] .
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "dtool_config.h"
+
+#ifndef HAVE_GETOPT
+#include "gnu_getopt.h"
+#else
+#include <getopt.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <signal.h>
+
+char **params = NULL;
+char *logfile_name = NULL;
+int logfile_fd = -1;
+
+#define TIME_BUFFER_SIZE 128
+
+/* We shouldn't respawn more than (COUNT_RESPAWN - 1) times over
+   COUNT_RESPAWN_TIME seconds. */
+#define COUNT_RESPAWN 5
+#define COUNT_RESPAWN_TIME 30
+
+void
+exec_process() {
+  /* First, output the command line to the log file. */
+  char **p;
+  for (p = params; *p != NULL; ++p) {
+    fprintf(stderr, "%s ", *p);
+  }
+  fprintf(stderr, "\n");
+  execvp(params[0], params);
+  fprintf(stderr, "Cannot exec %s: %s\n", params[0], strerror(errno));
+
+  /* Kill ourselves instead of exiting, to indicate the watching
+     process should stop. */
+  kill(getpid(), SIGTERM);
+
+  /* Shouldn't get here. */
+  exit(1); 
+}
+
+int
+spawn_process() {
+  /* Spawns the child process.  Returns true if the process terminated
+     by itself and should be respawned, false if it was explicitly
+     killed (or some other error condition exists), and it should not
+     respawn any more. */
+  pid_t child, wresult;
+  int status;
+
+  child = fork();
+  if (child < 0) {
+    /* Fork error. */
+    perror("fork");
+    return 0;
+  }
+
+  if (child == 0) {
+    /* Child.  Exec the process. */
+    fprintf(stderr, "Child pid is %d.\n", getpid());
+    exec_process();
+    /* Shouldn't get here. */
+    exit(1);
+  }
+
+  /* Parent.  Wait for the child to terminate, then diagnose the reason. */
+  wresult = waitpid(child, &status, 0);
+  if (wresult < 0) {
+    perror("waitpid");
+    return 0;
+  }
+
+  if (WIFSIGNALED(status)) {
+    int signal = WTERMSIG(status);
+    fprintf(stderr, "\nprocess caught signal %d.\n\n", signal);
+    /* A signal exit is a reason to respawn unless the signal is TERM
+       or KILL. */
+    return (signal != SIGTERM && signal != SIGKILL);
+
+  } else {
+    fprintf(stderr, "\nprocess exited with error code %d.\n\n", WEXITSTATUS(status));
+    /* Normal exit is always a reason to respawn. */
+    return 1;
+  }
+}
+
+void
+do_autorestart() {
+  char time_buffer[TIME_BUFFER_SIZE];
+  time_t now;
+  time_t count_respawn[COUNT_RESPAWN];
+  int cri, num_cri;
+
+  /* Make our process its own process group. */
+  setpgid(0, 0);
+
+  /* If we have a logfile, dup it onto stdout and stderr. */
+  if (logfile_fd >= 0) {
+    dup2(logfile_fd, STDOUT_FILENO);
+    dup2(logfile_fd, STDERR_FILENO);
+    close(logfile_fd);
+  }
+
+  now = time(NULL);
+  strftime(time_buffer, TIME_BUFFER_SIZE, "%T on %A, %d %b %Y", localtime(&now));
+  fprintf(stderr, "autorestart begun at %s.\n", time_buffer);
+
+  cri = 1;
+  num_cri = 1;
+  count_respawn[1] = now;
+  
+  while (spawn_process()) {
+    now = time(NULL);
+
+    /* Make sure we're not respawning too fast. */
+    cri = (cri + 1) % COUNT_RESPAWN;
+    count_respawn[cri] = now;
+    if (num_cri < COUNT_RESPAWN) {
+      num_cri++;
+    } else {
+      time_t last = count_respawn[(cri + 1) % COUNT_RESPAWN];
+      if (now - last < COUNT_RESPAWN_TIME) {
+        fprintf(stderr, "respawning too fast, giving up.\n");
+        break;
+      }
+    }
+      
+    strftime(time_buffer, TIME_BUFFER_SIZE, "%T on %A, %d %b %Y", localtime(&now));
+    fprintf(stderr, "respawning at %s.\n", time_buffer);
+  }
+
+  now = time(NULL);
+  strftime(time_buffer, TIME_BUFFER_SIZE, "%T on %A, %d %b %Y", localtime(&now));
+  fprintf(stderr, "autorestart terminated at %s.\n", time_buffer);
+  exit(0);
+}
+
+void
+double_fork() {
+  pid_t child, grandchild, wresult;
+  int status;
+
+  /* Fork once, then again, to disassociate the child from the command
+     shell process group. */
+  child = fork();
+  if (child < 0) {
+    /* Failure to fork. */
+    perror("fork");
+    exit(1);
+  }
+
+  if (child == 0) {
+    /* Child.  Fork again. */
+    grandchild = fork();
+    if (grandchild < 0) {
+      perror("fork");
+      exit(1);
+    }
+
+    if (grandchild == 0) {
+      /* Grandchild.  Begin useful work. */
+      do_autorestart();
+      /* Shouldn't get here. */
+      exit(1);
+    }
+
+    /* Child.  Report the new pid, then terminate gracefully. */
+    fprintf(stderr, "Spawned, monitoring pid is %d.\n", grandchild);
+    exit(0);
+  }
+
+  /* Parent.  Wait for the child to terminate, then return. */
+  wresult = waitpid(child, &status, 0);
+  if (wresult < 0) {
+    perror("waitpid");
+    exit(1);
+  }
+
+  if (!WIFEXITED(status)) {
+    if (WIFSIGNALED(status)) {
+      fprintf(stderr, "child caught signal %d unexpectedly.\n", WTERMSIG(status));
+    } else {
+      fprintf(stderr, "child exited with error code %d.\n", WEXITSTATUS(status));
+    }
+    exit(1);
+  }
+}
+
+void
+usage() {
+  fprintf(stderr,
+          "\n"
+          "autorestart [-l logfilename] program [args . . . ]\n\n");
+}
+
+void
+help() {
+  usage();
+  fprintf(stderr,
+          "This program is used to run a program as a background task and\n"
+          "automatically restart it should it terminate for any reason other\n"
+          "than explicit user kill.\n\n"
+          "If the program is terminated via a TERM or KILL signal (e.g. via\n"
+          "kill [pid] or kill -i [pid]), it is assumed the user meant for the\n"
+          "process to stop, and it is not restarted.\n\n");
+}
+
+int 
+main(int argc, char *argv[]) {
+  extern char *optarg;
+  extern int optind;
+  /* The initial '+' instructs GNU getopt not to reorder switches. */
+  static const char *optflags = "+l:h";
+  int flag;
+
+  flag = getopt(argc, argv, optflags);
+  while (flag != EOF) {
+    switch (flag) {
+    case 'l':
+      logfile_name = optarg;
+      break;
+
+    case 'h':
+      help();
+      return 1;
+
+    case '?':
+    case '+':
+      usage();
+      return 1;
+
+    default:
+      fprintf(stderr, "Unhandled switch: -%c\n", flag);
+      return 1;
+    }
+    flag = getopt(argc, argv, optflags);
+  }
+
+  argc -= (optind - 1);
+  argv += (optind - 1);
+
+  if (argc < 2) {
+    fprintf(stderr, "No program to execute given.\n");
+    usage();
+    return 1;
+  }
+
+  params = &argv[1];
+
+  if (logfile_name != NULL) {
+    logfile_fd = open(logfile_name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+    if (logfile_fd < 0) {
+      fprintf(stderr, "Cannot write to logfile %s: %s\n", 
+              logfile_name, strerror(errno));
+      return 1;
+    }
+    fprintf(stderr, "Generating output to %s.\n", logfile_name);
+  }
+
+  double_fork();
+
+  return 0;
+}
+