delorie.com/archives/browse.cgi   search  
Mail Archives: cygwin/2001/05/08/02:36:31

Mailing-List: contact cygwin-help AT sourceware DOT cygnus DOT com; run by ezmlm
List-Subscribe: <mailto:cygwin-subscribe AT sources DOT redhat DOT com>
List-Archive: <http://sources.redhat.com/ml/cygwin/>
List-Post: <mailto:cygwin AT sources DOT redhat DOT com>
List-Help: <mailto:cygwin-help AT sources DOT redhat DOT com>, <http://sources.redhat.com/ml/#faqs>
Sender: cygwin-owner AT sources DOT redhat DOT com
Delivered-To: mailing list cygwin AT sources DOT redhat DOT com
From: "Ralf Habacker" <Ralf DOT Habacker AT saght DOT tessag DOT com>
To: "Cygwin" <cygwin AT cygwin DOT com>
Subject: Problems implementing service installer in cron-2.9.3
Date: Tue, 8 May 2001 08:33:21 +0200
Message-ID: <001101c0d788$c6c0ebb0$6e032bb7@BRAMSCHE>
MIME-Version: 1.0
X-Priority: 3 (Normal)
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook 8.5, Build 4.71.2173.0
Importance: Normal
X-MimeOLE: Produced By Microsoft MimeOLE V5.50.4133.2400

------=_NextPart_000_0012_01C0D799.8A49BBB0
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 7bit

Hi,
I like to integrate a service installer in cron and have adapted the service
code from cygipc.
I can compile this stuff, install/remove the service, but starting don't
work.
Has anyone an idea, what I have done wrong ? The relating code is appended.

Regards

Ralf Habacker
EMail: Ralf AT habacker DOT de
       Ralf DOT Habacker AT saght DOT tessag DOT com

------=_NextPart_000_0012_01C0D799.8A49BBB0
Content-Type: application/octet-stream;
	name="service.cc"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="service.cc"

/* $Id: ip-daemon.c,v 1.14 2001/02/16 16:15:07 cwilson Exp $ */=0A=
=0A=
/*=0A=
 *   service installer=0A=
 *=0A=
 *   Copyright (C) 1997 Philippe CHAPUY=0A=
 *   Copyright (C) 1998 Ludovic LANGE=0A=
 *=0A=
 *   This program is free software; you can redistribute it and/or modify=0A=
 *   it under the terms of the GNU General Public License as published by=0A=
 *   the Free Software Foundation; either version 2 of the License, or=0A=
 *   (at your option) any later version.=0A=
 *=0A=
 *   This program is distributed in the hope that it will be useful,=0A=
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of=0A=
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the=0A=
 *   GNU General Public License for more details.=0A=
 *=0A=
 *   You should have received a copy of the GNU General Public License=0A=
 *   along with this program; if not, write to the Free Software=0A=
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=0A=
 */=0A=
=0A=
/*=0A=
 *   HISTORY:=0A=
 *   --------=0A=
 *=0A=
 *   13/05/1998 : Version 1.00 released=0A=
 *                First public release=0A=
 *                adress any comments to llange AT capgemini DOT fr=0A=
 *=0A=
 */=0A=
=0A=
/*=0A=
 * About fprintf, syslog, and log_message:=0A=
 *  if a message needs to be sent to the user, then use these=0A=
 *  functions under the following circumstances:=0A=
 *  - fprintf: stuff that is immediately user-interactive=0A=
 *      e.g. you don't want to see "help instructions" =0A=
 *      in the syslog.=0A=
 *  - syslog: when the block of code is only reachable=0A=
 *      when running as a service --- but, for simplicity,=0A=
 *      we just use log_message anyway.=0A=
 *  - log_message: when the block of code could be reached=0A=
 *      when running as a service OR standalone=0A=
 */=0A=
=0A=
/************************************************************************=
/=0A=
/*									*/=0A=
/************************************************************************=
/=0A=
=0A=
//#define EXTERN=0A=
#include <stdio.h>=0A=
#include <unistd.h>=0A=
#include <stdlib.h>=0A=
#include <signal.h>=0A=
#include <string.h>=0A=
#include <stdarg.h>=0A=
#include <windows.h>=0A=
#include <exceptions.h>=0A=
#include <sys/cygwin.h>=0A=
#include <sys/wait.h>=0A=
#include <syslog.h>=0A=
#include "service.h"=0A=
=0A=
int work_main(void);=0A=
=0A=
char * getVersion(char * buf, int len);=0A=
static void get_null_sa (SECURITY_ATTRIBUTES *sap);=0A=
int get_reg_entries(void);=0A=
int log_message(FILE *stream, int severity, const char *fmt, ...);=0A=
=0A=
char	verbose=3D1;=0A=
int  	service=3D0;=0A=
char	tight_security=3D0;=0A=
char	debug=3D0;=0A=
=0A=
#define is_winnt	(GetVersion() < 0x80000000)=0A=
=0A=
#define CROND_SERVICE_NAME	"cron-daemon1"=0A=
#define CROND_SERVICE_DISPLAY	"Cron Daemon"=0A=
#define CROND_PARAM_KEY	"SYSTEM\\CurrentControlSet\\Services\\" \=0A=
                        CROND_SERVICE_NAME \=0A=
                        "\\Parameters"=0A=
#define CROND_PARAM_SECURITY	"TightSecurity"=0A=
#define CROND_PARAM_DEBUG	"Debug"=0A=
=0A=
int server_pid =3D 0;=0A=
SERVICE_STATUS_HANDLE ssh;=0A=
SERVICE_STATUS ss;=0A=
HANDLE hStopEvent =3D NULL;=0A=
=0A=
=0A=
int install_reg_entries(const char *myname, int verbose)=0A=
{=0A=
  HKEY srv_key =3D NULL;=0A=
  DWORD disp;=0A=
  DWORD temp;=0A=
  char *err_func;=0A=
=0A=
  if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, CROND_PARAM_KEY, 0, "",=0A=
                     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,=0A=
                     NULL, &srv_key, &disp)=0A=
      !=3D ERROR_SUCCESS)=0A=
    {=0A=
      err_func =3D "RegCreateKeyEx";=0A=
      goto error;=0A=
    }=0A=
  temp =3D tight_security;=0A=
  if (RegSetValueEx(srv_key, CROND_PARAM_SECURITY, 0, REG_DWORD,=0A=
                    (CONST BYTE *) &temp, sizeof(DWORD))=0A=
      !=3D ERROR_SUCCESS)=0A=
    {=0A=
      err_func =3D "RegSetValueEx(TightSecurity=3D1)";=0A=
      goto error;=0A=
    }=0A=
  temp =3D debug;=0A=
  if (RegSetValueEx(srv_key, CROND_PARAM_DEBUG, 0, REG_DWORD,=0A=
                    (CONST BYTE *) &temp, sizeof(DWORD))=0A=
      !=3D ERROR_SUCCESS)=0A=
    {=0A=
      err_func =3D "RegSetValueEx(Debug=3D1)";=0A=
      goto error;=0A=
    }=0A=
  RegFlushKey(srv_key);=0A=
  RegCloseKey(srv_key);=0A=
  return 0;=0A=
error:=0A=
  if (srv_key)=0A=
    RegCloseKey(srv_key);=0A=
  if (verbose)=0A=
    log_message(stderr, LOG_ERR, "%s: %ld =3D %s()\n",=0A=
            myname, GetLastError(), err_func);=0A=
  log_message(stderr, LOG_ERR, =0A=
     "%s: installing registry entries for `cron-daemon' failed\n",=0A=
     myname);=0A=
  return 1;=0A=
}=0A=
=0A=
int get_reg_entries(void)=0A=
{=0A=
  HKEY srv_key =3D NULL;=0A=
  DWORD type, size;=0A=
  DWORD tight_security_temp;=0A=
  DWORD debug_temp;=0A=
=0A=
  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, CROND_PARAM_KEY, 0,=0A=
                   KEY_ALL_ACCESS, &srv_key)=0A=
      !=3D ERROR_SUCCESS)=0A=
    {=0A=
      log_message(stderr, LOG_WARNING, =0A=
		   "couldn't get parameters from registry, using defaults\n"=0A=
         "error %ld in RegOpenKeyEx()", GetLastError());=0A=
      return 1;=0A=
    }=0A=
  if (RegQueryValueEx(srv_key, CROND_PARAM_DEBUG, 0, &type,=0A=
                      (unsigned char *) &debug_temp, (size =3D =
sizeof(DWORD), &size))=0A=
      !=3D ERROR_SUCCESS)=0A=
    log_message(stderr, LOG_WARNING, =0A=
	    "couldn't get debug value from registry, "=0A=
      "using default value %d", debug);=0A=
  else {=0A=
    debug =3D (char) debug_temp;=0A=
	 log_message(stderr, LOG_DEBUG, "debug value: %d", debug);=0A=
  }=0A=
  if (RegQueryValueEx(srv_key, CROND_PARAM_SECURITY, 0, &type,=0A=
      (unsigned char *) &tight_security_temp, (size =3D sizeof(DWORD), =
&size))=0A=
      !=3D ERROR_SUCCESS)=0A=
    log_message(stderr, LOG_WARNING, =0A=
	    "couldn't get tight_security boolean from registry, "=0A=
       "using default value %d", tight_security);=0A=
  else {=0A=
    tight_security =3D (char) tight_security_temp;=0A=
    log_message(stderr, LOG_DEBUG, "tight_security value: %d", =
tight_security);=0A=
  }=0A=
  RegCloseKey(srv_key);=0A=
  return 0;=0A=
}=0A=
=0A=
VOID WINAPI=0A=
service_handler(DWORD ctrl)=0A=
{=0A=
       exception_list except_list;=0A=
       cygwin_internal (CW_INIT_EXCEPTIONS, &except_list);=0A=
       switch (ctrl) {=0A=
       case SERVICE_CONTROL_STOP:=0A=
               ss.dwCheckPoint =3D 1;=0A=
               ss.dwWaitHint =3D 3000L; // wait up to 3 seconds...=0A=
               ss.dwCurrentState =3D SERVICE_STOP_PENDING;=0A=
=0A=
               if (hStopEvent) {=0A=
                 log_message(stderr, LOG_INFO, "sending hStopEvent");=0A=
                 SetEvent(hStopEvent);=0A=
               }=0A=
               else=0A=
                 log_message(stderr, LOG_INFO, "no hStopEvent to send");=0A=
=0A=
               break;=0A=
       default:=0A=
               // ignore=0A=
               break;=0A=
       }=0A=
       SetServiceStatus(ssh, &ss);=0A=
}=0A=
=0A=
void WINAPI=0A=
service_main(DWORD argc, LPSTR *argv)=0A=
{=0A=
        exception_list except_list;=0A=
        cygwin_internal (CW_INIT_EXCEPTIONS, &except_list);=0A=
       // Initialized Cygwin exception handling for thread not=0A=
       // created by Cygwin.=0A=
=0A=
       if (! (ssh =3D RegisterServiceCtrlHandlerA(CROND_SERVICE_NAME,=0A=
                                 service_handler))) {=0A=
                log_message(stderr, LOG_ERR,=0A=
                       "error in service_main: %d =3D =
RegSrvCtrlHandler()",=0A=
                       GetLastError());=0A=
                log_message(stderr, LOG_ERR, "starting 'cron-daemon' as =
a service failed");=0A=
                return;=0A=
       }=0A=
       ss.dwServiceType =3D SERVICE_WIN32_SHARE_PROCESS;=0A=
       ss.dwCurrentState =3D SERVICE_START_PENDING;=0A=
       ss.dwControlsAccepted =3D SERVICE_ACCEPT_STOP;=0A=
       ss.dwWin32ExitCode =3D NO_ERROR;=0A=
       ss.dwCheckPoint =3D 1;=0A=
       ss.dwWaitHint =3D 3000L;=0A=
       SetServiceStatus(ssh, &ss);=0A=
=0A=
       get_reg_entries();=0A=
		 	 log_message(stderr, LOG_DEBUG, "service: %d, debug %d, =
tight_security %d",=0A=
		   service, debug, tight_security);=0A=
       work_main();=0A=
=0A=
       log_message(stderr, LOG_INFO, "'cron-daemon' service stopped");=0A=
       ss.dwCheckPoint =3D 0;=0A=
       ss.dwCurrentState =3D SERVICE_STOPPED;=0A=
       SetServiceStatus(ssh, &ss);=0A=
}=0A=
=0A=
int=0A=
install_as_service(const char *myname, int verbose)=0A=
{=0A=
  char mypath[MAX_PATH+5];=0A=
  char *err_func;=0A=
  SC_HANDLE sm =3D (SC_HANDLE) 0;=0A=
  SC_HANDLE sh =3D (SC_HANDLE) 0;=0A=
  SC_LOCK sl =3D (SC_LOCK) 0;=0A=
=0A=
  // get own full path name=0A=
  if (! GetModuleFileName(NULL, mypath, MAX_PATH))=0A=
    {=0A=
      err_func =3D "GetModuleFileName";=0A=
      goto error;=0A=
    }=0A=
  // we can't read parameters from registry unless we=0A=
  // already know we are a service.  So, appname must include=0A=
  // --service option:=0A=
  strcat(mypath, " \"--service\"");=0A=
  // open service manager database=0A=
  if (! (sm =3D OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)))=0A=
    {=0A=
      err_func =3D "OpenSCManager";=0A=
      goto error;=0A=
    }=0A=
  // lock service database=0A=
  if (! (sl =3D LockServiceDatabase(sm)))=0A=
    {=0A=
      err_func =3D "LockServiceDatabase";=0A=
      goto error;=0A=
    }=0A=
  // open service, go ahead if service doesn't exists=0A=
  if (! (sh =3D OpenService(sm, CROND_SERVICE_NAME, SERVICE_ALL_ACCESS)) =
&&=0A=
      GetLastError() !=3D ERROR_SERVICE_DOES_NOT_EXIST)=0A=
    {=0A=
      err_func =3D "OpenService";=0A=
      goto error;=0A=
    }=0A=
  // service already exists=0A=
  if (sh)=0A=
    {=0A=
      if (verbose)=0A=
        log_message(stderr, LOG_WARNING, "%s: service already =
installed", myname);=0A=
    }=0A=
  // no, create service=0A=
  else if (! (sh =3D CreateService(sm,=0A=
                                 CROND_SERVICE_NAME,=0A=
                                 CROND_SERVICE_DISPLAY,=0A=
                                 STANDARD_RIGHTS_REQUIRED |=0A=
                                 SERVICE_INTERROGATE |=0A=
                                 SERVICE_QUERY_STATUS |=0A=
                                 SERVICE_START |=0A=
                                 SERVICE_STOP,=0A=
                                 SERVICE_WIN32_OWN_PROCESS /*|=0A=
                                 SERVICE_INTERACTIVE_PROCESS*/,=0A=
                                 SERVICE_AUTO_START,=0A=
                                 SERVICE_ERROR_NORMAL,=0A=
                                 mypath,=0A=
                                 NULL,=0A=
                                 NULL,=0A=
                                 NULL,=0A=
                                 NULL,=0A=
                                 NULL=0A=
                                )))=0A=
    {=0A=
      err_func =3D "CreateService";=0A=
      goto error;=0A=
    }=0A=
=0A=
  // if CloseServiceHandle returns with error, it's not sure=0A=
  // whether anything works...=0A=
  if (! CloseServiceHandle(sh))=0A=
    {=0A=
      err_func =3D "CloseServiceHandle";=0A=
      goto error;=0A=
    }=0A=
  return 0;=0A=
error:=0A=
  if (verbose)=0A=
    log_message(stderr, LOG_ERR, "%s: %lu =3D %s()",=0A=
            myname, GetLastError(), err_func);=0A=
  log_message(stderr, LOG_ERR, "%s: installing service `cron-daemon' =
failed", myname);=0A=
  if (sh)=0A=
    CloseServiceHandle(sh);=0A=
  if (sl && ! UnlockServiceDatabase(sl))=0A=
    log_message(stderr, LOG_WARNING,=0A=
            "%s: CAUTION: UnlockServiceDatabase() failed "=0A=
            "with error %lu", myname, GetLastError());=0A=
  return 1;=0A=
}=0A=
=0A=
int=0A=
remove_as_service(const char *myname, int verbose)=0A=
{=0A=
  char *err_func;=0A=
  SC_HANDLE sm =3D (SC_HANDLE) 0;=0A=
  SC_HANDLE sh =3D (SC_HANDLE) 0;=0A=
  SC_LOCK sl =3D (SC_LOCK) 0;=0A=
  SERVICE_STATUS ss;=0A=
  int err;=0A=
=0A=
  // open service manager database=0A=
  if (! (sm =3D OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)))=0A=
    {=0A=
      err_func =3D "OpenSCManager";=0A=
      goto error;=0A=
    }=0A=
  // lock service database=0A=
  if (! (sl =3D LockServiceDatabase(sm)))=0A=
    {=0A=
      err_func =3D "LockServiceDatabase";=0A=
      goto error;=0A=
    }=0A=
  // check whether service exists=0A=
  if (! (sh =3D OpenService(sm, CROND_SERVICE_NAME, SERVICE_ALL_ACCESS)))=0A=
    {=0A=
      if (GetLastError() =3D=3D ERROR_SERVICE_DOES_NOT_EXIST)=0A=
        {=0A=
          if (verbose)=0A=
            log_message(stderr, LOG_ERR, "%s: service isn't installed", =
myname);=0A=
          if (! UnlockServiceDatabase(sl))=0A=
            log_message(stderr, LOG_WARNING,=0A=
                    "%s: CAUTION: UnlockServiceDatabase() failed "=0A=
                    "with error %lu", myname, GetLastError());=0A=
            return 0;=0A=
        }=0A=
      err_func =3D "OpenService";=0A=
      goto error;=0A=
    }=0A=
  // try stopping service=0A=
  // if NOT_ACTIVE or CANNOT_ACCEPT_CTRL go ahead, else exit=0A=
  if (! ControlService(sh, SERVICE_CONTROL_STOP, &ss) &&=0A=
      (err =3D GetLastError()) !=3D ERROR_SERVICE_NOT_ACTIVE &&=0A=
       err !=3D ERROR_SERVICE_CANNOT_ACCEPT_CTRL)=0A=
    {=0A=
      err_func =3D "ControlService";=0A=
      goto error;=0A=
    }=0A=
  // wait, 'til service is stopped=0A=
  do=0A=
    {=0A=
      sleep(1);=0A=
      if (! QueryServiceStatus(sh, &ss))=0A=
        {=0A=
          err_func =3D "QueryServiceStatus";=0A=
          goto error;=0A=
        }=0A=
    }=0A=
  while (ss.dwCurrentState !=3D SERVICE_STOPPED);=0A=
  // remove service=0A=
  if (! DeleteService(sh))=0A=
    {=0A=
      err_func =3D "DeleteService";=0A=
      goto error;=0A=
    }=0A=
  // if CloseServiceHandle returns with error, it's not sure=0A=
  // whether anything works...=0A=
  if (! CloseServiceHandle(sh))=0A=
    {=0A=
      sh =3D (SC_HANDLE) 0;=0A=
      err_func =3D "CloseServiceHandle";=0A=
      goto error;=0A=
    }=0A=
  sh =3D (SC_HANDLE) 0;=0A=
  // Datenbank-Lock aufheben=0A=
  if (sl && ! UnlockServiceDatabase(sl))=0A=
    {=0A=
      log_message(stderr, LOG_WARNING,=0A=
              "%s: CAUTION: UnlockServiceDatabase() failed "=0A=
              "with error %lu", myname, GetLastError());=0A=
    }=0A=
  return 0;=0A=
error:=0A=
  if (verbose)=0A=
    log_message(stderr, LOG_ERR, "%s: %lu =3D %s()",=0A=
            myname, GetLastError(), err_func);=0A=
  log_message(stderr, LOG_ERR, "%s: removing `cron-daemon' as service =
failed", myname);=0A=
  if (sh)=0A=
    CloseServiceHandle(sh);=0A=
  if (sl && ! UnlockServiceDatabase(sl))=0A=
    fprintf(stderr,=0A=
            "%s: CAUTION: UnlockServiceDatabase() failed "=0A=
            "with error %lu", myname, GetLastError());=0A=
  return 1;=0A=
}=0A=
=0A=
//=0A=
// start service =0A=
//=0A=
//=0A=
=0A=
int start_service(void)=0A=
{=0A=
    SERVICE_TABLE_ENTRYA ste[2];=0A=
    ste[0].lpServiceName =3D CROND_SERVICE_NAME;=0A=
    ste[0].lpServiceProc =3D service_main;=0A=
    ste[1].lpServiceName =3D NULL;=0A=
    ste[1].lpServiceProc =3D NULL;=0A=
    StartServiceCtrlDispatcherA(ste);=0A=
		return 0; =0A=
}=0A=
=0A=
=0A=
=0A=
char * getVersion(char * buf, int len)=0A=
{=0A=
  unsigned short LVersionMajor=3D1;=0A=
  unsigned short LVersionMinor=3D0;=0A=
=0A=
//  LVersionMajor=3D(unsigned short)VERSION_NUM;=0A=
//  LVersionMinor=3D(unsigned short)( (VERSION_NUM-LVersionMajor)*100 );=0A=
  snprintf(buf, len, "%d.%02d", LVersionMajor, LVersionMinor);=0A=
  return buf;=0A=
}=0A=
=0A=
static void get_null_sa (LPSECURITY_ATTRIBUTES sap)=0A=
{=0A=
  static SECURITY_DESCRIPTOR *null_sdp =3D 0;=0A=
  static SECURITY_DESCRIPTOR sd;=0A=
=0A=
  if (!sap)=0A=
    return;=0A=
  if (!null_sdp)=0A=
  {=0A=
    InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);=0A=
    SetSecurityDescriptorDacl (&sd, TRUE, 0, FALSE);=0A=
    null_sdp =3D &sd;=0A=
  }=0A=
  sap->nLength =3D sizeof *sap;=0A=
  sap->lpSecurityDescriptor =3D null_sdp;=0A=
  sap->bInheritHandle =3D TRUE;=0A=
}=0A=
=0A=
int log_message(FILE *stream, int severity, const char *fmt, ...)=0A=
{=0A=
  char buf[2000];=0A=
  va_list ap;=0A=
  int count;=0A=
=0A=
  if( (debug) || (severity < LOG_DEBUG))=0A=
  {=0A=
    count =3D snprintf (buf, 2000, "(cron-daemon) ");=0A=
    va_start (ap, fmt);=0A=
    count +=3D vsnprintf (buf + count, 2000 - count, fmt, ap);=0A=
    va_end (ap);=0A=
    if(service)=0A=
      syslog(severity, buf);=0A=
    else=0A=
    {=0A=
      snprintf(buf + count, 2000 - count, "\n");=0A=
      fprintf(stream, buf);=0A=
    }=0A=
  }=0A=
  return 0;=0A=
}=0A=
=0A=

------=_NextPart_000_0012_01C0D799.8A49BBB0
Content-Type: application/octet-stream;
	name="service.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="service.h"

#ifndef __SERVICE_H
#define __SERVICE_H

int install_reg_entries(const char *myname, int verbose);
int install_as_service(const char *myname, int verbose);
int remove_as_service(const char *myname, int verbose);
int start_service(void);
char * getVersion(char * buf, int len);

// show active service state 
extern int service;


#endif 

------=_NextPart_000_0012_01C0D799.8A49BBB0
Content-Type: application/octet-stream;
	name="Makefile"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="Makefile"

# Makefile - Makefile for cron=0A=
# Copyright (C) 1998 Corinna Vinschen <corinna DOT vinschen AT cityweb DOT de>=0A=
#=0A=
# This program is free software; you can redistribute it and/or modify=0A=
# it under the terms of the GNU General Public License as published by=0A=
# the Free Software Foundation; either version 2, or (at your option)=0A=
# any later version.=0A=
#=0A=
# This program is distributed in the hope that it will be useful,=0A=
# but WITHOUT ANY WARRANTY; without even the implied warranty of=0A=
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the=0A=
# GNU General Public License for more details.=0A=
#=0A=
# You should have received a copy of the GNU General Public License=0A=
# along with this program; if not, write to the Free Software=0A=
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=0A=
#=0A=
=0A=
#=0A=
# current version of cron=0A=
#=0A=
VERSION=3D    2.9.3=0A=
=0A=
#=0A=
# The following three settings affect the local security.=0A=
# Change them, to use your own local settings and don't tell about!=0A=
#=0A=
# PWBUFSIZ should be greater than 128.=0A=
#=0A=
# PWBUFPOS should be greater than 32 and less than PWBUFPOS - 32.=0A=
#=0A=
# PWKEYWORD should be exactly 8 characters long.=0A=
#=0A=
# PWBUFSIZ=3D   -DPWBUFSIZ=3D500=0A=
# PWBUFPOS=3D   -DPWBUFPOS=3D238=0A=
# PWKEYWORD=3D  -DPWKEYWORD=3D"\"\340\221\172\033\061\105\304\057\""=0A=
#=0A=
PWBUFSIZ=3D   -DPWBUFSIZ=3D512=0A=
PWBUFPOS=3D   -DPWBUFPOS=3D256=0A=
PWKEYWORD=3D  -DPWKEYWORD=3D"\"\107\056\114\273\146\231\033\266\""=0A=
=0A=
EXEEXT=3D.exe=0A=
#EXEEXT=3D=0A=
=0A=
PREFIX=3D/usr/local=0A=
BINDIR=3D$(PREFIX)/bin=0A=
SBINDIR=3D$(PREFIX)/sbin=0A=
MANDIR=3D$(PREFIX)/man=0A=
=0A=
CRONDIR=3D/var/cron=0A=
=0A=
CXX=3Dg++ -g=0A=
CXXFLAGS=3D$(PWBUFSIZ) $(PWBUFPOS) $(PWKEYWORD)=0A=
LDFLAGS=3D =0A=
LIBS=3D-L/usr/lib -lpopt=0A=
=0A=
COMMONOBJ=3D  CString.o \=0A=
	    cronPW.o \=0A=
	    crontab.o \=0A=
	    crontabList.o \=0A=
	    entry.o=0A=
=0A=
CRYPTOBJ=3D   encrypt.o=0A=
=0A=
CRONOBJ=3D    cron.o service.o $(COMMONOBJ) $(CRYPTOBJ)=0A=
=0A=
CRONTABOBJ=3D crontabApp.o $(COMMONOBJ) $(CRYPTOBJ)=0A=
=0A=
all: cron$(EXEEXT) crontab$(EXEEXT)=0A=
=0A=
cron$(EXEEXT): $(CRONOBJ)=0A=
	$(CXX) $(LDFLAGS) -o $@ $(CRONOBJ) $(LIBS)=0A=
=0A=
crontab$(EXEEXT): $(CRONTABOBJ)=0A=
	$(CXX) $(LDFLAGS) -o $@ $(CRONTABOBJ) -lnetapi32=0A=
=0A=
cronPW.o: encrypt.h=0A=
cron.o crontabList.o: crontabList.h=0A=
cron.o crontabApp.o crontabList.o cronPW.o: cronPW.h=0A=
cron.o crontabApp.o crontabList.o crontab.o: crontab.h=0A=
cron.o crontabApp.o crontabList.o crontab.o entry.o: entry.h=0A=
cron.o crontabApp.o $(COMMONOBJ): CString.h Makefile=0A=
=0A=
install: $(SBINDIR)/cron.exe $(BINDIR)/crontab$(EXEEXT)=0A=
	if [ ! -d $(CRONDIR) ]; then \=0A=
		mkdir $(CRONDIR); \=0A=
		cp deny $(CRONDIR); \=0A=
	fi=0A=
=0A=
#Don't use $(EXEEXT) here!=0A=
$(SBINDIR)/cron.exe: cron$(EXEEXT)=0A=
	cp cron$(EXEEXT) $@=0A=
	-chmod 775 $@=0A=
=0A=
$(BINDIR)/crontab$(EXEEXT): crontab$(EXEEXT)=0A=
	cp crontab$(EXEEXT) $@=0A=
	-chmod 775 $@=0A=
=0A=
docinstall: $(MANDIR)/man1/crontab.1 $(MANDIR)/man5/crontab.5 =
$(MANDIR)/man8/cron.8=0A=
=0A=
$(MANDIR)/man1/crontab.1:=0A=
	cp crontab.1 $(MANDIR)/man1=0A=
	-chmod 664 $@=0A=
=0A=
$(MANDIR)/man5/crontab.5:=0A=
	cp crontab.5 $(MANDIR)/man5=0A=
	-chmod 664 $@=0A=
=0A=
$(MANDIR)/man8/cron.8:=0A=
	cp cron.8 $(MANDIR)/man8=0A=
	-chmod 664 $@=0A=
=0A=
clean:=0A=
	-rm -f cron$(EXEEXT) crontab$(EXEEXT) *.o *.core *.tar.gz=0A=
=0A=
archive: cron-$(VERSION).src.tar.gz cron-$(VERSION).bin.tar.gz=0A=
=0A=
ARCLIST=3D	cron-$(VERSION)/COPYING \=0A=
		cron-$(VERSION)/ChangeLog \=0A=
		cron-$(VERSION)/Makefile \=0A=
		cron-$(VERSION)/*.c \=0A=
		cron-$(VERSION)/*.cc \=0A=
		cron-$(VERSION)/*.h \=0A=
		cron-$(VERSION)/*.[158] \=0A=
		cron-$(VERSION)/deny=0A=
=0A=
cron-$(VERSION).src.tar.gz: Makefile=0A=
	cd ..; tar czf cron-$(VERSION)/$@ $(ARCLIST)=0A=
=0A=
BINARCLIST=3D	$(SBINDIR)/cron$(EXEEXT) \=0A=
		$(BINDIR)/crontab$(EXEEXT) \=0A=
		$(MANDIR)/man1/crontab.1 \=0A=
		$(MANDIR)/man5/crontab.5 \=0A=
		$(MANDIR)/man8/cron.8 \=0A=
		$(CRONDIR)/deny=0A=
=0A=
cron-$(VERSION).bin.tar.gz: Makefile $(BINARCLIST)=0A=
	tar czf $@ $(BINARCLIST)=0A=
=0A=

------=_NextPart_000_0012_01C0D799.8A49BBB0
Content-Type: application/octet-stream;
	name="cron.cc"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="cron.cc"

/* cron.cc - main source of cron(8)=0A=
   Copyright (C) 1998 Corinna Vinschen <corinna DOT vinschen AT cityweb DOT de>=0A=
=0A=
   This program is free software; you can redistribute it and/or modify=0A=
   it under the terms of the GNU General Public License as published by=0A=
   the Free Software Foundation; either version 2, or (at your option)=0A=
   any later version.=0A=
=0A=
   This program is distributed in the hope that it will be useful,=0A=
   but WITHOUT ANY WARRANTY; without even the implied warranty of=0A=
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the=0A=
   GNU General Public License for more details.=0A=
=0A=
   You should have received a copy of the GNU General Public License=0A=
   along with this program; if not, write to the Free Software=0A=
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */=0A=
=0A=
#include <sys/types.h>=0A=
#include <sys/stat.h>=0A=
#include <sys/wait.h>=0A=
#include <sys/cygwin.h>=0A=
#include <io.h>=0A=
#include <stdio.h>=0A=
#include <stdlib.h>=0A=
#include <unistd.h>=0A=
#include <getopt.h>=0A=
#include <time.h>=0A=
#include <fcntl.h>=0A=
#include <process.h>=0A=
#include <windows.h>=0A=
#include <popt.h>=0A=
=0A=
#include "CString.h"=0A=
#include "entry.h"=0A=
#include "crontab.h"=0A=
#include "cronPW.h"=0A=
#include "crontabList.h"=0A=
#include "service.h"=0A=
=0A=
int work_main(void);=0A=
static void usage(poptContext optCon, FILE * f, char * name);=0A=
static void help(poptContext optCon, FILE * f, char * name);=0A=
static void version(poptContext optCon, FILE * f, char * name);=0A=
static void license(poptContext optCon, FILE * f, char * name);=0A=
=0A=
=0A=
LPSECURITY_ATTRIBUTES=0A=
NullSA (void)=0A=
{=0A=
  static PSECURITY_DESCRIPTOR pSDNull;=0A=
  static SECURITY_ATTRIBUTES SA;=0A=
=0A=
  if (!pSDNull)=0A=
    {=0A=
      pSDNull =3D (PSECURITY_DESCRIPTOR)=0A=
	LocalAlloc (LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);=0A=
      InitializeSecurityDescriptor (pSDNull, =
SECURITY_DESCRIPTOR_REVISION);=0A=
      SetSecurityDescriptorDacl (pSDNull, TRUE, NULL, FALSE);=0A=
    }=0A=
  SA.nLength =3D sizeof SA;=0A=
  SA.lpSecurityDescriptor =3D pSDNull;	// Security-Descriptor mit leerer =
ACL=0A=
=0A=
  SA.bInheritHandle =3D TRUE;	// Handle wird an Subprozesse vererbt=0A=
=0A=
  return &SA;=0A=
}=0A=
=0A=
int=0A=
OpenCronFile (const char *name)=0A=
{=0A=
  return open(name, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR);=0A=
}=0A=
=0A=
bool=0A=
WriteTimeStamp (int fdOut, int fdErr)=0A=
{=0A=
  time_t t =3D time ((time_t *) 0);=0A=
=0A=
  write (fdOut, "\ncron: ", 7);=0A=
  write (fdOut, ctime (&t), 25);=0A=
  write (fdErr, "\ncron: ", 7);=0A=
  write (fdErr, ctime (&t), 25);=0A=
  return false;=0A=
}=0A=
=0A=
void=0A=
WriteError (int fdErr, const char *text)=0A=
{=0A=
  DWORD written;=0A=
  CString ErrTxt;=0A=
=0A=
  ErrTxt.Format ("%d\n", GetLastError ());=0A=
  write (fdErr, text, strlen (text));=0A=
  write (fdErr, ": ", 2);=0A=
  write (fdErr, ErrTxt, ErrTxt.GetLength ());=0A=
}=0A=
=0A=
LPSTARTUPINFO=0A=
StartupInfo (int fdOut, int fdErr)=0A=
{=0A=
  static STARTUPINFO si;=0A=
=0A=
  memset (&si, 0, sizeof si);=0A=
  si.cb =3D sizeof si;=0A=
  si.dwFlags =3D STARTF_FORCEOFFFEEDBACK | STARTF_USESTDHANDLES;=0A=
  si.hStdOutput =3D (HANDLE) get_osfhandle (fdOut);=0A=
  si.hStdError =3D (HANDLE) get_osfhandle (fdErr);=0A=
  return &si;=0A=
}=0A=
=0A=
time_t=0A=
WaitForNextMinute (time_t last)=0A=
{=0A=
  time_t now;=0A=
=0A=
  do=0A=
    {=0A=
      Sleep (5000L);=0A=
      now =3D time ((time_t *) 0);=0A=
    }=0A=
  while (now / 60 =3D=3D last / 60);=0A=
  return now;=0A=
}=0A=
=0A=
HANDLE=0A=
CreateUserToken (const char *user, const char *pwd, int fdOut, int fdErr)=0A=
{=0A=
  HANDLE hUser;=0A=
=0A=
  if (! LogonUser ((char *) user, ".", (char *) pwd,=0A=
		   LOGON32_LOGON_INTERACTIVE,=0A=
		   LOGON32_PROVIDER_DEFAULT,=0A=
		   &hUser))=0A=
    {=0A=
      WriteTimeStamp (fdOut, fdErr);=0A=
      WriteError (fdErr, "LogonUser");=0A=
      return NULL;=0A=
    }=0A=
  return hUser;=0A=
}=0A=
=0A=
=0A=
int=0A=
main (int argc, char **argv)=0A=
{=0A=
  char *myname;=0A=
  int install =3D 0, remove =3D 0;=0A=
  poptContext optCon;=0A=
  const char ** rest;=0A=
  int rc;=0A=
	int ec =3D 0;=0A=
  int c;=0A=
  int debug =3D 0;=0A=
  =0A=
  struct poptOption generalOptionsTable[] =3D {=0A=
    { "quiet",  'q',  POPT_ARG_NONE, NULL, 'q', \=0A=
        "Quiet mode", NULL},=0A=
    { "install-as-service", '\0', POPT_ARG_NONE, NULL, 'I', \=0A=
        "Install as a windows service.", NULL},=0A=
    { "remove-as-service", '\0', POPT_ARG_NONE, NULL, 'R', \=0A=
        "Remove as a windows service.", NULL},=0A=
    { "service", 's', POPT_ARG_NONE, NULL, 's', \=0A=
        "Run in service mode.  This option should ONLY be used "=0A=
        "from within the NT/2K service manager system; do NOT "=0A=
        "use it in ANY other context -- shortcuts, .bat files, "=0A=
        "manual command-line entry, etc.", NULL},=0A=
    { "debug", 'd', POPT_ARG_NONE, NULL, 'd', \=0A=
        "Print execution trace to stderr (or syslog if in service =
mode)", NULL},=0A=
    { NULL, '\0', 0, NULL, 0, NULL, NULL }=0A=
  };=0A=
=0A=
  struct poptOption helpOptionsTable[] =3D {=0A=
    { "help",  '?',  POPT_ARG_NONE, NULL, '?', \=0A=
        "Show this help message", NULL},=0A=
    { "usage", '\0', POPT_ARG_NONE, NULL, 'u', \=0A=
        "Display brief usage message", NULL},=0A=
    { "version", '\0', POPT_ARG_NONE, NULL, 'v', \=0A=
        "Display version information", NULL},=0A=
    { "license", '\0', POPT_ARG_NONE, NULL, 'l', \=0A=
        "Display licensing information", NULL},=0A=
    { NULL, '\0', 0, NULL, 0, NULL, NULL }=0A=
  };=0A=
=0A=
  struct poptOption opt[] =3D {=0A=
    { NULL, '\0', POPT_ARG_INCLUDE_TABLE, generalOptionsTable, 0, \=0A=
        "General options", NULL },=0A=
    { NULL, '\0', POPT_ARG_INCLUDE_TABLE, helpOptionsTable, 0, \=0A=
        "Help options", NULL },=0A=
    { NULL, '\0', 0, NULL, 0, NULL, NULL }=0A=
  };=0A=
=0A=
  if (! (myname =3D strrchr (argv[0], '\\'))=0A=
      && ! (myname =3D strrchr (argv[0], '/')))=0A=
    myname =3D argv[0];=0A=
  else=0A=
    ++myname;=0A=
  if (strrchr (myname, '.'))=0A=
    *strrchr (myname, '.') =3D '\0';=0A=
=0A=
  optCon =3D poptGetContext(NULL, argc, (const char **) argv, opt, 0);=0A=
=0A=
  while ((rc =3D poptGetNextOpt(optCon)) > 0) {=0A=
    switch (rc) {=0A=
      case '?': help(optCon, stderr, myname);=0A=
                goto exit;=0A=
=0A=
      case 'v': version(optCon, stderr, myname);=0A=
                goto exit;=0A=
=0A=
      case 'l': license(optCon, stderr, myname);=0A=
                goto exit;=0A=
=0A=
      case 'd':	debug =3D 1;=0A=
        				break;=0A=
=0A=
      case 'I':	install =3D 1;=0A=
        				break;=0A=
       =0A=
      case 'R':	remove =3D 1;=0A=
        				break;=0A=
        =0A=
      case 's':	service =3D 1;=0A=
        				break;=0A=
=0A=
      default:  usage(optCon, stderr, myname);=0A=
                goto exit;=0A=
  	}=0A=
	}=0A=
/*=0A=
  if (rc < -1 ) {=0A=
    log_message(stderr, LOG_ERR, "%s: bad argument %s: %s",myname, =
poptBadOption(optCon, POPT_BADOPTION_NOALIAS),=0A=
      poptStrerror(rc));=0A=
    ec =3D 2;=0A=
    goto exit;=0A=
  }=0A=
=0A=
  if (install && remove) {=0A=
    log_message(stderr, LOG_ERR, =0A=
    	"%s: can't specify both --install-as-service and =
--remove-as-service\nNo action taken.", myname);=0A=
    ec =3D 2;=0A=
    goto exit;=0A=
  }=0A=
*/=0A=
  rest =3D poptGetArgs(optCon);=0A=
  if (rest) {=0A=
    char buf[2000];=0A=
    int count;=0A=
    count =3D snprintf(buf, 2000, "Extra args ignored: ");=0A=
    while (*rest) =0A=
      count +=3D snprintf(buf + count, 2000 - count, "%s ", *rest++);=0A=
//    log_message(stderr, LOG_WARNING, buf);=0A=
  }=0A=
=0A=
  if (!debug && !install && !remove && !service)=0A=
    switch (pid_t pid =3D fork ())=0A=
      {=0A=
      	case -1:=0A=
						perror (myname);=0A=
						return 1;=0A=
      	case 0:=0A=
						close (0);=0A=
						close (1);=0A=
						close (2);=0A=
						setsid ();=0A=
						break;=0A=
=0A=
      	default:=0A=
						return 0;=0A=
  	}=0A=
	  =0A=
  // install cron as service =0A=
  if (install) {=0A=
    ec =3D install_as_service(myname, 1);=0A=
    if (!ec) {=0A=
      ec =3D install_reg_entries(myname, 1);=0A=
      if (ec)=0A=
        remove_as_service(myname, 1);=0A=
    }=0A=
  }=0A=
  else if (remove) {=0A=
    ec =3D remove_as_service(myname, 1);=0A=
  }=0A=
  else if (service) {=0A=
  	ec =3D start_service();=0A=
  }=0A=
  else {=0A=
    work_main();=0A=
    ec =3D 0;=0A=
  }=0A=
exit:=0A=
  poptFreeContext(optCon);=0A=
//  closelog();=0A=
  return(ec);=0A=
}=0A=
=0A=
=0A=
int work_main(void)=0A=
{=0A=
  CCrontabList cronlist;=0A=
  time_t now =3D time ((time_t *) 0);=0A=
=0A=
  while (now =3D WaitForNextMinute (now))=0A=
    {=0A=
      CLEntry *listentry;=0A=
      CEntry *entry;=0A=
=0A=
      cronlist.Open (GetCronDir ());=0A=
      while (listentry =3D cronlist.NextEntry ())=0A=
	{=0A=
	  bool first =3D true;=0A=
=0A=
	  int fdStdOut =3D OpenCronFile (listentry->home + "/.cron.stdout");=0A=
	  int fdStdErr =3D OpenCronFile (listentry->home + "/.cron.stderr");=0A=
=0A=
	  while (entry =3D listentry->crontab.NextEntry ())=0A=
	    if (*entry =3D=3D now)=0A=
              {=0A=
		if (first)=0A=
		  first =3D WriteTimeStamp (fdStdOut, fdStdErr);=0A=
		switch (pid_t pid =3D fork ())=0A=
		  {=0A=
		  case -1: // Error=0A=
		    WriteError (fdStdErr, "error invoking fork");=0A=
		    break;=0A=
=0A=
		  case 0: // Child=0A=
	            HANDLE hUser;=0A=
		    int pd[2];=0A=
=0A=
		    putenv ("LOGNAME=3D" + listentry->user);=0A=
		    putenv ("USER=3D" + listentry->user);=0A=
		    putenv ("HOME=3D" + listentry->home);=0A=
		    putenv (CString ("PATH=3D/bin:/usr/bin:") + getenv ("PATH"));=0A=
		    putenv ("SHELL=3D/bin/sh");=0A=
=0A=
		    if (pipe (pd))=0A=
		      {=0A=
			WriteError (fdStdErr, "error creating pipe");=0A=
			exit (1);=0A=
		      }=0A=
		    dup2 (fdStdOut, 1);=0A=
		    dup2 (fdStdErr, 2);=0A=
		    chdir (listentry->home);=0A=
		    switch (fork ())=0A=
		      {=0A=
		      case -1: // Error=0A=
			WriteError (fdStdErr, "error invoking fork (2)");=0A=
			break;=0A=
=0A=
		      case 0: // Child=0A=
			dup2 (pd[0], 0);=0A=
			close (pd[0]);=0A=
			close (pd[1]);=0A=
=0A=
			if ((hUser =3D CreateUserToken (listentry->user,=0A=
					 cronlist.GetPwd (listentry->user),=0A=
					 fdStdOut,=0A=
					 fdStdErr)) =3D=3D INVALID_HANDLE_VALUE)=0A=
			  exit (1);=0A=
			sexecl (hUser,=0A=
				"/bin/sh", "sh",=0A=
				"-c", entry->cmd.c_str (),=0A=
				NULL);=0A=
			WriteError (fdStdErr, "error invoking sexecl");=0A=
			break;=0A=
=0A=
		      default: // Parent=0A=
			close (pd[0]);=0A=
			if (! entry->std_in.IsEmpty ())=0A=
			  write (pd[1], entry->std_in,=0A=
				        entry->std_in.GetLength ());=0A=
			close (pd[1]);=0A=
			break;=0A=
		      }=0A=
		    return 0;=0A=
		    /*NOTREACHED*/=0A=
=0A=
		  default: // Parent=0A=
		    waitpid (pid, NULL, 0);=0A=
		    break;=0A=
		  }=0A=
	      }=0A=
	  close (fdStdOut);=0A=
	  close (fdStdErr);=0A=
	}=0A=
=0A=
    }=0A=
  cronlist.Close ();=0A=
}=0A=
=0A=
static void printTopDescription(FILE * f, char * name)=0A=
{=0A=
  char buf[512];=0A=
  fprintf(f, "%s : Cygwin Cron Version %s (c) 1998 PCH/LLA, Changes (c) =
2001 CSW\n", =0A=
          name, getVersion(buf, 512));=0A=
  fprintf(f, "  This daemon provides crontab functionality.\n\n");=0A=
}=0A=
=0A=
static void printLicense(FILE * f, char * name)=0A=
{=0A=
  fprintf(f, "This program is free software; you can redistribute it =
and/or\n");=0A=
  fprintf(f, "modify it under the terms of the GNU General Public =
License\n");=0A=
  fprintf(f, "as published by the Free Software Foundation; either =
version 2\n");=0A=
  fprintf(f, "of the License, or (at your option) any later version.\n");=0A=
  fprintf(f, "\n");=0A=
  fprintf(f, "This program is distributed in the hope that it will be =
useful,\n");=0A=
  fprintf(f, "but WITHOUT ANY WARRANTY; without even the implied =
warranty of\n");=0A=
  fprintf(f, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See =
the\n");=0A=
  fprintf(f, "GNU General Public License for more details.\n");=0A=
  fprintf(f, "\n");=0A=
  fprintf(f, "You should have received a copy of the GNU General Public =
License\n");=0A=
  fprintf(f, "along with this program; if not, write to the Free =
Software\n");=0A=
  fprintf(f, "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  =
02111-1307, USA.\n");=0A=
  fprintf(f, "\n");=0A=
  fprintf(f, "See the COPYING file for license information.\n");=0A=
}=0A=
=0A=
static void usage(poptContext optCon, FILE * f, char * name)=0A=
{=0A=
  poptPrintUsage(optCon, f, 0);=0A=
}=0A=
=0A=
static void help(poptContext optCon, FILE * f, char * name)=0A=
{=0A=
  printTopDescription(f, name);=0A=
  poptPrintHelp(optCon, f, 0);=0A=
}=0A=
=0A=
static void version(poptContext optCon, FILE * f, char * name)=0A=
{=0A=
  printTopDescription(f, name);=0A=
}=0A=
=0A=
static void license(poptContext optCon, FILE * f, char * name)=0A=
{=0A=
  printTopDescription(f, name);=0A=
  printLicense(f, name);=0A=
}=0A=


------=_NextPart_000_0012_01C0D799.8A49BBB0
Content-Type: text/plain; charset=us-ascii

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

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019