This is the mail archive of the cygwin-apps@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

cygrunsrv patch for system shutdown handling


Attached is a patch to cygrunsrv that adds an optional feature where
cygrunsrv accepts SERVICE_CONTROL_SHUTDOWN notifications from the
Service Control Manager and explicitly terminates the managed
application process on receipt of such a notification.  This gives the
application a chance to shutdown cleanly, just as it does during a
manual stop ("net stop").

This feature is useful with the Cygwin port of PostgreSQL.  Without this
feature it seems that the abrupt termination of the postmaster process
during system shutdown leaves the PostgreSQL data in a state where
postmaster cannot start up without manual intervention (the
postmaster.pid file, in particular).  Note that postmaster/postgres must
also ignore SIGHUP signals for this to work -- a separate patch.

The bulk of the patch is the usual stuff to enable a new command line
option and manage the registry value.  The interesting part of the code
is merely the new set_service_controls_accepted() function that enables
the service to accept the shutdown control, and the change to
service_handler() that treats the SERVICE_CONTROL_SHUTDOWN notification
exactly like SERVICE_CONTROL_STOP.  More elaborate handling of the
shutdown case is possible, such as sending STOP_PENDING messages until
the managed process actually terminates, but in practice this simple
scheme works just fine (for PostgreSQL at least).

-- 
Fred Yankowski           fred@OntoSys.com      tel: +1.630.879.1312
Principal Consultant     www.OntoSys.com       fax: +1.630.879.1370
OntoSys, Inc             38W242 Deerpath Rd, Batavia, IL 60510, USA
Index: crit.cc
===================================================================
RCS file: /home/cvs/cvsroot/src/cygrunsrv/crit.cc,v
retrieving revision 1.1.1.1
diff -u -p -r1.1.1.1 crit.cc
--- crit.cc	2001/05/18 20:10:32	1.1.1.1
+++ crit.cc	2001/06/15 19:19:43
@@ -69,4 +69,16 @@ set_service_status (DWORD state, DWORD c
   LeaveCriticalSection (&ssc);
 }
 
-
+void
+set_service_controls_accepted (bool accept_shutdown)
+{
+  EnterCriticalSection (&ssc);
+  if (ssh)
+    {
+      ss.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+      if (accept_shutdown)
+	ss.dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
+      SetServiceStatus(ssh, &ss);
+    }
+  LeaveCriticalSection (&ssc);
+}
Index: crit.h
===================================================================
RCS file: /home/cvs/cvsroot/src/cygrunsrv/crit.h,v
retrieving revision 1.1.1.1
diff -u -p -r1.1.1.1 crit.h
--- crit.h	2001/05/18 20:10:32	1.1.1.1
+++ crit.h	2001/06/15 19:19:43
@@ -29,5 +29,6 @@ extern void set_service_status (DWORD st
 				DWORD check_point = 0,
 		                DWORD wait_hint = 0,
 				DWORD exit_code = NO_ERROR);
+extern void set_service_controls_accepted(bool shutdown);
 
 #endif /* _CRIT_H */
Index: cygrunsrv.cc
===================================================================
RCS file: /home/cvs/cvsroot/src/cygrunsrv/cygrunsrv.cc,v
retrieving revision 1.7
diff -u -p -r1.7 cygrunsrv.cc
--- cygrunsrv.cc	2001/05/31 16:09:09	1.7
+++ cygrunsrv.cc	2001/06/15 19:19:44
@@ -63,16 +63,18 @@ struct option longopts[] = {
   { "stdin", required_argument, NULL, '0' },
   { "stdout", required_argument, NULL, '1' },
   { "stderr", required_argument, NULL, '2' },
+  { "shutdown", no_argument, NULL, 'o' },
   { "help", no_argument, NULL, 'h' },
   { "version", no_argument, NULL, 'v' },
   { 0, no_argument, NULL, 0 }
 };
 
-char *opts = "I:R:S:E:p:a:c:e:d:u:w:t:s:y:0:1:2:hv";
+char *opts = "I:R:S:E:p:a:c:e:d:ou:w:t:s:y:0:1:2:hv";
 
 char *appname;
 char *svcname;
 DWORD termsig;
+DWORD shutdown;
 
 enum action_t {
   Undefined,
@@ -124,7 +126,7 @@ int
 install_registry_keys (const char *name, const char *path,
 		       char *args, char *dir, env_t *env, DWORD termsig,
 		       const char *in_stdin, const char *in_stdout,
-		       const char *in_stderr)
+		       const char *in_stderr, DWORD shutdown)
 {
   HKEY srv_key = NULL;
   HKEY env_key = NULL;
@@ -181,6 +183,11 @@ install_registry_keys (const char *name,
 		       (const BYTE *) in_stderr,
 		       strlen (in_stderr) + 1) != ERROR_SUCCESS)
       err_out (RegSetValueEx);
+  if (shutdown)
+    if (RegSetValueEx (srv_key, PARAM_SHUTDOWN, 0, REG_DWORD,
+		       (const BYTE *) &shutdown,
+		       sizeof(DWORD)) != ERROR_SUCCESS)
+      err_out (RegSetValueEx);
   RegFlushKey (srv_key);
 
 out:
@@ -248,7 +255,8 @@ reeval_io_path (int fd, char *&io_path, 
 int
 get_reg_entries (const char *name, char *&path, char *&args, char *&dir,
 		 env_t *&env, DWORD *termsig_p,
-		 char *&stdin_path, char *&stdout_path, char *&stderr_path)
+		 char *&stdin_path, char *&stdout_path, char *&stderr_path,
+		 DWORD *shutdown_p)
 {
   HKEY srv_key = NULL;
   HKEY env_key = NULL;
@@ -283,6 +291,11 @@ get_reg_entries (const char *name, char 
 		       (BYTE *) termsig_p,
 		       (size = sizeof(*termsig_p), &size)) != ERROR_SUCCESS)
     *termsig_p = SIGTERM;  // the default
+  /* Get (optional) shutdown flag. */
+  if (RegQueryValueEx (srv_key, PARAM_SHUTDOWN, 0, &type,
+		       (BYTE *) shutdown_p,
+		       (size = sizeof(*shutdown_p), &size)) != ERROR_SUCCESS)
+    *shutdown_p = 0;  // the default
   /* Get (optional) stdin/stdout/stderr redirection files. */
   if ((ret = get_opt_string_entry (srv_key, PARAM_STDIN, stdin_path)))
     goto out;
@@ -820,6 +833,7 @@ service_handler (DWORD ctrl)
   switch (ctrl)
     {
     case SERVICE_CONTROL_STOP:
+    case SERVICE_CONTROL_SHUTDOWN:
       /* Since the service_handler doesn't run in the same thread as
          the service_main routine, it has to setup exception handling. */
       exception_list except_list;
@@ -895,13 +909,15 @@ service_main (DWORD argc, LPSTR *argv)
   char *stdout_path = NULL;
   char *stderr_path = NULL;
   if (err = get_reg_entries (svcname, path, args, dir, env, &termsig,
-			     stdin_path, stdout_path, stderr_path))
+			     stdin_path, stdout_path, stderr_path,
+			     &shutdown))
     {
       syslog_starterr ("get_reg_entries", err);
       set_service_status (SERVICE_STOPPED, 0, 0, err);
       return;
     }
   
+  set_service_controls_accepted ( shutdown );
   report_service_status ();
 
   /* Step 2: Further preparations:
@@ -1026,6 +1042,7 @@ main (int argc, char **argv)
   char *in_stdin = NULL;
   char *in_stdout = NULL;
   char *in_stderr = NULL;
+  int in_shutdown = 0;
 
   appname = argv[0];
 
@@ -1107,6 +1124,13 @@ main (int argc, char **argv)
 	  return error (OnlyOneDisp);
 	in_disp = optarg;
 	break;
+      case 'o':
+	if (action != Install)
+	  return error (ShutdownNotAllowed);
+	if (in_shutdown)
+	  return error (OnlyOneShutdown);
+	in_shutdown = 1;
+	break;
       case 's':
 	if (action != Install)
 	  return error (SigNotAllowed);
@@ -1199,7 +1223,8 @@ main (int argc, char **argv)
         return ret;
       if (ret = install_registry_keys (in_name, in_path, in_args, in_dir,
 				       in_env, in_termsig,
-				       in_stdin, in_stdout, in_stderr))
+				       in_stdin, in_stdout, in_stderr,
+				       in_shutdown))
         remove_service (in_name);
       return ret;
       break;
Index: cygrunsrv.h
===================================================================
RCS file: /home/cvs/cvsroot/src/cygrunsrv/cygrunsrv.h,v
retrieving revision 1.6
diff -u -p -r1.6 cygrunsrv.h
--- cygrunsrv.h	2001/05/31 16:09:09	1.6
+++ cygrunsrv.h	2001/06/15 19:19:44
@@ -31,6 +31,7 @@
 #define PARAM_STDERR	"StdErr"
 #define PARAM_ENVIRON	"Environment"
 #define PARAM_TERMSIG	"TermSig"
+#define PARAM_SHUTDOWN	"Shutdown"
 
 #define DEF_STDIN_PATH	"/dev/null"
 #define DEF_LOG_PATH	"/var/log/"
Index: utils.cc
===================================================================
RCS file: /home/cvs/cvsroot/src/cygrunsrv/utils.cc,v
retrieving revision 1.6
diff -u -p -r1.6 utils.cc
--- utils.cc	2001/05/31 16:09:09	1.6
+++ utils.cc	2001/06/15 19:19:44
@@ -57,6 +57,8 @@ char *reason_list[] = {
   "--dep is only allowed with --install",
   "--std{in,out,err} are only allowed with --install",
   "Each of --std{in,out,err} is allowed only once",
+  "--shutdown is only allowed with --install",
+  "Only one --shutdown is allowed",
   "Trailing commandline arguments not allowed",
   "You must specify one of the `-IRSE' options",
   "Error installing a service",
@@ -159,6 +161,7 @@ usage ()
   uprint ("                            Default is /var/log/<svc_name>.log.");
   uprint ("  -2, --stderr <file>       Optional output file used for stderr redirection.");
   uprint ("                            Default is /var/log/<svc_name>.log.");
+  uprint ("  -o, --shutdown            Stop service application during system shutdown.");
   uprint ("\nInformative output:");
   uprint ("  -h, --help                print this help, then exit.");
   uprint ("  -v, --version             print cygrunsrv program version number, then exit.");
Index: utils.h
===================================================================
RCS file: /home/cvs/cvsroot/src/cygrunsrv/utils.h,v
retrieving revision 1.5
diff -u -p -r1.5 utils.h
--- utils.h	2001/05/31 16:09:09	1.5
+++ utils.h	2001/06/15 19:19:44
@@ -48,6 +48,8 @@ enum reason_t {
   DepNotAllowed,
   IONotAllowed,
   OnlyOneIO,
+  ShutdownNotAllowed,
+  OnlyOneShutdown,
   TrailingArgs,
   StartAsSvcErr,
   InstallErr,
Index: ChangeLog
===================================================================
RCS file: /home/cvs/cvsroot/src/cygrunsrv/ChangeLog,v
retrieving revision 1.11
diff -u -p -r1.11 ChangeLog
--- ChangeLog	2001/05/31 16:09:09	1.11
+++ ChangeLog	2001/06/15 19:19:43
@@ -1,3 +1,24 @@
+2001-06-15  Fred Yankowski  <fred@ontosys.com>
+
+	* utils.h (reason_t): Add codes for --shutdown errors.
+	* utils.cc (reason_list): Add error strings for --shutdown.
+	(usage): Add help text for --shutdown option.
+	* cygrunsrv.h (PARAM_SHUTDOWN): New registry name for shutdown
+	parameter.
+	* cygrunsrv.cc (longopts): Add '--shutdown' option.
+	(opts): Add '-o' option.
+	(shutdown): Define new global.
+	(install_registry_keys): Add 'shutdown' parameter, and writing
+	parameter to registry.
+	(get_reg_entries): Add 'shutdown_p' parameter and reading
+	parameter from registry.
+	(service_handler): Case for SERVICE_CONTROL_SHUTDOWN control.
+	(service_main): Set global 'shutdown' parameter, and call
+	set_service_controls_accepted.
+	(main): Add handling for '--shutdown' parameter.
+	* crit.h (set_service_controls_accepted): Declare new function.
+	* crit.cc (set_service_controls_accepted): New function.
+
 2001-05-31  Corinna Vinschen <corinna@vinschen.de>
 
 	* cygrunsrv.cc (longopts): Add `--chdir' option.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]