[patch] several new features for cygrunsrv

Brian Dessent brian@dessent.net
Sun May 22 05:18:00 GMT 2005


Corinna Vinschen wrote:

> I really like this patch, cool stuff.  However, I have two nits.
> 
> First, your patch adds new options, so it should also add some wording to
> cygrunsrv.README.

Okay, here is a new patch.

* --list outputs one service per line to avoid the awkward quoting issue.
* checks basename only, so that it will match any service ending in
cygrunsrv.exe (or whatever the binary might be renamed to.)
* mention new features in .README
* fixes goof with undersized static char buf[34]
* ServiceType_desc has bitwise=true now, as I discovered that when you install
with --interactive, both SERVICE_WIN32_OWN_PROCESS and
SERVICE_INTERACTIVE_PROCESS are set, and it was showing (Unknown) before.
* if both stdout_path and stderr_path are the same, they are combined into one
line in the --verbose output, to save some space.

I did not change the version number, but I imagine you would want to bump that. 
I'll leave that up to you.

Still to-do:

* patch cygcheck to call "cygrunsrv -LV" and include output, iff it can be found
and iff running NT.

* I started looking into adding logic for "cygrunsrv -I" that would print a big
warning to stderr if it detects a mount problem, i.e. trying to install system
service with user mode mounts.  I had two ideas for how to accomplish this:

1. Just walk the mount entries and check to see that /usr/bin is not mounted
user.

2. Impersonate the user specified (or .\SYSTEM) and try to stat() the --path
value.

#2 is more robust (i.e. it wouldn't falsely error if the user really did know
what he's doing and is installing a service to run as his local user account
with only user mode mounts.)  It's also more complicated though.  Do I presume
correctly that cygwin will read the correct registry keys while impersonating,
so that I can just seteuid() and then stat() the file trying to be installed as
a service?

Brian


2005-05-21  Brian Dessent  <brian@dessent.net>

	* cygrunsrv.cc: Add include.
	(longopts): Add '--list' and '--verbose' options.
	(opts): Add '-L' and '-V' options; keep order consistent with above.
	(action_t): Add 'List'.
	(err_out_set_error): Define version of 'err_out' macro that allows for
	convenient setting the error code.
	(get_description): New function.
	(install_service): Use 'err_out_set_error' instead throughout.
	(start_service): Ditto.
	(stop_service): Ditto.	
	(ServiceType_desc): Add.  Use structs to map DWORD fields onto strings.
	(StartType_desc): Ditto.
	(CurrentState_desc): Ditto.
	(ControlsAccepted_desc): Ditto.
	(make_desc): Add new function that generalizes the task of creating
	a textual field from a binary DWORD.
	(serviceTypeToString): Remove.
	(serviceStateToString): Ditto.
	(controlsToString): Ditto.
	(parsedoublenull): Add new helper function for parsing lists of
	strings, which is used below when printing the 'lpDependencies' value.
	(print_service): Add new function that is responsible for generating
	the formatted output for --list and --query commands.
	(QSC_BUF_SIZE): Add.
	(query_service): Add verbosity parameter.  Remove printf output from
	here, call 'print_service' instead.  Call QueryServiceConfig to
	retrieve more detail on the service.
	(same_filename): New function.
	(list_services): Add new function that implements -L,--list command.
	Call EnumServicesStatus to get names of all services, and then
	determine which ones are cygrunsrv services.  List their names, or 
	call print_service() if verbosity was requested.
	(main): Declare new variable 'verbosity'.  Support new command line
	switches.  Pass on verbosity information to query_service and
	list_services.
	* utils.cc (reason_list): Update error text.
	(usage): Document new switches in the help text.
	* utils.h (reason_t): Add new symbolic name for error text.
	* cygrunsrv.README: Update documentation for new flags.
-------------- next part --------------
--- /tmp/cygrunsrv-1.02-1/cygrunsrv.cc	2005-05-16 07:55:41.000000000 -0700
+++ cygrunsrv.cc	2005-05-21 18:07:15.093750000 -0700
@@ -29,6 +29,7 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
+#include <libgen.h>
 #include <sys/strace.h>
 #include <sys/wait.h>
 
@@ -51,6 +52,7 @@ struct option longopts[] = {
   { "start", required_argument, NULL, 'S' },
   { "stop", required_argument, NULL, 'E' },
   { "query", required_argument, NULL, 'Q' },
+  { "list", no_argument, NULL, 'L' },
   { "path", required_argument, NULL, 'p' },
   { "args", required_argument, NULL, 'a' },
   { "chdir", required_argument, NULL, 'c' },
@@ -69,6 +71,7 @@ struct option longopts[] = {
   { "shutdown", no_argument, NULL, 'o' },
   { "interactive", no_argument, NULL, 'i' },
   { "nohide", no_argument, NULL, 'j' },
+  { "verbose", no_argument, NULL, 'V' },
   { "help", no_argument, NULL, 'h' },
   { "version", no_argument, NULL, 'v' },
   { 0, no_argument, NULL, 0 }
@@ -77,8 +80,9 @@ struct option longopts[] = {
 char *opts = "I:"
   "R:"
   "S:"
-  "Q:"
   "E:"
+  "Q:"
+  "L"
   "p:"
   "a:"
   "c:"
@@ -97,6 +101,7 @@ char *opts = "I:"
   "n"
   "i"
   "j"
+  "V"
   "h"
   "v";
 
@@ -118,7 +123,8 @@ enum action_t {
   Remove,
   Start,
   Stop,
-  Query
+  Query,
+  List
 };
 
 enum type_t {
@@ -156,6 +162,8 @@ eval_wait_time (register DWORD wait)
 }
 
 #define err_out(name)	{err_func = #name; err = GetLastError (); goto out;}
+#define err_out_set_error(name, error) \
+            {err_func = #name; err = error; SetLastError (error); goto out;}
 
 /* Installs the subkeys of the service registry entry so that cygrunsrv
    can determine what application to start on service startup. */
@@ -463,6 +471,36 @@ out:
   return ret;
 }
 
+/* Retrieves the description of the service.  Note: it would be so much
+   cleaner to do this by a simple call to QueryServiceConfig2(), but alas this
+   does not exist in NT4.  *sigh*  */
+int
+get_description (const char *name, char *&descr)
+{
+  HKEY desc_key = NULL;
+  char desc_key_path[MAX_PATH];
+  int ret;
+
+  strcat (strcpy (desc_key_path, SRV_KEY), name);
+  if ((ret = RegOpenKeyEx (HKEY_LOCAL_MACHINE, desc_key_path, 0,
+  			   KEY_READ, &desc_key)) != ERROR_SUCCESS)
+    goto out;
+  
+  if ((ret = get_opt_string_entry (desc_key, DESC, descr)))
+    goto out;
+
+  ret = 0;
+
+out:
+  if (ret)
+    descr = NULL;
+  if (desc_key)
+    RegCloseKey (desc_key);
+  return ret;
+}
+
+
+
 int
 add_env_var (char *envstr, env_t *&env)
 {
@@ -575,10 +613,7 @@ install_service (const char *name, const
       GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
     err_out (OpenService);
   if (sh)
-    {
-      SetLastError (ERROR_SERVICE_EXISTS);
-      err_out (OpenService);
-    }
+    err_out_set_error (OpenService, ERROR_SERVICE_EXISTS);
   /* Set optional dependencies. */
   if (deps)
     {
@@ -587,10 +622,7 @@ install_service (const char *name, const
 	concat_length += (strlen(deps[i]) + 1);
       concat_length++;
       if (! (dependencies = (char *) malloc(concat_length)))
-	{
-	  SetLastError(ERROR_OUTOFMEMORY);
-	  err_out(malloc);
-	}
+	err_out_set_error (malloc, ERROR_OUTOFMEMORY);
       char *p = dependencies;
       for (int i = 0; i < MAX_DEPS && deps[i]; i++)
 	{
@@ -626,10 +658,7 @@ install_service (const char *name, const
 	    }
 	}
       if (!(username = (char *) malloc (strlen (user) + 3)))
-        {
-	  SetLastError (ERROR_OUTOFMEMORY);
-	  err_out (malloc);
-	}
+        err_out_set_error (malloc, ERROR_OUTOFMEMORY);
       /* If no "\" is part of the name, prepend ".\" */
       if (!strchr (user, '\\'))
         strcat (strcpy (username, ".\\"), user);
@@ -789,18 +818,12 @@ start_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_RUNNING)
-    {
-      SetLastError (ERROR_SERVICE_NOT_ACTIVE);
-      err_out (QueryServiceStatus);
-    }
+    err_out_set_error (QueryServiceStatus, ERROR_SERVICE_NOT_ACTIVE);
 
 out:
   if (sh)
@@ -858,10 +881,7 @@ stop_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);
 	    }
         }
     }
@@ -875,104 +895,240 @@ out:
   return err == 0 ? 0 : error (StopErr, err_func, err);
 }
 
-char *
-serviceTypeToString(DWORD stype)
-{
-  switch (stype) {
-    case SERVICE_WIN32_OWN_PROCESS:
-      return "Own Process";
-      break;
-    case SERVICE_WIN32_SHARE_PROCESS:
-      return "Share Process";
-      break;
-    case SERVICE_KERNEL_DRIVER:
-      return "Kernel Driver";
-    case SERVICE_FILE_SYSTEM_DRIVER:
-      return "File System Driver";
-    case SERVICE_INTERACTIVE_PROCESS:
-      return "Interactive Process";
-    default:
-      return "Undefined type";
-  }
+/* these are used to turn DWORDs into desciptive text */
+static struct desc_type { bool bitwise; DWORD flag; const char *meaning; } 
+
+/* Note: service installed with --interactive will have
+   (SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS) so this
+   needs to have bitmask = true.  */
+ServiceType_desc[] =
+{
+  {true, 0,                                    "(Unknown)"},
+  {true, SERVICE_WIN32_OWN_PROCESS,            "Own Process, "},
+  {true, SERVICE_WIN32_SHARE_PROCESS,          "Shared Process, "},
+  {true, SERVICE_KERNEL_DRIVER,                "Kernel Driver, "},
+  {true, SERVICE_FILE_SYSTEM_DRIVER,           "Filesystem Driver, "},
+  {true, SERVICE_INTERACTIVE_PROCESS,          "Interactive, "},
+  {true, 0, NULL}
+},
+
+StartType_desc[] =
+{
+  {false, 0,                                   "(Unknown)"},
+  {false, SERVICE_BOOT_START,                  "Boot"},
+  {false, SERVICE_SYSTEM_START,                "System"},
+  {false, SERVICE_AUTO_START,                  "Automatic"},
+  {false, SERVICE_DISABLED,                    "Disabled"},
+  {false, SERVICE_DEMAND_START,                "Manual"},
+  {false, 0, NULL}
+},
+
+CurrentState_desc[] =
+{
+  {false, 0,                                   "(Unknown)"},
+  {false, SERVICE_STOPPED,                     "Stopped"},
+  {false, SERVICE_START_PENDING,               "Start Pending"},
+  {false, SERVICE_STOP_PENDING,                "Stop Pending"}, 
+  {false, SERVICE_RUNNING,                     "Running"},  
+  {false, SERVICE_CONTINUE_PENDING,            "Continue Pending"},
+  {false, SERVICE_PAUSE_PENDING,               "Pause Pending"},
+  {false, SERVICE_PAUSED,                      "Paused"}, 
+  {false, 0, NULL}
+},
+
+ControlsAccepted_desc[] = 
+{
+  {true, 0,                                    "(none)"},
+  {true, SERVICE_ACCEPT_STOP,                  "Stop, "},
+  {true, SERVICE_ACCEPT_SHUTDOWN,              "Shutdown, "},
+  {true, SERVICE_ACCEPT_PAUSE_CONTINUE,        "Pause, Continue, "},
+  {true, SERVICE_ACCEPT_PARAMCHANGE,           "Change Parameters, "},
+  {true, SERVICE_ACCEPT_NETBINDCHANGE,         "Change Network Binding, "},
+  {true, SERVICE_ACCEPT_HARDWAREPROFILECHANGE, "Hardware Profile Change Notify, "},
+  {true, SERVICE_ACCEPT_POWEREVENT,            "Power Status Change Notify, "},
+  {true, SERVICE_ACCEPT_SESSIONCHANGE,         "Session Status Change Notify, "},
+  {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.  */
+const char *
+make_desc(struct desc_type *desc, DWORD thing)
+{
+  static char buf[256];
+
+  if (desc[0].bitwise)
+    {
+      buf[0] = '\0';
+      for (int i = 1; desc[i].meaning; i++)
+        if (thing & desc[i].flag)
+          strcat (buf, desc[i].meaning);
+  
+      char *ptr = strchr (buf, '\0');
+      if (ptr - buf > 2 && ptr[-1] == ' ' && ptr[-2] == ',')
+        {
+          ptr[-2] = '\0';               /* remove tailing ", " */
+          return (buf);
+        }
+    }
+  else
+    for (int i = 1; desc[i].meaning; i++)
+      if (thing == desc[i].flag)
+        return (desc[i].meaning);
+
+  return desc[0].meaning;               /* default value */        
 }
 
+/* Passed a pointer to a double-NULL terminated list of strings, this 
+   returns a formatted list of those items, each delimited by `delim'.  */
 char *
-serviceStateToString(DWORD state)
+parsedoublenull (const char *input, const char *delim)
 {
-  switch (state) {
-    case SERVICE_STOPPED:
-      return "Stopped";
-    case SERVICE_START_PENDING:
-      return "Start Pending";
-    case SERVICE_STOP_PENDING:
-      return "Stop Pending";
-    case SERVICE_RUNNING:
-      return "Running";
-    case SERVICE_CONTINUE_PENDING:
-      return "Continue Pending";
-    case SERVICE_PAUSE_PENDING:
-      return "Pause Pending";
-    case SERVICE_PAUSED:
-      return "Paused";
-    default:
-      return "Undefined state";
-  }
-}
-
-#define ACCEPT_STOP_MSG           "Accept Stop"
-#define ACCEPT_PAUSE_CONTINUE_MSG "Accept Pause Continue"
-#define ACCEPT_SHUTDOWN_MSG       "Accept Shutdown"
-char *
-controlsToString(DWORD controls)
+  char *base, *end;
+  static char buf[256];
+  int used = 0, dsiz = strlen (delim);
+  
+#define parsedoublenull_done (*end == 0 && *base == 0)
+
+  for (buf[0] = 0, base = end = (char *) input; !parsedoublenull_done; end++)
+    if (*end == 0)
+      {
+        if ((used += ((end - base) + dsiz)) >= sizeof(buf))
+          break;     /* don't overflow */
+        strcat (buf, base);
+        base = end + 1;
+        if (!parsedoublenull_done)
+          strcat (buf, delim);
+      }
+  return buf;
+}
+
+/* Passed the name, opened handle, and status information about a service, 
+   this formats all the information and outputs it to stdout.  */
+void
+print_service (const char *name, SC_HANDLE &sh, SERVICE_STATUS &ss, 
+               QUERY_SERVICE_CONFIG *qsc, bool verbose)
 {
-  static char buf[sizeof(ACCEPT_STOP_MSG) + sizeof(ACCEPT_PAUSE_CONTINUE_MSG) 
-    + sizeof(ACCEPT_SHUTDOWN_MSG) + 5];
-  buf[0] = '\0';
-
-  if ( controls & SERVICE_ACCEPT_STOP ) {
-    strcat(buf, ACCEPT_STOP_MSG);
-  }
-  if ( controls & SERVICE_ACCEPT_PAUSE_CONTINUE ) {
-    if ( buf[0] != '\0' ) 
-      strcat(buf, ", ");
-    strcat(buf, ACCEPT_STOP_MSG);
-  }
-  if ( controls & SERVICE_ACCEPT_SHUTDOWN ) {
-    if ( buf[0] != '\0' ) 
-      strcat(buf, ", ");
-    strcat(buf, ACCEPT_SHUTDOWN_MSG);
-  }
-  return(buf);
+  char *descrip = NULL, *path = NULL, *args = NULL, *dir = NULL, 
+       *stdin_path = NULL, *stdout_path = NULL, *stderr_path = NULL;
+  DWORD termsignal, neverex, shutd, interact, showc;
+  env_t *env = NULL;
+
+#define P(x, y) printf ("%-20s: %s\n", x, y)
+
+  P("Service",            name);
+  if (strcmp (name, qsc->lpDisplayName))
+    P("Display name",     qsc->lpDisplayName);
+  if (!get_description (name, descrip) && descrip && strlen (descrip))
+    P("Description", descrip);
+    
+  P("Current State",      make_desc(CurrentState_desc, ss.dwCurrentState));
+  if (ss.dwControlsAccepted)
+    P("Controls Accepted",  make_desc(ControlsAccepted_desc, ss.dwControlsAccepted));
+
+  /* Get the cygrunsrv-specific things from the registry. */
+  if (get_reg_entries (name, path, args, dir, env, &termsignal,
+		            stdin_path, stdout_path, stderr_path,
+			    &neverex, &shutd, &interact, &showc))
+    return;  /* bail on error */
+
+  printf ("%-20s: %s", "Command", path);
+  if (args)
+    printf (" %s\n", args);
+  else
+    fputc ('\n', stdout);
+
+  if (verbose)
+    {      
+      if (dir)
+        P("Working Dir", dir);
+      if (stdin_path)
+        P("stdin path", stdin_path);
+      if (stdout_path && stderr_path && !stricmp(stdout_path, stderr_path))
+        P("stdout+stderr path", stdout_path);
+      else
+        {
+          if (stdout_path)
+            P("stdout path", stdout_path);
+          if (stderr_path)
+            P("stderr path", stderr_path);
+        }
+        
+      char tmp[128] = {0};
+      if (neverex)
+        strcat (tmp, "--neverexits ");
+      if (shutd)
+        strcat (tmp, "--shutdown ");
+      if (interact)
+        strcat (tmp, "--interactive ");
+      if (interact)
+        strcat (tmp, "--nohide ");
+      if (strlen(tmp))
+        P("Special flags", tmp);
+      
+      if (termsignal != SIGTERM)
+        P("Termination Signal", strsignal (termsignal));
+
+      if (env)
+        {
+          printf ("%-20s: ", "Environment");
+          for (int i = 0; i <= MAX_ENV && env[i].name; ++i)
+            printf ("%s=\"%s\" ", env[i].name, env[i].val);
+          fputc ('\n', stdout);
+        }
+      
+      P("Process Type", make_desc(ServiceType_desc, ss.dwServiceType));
+      P("Startup", make_desc(StartType_desc, qsc->dwStartType)); 
+      if (qsc->lpDependencies && strlen (qsc->lpDependencies))
+        P("Dependencies", parsedoublenull(qsc->lpDependencies, ", "));
+      if (qsc->lpServiceStartName)
+        P("Account", qsc->lpServiceStartName);
+    }
+
+#undef P
+  fputc ('\n', stdout);
 }
 
+/* According to the platform SDK, the maximum size that a QUERY_SERVICE_CONFIG
+   buffer need be is 8kb.  */
+#define QSC_BUF_SIZE 8192
+
 /* Query service `name'. */
 int
-query_service (const char *name)
+query_service (const char *name, bool verbose)
 {
   SC_HANDLE sm = (SC_HANDLE) 0;
   SC_HANDLE sh = (SC_HANDLE) 0;
   SERVICE_STATUS ss;
+  QUERY_SERVICE_CONFIG *qsc_buf = NULL;
   int cnt = 0;
   char *err_func;
-  DWORD err = 0;
+  DWORD err = 0, bytes_needed;
+
+  /* Allocate qsc buffer. */
+  if ((qsc_buf = (QUERY_SERVICE_CONFIG *) malloc (QSC_BUF_SIZE)) == NULL)
+    err_out_set_error (malloc, ERROR_OUTOFMEMORY);
 
   /* Open service manager database. */
   if (!(sm = OpenSCManager (NULL, NULL, SC_MANAGER_CONNECT)))
     err_out (OpenSCManager);
+
   /* Check whether service exists. */
   if (!(sh = OpenService (sm, name,  SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG)))
     err_out (OpenService);
-  printf("Service %s exists\n", name);
 
-  /* Get the status and print it out */
+  /* Get the current status of the service. */
   if (!QueryServiceStatus(sh, &ss))
     err_out (QueryServiceStatus);
-  printf("%-20s: %s\n", "Type", serviceTypeToString(ss.dwServiceType));
-  printf("%-20s: %s\n", "Current State", 
-      serviceStateToString(ss.dwCurrentState));
-  printf("%-20s: %s\n", "Controls Accepted", \
-      controlsToString(ss.dwControlsAccepted));
 
+  /* Get configuration info about the service. */
+  if (!QueryServiceConfig (sh, qsc_buf, QSC_BUF_SIZE, &bytes_needed))
+    err_out (QueryServiceConfig);
+
+  print_service (name, sh, ss, qsc_buf, verbose);
+   
   err = 0;
 
 out:
@@ -980,11 +1136,109 @@ out:
     CloseServiceHandle (sh);
   if (sm)
     CloseServiceHandle (sm);
+  if (qsc_buf)
+    free (qsc_buf);
   return err == 0 ? 0 : error (QueryErr, err_func, err);
 }
 
+/* Returns true if the two file/path names end in the same filename */
+bool
+same_filename (const char *a, const char *b)
+{
+  char a_buf[MAX_PATH + 1], b_buf[MAX_PATH + 1];
+  
+  strcpy (a_buf, basename ((char *) a));
+  strcpy (b_buf, basename ((char *) b));
+  return !stricmp (a_buf, b_buf);
+}
+
+/* Iterates through all services and reports on 
+   those that are cygrunsrv-managed.  */
+int
+list_services (bool verbose)
+{
+  char mypath[MAX_PATH];
+  SC_HANDLE sm = (SC_HANDLE) 0;
+  SC_HANDLE sh = (SC_HANDLE) 0;
+  ENUM_SERVICE_STATUS *srv_buf = NULL;
+  QUERY_SERVICE_CONFIG *qsc_buf = NULL;
+  SERVICE_STATUS ss;
+  DWORD bytes_needed, num_services, resume_handle = 0;
+  char *err_func;
+  DWORD err = 0;
+
+  /* Get our own filename so that we can tell which services are ours.  */
+  if (!GetModuleFileName (NULL, mypath, MAX_PATH))
+    err_out (GetModuleFileName);
+  
+  /* This buffer will be used for querying the details of a service.  */
+  if ((qsc_buf = (QUERY_SERVICE_CONFIG *) malloc (QSC_BUF_SIZE)) == NULL)
+    err_out_set_error (malloc, ERROR_OUTOFMEMORY);
+    
+  /* Open service manager database.  */
+  if (!(sm = OpenSCManager (NULL, NULL, SC_MANAGER_CONNECT | 
+                                        SC_MANAGER_ENUMERATE_SERVICE)))
+    err_out (OpenSCManager);
+
+  /* First call with lpServices to NULL to get length of needed buffer.  */
+  if (EnumServicesStatus (sm, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0,
+                          &bytes_needed, &num_services, &resume_handle) != 0)
+    err_out (EnumServiceStatus);
+    
+  if ((srv_buf = (ENUM_SERVICE_STATUS *) malloc (bytes_needed)) == NULL) 
+    err_out_set_error (malloc, ERROR_OUTOFMEMORY);
+
+  /* Call the function for real this time with the allocated buffer.
+     FIXME: In theory this should be a while loop that checks for ERROR_MORE_DATA
+     and continues fetching the remaining records.  However, we'll just trust
+     that the value returned above in bytes_needed was sufficient to get all
+     records in a single pass.  */
+  if (!EnumServicesStatus (sm, SERVICE_WIN32, SERVICE_STATE_ALL, srv_buf, 
+                  bytes_needed, &bytes_needed, &num_services, &resume_handle))
+    err_out (EnumServiceStatus);
+
+  for (int i = 0; i < num_services; i++)
+    {
+      /* get details of this service and see if it's one of ours.  */
+      if (!(sh = OpenService (sm, srv_buf[i].lpServiceName, GENERIC_READ)))
+        err_out (OpenService);
+      
+      if (!QueryServiceConfig (sh, qsc_buf, QSC_BUF_SIZE, &bytes_needed))
+        err_out (QueryServiceConfig);
+      
+      /* is this us? */
+      if (same_filename (qsc_buf->lpBinaryPathName, mypath))
+        {          
+          if (!verbose)
+            printf ("%s\n", srv_buf[i].lpServiceName);
+          else
+            {
+              if (!QueryServiceStatus(sh, &ss))
+                err_out (QueryServiceStatus);
+
+              print_service (srv_buf[i].lpServiceName, sh, ss, qsc_buf, verbose);
+            }
+        }
+      CloseServiceHandle (sh);
+    }
+
+  //fputc ('\n', stdout);  
+  err = 0;
+
+out:
+  if (qsc_buf)
+    free (qsc_buf);
+  if (srv_buf)
+    free (srv_buf);
+  if (sh)
+    CloseServiceHandle (sh);
+  if (sm)
+    CloseServiceHandle (sm);
+  return err == 0 ? 0 : error (ListErr, err_func, err);
+}
 
 #undef err_out
+#undef err_out_set_error
 
 int server_pid;
 
@@ -1338,6 +1592,7 @@ main (int argc, char **argv)
   int in_shutdown = 0;
   int in_interactive = 0;
   int in_showcons = 0;
+  bool verbose = false;
 
   appname = argv[0];
 
@@ -1391,6 +1646,11 @@ main (int argc, char **argv)
 	action = Query;
 	in_name = optarg;
 	break;
+      case 'L':
+      	if (action != Undefined) 
+	  return error (ReqAction);
+	action = List;
+	break;
       case 'p':
 	if (action != Install)
 	  return error (PathNotAllowed);
@@ -1530,6 +1790,9 @@ main (int argc, char **argv)
 	  return error (OnlyOneIO);
 	in_stderr = optarg;
 	break;
+      case 'V':
+        verbose = true;
+        break;
       case 'h':
 	return usage ();
       case 'v':
@@ -1573,7 +1836,10 @@ main (int argc, char **argv)
       return stop_service (in_name);
       break;
     case Query:
-      return query_service (in_name);
+      return query_service (in_name, verbose);
+      break;
+    case List:
+      return list_services (verbose);
       break;
     }
   return error (ReqAction);
--- /tmp/cygrunsrv-1.02-1/utils.cc	2004-04-07 07:06:05.000000000 -0700
+++ utils.cc	2005-05-19 10:55:41.609375000 -0700
@@ -32,7 +32,7 @@
 
 char *reason_list[] = {
   "",
-  "Exactly one of --install, --remove, --start or --stop is required",
+  "Exactly one of --install, --remove, --start, --stop, --query, or --list is required",
   "--path is required with --install",
   "Given path doesn't point to a valid executable",
   "--path is only allowed with --install",
@@ -75,6 +75,7 @@ char *reason_list[] = {
   "Error starting a service",
   "Error stopping a service",
   "Error querying a service",
+  "Error enumerating services",
   NULL
 };
 
@@ -135,6 +136,8 @@ usage ()
   uprint ("  -S, --start <svc_name>    Starts a service named <svc_name>.");
   uprint ("  -E, --stop <svc_name>     Stops a service named <svc_name>.");
   uprint ("  -Q, --query <svc_name>    Queries a service named <svc_name>.");
+  uprint ("  -L, --list                Lists services that have been installed");
+  uprint ("                            with cygrunsrv.");
   uprint ("\nRequired install options:");
   uprint ("  -p, --path <app_path>     Application path which is run as a service.");
   uprint ("\nMiscellaneous install options:");
@@ -180,6 +183,8 @@ usage ()
   uprint ("  -j, --nohide              Don't hide console window when service interacts");
   uprint ("                            with desktop.");
   uprint ("\nInformative output:");
+  uprint ("  -V, --verbose             When used with --query or --list, causes extra");
+  uprint ("                            information to be printed.");
   uprint ("  -h, --help                print this help, then exit.");
   uprint ("  -v, --version             print cygrunsrv program version number, then exit.");
   uprint ("\nReport bugs to <cygwin@cygwin.com>.");
--- /tmp/cygrunsrv-1.02-1/utils.h	2004-04-07 07:06:05.000000000 -0700
+++ utils.h	2005-05-19 10:56:49.750000000 -0700
@@ -66,6 +66,7 @@ enum reason_t {
   StartErr,
   StopErr,
   QueryErr,
+  ListErr,
   MaxReason		/* Always the last element */
 };
 
--- /tmp/cygrunsrv-1.02-1/cygrunsrv.README	2004-04-07 07:06:05.000000000 -0700
+++ cygrunsrv.README	2005-05-21 18:17:40.562500000 -0700
@@ -62,7 +62,22 @@ Query a service:
 
 cygrunsrv -Q <svc_name>
 cygrunsrv --query <svc_name>
-  reports on the existence and status of the service.
+  reports on the existence and status of the service.  Use the 
+  --verbose or -V flag to receive extra information.
+
+**********************************************
+List services installed with cygrunsrv:
+
+cygrunsrv -L
+cygrunsrv --list
+  lists all services that have been installed with cygrunsrv, 
+  one per line. You can use this for example to stop all 
+  running cygwin services as follows:
+  
+  $ cygrunsrv -L | (while read S; do cygrunsrv -E $S; done)
+  
+  You can combine this with the -V / --verbose option to get 
+  full details of each service instead of just names.
 
 **********************************************
 Remove a service:


-------------- next part --------------
--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/


More information about the Cygwin mailing list