Changes On Branch tsbg-win-service
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch tsbg-win-service Excluding Merge-Ins

This is equivalent to a diff from e14f0fe2f3 to 4bf9048e25

2011-07-18
20:04
Merge the windows-service command into trunk. check-in: 06e9ca23e7 user: drh tags: trunk
20:00
Update the MSVC makefile so that it works with the new "service" command. Closed-Leaf check-in: 4bf9048e25 user: drh tags: tsbg-win-service
2011-07-15
18:06
Enable Fossil to run as a Windows service and add a "fossil service" command. check-in: 91c2f65a6e user: tsbg tags: tsbg-win-service
04:50
Add file local.tcl to the autosetup directory. Containing a configuration option to prevent autosetup from using a pager. check-in: e14f0fe2f3 user: dan tags: trunk
2011-07-14
15:28
Add a commentary section to the check-in checklist. Also add the "verify makefiles" item. /doc/trunk/www/checkin.wiki check-in: 39624620ac user: drh tags: trunk

Changes to src/main.c.

  1436   1436   #else
  1437   1437     /* Win32 implementation */
  1438   1438     if( isUiCmd ){
  1439   1439       zBrowser = db_get("web-browser", "start");
  1440   1440       zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser);
  1441   1441     }
  1442   1442     db_close(1);
  1443         -  win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags);
         1443  +  if( win32_http_service(iPort, zNotFound, flags) ){
         1444  +    win32_http_server(iPort, mxPort, zBrowserCmd,
         1445  +                      zStopperFile, zNotFound, flags);
         1446  +  }
  1444   1447   #endif
  1445   1448   }
  1446   1449   
  1447   1450   /*
  1448   1451   ** COMMAND:  test-echo
  1449   1452   **
  1450   1453   ** Echo all command-line arguments (enclosed in [...]) to the screen so that

Changes to src/makemake.tcl.

   651    651   ZLIB    = zlib.lib
   652    652   
   653    653   INCL   = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
   654    654   
   655    655   CFLAGS = -nologo -MT -O2
   656    656   BCC    = $(CC) $(CFLAGS)
   657    657   TCC    = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
   658         -LIBS   = $(ZLIB) ws2_32.lib $(SSLLIB)
          658  +LIBS   = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
   659    659   LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
   660    660   }
   661    661   regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS
   662    662   writeln "SQLITE_OPTIONS = $MSC_SQLITE_OPTIONS\n"
   663    663   writeln -nonewline "SRC   = "
   664    664   foreach s [lsort $src] {
   665    665     writeln -nonewline "${s}_.c "

Changes to src/winhttp.c.

    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
    15     15   **
    16     16   *******************************************************************************
    17     17   **
    18     18   ** This file implements a very simple (and low-performance) HTTP server
    19         -** for windows.
           19  +** for windows. It also implements a Windows Service which allows the HTTP
           20  +** server to be run without any user logged on.
    20     21   */
    21     22   #include "config.h"
    22     23   #ifdef _WIN32
    23     24   /* This code is for win32 only */
    24         -#include "winhttp.h"
    25     25   #include <windows.h>
           26  +#include "winhttp.h"
    26     27   
    27     28   /*
    28     29   ** The HttpRequest structure holds information about each incoming
    29     30   ** HTTP request.
    30     31   */
    31     32   typedef struct HttpRequest HttpRequest;
    32     33   struct HttpRequest {
................................................................................
    58     59     }
    59     60     return 0;
    60     61   }
    61     62   
    62     63   /*
    63     64   ** Process a single incoming HTTP request.
    64     65   */
    65         -void win32_process_one_http_request(void *pAppData){
           66  +static void win32_process_one_http_request(void *pAppData){
    66     67     HttpRequest *p = (HttpRequest*)pAppData;
    67     68     FILE *in = 0, *out = 0;
    68     69     int amt, got;
    69     70     int wanted = 0;
    70     71     char *z;
    71     72     char zRequestFName[100];
    72     73     char zReplyFName[100];
................................................................................
   143    144   ){
   144    145     WSADATA wd;
   145    146     SOCKET s = INVALID_SOCKET;
   146    147     SOCKADDR_IN addr;
   147    148     int idCnt = 0;
   148    149     int iPort = mnPort;
   149    150     Blob options;
          151  +  char zTmpPath[MAX_PATH];
   150    152   
   151    153     if( zStopper ) file_delete(zStopper);
   152    154     blob_zero(&options);
   153    155     if( zNotFound ){
   154    156       blob_appendf(&options, " --notfound %s", zNotFound);
   155    157     }
   156    158     if( g.useLocalauth ){
................................................................................
   187    189       if( mnPort==mxPort ){
   188    190         fossil_fatal("unable to open listening socket on ports %d", mnPort);
   189    191       }else{
   190    192         fossil_fatal("unable to open listening socket on any"
   191    193                      " port in the range %d..%d", mnPort, mxPort);
   192    194       }
   193    195     }
   194         -  zTempPrefix = mprintf("fossil_server_P%d_", iPort);
          196  +  if( !GetTempPath(sizeof(zTmpPath), zTmpPath) ){
          197  +    fossil_fatal("unable to get path to the temporary directory.");
          198  +  }
          199  +  zTempPrefix = mprintf("%sfossil_server_P%d_", zTmpPath, iPort);
   195    200     fossil_print("Listening for HTTP requests on TCP port %d\n", iPort);
   196    201     if( zBrowser ){
   197    202       zBrowser = mprintf(zBrowser, iPort);
   198    203       fossil_print("Launch webbrowser: %s\n", zBrowser);
   199    204       fossil_system(zBrowser);
   200    205     }
   201    206     fossil_print("Type Ctrl-C to stop the HTTP server\n");
          207  +  /* Set the service status to running and pass the listener socket to the
          208  +  ** service handling procedures. */
          209  +  win32_http_service_running(s);
   202    210     for(;;){
   203    211       SOCKET client;
   204    212       SOCKADDR_IN client_addr;
   205    213       HttpRequest *p;
   206    214       int len = sizeof(client_addr);
          215  +    int wsaError;
   207    216   
   208    217       client = accept(s, (struct sockaddr*)&client_addr, &len);
   209         -    if( zStopper && file_size(zStopper)>=0 ){
   210         -      break;
   211         -    }
   212    218       if( client==INVALID_SOCKET ){
   213         -      closesocket(s);
   214         -      fossil_fatal("error from accept()");
          219  +      /* If the service control handler has closed the listener socket, 
          220  +      ** cleanup and return, otherwise report a fatal error. */
          221  +      wsaError =  WSAGetLastError();
          222  +      if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){
          223  +        WSACleanup();
          224  +        return;
          225  +      }else{
          226  +        closesocket(s);
          227  +        WSACleanup();
          228  +        fossil_fatal("error from accept()");
          229  +      }
          230  +    }else if( zStopper && file_size(zStopper)>=0 ){
          231  +      break;
   215    232       }
   216    233       p = fossil_malloc( sizeof(*p) );
   217    234       p->id = ++idCnt;
   218    235       p->s = client;
   219    236       p->addr = client_addr;
   220    237       p->zOptions = blob_str(&options);
   221    238       _beginthread(win32_process_one_http_request, 0, (void*)p);
   222    239     }
   223    240     closesocket(s);
   224    241     WSACleanup();
   225    242   }
          243  +
          244  +/*
          245  +** The HttpService structure is used to pass information to the service main
          246  +** function and to the service control handler function.
          247  +*/
          248  +typedef struct HttpService HttpService;
          249  +struct HttpService {
          250  +  int port;                 /* Port on which the http server should run */
          251  +  const char *zNotFound;    /* The --notfound option, or NULL */
          252  +  int flags;                /* One or more HTTP_SERVER_ flags */
          253  +  int isRunningAsService;   /* Are we running as a service ? */
          254  +  const char *zServiceName; /* Name of the service */
          255  +  SOCKET s;                 /* Socket on which the http server listens */
          256  +};
          257  +
          258  +/*
          259  +** Variables used for running as windows service.
          260  +*/
          261  +static HttpService hsData = {8080, NULL, 0, 0, NULL, INVALID_SOCKET};
          262  +static SERVICE_STATUS ssStatus;
          263  +static SERVICE_STATUS_HANDLE sshStatusHandle;
          264  +
          265  +/*
          266  +** Get message string of the last system error. Return a pointer to the
          267  +** message string. Call fossil_mbcs_free() to deallocate any memory used
          268  +** to store the message string when done.
          269  +*/
          270  +static char *win32_get_last_errmsg(void){
          271  +  DWORD nMsg;
          272  +  LPTSTR tmp = NULL;
          273  +  char *zMsg = NULL;
          274  +
          275  +  nMsg = FormatMessage(
          276  +           FORMAT_MESSAGE_ALLOCATE_BUFFER |
          277  +           FORMAT_MESSAGE_FROM_SYSTEM     |
          278  +           FORMAT_MESSAGE_IGNORE_INSERTS,
          279  +           NULL,
          280  +           GetLastError(),
          281  +           MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
          282  +           (LPTSTR) &tmp,
          283  +           0,
          284  +           NULL
          285  +         );
          286  +  if( nMsg ){
          287  +    zMsg = fossil_mbcs_to_utf8(tmp);
          288  +  }else{
          289  +    fossil_fatal("unable to get system error message.");
          290  +  }
          291  +  if( tmp ){
          292  +    LocalFree((HLOCAL) tmp);
          293  +  }
          294  +  return zMsg;
          295  +}
          296  +
          297  +/*
          298  +** Report the current status of the service to the service control manager.
          299  +** Make sure that during service startup no control codes are accepted.
          300  +*/
          301  +static void win32_report_service_status(
          302  +  DWORD dwCurrentState,     /* The current state of the service */
          303  +  DWORD dwWin32ExitCode,    /* The error code to report */
          304  +  DWORD dwWaitHint          /* The estimated time for a pending operation */
          305  +){
          306  +  if( dwCurrentState==SERVICE_START_PENDING) {
          307  +    ssStatus.dwControlsAccepted = 0;
          308  +  }else{
          309  +    ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
          310  +  }
          311  +  ssStatus.dwCurrentState = dwCurrentState;
          312  +  ssStatus.dwWin32ExitCode = dwWin32ExitCode;
          313  +  ssStatus.dwWaitHint = dwWaitHint;
          314  +
          315  +  if( (dwCurrentState==SERVICE_RUNNING) || 
          316  +      (dwCurrentState==SERVICE_STOPPED) ){
          317  +    ssStatus.dwCheckPoint = 0;
          318  +  }else{
          319  +    ssStatus.dwCheckPoint++;
          320  +  }
          321  +  SetServiceStatus(sshStatusHandle, &ssStatus);
          322  +  return ;
          323  +}
          324  +
          325  +/*
          326  +** Handle control codes sent from the service control manager.
          327  +** The control dispatcher in the main thread of the service process invokes
          328  +** this function whenever it receives a control request from the service
          329  +** control manager.
          330  +*/
          331  +static void WINAPI win32_http_service_ctrl(
          332  +  DWORD dwCtrlCode
          333  +){
          334  +  switch( dwCtrlCode ){
          335  +    case SERVICE_CONTROL_STOP: {
          336  +      win32_report_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
          337  +      if( hsData.s != INVALID_SOCKET ){
          338  +        closesocket(hsData.s);
          339  +      }
          340  +      win32_report_service_status(ssStatus.dwCurrentState, NO_ERROR, 0);
          341  +      break;
          342  +    }
          343  +    default: {
          344  +      break;
          345  +    }
          346  +  }
          347  +  return;
          348  +}
          349  +
          350  +/*
          351  +** This is the main entry point for the service.
          352  +** When the service control manager receives a request to start the service,
          353  +** it starts the service process (if it is not already running). The main
          354  +** thread of the service process calls the StartServiceCtrlDispatcher
          355  +** function with a pointer to an array of SERVICE_TABLE_ENTRY structures.
          356  +** Then the service control manager sends a start request to the service
          357  +** control dispatcher for this service process. The service control dispatcher
          358  +** creates a new thread to execute the ServiceMain function (this function)
          359  +** of the service being started.
          360  +*/
          361  +static void WINAPI win32_http_service_main(
          362  +  DWORD argc,              /* Number of arguments in argv */
          363  +  LPTSTR *argv             /* Arguments passed */
          364  +){
          365  +
          366  +  /* Update the service information. */
          367  +  hsData.isRunningAsService = 1;
          368  +  if( argc>0 ){
          369  +    hsData.zServiceName = argv[0];
          370  +  }
          371  +
          372  +  /* Register the service control handler function */
          373  +  sshStatusHandle = RegisterServiceCtrlHandler("", win32_http_service_ctrl);
          374  +  if( !sshStatusHandle ){
          375  +    win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0);
          376  +    return;
          377  +  }
          378  +
          379  +  /* Set service specific data and report that the service is starting. */
          380  +  ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
          381  +  ssStatus.dwServiceSpecificExitCode = 0;
          382  +  win32_report_service_status(SERVICE_START_PENDING, NO_ERROR, 3000);
          383  +
          384  +   /* Execute the http server */
          385  +  win32_http_server(hsData.port, hsData.port,
          386  +                    NULL, NULL, hsData.zNotFound, hsData.flags);
          387  +
          388  +  /* Service has stopped now. */
          389  +  win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0);
          390  +  return;
          391  +}
          392  +
          393  +/*
          394  +** When running as service, update the HttpService structure with the
          395  +** listener socket and update the service status. This procedure must be
          396  +** called from the http server when he is ready to accept connections.
          397  +*/
          398  +LOCAL void win32_http_service_running(SOCKET s){
          399  +  if( hsData.isRunningAsService ){
          400  +    hsData.s = s;
          401  +    win32_report_service_status(SERVICE_RUNNING, NO_ERROR, 0);
          402  +  }
          403  +}
          404  +
          405  +/*
          406  +** Try to start the http server as a windows service. If we are running in
          407  +** a interactive console session, this routine fails and returns a non zero
          408  +** integer value. When running as service, this routine does not return until
          409  +** the service is stopped. In this case, the return value is zero.
          410  +*/
          411  +int win32_http_service(
          412  +  int nPort,                /* TCP port number */
          413  +  const char *zNotFound,    /* The --notfound option, or NULL */
          414  +  int flags                 /* One or more HTTP_SERVER_ flags */
          415  +){
          416  +  /* Define the service table. */
          417  +  SERVICE_TABLE_ENTRY ServiceTable[] =
          418  +    {{"", (LPSERVICE_MAIN_FUNCTION)win32_http_service_main}, {NULL, NULL}};
          419  +  
          420  +  /* Initialize the HttpService structure. */
          421  +  hsData.port = nPort;
          422  +  hsData.zNotFound = zNotFound;
          423  +  hsData.flags = flags;
          424  +
          425  +  /* Try to start the control dispatcher thread for the service. */
          426  +  if( !StartServiceCtrlDispatcher(ServiceTable) ){
          427  +    if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){
          428  +      return 1;
          429  +    }else{
          430  +      fossil_fatal("error from StartServiceCtrlDispatcher()");
          431  +    }
          432  +  }
          433  +  return 0;
          434  +}
          435  +
          436  +/*
          437  +** COMMAND: service
          438  +**
          439  +** Usage: fossil service METHOD ?SERVICE-NAME? ?OPTIONS?
          440  +**
          441  +** Where METHOD is one of: create delete show start stop.
          442  +**
          443  +** The service command can be used to create and control instances of Fossil
          444  +** which are are running as Windows services. No user needs to be logged on
          445  +** when running Fossil as a service. In the following description of the
          446  +** methods, "Fossil-DSCM" will be used as the default SERVICE-NAME, if no
          447  +** SERVICE-NAME is specified.
          448  +** 
          449  +**    fossil service create ?SERVICE-NAME? ?OPTIONS?
          450  +**
          451  +**         Creates a service. The following service specific options are
          452  +**         available:
          453  +**
          454  +**         -D|--display DISPLAY-NAME
          455  +**
          456  +**              Sets the display name of the service. This name will be showed
          457  +**              by graphical interface programs. By default, the display name
          458  +**              equals to the service name.
          459  +**
          460  +**         -S|--start TYPE
          461  +**
          462  +**              Sets the start type of the service. TYPE can be "manual",
          463  +**              which means you need to start the service yourself with the
          464  +**              'fossil service start' command or with the "net start" command
          465  +**              from the operating system. If TYPE is set to "auto", the service
          466  +**              will be started automatically by the system during startup.
          467  +**
          468  +**         -U|--username USERNAME
          469  +**
          470  +**              Specifies the user account which will be used to run the
          471  +**              service. The account needs the "Logon as a service" right
          472  +**              enabled in its profile. Specify local accounts as follows:
          473  +**              ".\\USERNAME". By default, the "LocalSystem" account will be
          474  +**              used.
          475  +**
          476  +**         -W|--password PASSWORD
          477  +**
          478  +**              Password for the user account.
          479  +**
          480  +**         The following options are more or less the same as for the "server"
          481  +**         command and influence the behaviour of the http server:
          482  +**
          483  +**         -p|--port TCPPORT
          484  +**
          485  +**              Specifies the TCP port (default port is 8080) on which the
          486  +**              server should listen.
          487  +**
          488  +**         -R|--repository REPOSITORY
          489  +**
          490  +**              Specifies the name of the repository to be served.
          491  +**              The repository option may be omitted if the working directory
          492  +**              is within an open checkout.
          493  +**              The REPOSITORY can be a directory (aka folder) that contains
          494  +**              one or more respositories with names ending in ".fossil".
          495  +**              In that case, the first element of the URL is used to select
          496  +**              among the various repositories.
          497  +**
          498  +**         --notfound URL
          499  +**
          500  +**              If REPOSITORY is a directory that contains one or more
          501  +**              respositories with names of the form "*.fossil" then the
          502  +**              first element of the URL  pathname selects among the various
          503  +**              repositories. If the pathname does not select a valid
          504  +**              repository and the --notfound option is available,
          505  +**              then the server redirects (HTTP code 302) to the URL of
          506  +**              --notfound.
          507  +**
          508  +**         --localauth
          509  +**
          510  +**              Enables automatic login if the --localauth option is present
          511  +**              and the "localauth" setting is off and the connection is from
          512  +**              localhost.
          513  +**
          514  +**
          515  +**    fossil service delete ?SERVICE-NAME?
          516  +**
          517  +**         Deletes a service. If the service is currently running, it will be
          518  +**         stopped first and then deleted.
          519  +**
          520  +**
          521  +**    fossil service show ?SERVICE-NAME?
          522  +**
          523  +**         Shows how the service is configured and its current state.
          524  +**
          525  +**
          526  +**    fossil service start ?SERVICE-NAME?
          527  +**
          528  +**         Start the service.
          529  +**
          530  +**
          531  +**    fossil service stop ?SERVICE-NAME?
          532  +**
          533  +**         Stop the service.
          534  +**
          535  +**
          536  +** NOTE: This command is available on Windows operating systems only and
          537  +**       requires administrative rights on the machine executed.
          538  +**
          539  +*/
          540  +void cmd_win32_service(void){
          541  +  int n;
          542  +  const char *zMethod;
          543  +  const char *zSvcName = "Fossil-DSCM";    /* Default service name */
          544  +
          545  +  if( g.argc<3 ){
          546  +    usage("create|delete|show|start|stop ...");
          547  +  }
          548  +  zMethod = g.argv[2];
          549  +  n = strlen(zMethod);
          550  +  if( g.argc==4 ){
          551  +    zSvcName = g.argv[3];
          552  +  }
          553  +
          554  +  if( strncmp(zMethod, "create", n)==0 ){
          555  +    SC_HANDLE hScm;
          556  +    SC_HANDLE hSvc;
          557  +    SERVICE_DESCRIPTION
          558  +      svcDescr = {"Fossil - Distributed Software Configuration Management"};
          559  +    char *zErrFmt = "unable to create service '%s': %s";
          560  +    DWORD dwStartType = SERVICE_DEMAND_START;
          561  +    const char *zDisplay;
          562  +    const char *zStart;
          563  +    const char *zUsername;
          564  +    const char *zPassword;
          565  +    const char *zPort;
          566  +    const char *zNotFound;
          567  +    const char *zLocalAuth;
          568  +    const char *zRepository;
          569  +    Blob binPath;
          570  +
          571  +    /* Process service creation specific options. */
          572  +    zDisplay = find_option("display", "D", 1);
          573  +    if( !zDisplay ){
          574  +      zDisplay = zSvcName;
          575  +    }
          576  +    zStart = find_option("start", "S", 1);
          577  +    if( zStart ){
          578  +      if( strncmp(zStart, "auto", strlen(zStart))==0 ){
          579  +        dwStartType = SERVICE_AUTO_START;
          580  +      }else  if( strncmp(zStart, "manual", strlen(zStart))==0 ){
          581  +        dwStartType = SERVICE_DEMAND_START;
          582  +      }else{
          583  +        fossil_fatal(zErrFmt, zSvcName,
          584  +                     "specify 'auto' or 'manual' for the '-S|--start' option");
          585  +      }
          586  +    }
          587  +    zUsername = find_option("username", "U", 1);
          588  +    zPassword = find_option("password", "W", 1);
          589  +    /* Process options for Fossil running as server. */
          590  +    zPort = find_option("port", "P", 1);
          591  +    if( zPort && (atoi(zPort)<=0) ){
          592  +      fossil_fatal(zErrFmt, zSvcName,
          593  +                   "port number must be in the range 1 - 65535.");
          594  +    }
          595  +    zNotFound = find_option("notfound", 0, 1);
          596  +    zLocalAuth = find_option("localauth", 0, 0);
          597  +    zRepository = find_option("repository", "R", 1);
          598  +    if( !zRepository ){
          599  +      db_must_be_within_tree();
          600  +    }else if( file_isdir(zRepository)==1 ){
          601  +      g.zRepositoryName = mprintf("%s", zRepository);
          602  +      file_simplify_name(g.zRepositoryName, -1);
          603  +    }else{
          604  +      db_open_repository(zRepository);
          605  +    }
          606  +    db_close(0);
          607  +    verify_all_options();
          608  +    if( g.argc>4 ) fossil_fatal("to much arguments for create method.");
          609  +    /* Build the fully-qualified path to the service binary file. */
          610  +    blob_zero(&binPath);
          611  +    blob_appendf(&binPath, "\"%s\" server", fossil_nameofexe());
          612  +    if( zPort ) blob_appendf(&binPath, " --port %s", zPort);
          613  +    if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound);
          614  +    if( zLocalAuth ) blob_append(&binPath, " --localauth", -1);
          615  +    blob_appendf(&binPath, " \"%s\"", g.zRepositoryName);
          616  +    /* Create the service. */
          617  +    hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
          618  +    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          619  +    hSvc = CreateService(
          620  +             hScm,                                    /* Handle to the SCM */
          621  +             fossil_utf8_to_mbcs(zSvcName),           /* Name of the service */
          622  +             fossil_utf8_to_mbcs(zDisplay),           /* Display name */
          623  +             SERVICE_ALL_ACCESS,                      /* Desired access */
          624  +             SERVICE_WIN32_OWN_PROCESS,               /* Service type */
          625  +             dwStartType,                             /* Start type */
          626  +             SERVICE_ERROR_NORMAL,                    /* Error control */
          627  +             fossil_utf8_to_mbcs(blob_str(&binPath)), /* Binary path */
          628  +             NULL,                                    /* Load ordering group */
          629  +             NULL,                                    /* Tag value */
          630  +             NULL,                                    /* Service dependencies */
          631  +             fossil_utf8_to_mbcs(zUsername),          /* Service account */
          632  +             fossil_utf8_to_mbcs(zPassword)           /* Account password */
          633  +           );
          634  +    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          635  +    /* Set the service description. */
          636  +    ChangeServiceConfig2(hSvc, SERVICE_CONFIG_DESCRIPTION, &svcDescr);
          637  +    fossil_print("Service '%s' successfully created.\n", zSvcName);
          638  +    CloseServiceHandle(hSvc);
          639  +    CloseServiceHandle(hScm);
          640  +  }else
          641  +  if( strncmp(zMethod, "delete", n)==0 ){
          642  +    SC_HANDLE hScm;
          643  +    SC_HANDLE hSvc;
          644  +    SERVICE_STATUS sstat;
          645  +    char *zErrFmt = "unable to delete service '%s': %s";
          646  +
          647  +    verify_all_options();
          648  +    if( g.argc>4 ) fossil_fatal("to much arguments for delete method.");
          649  +    hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
          650  +    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          651  +    hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), SERVICE_ALL_ACCESS);
          652  +    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          653  +    QueryServiceStatus(hSvc, &sstat);
          654  +    if( sstat.dwCurrentState!=SERVICE_STOPPED ){
          655  +      fossil_print("Stopping service '%s'", zSvcName);
          656  +      if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
          657  +        if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
          658  +          fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          659  +        }
          660  +      }
          661  +      while( sstat.dwCurrentState!=SERVICE_STOPPED ){
          662  +        Sleep(100);
          663  +        fossil_print(".");
          664  +        QueryServiceStatus(hSvc, &sstat);
          665  +      }
          666  +      fossil_print("\nService '%s' stopped.\n", zSvcName);
          667  +    }
          668  +    if( !DeleteService(hSvc) ){
          669  +      if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){
          670  +        fossil_warning("Service '%s' already marked for delete.\n", zSvcName);
          671  +      }else{
          672  +        fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          673  +      }
          674  +    }else{
          675  +      fossil_print("Service '%s' successfully deleted.\n", zSvcName);
          676  +    }
          677  +    CloseServiceHandle(hSvc);
          678  +    CloseServiceHandle(hScm);
          679  +  }else
          680  +  if( strncmp(zMethod, "show", n)==0 ){
          681  +    SC_HANDLE hScm;
          682  +    SC_HANDLE hSvc;
          683  +    SERVICE_STATUS sstat;
          684  +    LPQUERY_SERVICE_CONFIG pSvcConfig;
          685  +    LPSERVICE_DESCRIPTION pSvcDescr;
          686  +    BOOL bStatus;
          687  +    DWORD nRequired;
          688  +    char *zErrFmt = "unable to show service '%s': %s";
          689  +    static const char *zSvcTypes[] = {
          690  +      "Driver service",
          691  +      "File system driver service",
          692  +      "Service runs in its own process",
          693  +      "Service shares a process with other services",
          694  +      "Service can interact with the desktop"
          695  +    };
          696  +    const char *zSvcType = "";
          697  +    static char *zSvcStartTypes[] = {
          698  +      "Started by the system loader",
          699  +      "Started by the IoInitSystem function",
          700  +      "Started automatically by the service control manager",
          701  +      "Started manually",
          702  +      "Service cannot be started"
          703  +    };
          704  +    const char *zSvcStartType = "";
          705  +    static const char *zSvcStates[] = {
          706  +      "Stopped", "Starting", "Stopping", "Running",
          707  +      "Continue pending", "Pause pending", "Paused"
          708  +    };
          709  +    const char *zSvcState = "";
          710  +
          711  +    verify_all_options();
          712  +    if( g.argc>4 ) fossil_fatal("to much arguments for show method.");
          713  +    hScm = OpenSCManager(NULL, NULL, GENERIC_READ);
          714  +    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          715  +    hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), GENERIC_READ);
          716  +    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          717  +    /* Get the service configuration */
          718  +    bStatus = QueryServiceConfig(hSvc, NULL, 0, &nRequired);
          719  +    if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
          720  +      fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          721  +    }
          722  +    pSvcConfig = fossil_malloc(nRequired);
          723  +    bStatus = QueryServiceConfig(hSvc, pSvcConfig, nRequired, &nRequired);
          724  +    if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          725  +    /* Translate the service type */
          726  +    switch( pSvcConfig->dwServiceType ){
          727  +      case SERVICE_KERNEL_DRIVER:       zSvcType = zSvcTypes[0]; break;
          728  +      case SERVICE_FILE_SYSTEM_DRIVER:  zSvcType = zSvcTypes[1]; break;
          729  +      case SERVICE_WIN32_OWN_PROCESS:   zSvcType = zSvcTypes[2]; break;
          730  +      case SERVICE_WIN32_SHARE_PROCESS: zSvcType = zSvcTypes[3]; break;
          731  +      case SERVICE_INTERACTIVE_PROCESS: zSvcType = zSvcTypes[4]; break;
          732  +    }
          733  +    /* Translate the service start type */
          734  +    switch( pSvcConfig->dwStartType ){
          735  +      case SERVICE_BOOT_START:    zSvcStartType = zSvcStartTypes[0]; break;
          736  +      case SERVICE_SYSTEM_START:  zSvcStartType = zSvcStartTypes[1]; break;
          737  +      case SERVICE_AUTO_START:    zSvcStartType = zSvcStartTypes[2]; break;
          738  +      case SERVICE_DEMAND_START:  zSvcStartType = zSvcStartTypes[3]; break;
          739  +      case SERVICE_DISABLED:      zSvcStartType = zSvcStartTypes[4]; break;
          740  +    }
          741  +    /* Get the service description. */
          742  +    bStatus = QueryServiceConfig2(hSvc, SERVICE_CONFIG_DESCRIPTION,
          743  +                                  NULL, 0, &nRequired);
          744  +    if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
          745  +      fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          746  +    }
          747  +    pSvcDescr = fossil_malloc(nRequired);
          748  +    bStatus = QueryServiceConfig2(hSvc, SERVICE_CONFIG_DESCRIPTION,
          749  +                                  (LPBYTE)pSvcDescr, nRequired, &nRequired);
          750  +    if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          751  +    /* Retrieves the current status of the specified service. */
          752  +    bStatus = QueryServiceStatus(hSvc, &sstat);
          753  +    if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          754  +    /* Translate the current state. */
          755  +    switch( sstat.dwCurrentState ){
          756  +      case SERVICE_STOPPED:          zSvcState = zSvcStates[0]; break;
          757  +      case SERVICE_START_PENDING:    zSvcState = zSvcStates[1]; break;
          758  +      case SERVICE_STOP_PENDING:     zSvcState = zSvcStates[2]; break;
          759  +      case SERVICE_RUNNING:          zSvcState = zSvcStates[3]; break;
          760  +      case SERVICE_CONTINUE_PENDING: zSvcState = zSvcStates[4]; break;
          761  +      case SERVICE_PAUSE_PENDING:    zSvcState = zSvcStates[5]; break;
          762  +      case SERVICE_PAUSED:           zSvcState = zSvcStates[6]; break;
          763  +    }
          764  +    /* Print service information to terminal */
          765  +    fossil_print("Service name .......: %s\n", zSvcName);
          766  +    fossil_print("Display name .......: %s\n",
          767  +                 fossil_mbcs_to_utf8(pSvcConfig->lpDisplayName));
          768  +    fossil_print("Service description : %s\n",
          769  +                 fossil_mbcs_to_utf8(pSvcDescr->lpDescription));
          770  +    fossil_print("Service type .......: %s.\n", zSvcType);
          771  +    fossil_print("Service start type .: %s.\n", zSvcStartType);
          772  +    fossil_print("Binary path name ...: %s\n",
          773  +                 fossil_mbcs_to_utf8(pSvcConfig->lpBinaryPathName));
          774  +    fossil_print("Service username ...: %s\n",
          775  +                 fossil_mbcs_to_utf8(pSvcConfig->lpServiceStartName));
          776  +    fossil_print("Current state ......: %s.\n", zSvcState);
          777  +    /* Cleanup */
          778  +    fossil_free(pSvcConfig);
          779  +    fossil_free(pSvcDescr);
          780  +    CloseServiceHandle(hSvc);
          781  +    CloseServiceHandle(hScm);
          782  +  }else
          783  +  if( strncmp(zMethod, "start", n)==0 ){
          784  +    SC_HANDLE hScm;
          785  +    SC_HANDLE hSvc;
          786  +    SERVICE_STATUS sstat;
          787  +    char *zErrFmt = "unable to start service '%s': %s";
          788  +
          789  +    verify_all_options();
          790  +    if( g.argc>4 ) fossil_fatal("to much arguments for start method.");
          791  +    hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
          792  +    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          793  +    hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), SERVICE_ALL_ACCESS);
          794  +    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          795  +    QueryServiceStatus(hSvc, &sstat);
          796  +    if( sstat.dwCurrentState!=SERVICE_RUNNING ){
          797  +      fossil_print("Starting service '%s'", zSvcName);
          798  +      if( sstat.dwCurrentState!=SERVICE_START_PENDING ){
          799  +        if( !StartService(hSvc, 0, NULL) ){
          800  +          fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          801  +        }
          802  +      }
          803  +      while( sstat.dwCurrentState!=SERVICE_RUNNING ){
          804  +        Sleep(100);
          805  +        fossil_print(".");
          806  +        QueryServiceStatus(hSvc, &sstat);
          807  +      }
          808  +      fossil_print("\nService '%s' started.\n", zSvcName);
          809  +    }else{
          810  +      fossil_print("Service '%s' is already started.\n", zSvcName);
          811  +    }
          812  +    CloseServiceHandle(hSvc);
          813  +    CloseServiceHandle(hScm);
          814  +  }else
          815  +  if( strncmp(zMethod, "stop", n)==0 ){
          816  +    SC_HANDLE hScm;
          817  +    SC_HANDLE hSvc;
          818  +    SERVICE_STATUS sstat;
          819  +    char *zErrFmt = "unable to stop service '%s': %s";
          820  +
          821  +    verify_all_options();
          822  +    if( g.argc>4 ) fossil_fatal("to much arguments for stop method.");
          823  +    hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
          824  +    if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          825  +    hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), SERVICE_ALL_ACCESS);
          826  +    if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          827  +    QueryServiceStatus(hSvc, &sstat);
          828  +    if( sstat.dwCurrentState!=SERVICE_STOPPED ){
          829  +      fossil_print("Stopping service '%s'", zSvcName);
          830  +      if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
          831  +        if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
          832  +          fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg());
          833  +        }
          834  +      }
          835  +      while( sstat.dwCurrentState!=SERVICE_STOPPED ){
          836  +        Sleep(100);
          837  +        fossil_print(".");
          838  +        QueryServiceStatus(hSvc, &sstat);
          839  +      }
          840  +      fossil_print("\nService '%s' stopped.\n", zSvcName);
          841  +    }else{
          842  +      fossil_print("Service '%s' is already stopped.\n", zSvcName);
          843  +    }
          844  +    CloseServiceHandle(hSvc);
          845  +    CloseServiceHandle(hScm);
          846  +  }else
          847  +  {
          848  +    fossil_fatal("METHOD should be one of:"
          849  +                 " create delete show start stop");
          850  +  }
          851  +  return;
          852  +}
          853  +
          854  +#else /* _WIN32  -- This code is for win32 only */
          855  +#include "winhttp.h"
          856  +
          857  +void cmd_win32_service(void){
          858  +  fossil_fatal("The service command is platform specific "
          859  +               "and not available on this platform."); 
          860  +  return;
          861  +}
   226    862   
   227    863   #endif /* _WIN32  -- This code is for win32 only */

Changes to win/Makefile.msc.

    29     29   ZLIB    = zlib.lib
    30     30   
    31     31   INCL   = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
    32     32   
    33     33   CFLAGS = -nologo -MT -O2
    34     34   BCC    = $(CC) $(CFLAGS)
    35     35   TCC    = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
    36         -LIBS   = $(ZLIB) ws2_32.lib $(SSLLIB)
           36  +LIBS   = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
    37     37   LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
    38     38   
    39     39   SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 /DSQLITE_THREADSAFE=0 /DSQLITE_DEFAULT_FILE_FORMAT=4 /DSQLITE_ENABLE_STAT2 /Dlocaltime=fossil_localtime /DSQLITE_ENABLE_LOCKING_STYLE=0
    40     40   
    41     41   SRC   = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c 
    42     42   
    43     43   OBJ   = $(OX)\add$O $(OX)\allrepo$O $(OX)\attach$O $(OX)\bag$O $(OX)\bisect$O $(OX)\blob$O $(OX)\branch$O $(OX)\browse$O $(OX)\captcha$O $(OX)\cgi$O $(OX)\checkin$O $(OX)\checkout$O $(OX)\clearsign$O $(OX)\clone$O $(OX)\comformat$O $(OX)\configure$O $(OX)\content$O $(OX)\db$O $(OX)\delta$O $(OX)\deltacmd$O $(OX)\descendants$O $(OX)\diff$O $(OX)\diffcmd$O $(OX)\doc$O $(OX)\encode$O $(OX)\event$O $(OX)\export$O $(OX)\file$O $(OX)\finfo$O $(OX)\glob$O $(OX)\graph$O $(OX)\gzip$O $(OX)\http$O $(OX)\http_socket$O $(OX)\http_ssl$O $(OX)\http_transport$O $(OX)\import$O $(OX)\info$O $(OX)\leaf$O $(OX)\login$O $(OX)\main$O $(OX)\manifest$O $(OX)\md5$O $(OX)\merge$O $(OX)\merge3$O $(OX)\name$O $(OX)\path$O $(OX)\pivot$O $(OX)\popen$O $(OX)\pqueue$O $(OX)\printf$O $(OX)\rebuild$O $(OX)\report$O $(OX)\rss$O $(OX)\schema$O $(OX)\search$O $(OX)\setup$O $(OX)\sha1$O $(OX)\shun$O $(OX)\skins$O $(OX)\sqlcmd$O $(OX)\stash$O $(OX)\stat$O $(OX)\style$O $(OX)\sync$O $(OX)\tag$O $(OX)\tar$O $(OX)\th_main$O $(OX)\timeline$O $(OX)\tkt$O $(OX)\tktsetup$O $(OX)\undo$O $(OX)\update$O $(OX)\url$O $(OX)\user$O $(OX)\verify$O $(OX)\vfile$O $(OX)\wiki$O $(OX)\wikiformat$O $(OX)\winhttp$O $(OX)\xfer$O $(OX)\zip$O $(OX)\shell$O $(OX)\sqlite3$O $(OX)\th$O $(OX)\th_lang$O