This is the mail archive of the cygwin@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]

cygwin initialization, was cygrunsrv fails to start


Hi All...

I'm looking for ideas on how to further debug this problem. I'll take any 
thoughts or suggestions.

I would like to understand the sequence of events when the cygwin1.dll is 
first loaded.

This happens in response to the first .exe image that references it, right?

Is there any "automatic" initialization of the cygwin1.dll, or does it wait 
until the first posix call from the .exe image?

I am trying to figure out why cygrunsrv works sometimes and fails sometimes 
on one of my win2kpro-sp2 boxes. Sometimes it works and sometimes it does 
not.

I have placed

FILE *f;
f=fopen("000main.txt","w");
fputs("main",f);
fflush(f);
fclose(f);

at the beginning of the code, after main (and a couple of simple assignment 
statements. (The code is attached.)

The result is that sometimes when I boot up it works and I get the expected 
file output. And sometimes when I boot up, it does not work and I get no 
output file. When it fails, I also get the timeout errors in the system log. 
There are no other cases.

Other service wrappers (srvany and invoker) work fine and there is no other 
strangeness on this machine.

Is there a way to do non-cygin I/O at the beginning that would happen before 
the cygwin1.dll ever got "touched" or initialized in any way?

Is there any thing else I should look at?

Thanks,

...Karl
_________________________________________________________________
Get your FREE download of MSN Explorer at http://explorer.msn.com
/*
* cygrunsrv.cc: implementation of a Cygwin service starter application
*               similar to SRVANY or Invoker but straight implemented
*		 to support Cygwin applications.
*
* Copyright 2001  Corinna Vinschen, <corinna@vinschen.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <errno.h>
#include <getopt.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/strace.h>
#include <sys/wait.h>

#include <windows.h>
#include <wininet.h>
#include <lmcons.h>

#include <exceptions.h>
#include <sys/cygwin.h>

#include "cygrunsrv.h"
#include "crit.h"
#include "utils.h"

static char *SCCSid = "@(#)cygrunsrv V0.93, " __DATE__ "\n";

struct option longopts[] = {
  { "install", required_argument, NULL, 'I' },
  { "remove", required_argument, NULL, 'R' },
  { "start", required_argument, NULL, 'S' },
  { "stop", required_argument, NULL, 'E' },
  { "path", required_argument, NULL, 'p' },
  { "args", required_argument, NULL, 'a' },
  { "env", required_argument, NULL, 'e' },
  { "disp", required_argument, NULL, 'd' },
  { "user", required_argument, NULL, 'u' },
  { "passwd", required_argument, NULL, 'w' },
  { "type", required_argument, NULL, 't' },
  { "termsig", required_argument, NULL, 's' },
  { "dep", required_argument, NULL, 'y' },
  { "stdin", required_argument, NULL, '0' },
  { "stdout", required_argument, NULL, '1' },
  { "stderr", required_argument, NULL, '2' },
  { "help", no_argument, NULL, 'h' },
  { "version", no_argument, NULL, 'v' },
  { 0, no_argument, NULL, 0 }
};

char *opts = "I:R:S:E:p:a:e:d:u:w:t:s:y:0:1:2:hv";

char *appname;
char *svcname;
DWORD termsig;

enum action_t {
  Undefined,
  Install,
  Remove,
  Start,
  Stop
};

enum type_t {
  NoType,
  Auto,
  Manual
};

struct env_t
{
  char *name;
  char *val;
};

int
version ()
{
  printf ("%s\n", SCCSid + 4);
  printf ("Copyright 2001 Corinna Vinschen\n");
  printf ("This program comes with NO WARRANTY, to the extent permitted by 
law.\n");
  printf ("You may redistribute it under the terms of the GNU General Public 
License;\n");
  printf ("see the file named COPYING for details.\n");
  printf ("Written by Corinna Vinschen.\n");
  return 0;
}

static inline DWORD
eval_wait_time (register DWORD wait)
{
  if ((wait /= 10) < 1000)
    wait = 1000L;
  else if (wait > 10000L)
    wait = 10000L;
  return wait;
}

#define err_out(name)	{err_func = #name; err = GetLastError (); goto out;}

/* Installs the subkeys of the service registry entry so that cygrunsrv
   can determine what application to start on service startup. */
int
install_registry_keys (const char *name, const char *path,
		       char *args, env_t *env, DWORD termsig,
		       const char *in_stdin, const char *in_stdout,
		       const char *in_stderr)
{
  HKEY srv_key = NULL;
  HKEY env_key = NULL;
  DWORD disp;
  char param_key[MAX_PATH];
  char *err_func;
  DWORD err = 0;

  strcat (strcat (strcpy (param_key, SRV_KEY), name), PARAM_KEY);
  if (RegCreateKeyEx (HKEY_LOCAL_MACHINE, param_key, 0, "",
  		      REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
		      NULL, &srv_key, &disp) != ERROR_SUCCESS)
    err_out (RegCreateKeyEx);
  if (RegSetValueEx (srv_key, PARAM_PATH, 0, REG_SZ,
  		     (const BYTE *) path, strlen (path) + 1) != ERROR_SUCCESS)
    err_out (RegSetValueEx);
  if (args &&
      RegSetValueEx (srv_key, PARAM_ARGS, 0, REG_SZ,
  		     (const BYTE *) args, strlen (args) + 1) != ERROR_SUCCESS)
    err_out (RegSetValueEx);
  if (env)
    {
      if (RegCreateKeyEx (srv_key, PARAM_ENVIRON, 0, "",
      			  REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
			  NULL, &env_key, &disp) != ERROR_SUCCESS)
	err_out (RegCreateKeyEx);
      for (int i = 0; i <= MAX_ENV && env[i].name; ++i)
	if (RegSetValueEx (env_key, env[i].name, 0, REG_SZ,
			   (const BYTE *) env[i].val,
			   strlen (env[i].val) + 1) != ERROR_SUCCESS)
	  err_out (RegSetValueEx);
    }
  if (termsig)
    if (RegSetValueEx (srv_key, PARAM_TERMSIG, 0, REG_DWORD,
		       (const BYTE *) &termsig,
		       sizeof(DWORD)) != ERROR_SUCCESS)
      err_out (RegSetValueEx);
  if (in_stdin)
    if (RegSetValueEx (srv_key, PARAM_STDIN, 0, REG_SZ,
		       (const BYTE *) in_stdin,
		       strlen (in_stdin) + 1) != ERROR_SUCCESS)
      err_out (RegSetValueEx);
  if (in_stdout)
    if (RegSetValueEx (srv_key, PARAM_STDOUT, 0, REG_SZ,
		       (const BYTE *) in_stdout,
		       strlen (in_stdout) + 1) != ERROR_SUCCESS)
      err_out (RegSetValueEx);
  if (in_stderr)
    if (RegSetValueEx (srv_key, PARAM_STDERR, 0, REG_SZ,
		       (const BYTE *) in_stderr,
		       strlen (in_stderr) + 1) != ERROR_SUCCESS)
      err_out (RegSetValueEx);
  RegFlushKey (srv_key);

out:
  if (env_key)
    RegCloseKey (env_key);
  if (srv_key)
    RegCloseKey (srv_key);
  return err == 0 ? 0 : error (InstallErr, err_func, err);
}

/* Get optional string values from registry. */
int
get_opt_string_entry (HKEY key, const char *entry, char*&value)
{
  int ret;
  DWORD type, size = 0;

  /* Not finding the key is no error. */
  if ((ret = RegQueryValueEx (key, entry, 0, &type, NULL, &size)))
    return ERROR_SUCCESS;
  if (!(value = (char *) malloc (size)))
    return ERROR_OUTOFMEMORY;
  if ((ret = RegQueryValueEx (key, entry, 0, &type, (BYTE *) value, &size)))
    {
      free (value);
      value = NULL;
    }
  return ret;
}

int
reeval_io_path (int fd, char *&io_path, const char *svc_name)
{
  char buf[MAX_PATH + 1];

  if (!io_path)
    if (fd == STDIN_FILENO)
      {
	if (!(io_path = strdup (DEF_STDIN_PATH)))
	  return ERROR_OUTOFMEMORY;
      }
    else
      {
	strcat (strcat (strcpy (buf, DEF_LOG_PATH), svc_name), ".log");
	if (!(io_path = (char *) malloc (strlen (buf) + 1)))
	  return ERROR_OUTOFMEMORY;
        strcpy (io_path, buf);
      }
  /* convert relative paths to absolute ones, relative to DEF_LOG_PATH. */
  if (io_path[0] != '/' && io_path[1] != ':')
    {
      if (strlen (DEF_LOG_PATH) + strlen (io_path) > MAX_PATH)
        return ERROR_INVALID_PARAMETER;
      strcat (strcpy (buf, DEF_LOG_PATH), io_path);
      free (io_path);
      if (!(io_path = (char *) malloc (strlen (buf) + 1)))
	return ERROR_OUTOFMEMORY;
      strcpy (io_path, buf);
    }
  return ERROR_SUCCESS;
}

/* Retrieves the subkeys of the service registry key to determine what
   application to start from `service_main'. */
int
get_reg_entries (const char *name, char *&path, char *&args, env_t *&env,
		 DWORD *termsig_p, char *&stdin_path, char *&stdout_path,
		 char *&stderr_path)
{
  HKEY srv_key = NULL;
  HKEY env_key = NULL;
  DWORD type, size;
  char param_key[MAX_PATH];
  int ret;

  strcat (strcat (strcpy (param_key, SRV_KEY), name), PARAM_KEY);
  if ((ret = RegOpenKeyEx (HKEY_LOCAL_MACHINE, param_key, 0,
  			   KEY_READ, &srv_key)) != ERROR_SUCCESS)
    goto out;
  /* Get path. */
  if ((ret = RegQueryValueEx (srv_key, PARAM_PATH, 0, &type,
                              NULL, (size = 0, &size))) != ERROR_SUCCESS)
    goto out;
  if (!(path = (char *) malloc (size)))
    {
      ret = ERROR_OUTOFMEMORY;
      goto out;
    }
  if ((ret = RegQueryValueEx (srv_key, PARAM_PATH, 0, &type,
                              (BYTE *) path, &size)) != ERROR_SUCCESS)
    goto out;
  /* Get (optional) args. */
  if ((ret = get_opt_string_entry (srv_key, PARAM_ARGS, args)))
    goto out;
  /* Get (optional) termination signal. */
  if (RegQueryValueEx (srv_key, PARAM_TERMSIG, 0, &type,
		       (BYTE *) termsig_p,
		       (size = sizeof(*termsig_p), &size)) != ERROR_SUCCESS)
    *termsig_p = SIGTERM;  // the default
  /* Get (optional) stdin/stdout/stderr redirection files. */
  if ((ret = get_opt_string_entry (srv_key, PARAM_STDIN, stdin_path)))
    goto out;
  else if (reeval_io_path (STDIN_FILENO, stdin_path, name))
    goto out;
  if ((ret = get_opt_string_entry (srv_key, PARAM_STDOUT, stdout_path)))
    goto out;
  else if (reeval_io_path (STDOUT_FILENO, stdout_path, name))
    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))
    goto out;
  /* Get (optional) environment strings. */
  strcat (strcat (param_key, "\\"), PARAM_ENVIRON);
  if ((ret = RegOpenKeyEx (HKEY_LOCAL_MACHINE, param_key, 0,
  			   KEY_READ, &env_key)) == ERROR_SUCCESS)
    {
      char kname[256];
      char kvalue[256];
      DWORD ksize;
      int idx;

      if (!(env = (env_t *) calloc (MAX_ENV + 1,  sizeof (env_t))))
	{
	  ret = ERROR_OUTOFMEMORY;
	  goto out;
	}
      idx = 0;
      for (int i = 0; i <= MAX_ENV + 2; ++i)
	{
	  ret = RegEnumValue (env_key, i, kname, (size = 256, &size), 0,
			      NULL, (BYTE *) kvalue, (ksize = 256, &ksize));
	  if (ret == ERROR_NO_MORE_ITEMS)
	    break;
	  if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)
	    goto out;
	  if (!(env[idx].name = strdup (kname)) ||
	      !(env[idx].val = strdup (kvalue)))
	    {
	      ret = ERROR_OUTOFMEMORY;
	      goto out;
	    }
	  strcpy (env[idx].name, kname);
	  strcpy (env[idx].val, kvalue);
	  ++idx;
	}
      if (!idx)
	{
	  free (env);
	  env = NULL;
	}
    }
  if (args && !strlen (args))
    {
      free (args);
      args = NULL;
    }
  ret = 0;

out:
  if (ret)
    {
      path = args = NULL;
      env = NULL;
    }
  if (env_key)
    RegCloseKey (env_key);
  if (srv_key)
    RegCloseKey (srv_key);
  return ret;
}

int
add_env_var (char *envstr, env_t *&env)
{
  if (!envstr)
    return error (InstallErr, "NULL environment string not allowed");
  if (!env && !(env = (env_t *) calloc (MAX_ENV + 1,  sizeof (env_t))))
    return error (InstallErr, "Out of memory");
  char *name = envstr;
  char *value = strchr (envstr, '=');
  if (!value)
    return error (InstallErr, "Malformed environment string");
  *value++ = '\0';
  for (int i = 0; i <= MAX_ENV; ++i)
    if (!env[i].name)
      {
        env[i].name = name;
	env[i].val = value;
	return 0;
      }
    else if (!strcmp (env[i].name, name))
      return 0;
  return error (InstallErr, "Only " MAX_ENV_STR
  			    " environment variables allowed");
}

int
add_dep(char *service_name, char **&deps)
{
  if (!service_name)
    return error(InstallErr, "NULL dependency name not allowed");
  if (!deps && !(deps = (char **) calloc(MAX_DEPS + 1, sizeof(char *))))
    return error(InstallErr, "Out of memory");
  for (int i = 0; i <= MAX_DEPS; ++i)
    if (! deps[i])
      {
	if (! (deps[i] = strdup(service_name)))
	  return error(InstallErr, "Out of memory");
	return 0;
      }
  return error(InstallErr, "Only " MAX_DEPS_STR " dependency values 
allowed");
}

/* The strings `path' and `args' are evaluated to an arglist which
   can be used as `argv' argument to execv. It's splitted at the
   spaces. One level of " or ' quotes are supported as well. */

char *arglistbuf[1024];

int
eval_arglist (const char *path, char *args, char **&arglist)
{
  char **tmp, stopchar;
  int count = 2;

  arglist = (char **) malloc (count * sizeof (char *));
  if (!arglist)
    return ERROR_OUTOFMEMORY;
  arglist[0] = (char *) path;
  arglist[1] = NULL;
  if (!args)
    return 0;
  for (char *c = args; *c; ++c)
    if (*c != ' ')
      {
	if (*c == '\'' || *c == '"')
	  {
	    stopchar = *c;
	    ++c;
	  }
	else
	  stopchar = ' ';
	if (!(tmp = (char **) realloc (arglist, ++count * sizeof (char *))))
	  return ERROR_OUTOFMEMORY;
	arglist = tmp;
	arglist[count - 2] = c;
	arglist[count - 1] = NULL;
	while (*c && *c != stopchar)
	  ++c;
	if (*c)
	  *c = '\0';
	else
	  --c;
      }
  return 0;
}

/* Installs cygrunsrv as service `name' with display name `disp'. */
int
install_service (const char *name, const char *disp, type_t type,
		 char *user, char *pass, char **deps)
{
  char mypath[MAX_PATH];
  SC_HANDLE sm = (SC_HANDLE) 0;
  SC_HANDLE sh = (SC_HANDLE) 0;
  SC_LOCK sl = (SC_LOCK) 0;
  char userbuf[INTERNET_MAX_HOST_NAME_LENGTH + UNLEN + 2];
  char *username = NULL;
  char *dependencies = NULL;
  char *err_func;
  DWORD err = 0;

  /* Get own full path. */
  if (!GetModuleFileName (NULL, mypath, MAX_PATH))
    err_out (GetModuleFileName);
  /* Open service manager database. */
  if (!(sm = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS)))
    err_out (OpenSCManager);
  /* Lock service database. */
  if (!(sl = LockServiceDatabase (sm)))
    err_out (LockServiceDatabase);
  /* Open service, go ahead if service doesn't exists. */
  if (!(sh = OpenService (sm, name, SERVICE_ALL_ACCESS)) &&
      GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
    err_out (OpenService);
  if (sh)
    {
      SetLastError (ERROR_SERVICE_EXISTS);
      err_out (OpenService);
    }
  /* Set optional dependencies. */
  if (deps)
    {
      int concat_length = 0;
      for (int i = 0; i < MAX_DEPS && deps[i]; i++)
	concat_length += (strlen(deps[i]) + 1);
      concat_length++;
      if (! (dependencies = (char *) malloc(concat_length)))
	{
	  SetLastError(ERROR_OUTOFMEMORY);
	  err_out(malloc);
	}
      char *p = dependencies;
      for (int i = 0; i < MAX_DEPS && deps[i]; i++)
	{
	  strcpy(p, deps[i]);
	  p += (strlen(deps[i]) + 1);
	}
      *p = '\0';
      /* dependencies now holds the concatenation of all the
         dependent service names, each terminated by a null and the
         whole thing terminated by a final null. */
    }
  /* Check username. */
  if (user)
    {
      /* Check if user is in /etc/passwd. */
      struct passwd *pw = getpwnam (user);

      /* If yes, try to extract an U- entry, if any.
         Otherwise treat user as the windows user name. */
      if (pw && pw->pw_gecos)
        {
	  char *c;

	  if ((c = strstr (pw->pw_gecos, "U-")) != NULL &&
	      (c == pw->pw_gecos || c[-1] == ','))
	    {
	      userbuf[0] = '\0';
	      strncat (userbuf, c + 2,
	      	       INTERNET_MAX_HOST_NAME_LENGTH + UNLEN + 1);
	      if ((c = strchr (userbuf, ',')) != NULL)
	        *c = '\0';
	      user = userbuf;
	    }
	}
      if (!(username = (char *) malloc (strlen (user) + 3)))
        {
	  SetLastError (ERROR_OUTOFMEMORY);
	  err_out (malloc);
	}
      /* If no "\" is part of the name, prepend ".\" */
      if (!strchr (user, '\\'))
        strcat (strcpy (username, ".\\"), user);
      else
        strcpy (username, user);
    }
  /* If a username other than .\System is given but no password, ask for it. 
*/
  char buf[128];
  if (username && strcasecmp (username, ".\\System") && !pass)
    {
      while (!pass)
        {
	  char *p;
	  sprintf (buf, "Enter password of user `%s': ", username);
	  p = getpass (buf);
	  strcpy (buf, p);
	  p = getpass ("Reenter, please: ");
	  if (!strcmp (buf, p))
	    pass = buf;
	  else
	    printf ("Sorry, passwords do not match.\n");
	  while (p && *p)
	    *p++ = '\0';
	}
    }
  /* Try to create service. */
  if (!(sh = CreateService (sm, name, disp, SERVICE_ALL_ACCESS,
			    SERVICE_WIN32_OWN_PROCESS,
			    type == Auto ? SERVICE_AUTO_START
			    		 : SERVICE_DEMAND_START,
			    SERVICE_ERROR_NORMAL, mypath, NULL, NULL,
			    dependencies,
			    username, username ? pass ?: "" : NULL)))
    err_out (CreateService);

out:
  if (pass == buf)
    while (*pass)
      *pass++ = '\0';
  if (sh)
    CloseServiceHandle (sh);
  if (sl)
    UnlockServiceDatabase (sl);
  if (sm)
    CloseServiceHandle (sm);
  return err == 0 ? 0 : error (InstallErr, err_func, err);
}

/* Remove service `name'. */
int
remove_service (const char *name)
{
  SC_HANDLE sm = (SC_HANDLE) 0;
  SC_HANDLE sh = (SC_HANDLE) 0;
  SC_LOCK sl = (SC_LOCK) 0;
  SERVICE_STATUS ss;
  int cnt = 0;
  char *err_func;
  DWORD err = 0;

  /* Open service manager database. */
  if (!(sm = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS)))
    err_out (OpenSCManager);
  /* Lock service database. */
  if (!(sl = LockServiceDatabase (sm)))
    err_out (LockServiceDatabase);
  /* Check whether service exists. */
  if (!(sh = OpenService (sm, name, SERVICE_ALL_ACCESS)))
    err_out (OpenService);
  /* Try stopping service.
     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)
    sleep (1);
  /* Any error besides ERROR_SERVICE_NOT_ACTIVE is treated as an error. */
  if (err && err != ERROR_SERVICE_NOT_ACTIVE)
    err_out (ControlService);
  /* If service is active, wait until it's stopped. */
  if (!err)
    {
      if (!QueryServiceStatus (sh, &ss))
	err_out (QueryServiceStatus);

      DWORD last_check = ss.dwCheckPoint;
      DWORD last_tick = GetTickCount ();

      while (ss.dwCurrentState == SERVICE_STOP_PENDING)
        {
	  Sleep (eval_wait_time (ss.dwWaitHint));
	  if (!QueryServiceStatus (sh, &ss))
	    err_out (QueryServiceStatus);
	  if (ss.dwCurrentState == SERVICE_STOP_PENDING)
	    {
	      if (ss.dwCheckPoint > last_check)
		{
		  last_check = ss.dwCheckPoint;
		  last_tick = GetTickCount ();
		}
	      else if (GetTickCount() - last_tick > ss.dwWaitHint)
		{
		  SetLastError (ERROR_SERVICE_REQUEST_TIMEOUT);
		  err_out (QueryServiceStatus);
		}
	    }
        }
    }
  err = 0;
  /* Now try to remove service. */
  if (!DeleteService (sh))
    err_out (DeleteService);

out:
  if (sh)
    CloseServiceHandle (sh);
  if (sl)
    UnlockServiceDatabase (sl);
  if (sm)
    CloseServiceHandle (sm);
  return err == 0 ? 0 : error (RemoveErr, err_func, err);
}

/* Start service `name'. */
int
start_service (const char *name)
{
  SC_HANDLE sm = (SC_HANDLE) 0;
  SC_HANDLE sh = (SC_HANDLE) 0;
  SERVICE_STATUS ss;
  int cnt = 0;
  DWORD last_check;
  DWORD last_tick;
  char *err_func;
  DWORD err = 0;

  /* Open service manager database. */
  if (!(sm = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS)))
    err_out (OpenSCManager);
  /* Check whether service exists. */
  if (!(sh = OpenService (sm, name, SERVICE_ALL_ACCESS)))
    err_out (OpenService);
  /* Start service. */
  if (!StartService (sh, 0, NULL) &&
      GetLastError () != ERROR_SERVICE_ALREADY_RUNNING)
    err_out (StartService);
  /* Wait until start was successful. */
  if (!QueryServiceStatus (sh, &ss))
    err_out (QueryServiceStatus);

  last_check = ss.dwCheckPoint;
  last_tick = GetTickCount ();

  while (ss.dwCurrentState == SERVICE_START_PENDING)
    {
      Sleep (eval_wait_time (ss.dwWaitHint));
      if (!QueryServiceStatus (sh, &ss))
	err_out (QueryServiceStatus);
      if (ss.dwCurrentState == SERVICE_START_PENDING)
	{
	  if (ss.dwCheckPoint > last_check)
	    {
	      last_check = ss.dwCheckPoint;
	      last_tick = GetTickCount ();
	    }
	  else if (GetTickCount() - last_tick > ss.dwWaitHint)
	    {
	      SetLastError (ERROR_SERVICE_REQUEST_TIMEOUT);
	      err_out (QueryServiceStatus);
	    }
	}
    }

  if (ss.dwCurrentState != SERVICE_RUNNING)
    {
      SetLastError (ERROR_SERVICE_NOT_ACTIVE);
      err_out (QueryServiceStatus);
    }

out:
  if (sh)
    CloseServiceHandle (sh);
  if (sm)
    CloseServiceHandle (sm);
  return err == 0 ? 0 : error (StartErr, err_func, err);
}

/* Stop service `name'. */
int
stop_service (const char *name)
{
  SC_HANDLE sm = (SC_HANDLE) 0;
  SC_HANDLE sh = (SC_HANDLE) 0;
  SC_LOCK sl = (SC_LOCK) 0;
  SERVICE_STATUS ss;
  int cnt = 0;
  char *err_func;
  DWORD err = 0;

  /* Open service manager database. */
  if (!(sm = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS)))
    err_out (OpenSCManager);
  /* Lock service database. */
  if (!(sl = LockServiceDatabase (sm)))
    err_out (LockServiceDatabase);
  /* Check whether service exists. */
  if (!(sh = OpenService (sm, name, SERVICE_ALL_ACCESS)))
    err_out (OpenService);
  /* Try stopping service.
     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)
    sleep (1);
  /* Any error besides ERROR_SERVICE_NOT_ACTIVE is treated as an error. */
  if (err && err != ERROR_SERVICE_NOT_ACTIVE)
    err_out (ControlService);
  /* If service is active, wait until it's stopped. */
  if (!err)
    {
      if (!QueryServiceStatus (sh, &ss))
	err_out (QueryServiceStatus);

      DWORD last_check = ss.dwCheckPoint;
      DWORD last_tick = GetTickCount ();

      while (ss.dwCurrentState == SERVICE_STOP_PENDING)
        {
	  Sleep (eval_wait_time (ss.dwWaitHint));
	  if (!QueryServiceStatus (sh, &ss))
	    err_out (QueryServiceStatus);
	  if (ss.dwCurrentState == SERVICE_STOP_PENDING)
	    {
	      if (ss.dwCheckPoint > last_check)
		{
		  last_check = ss.dwCheckPoint;
		  last_tick = GetTickCount ();
		}
	      else if (GetTickCount() - last_tick > ss.dwWaitHint)
		{
		  SetLastError (ERROR_SERVICE_REQUEST_TIMEOUT);
		  err_out (QueryServiceStatus);
		}
	    }
        }
    }
  err = 0;

out:
  if (sh)
    CloseServiceHandle (sh);
  if (sl)
    UnlockServiceDatabase (sl);
  if (sm)
    CloseServiceHandle (sm);
  return err == 0 ? 0 : error (StopErr, err_func, err);
}

#undef err_out

int server_pid;

/* What really is to do when service should be stopped. */
int
terminate_child ()
{
  set_service_status (SERVICE_STOP_PENDING, 1, 21000L);
  for (int i = 0; i < 20; ++i)
    if (!server_pid)
      sleep (1);
    else
      {
	kill (server_pid, termsig);
	report_service_status ();
	return 0;
      }
  set_service_status (SERVICE_RUNNING);
  return -1;
}

void
sigterm_handler (int sig)
{
  syslog (LOG_INFO, "Received signal `%d', terminate child...", sig);
  terminate_child ();
}

/* Service handler routine which is called from SCM to check status,
   stop the service, etc. */
VOID WINAPI
service_handler (DWORD ctrl)
{
  switch (ctrl)
    {
    case SERVICE_CONTROL_STOP:
      /* 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;
      cygwin_internal (CW_INIT_EXCEPTIONS, &except_list);

      terminate_child ();
      break;
    case SERVICE_CONTROL_INTERROGATE:
      break;
    }
  report_service_status (false);
}

/* Used by the service_main thread. */
exception_list except_list;

/* Various initialization task. */
int
prepare_daemon ()
{
  /* Allocate a console. */
  AllocConsole ();
  /* Initialize exception handling. */
  cygwin_internal (CW_INIT_EXCEPTIONS, &except_list);
  /* Set up signal handler. */
  signal (SIGQUIT, sigterm_handler);
  signal (SIGINT, sigterm_handler);
  signal (SIGHUP, sigterm_handler);
  signal (SIGTERM, sigterm_handler);

  return 0;
}

/* Service main routine. Actual worker. Starts and controls application
   given in the service registry subkeys . */
void WINAPI
service_main (DWORD argc, LPSTR *argv)
{
  DWORD err;
  SERVICE_STATUS_HANDLE ssh;
  svcname = argv[0];
FILE *f;
f=fopen("000service_main.txt","w");
fputs("service_main",f);
fflush(f);
fclose(f);
  /* Initialize syslog facility. */
  openlog (svcname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);

  /* Tell SCM that we are alive and well. */
  if (!(ssh = RegisterServiceCtrlHandlerA (svcname, service_handler)))
    {
      err = GetLastError ();
      syslog_starterr ("RegSrvCtrlHandler", GetLastError ());
      return;
    }

  init_service_status (ssh);

  report_service_status ();

  if (err = prepare_daemon ())
    {
      syslog_starterr ("prepare_daemon", err);
      set_service_status (SERVICE_STOPPED, 0, 0, err);
      return;
    }

  report_service_status ();

  /* Step 1: Get information from registry. */
  char *path = NULL;
  char *args = NULL;
  env_t *env = NULL;
  char *stdin_path = NULL;
  char *stdout_path = NULL;
  char *stderr_path = NULL;
  if (err = get_reg_entries (svcname, path, args, env, &termsig,
			     stdin_path, stdout_path, stderr_path))
    {
      syslog_starterr ("get_reg_entries", err);
      set_service_status (SERVICE_STOPPED, 0, 0, err);
      return;
    }

  report_service_status ();

  /* Step 2: Further preparations:
     - Evaluate arguments to a list for execv
     - Add environment strings from registry to environment
     - Add /bin to $PATH so that always cygwin1.dll is in the path. */
  char **arglist;
  if (err = eval_arglist (path, args, arglist))
    {
      syslog_starterr ("eval_arglist", err);
      set_service_status (SERVICE_STOPPED, 0, 0, err);
      return;
    }

  report_service_status ();

  if (env)
    for (int i = 0; i <= MAX_ENV && env[i].name; ++i)
      setenv (env[i].name, env[i].val, 1);

  report_service_status ();

  char *env_path = getenv ("PATH");
  if (!env_path)
    setenv ("PATH", "/bin", 1);
  else
    {
      char env_tmp[strlen (env_path) + 6];
      strcat (strcpy (env_tmp, env_path), ":/bin");
      setenv ("PATH", env_tmp, 1);
    }

  report_service_status ();

  /* Step 3: Fork child */
  switch (server_pid = fork ())
    {
    case -1: /* Error */
      syslog_starterr ("fork", 0, err);
      break;

    case 0:  /* Child */
      if (redirect_io (stdin_path, stdout_path, stderr_path))
        exit (1);
      exit (execv (path, arglist));

    default: /* Parent */
      report_service_status ();

      /* Free some unused memory. */
      free (path);
      free (arglist);
      if (env)
	{
	  for (int i = 0; i <= MAX_ENV && env[i].name; ++i)
	    {
	      free (env[i].name);
	      free (env[i].val);
	    }
	  free (env);
	}

      report_service_status ();

      /* Wait a second here to allow the child process to `execv'. If that
         fails, the child sends itself a signal which we can ask for in
	 `waitpid'. */

      sleep (1);

      report_service_status ();

      int status;
      if (!waitpid (server_pid, &status, WNOHANG))
        {
	  /* Child has probably `execv'd properly. */
	  set_service_status (SERVICE_RUNNING);
	  syslog(LOG_INFO, "`%s' service started", svcname);
          waitpid (server_pid, &status, 0);
        }
      else if (WIFEXITED (status))
        syslog_starterr ("execv", 0, WEXITSTATUS (status));
      else if (WIFSIGNALED (status))
        syslog (LOG_ERR, "starting service `%s' failed: signal %d raised",
		svcname, WTERMSIG (status));

      syslog(LOG_INFO, "`%s' service stopped", svcname);

      break;
    }
  set_service_status (SERVICE_STOPPED);
}

int
main (int argc, char **argv)
{
  int c;
  action_t action = Undefined;
  char *in_name = NULL;
  char *in_path = NULL;
  char *in_args = NULL;
  env_t *in_env = NULL;
  char *in_disp = NULL;
  type_t in_type = NoType;
  char *in_user = NULL;
  char *in_pass = NULL;
  int in_termsig = 0;
  char **in_deps = 0;
  char *in_stdin = NULL;
  char *in_stdout = NULL;
  char *in_stderr = NULL;

  appname = argv[0];


  if (argc < 2)
    {
      /* Started without parameters: Start the service.
         argv[0] contains the name of the service. */

      SERVICE_TABLE_ENTRYA ste[2];

      ste[0].lpServiceName = appname;
      ste[0].lpServiceProc = service_main;
      ste[1].lpServiceName = NULL;
      ste[1].lpServiceProc = NULL;
FILE *f;
f=fopen("000main.txt","w");
fputs("main",f);
fflush(f);
fclose(f);
      if (!StartServiceCtrlDispatcherA(ste))
	return error (StartAsSvcErr);
      return 0;
    }
  /* Started with parameters: Install, deinstall, start or stop a service. 
*/
  while ((c = getopt_long(argc, argv, opts, longopts, NULL)) != EOF)
    switch (c)
      {
      case 'I':
	if (action != Undefined)
	  return error (ReqAction);
	action = Install;
	in_name = optarg;
	break;
      case 'R':
	if (action != Undefined)
	  return error (ReqAction);
	action = Remove;
	in_name = optarg;
	break;
      case 'S':
	if (action != Undefined)
	  return error (ReqAction);
	action = Start;
	in_name = optarg;
	break;
      case 'E':
	if (action != Undefined)
	  return error (ReqAction);
	action = Stop;
	in_name = optarg;
	break;
      case 'p':
	if (action != Install)
	  return error (PathNotAllowed);
	if (in_path)
	  return error (OnlyOnePath);
	in_path = optarg;
	break;
      case 'a':
	if (action != Install)
	  return error (ArgsNotAllowed);
	if (in_args)
	  return error (OnlyOneArgs);
	in_args = optarg;
	break;
      case 'e':
	if (action != Install)
	  return error (EnvNotAllowed);
	if (add_env_var (optarg, in_env))
	  return 1;
	break;
      case 'd':
	if (action != Install)
	  return error (DispNotAllowed);
	if (in_disp)
	  return error (OnlyOneDisp);
	in_disp = optarg;
	break;
      case 's':
	if (action != Install)
	  return error (SigNotAllowed);
	if (in_termsig)
	  return error (OnlyOneSig);
	if (! (in_termsig = atoi(optarg)))
	  {
	    char sig_name[128];
	    sprintf(sig_name, "SIG%s", optarg);
	    if (! (in_termsig = strtosigno(sig_name)))
	      return error (InvalidSig);
	  }
	break;
      case 't':
	if (action != Install)
	  return error (TypeNotAllowed);
	if (in_type != NoType)
	  return error (OnlyOneType);
	if (!strcasecmp (optarg, "a") || !strcasecmp (optarg, "auto"))
	  in_type = Auto;
	else if (!strcasecmp (optarg, "m") || !strcasecmp (optarg, "manual"))
	  in_type = Manual;
	else
	  return error (InvalidType);
	break;
      case 'u':
	if (action != Install)
	  return error (UserNotAllowed);
	if (in_user)
	  return error (OnlyOneUser);
	in_user = optarg;
	break;
      case 'w':
	if (action != Install)
	  return error (PassNotAllowed);
	if (in_pass)
	  return error (OnlyOnePass);
	in_pass = optarg;
	break;
      case 'y':
	if (action != Install)
	  return error (DepNotAllowed);
	if (add_dep(optarg, in_deps))
	  return 1;
	break;
      case '0':
	if (action != Install)
	  return error (IONotAllowed);
	if (in_stdin)
	  return error (OnlyOneIO);
	in_stdin = optarg;
	break;
      case '1':
	if (action != Install)
	  return error (IONotAllowed);
	if (in_stdin)
	  return error (OnlyOneIO);
	in_stdout = optarg;
	break;
      case '2':
	if (action != Install)
	  return error (IONotAllowed);
	if (in_stdin)
	  return error (OnlyOneIO);
	in_stderr = optarg;
	break;
      case 'h':
	return usage ();
      case 'v':
        return version ();
      default:
        return error (UnrecognizedOption);
      }
  if (optind < argc)
    return error (TrailingArgs);
  switch (action)
    {
    case Install:
      int ret;

      if (!in_path)
        return error (ReqPath);
      if (!in_disp)
	in_disp = in_name;
      if (in_type == NoType)
        in_type = Auto;
      if (!is_executable (in_path))
        return error (InvalidPath);
      if (ret = install_service (in_name, in_disp, in_type, in_user, 
in_pass, in_deps))
        return ret;
      if (ret = install_registry_keys (in_name, in_path, in_args, in_env,
				       in_termsig, in_stdin, in_stdout,
				       in_stderr))
        remove_service (in_name);
      return ret;
      break;
    case Remove:
      return remove_service (in_name);
      break;
    case Start:
      return start_service (in_name);
      break;
    case Stop:
      return stop_service (in_name);
      break;
    }
  return error (ReqAction);
}


--
Want to unsubscribe from this list?
Check out: http://cygwin.com/ml/#unsubscribe-simple

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