Mail Archives: cygwin/2001/06/03/14:39:32
------=_NextPart_000_2f6e_6628_426d
Content-Type: text/plain; format=flowed
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
------=_NextPart_000_2f6e_6628_426d
Content-Type: text/plain; name="cygrunsrv.cc"; format=flowed
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="cygrunsrv.cc"
/*
* 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 AT vinschen DOT 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);
}
------=_NextPart_000_2f6e_6628_426d
Content-Type: text/plain; charset=us-ascii
--
Want to unsubscribe from this list?
Check out: http://cygwin.com/ml/#unsubscribe-simple
------=_NextPart_000_2f6e_6628_426d--
- Raw text -