public inbox for cygwin-apps-cvs@sourceware.org
help / color / mirror / Atom feed
From: corinna@sourceware.org
To: cygwin-apps-cvs@sourceware.org
Subject: [cygrunsrv - a wrapper for unix daemons to make them controllable as windows services] branch master, updated. 7d413709d16d6248d97c23a05e0c9ffffe21dfc9
Date: Fri, 15 Nov 2019 11:33:00 -0000	[thread overview]
Message-ID: <20191115113342.60206.qmail@sourceware.org> (raw)




https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/cygrunsrv.git;h=7d413709d16d6248d97c23a05e0c9ffffe21dfc9

commit 7d413709d16d6248d97c23a05e0c9ffffe21dfc9
Author: Anton Lavrentiev via cygwin-patches <cygwin-patches@cygwin.com>
Date:   Wed Nov 13 13:28:14 2019 -0500

    cygrunsrv: Added options -T and -X; fixed a couple minor issues
    
    1. https://www.cygwin.com/ml/cygwin-patches/2019-q4/msg00107.html
    2. Fixed an issue with "premature exit" error message printed if service is stopped from SCM by an operator
    3. Fixed a potential issue with reporting 0 exit code when a service being stopped did not actually stop


Diff:
---
 ChangeLog    |    6 +++
 cygrunsrv.cc |  125 ++++++++++++++++++++++++++++++++++++++++++----------------
 cygrunsrv.h  |    5 ++
 utils.cc     |    8 ++++
 utils.h      |    5 ++
 5 files changed, 115 insertions(+), 34 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index b68ff6b..5466c69 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2019-11-12  Anton Lavrentiev <lavr@ncbi.nlm.nih.gov>
+
+        * Added the -T (for timeout) and the -X (for stop timeout) options
+        * cygrunsrv.cc:  issue no "premature exit" error when stopped by SCM
+        (and daemon catches the internal signal then exits)
+
 2015-01-28  Corinna Vinschen <corinna@vinschen.de>
 
 	* cygrunsrv.README: Fix typo.
diff --git a/cygrunsrv.cc b/cygrunsrv.cc
index adb6ad3..31a5efd 100644
--- a/cygrunsrv.cc
+++ b/cygrunsrv.cc
@@ -82,6 +82,8 @@ struct option longopts[] = {
   { "shutdown", no_argument, NULL, 'o' },
   { "interactive", no_argument, NULL, 'i' },
   { "nohide", no_argument, NULL, 'j' },
+  { "timeout", required_argument, NULL, 'T'},
+  { "stop-timeout", required_argument, NULL, 'X'},
   { "verbose", no_argument, NULL, 'V' },
   { "help", no_argument, NULL, 'h' },
   { "version", no_argument, NULL, 'v' },
@@ -116,6 +118,8 @@ const char *opts = "I:"
   "n"
   "i"
   "j"
+  "T:"
+  "X:"
   "V"
   "h"
   "v";
@@ -131,6 +135,8 @@ DWORD preshutdown;
 DWORD shutdown;
 DWORD interactive;
 DWORD showcons;
+DWORD timeout = SERVICE_TMO;
+DWORD x_timeout = SERVICE_TMO;
 
 DWORD shutting_down = 0;
 static char service_main_exitval = 1;
@@ -245,7 +251,8 @@ install_registry_keys (const char *name, const char *desc, const char *path,
 		       const char *in_stdout, const char *in_stderr,
 		       const char *in_pidfile, DWORD neverexits,
 		       DWORD preshutdown, DWORD shutdown,
-		       DWORD interactive, DWORD showcons)
+		       DWORD interactive, DWORD showcons,
+		       DWORD timeout, DWORD x_timeout)
 {
   HKEY srv_key = NULL;
   HKEY env_key = NULL;
@@ -358,6 +365,17 @@ install_registry_keys (const char *name, const char *desc, const char *path,
 			      (const BYTE *) &showcons,
 			      sizeof(DWORD))) != ERROR_SUCCESS)
       err_out_set_error (RegSetValueEx, ret);
+  if (timeout)
+    if ((ret = RegSetValueEx (srv_key, PARAM_TIMEOUT, 0, REG_DWORD,
+                              (const BYTE *) &timeout,
+                              sizeof(DWORD))) != ERROR_SUCCESS)
+      err_out_set_error(RegSetValueEx, ret);
+  if (x_timeout)
+    if ((ret = RegSetValueEx (srv_key, PARAM_X_TIMEOUT, 0, REG_DWORD,
+                              (const BYTE *) &x_timeout,
+                              sizeof(DWORD))) != ERROR_SUCCESS)
+      err_out_set_error(RegSetValueEx, ret);
+
   RegFlushKey (srv_key);
 
 out:
@@ -432,7 +450,8 @@ get_reg_entries (const char *name, HKEY hklm, char *&path, char *&args,
 		 char *&stdin_path, char *&stdout_path, char *&stderr_path,
 		 char *&pidfile_path, DWORD *neverexits_p,
 		 DWORD *preshutdown_p, DWORD *shutdown_p,
-		 DWORD *interactive_p, DWORD *showcons_p)
+		 DWORD *interactive_p, DWORD *showcons_p,
+		 DWORD *timeout_p, DWORD *x_timeout_p)
 {
   HKEY srv_key = NULL;
   HKEY env_key = NULL;
@@ -500,6 +519,16 @@ get_reg_entries (const char *name, HKEY hklm, char *&path, char *&args,
 		       (BYTE *) showcons_p,
 		       (size = sizeof(*showcons_p), &size)) != ERROR_SUCCESS)
     *showcons_p = 0;  // the default
+  /* Get (optional) timeout. */
+  if (RegQueryValueEx (srv_key, PARAM_TIMEOUT, 0, &type,
+	               (BYTE *) timeout_p,
+	               (size = sizeof(*timeout_p), &size)) != ERROR_SUCCESS)
+    *timeout_p = SERVICE_TMO;  // the default
+  /* Get (optional) stop timeout. */
+  if (RegQueryValueEx (srv_key, PARAM_X_TIMEOUT, 0, &type,
+	               (BYTE *) x_timeout_p,
+	               (size = sizeof(*x_timeout_p), &size)) != ERROR_SUCCESS)
+    *x_timeout_p = *timeout_p;  // the default is same as timeout
   /* Get (optional) stdin/stdout/stderr redirection files. */
   if ((ret = get_opt_string_entry (srv_key, PARAM_STDIN, stdin_path)))
     goto out;
@@ -511,7 +540,7 @@ get_reg_entries (const char *name, HKEY hklm, char *&path, char *&args,
     goto out;
   if ((ret = get_opt_string_entry (srv_key, PARAM_STDERR, stderr_path)))
     goto out;
-  else if (reeval_io_path (STDERR_FILENO, stderr_path, name))
+  else if ((ret = reeval_io_path(STDERR_FILENO, stderr_path, name)))
     goto out;
   /* Get (optional) environment strings. */
   strcat (strcat (param_key, "\\"), PARAM_ENVIRON);
@@ -611,8 +640,6 @@ out:
   return ret;
 }
 
-
-
 int
 add_env_var (char *envstr, env_t *&env)
 {
@@ -634,8 +661,7 @@ add_env_var (char *envstr, env_t *&env)
       }
     else if (!strcmp (env[i].name, name))
       return 0;
-  return error (InstallErr, "Only " MAX_ENV_STR
-  			    " environment variables allowed");
+  return error (InstallErr, "Only " MAX_ENV_STR " environment variables allowed");
 }
 
 int
@@ -906,7 +932,7 @@ install_service (const char *name, const char *crspath, const char *disp,
 	  sprintf (pwdbuf, "Enter password of user `%s': ", username);
 	  p = getpass (pwdbuf);
 	  strcpy (pwdbuf, p);
-	  p = getpass ("Reenter, please: ");
+	  p = getpass ("Re-enter, please: ");
 	  if (!strcmp (pwdbuf, p))
 	    pass = pwdbuf;
 	  else
@@ -965,7 +991,7 @@ remove_service (const char *name)
      If CANNOT_ACCEPT_CTRL wait and try again up to 30secs. */
   while (!ControlService (sh, SERVICE_CONTROL_STOP, &ss) &&
          (err = GetLastError ()) == ERROR_SERVICE_CANNOT_ACCEPT_CTRL &&
-	 ++cnt < 30)
+	 ++cnt < SERVICE_TMO)
     sleep (1);
   /* Any error besides ERROR_SERVICE_NOT_ACTIVE is treated as an error. */
   if (err && err != ERROR_SERVICE_NOT_ACTIVE)
@@ -992,12 +1018,11 @@ remove_service (const char *name)
 		  last_tick = GetTickCount ();
 		}
 	      else if (GetTickCount() - last_tick > ss.dwWaitHint)
-		{
-		  SetLastError (ERROR_SERVICE_REQUEST_TIMEOUT);
-		  err_out (QueryServiceStatus);
-		}
+		err_out_set_error (QueryServiceStatus, ERROR_SERVICE_REQUEST_TIMEOUT);
 	    }
         }
+      if (ss.dwCurrentState != SERVICE_STOPPED)
+        err_out_set_error (QueryServiceStatus, ERROR_SERVICE_CANNOT_ACCEPT_CTRL);
     }
   err = 0;
   /* Now try to remove service. */
@@ -1096,7 +1121,7 @@ stop_service (const char *name)
      If CANNOT_ACCEPT_CTRL wait and try again up to 30secs. */
   while (!ControlService (sh, SERVICE_CONTROL_STOP, &ss) &&
          (err = GetLastError ()) == ERROR_SERVICE_CANNOT_ACCEPT_CTRL &&
-	 ++cnt < 30)
+        ++cnt < SERVICE_TMO)
     sleep (1);
   /* Any error besides ERROR_SERVICE_NOT_ACTIVE is treated as an error. */
   if (err && err != ERROR_SERVICE_NOT_ACTIVE)
@@ -1123,9 +1148,11 @@ stop_service (const char *name)
 		  last_tick = GetTickCount ();
 		}
 	      else if (GetTickCount() - last_tick > ss.dwWaitHint)
-	        err_out_set_error (QueryServiceStatus, ERROR_SERVICE_REQUEST_TIMEOUT);
+		err_out_set_error (QueryServiceStatus, ERROR_SERVICE_REQUEST_TIMEOUT);
 	    }
         }
+      if (ss.dwCurrentState != SERVICE_STOPPED)
+        err_out_set_error (QueryServiceStatus, ERROR_SERVICE_CANNOT_ACCEPT_CTRL);
     }
   err = 0;
 
@@ -1193,7 +1220,6 @@ ControlsAccepted_desc[] =
   {true, 0, NULL }
 };
 
-
 /* Passed one of the above static arrays and a DWORD, this returns a 
    pointer to a descriptive string: either a concatenation of matching
    items (if bitwise is true) otherwise just the matching item.  */
@@ -1281,7 +1307,7 @@ print_service (const char *name, HKEY hklm, SC_HANDLE &sh, SERVICE_STATUS &ss,
   if (get_reg_entries (name, hklm, path, args, dir, env, &termsignal,
 		       &shutsignal, stdin_path, stdout_path, stderr_path,
 		       pidfile_path, &neverex, &preshutd, &shutd, &interact,
-		       &showc))
+		       &showc, &timeout, &x_timeout))
     return;  /* bail on error */
 
   printf ("%-20s: %s", "Command", path);
@@ -1321,6 +1347,17 @@ print_service (const char *name, HKEY hklm, SC_HANDLE &sh, SERVICE_STATUS &ss,
       if (shutsignal != termsignal)
         P("Shutdown Signal", strsignal (shutsignal));
 
+      if (timeout != SERVICE_TMO)
+        {
+          sprintf (tmp, "%u", timeout);
+          P(timeout != x_timeout ? "Start Timeout" : "Start/Stop Timeout", tmp);
+        }
+      if (x_timeout != timeout)
+        {
+          sprintf (tmp, "%u", x_timeout);
+          P("Stop Timeout", tmp);
+        }
+
       if (env)
         {
           printf ("%-20s: ", "Environment");
@@ -1483,7 +1520,7 @@ list_services (const char *server, bool verbose)
 	      else
 		printf ("%s\n", srv_buf[i].lpServiceName);
 	    }
-          else
+	  else
             {
               if (!QueryServiceStatus(sh, &ss))
                 err_out (QueryServiceStatus);
@@ -1520,8 +1557,9 @@ int server_pid;
 int
 terminate_child (DWORD sig)
 {
-  set_service_status (SERVICE_STOP_PENDING, 1, 21000L);
-  for (int i = 0; i < 20; ++i)
+  shutting_down = 1;
+  set_service_status (SERVICE_STOP_PENDING, 1, x_timeout * 1000L);
+  for (DWORD i = 1; i < x_timeout; ++i)
     if (!server_pid)
       sleep (1);
     else
@@ -1541,7 +1579,6 @@ terminate_child (DWORD sig)
 void
 sigterm_handler (int sig)
 {
-  shutting_down = 1;
   syslog (LOG_INFO, "Received signal `%d', terminate child...", sig);
   terminate_child (termsig);
 }
@@ -1737,7 +1774,8 @@ service_main (DWORD argc, LPSTR *argv)
   if ((err = get_reg_entries (svcname, HKEY_LOCAL_MACHINE, path, args, dir, env,
 			      &termsig, &shutsig, stdin_path, stdout_path,
 			      stderr_path, pidfile_path, &neverexits,
-			      &preshutdown, &shutdown, &interactive, &showcons))
+			      &preshutdown, &shutdown, &interactive, &showcons,
+			      &timeout, &x_timeout))
       != 0)
     {
       syslog_starterr ("get_reg_entries", err);
@@ -1883,24 +1921,24 @@ service_main (DWORD argc, LPSTR *argv)
   else
     {
       /* Pid file given, first wait for daemon's fork(). */
-      int i;
-      for (i = 1; i <= 30; i++) {
-       if ((ret = waitpid (child_pid, &status, WNOHANG)) != 0)
-	 break;
-       syslog (LOG_INFO, "service `%s': waiting for fork of %d (#%d)",
-	 svcname, child_pid, i);
-       sleep(1);
-       report_service_status ();
+      DWORD i;
+      for (i = 1; i <= timeout; ++i) {
+	if ((ret = waitpid (child_pid, &status, WNOHANG)) != 0)
+	  break;
+	syslog (LOG_INFO, "service `%s': waiting for fork of %d (#%u)",
+	  svcname, child_pid, (unsigned int) i);
+	sleep(1);
+	report_service_status ();
       }
       if (ret == child_pid && WIFEXITED(status) && WEXITSTATUS(status) == 0)
 	{
 	 /* Daemon has fork()ed successfully, wait for pidfile. */
 	 int read_pid;
-	 for (i = 1; i <= 30; i++) {
+         for (; i <= timeout; ++i) {
 	   if ((read_pid = read_pidfile (pidfile_path, forktime)) != -1)
 	     break;
-	   syslog (LOG_INFO, "service `%s': waiting for file %s (#%d)",
-	     svcname, pidfile_path, i);
+	   syslog (LOG_INFO, "service `%s': waiting for file %s (#%u)",
+	     svcname, pidfile_path, (unsigned int) i);
 	   sleep(1);
 	   report_service_status ();
 	 }
@@ -2048,6 +2086,8 @@ main (int argc, char **argv)
   int in_shutdown = 0;
   int in_interactive = 0;
   int in_showcons = 0;
+  int in_timeout = 0;
+  int in_x_timeout = 0;
   bool verbose = false;
 
   setlocale (LC_ALL, "");
@@ -2281,6 +2321,22 @@ main (int argc, char **argv)
 	  return error (OnlyOneIO);
 	in_pidfile = optarg;
 	break;
+      case 'T':
+        if (action != Install)
+          return error (TimeoutNotAllowed);
+        if (in_timeout)
+	  return error (OnlyOneTimeout);
+        if ((in_timeout = atoi (optarg)) <= 0)
+	  return error (InvalidTimeout);
+        break;
+      case 'X':
+        if (action != Install)
+	  return error (XTimeoutNotAllowed);
+        if (in_x_timeout)
+	  return error (OnlyOneXTimeout);
+        if ((in_x_timeout = atoi (optarg)) <= 0)
+	  return error (InvalidTimeout);
+        break;
       case 'V':
         verbose = true;
         break;
@@ -2320,7 +2376,8 @@ main (int argc, char **argv)
 					in_stdin, in_stdout, in_stderr,
 					in_pidfile, in_neverexits,
 					in_preshutdown, in_shutdown,
-					in_interactive, in_showcons)) != 0)
+					in_interactive, in_showcons,
+					in_timeout, in_x_timeout)) != 0)
         remove_service (in_name);
       return ret;
       break;
diff --git a/cygrunsrv.h b/cygrunsrv.h
index b21ff18..7839487 100644
--- a/cygrunsrv.h
+++ b/cygrunsrv.h
@@ -40,6 +40,8 @@
 #define PARAM_SHUTDOWN	"Shutdown"
 #define PARAM_INTERACT	"Interactive"
 #define PARAM_SHOWCONS	"ShowConsole"
+#define PARAM_TIMEOUT   "Timeout"
+#define PARAM_X_TIMEOUT "StopTimeout"
 
 #define CYG_ROOT	"SOFTWARE\\Cygnus Solutions\\Cygwin\\mounts v2\\/"
 #define CYG_ROOT_VAL	"native"
@@ -56,6 +58,9 @@
 #define MAX_DEPS	16
 #define MAX_DEPS_STR	STRINGIFY(MAX_DEPS)
 
+#define SERVICE_TMO     30
+#define SERVICE_TMO_STR STRINGIFY(SERVICE_TMO)
+
 extern char *appname;
 extern char *svcname;
 
diff --git a/utils.cc b/utils.cc
index 62e7f80..142f2c7 100644
--- a/utils.cc
+++ b/utils.cc
@@ -75,6 +75,11 @@ const char *reason_list[] = {
   "--interactive not allowed with --user",
   "--nohide is only allowed with --install",
   "Only one --nohide is allowed",
+  "--timeout is only allowed with --install",
+  "Only one --timeout allowed",
+  "Invalid timeout value",
+  "--stop-timeout is only allowed with --install",
+  "Only one --stop-timeout allowed",
   "Trailing commandline arguments not allowed",
   "You must specify one of the `-IRSE' options",
   "Error installing a service",
@@ -207,6 +212,9 @@ usage ()
 "                            (No effect since Windows Vista/Longhorn).\n"
 "  -j, --nohide              Don't hide console window when service interacts\n"
 "                            with desktop.\n"
+"  -T, --timeout <secs>      Timeout for lengthy service operations (start/stop),\n"
+"                            default is " SERVICE_TMO_STR " seconds\n"
+"  -X, --stop-timeout <secs> Timeout to stop service (defaults to the -T value).\n"
 "\n"
 "Informative output:\n"
 "  -V, --verbose             When used with --query or --list, causes extra\n"
diff --git a/utils.h b/utils.h
index 9b4b09c..f648d5c 100644
--- a/utils.h
+++ b/utils.h
@@ -66,6 +66,11 @@ enum reason_t {
   NoInteractiveWithUser,
   ShowconsNotAllowed,
   OnlyOneShowcons,
+  TimeoutNotAllowed,
+  OnlyOneTimeout,
+  InvalidTimeout,
+  XTimeoutNotAllowed,
+  OnlyOneXTimeout,
   TrailingArgs,
   StartAsSvcErr,
   InstallErr,


                 reply	other threads:[~2019-11-15 11:33 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191115113342.60206.qmail@sourceware.org \
    --to=corinna@sourceware.org \
    --cc=cygwin-apps-cvs@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).