Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -1438,11 +1438,14 @@ if( isUiCmd ){ zBrowser = db_get("web-browser", "start"); zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser); } db_close(1); - win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags); + if( win32_http_service(iPort, zNotFound, flags) ){ + win32_http_server(iPort, mxPort, zBrowserCmd, + zStopperFile, zNotFound, flags); + } #endif } /* ** COMMAND: test-echo Index: src/makemake.tcl ================================================================== --- src/makemake.tcl +++ src/makemake.tcl @@ -653,11 +653,11 @@ INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR) CFLAGS = -nologo -MT -O2 BCC = $(CC) $(CFLAGS) TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) -LIBS = $(ZLIB) ws2_32.lib $(SSLLIB) +LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR) } regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS writeln "SQLITE_OPTIONS = $MSC_SQLITE_OPTIONS\n" writeln -nonewline "SRC = " Index: src/winhttp.c ================================================================== --- src/winhttp.c +++ src/winhttp.c @@ -14,17 +14,18 @@ ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file implements a very simple (and low-performance) HTTP server -** for windows. +** for windows. It also implements a Windows Service which allows the HTTP +** server to be run without any user logged on. */ #include "config.h" #ifdef _WIN32 /* This code is for win32 only */ -#include "winhttp.h" #include <windows.h> +#include "winhttp.h" /* ** The HttpRequest structure holds information about each incoming ** HTTP request. */ @@ -60,11 +61,11 @@ } /* ** Process a single incoming HTTP request. */ -void win32_process_one_http_request(void *pAppData){ +static void win32_process_one_http_request(void *pAppData){ HttpRequest *p = (HttpRequest*)pAppData; FILE *in = 0, *out = 0; int amt, got; int wanted = 0; char *z; @@ -145,10 +146,11 @@ SOCKET s = INVALID_SOCKET; SOCKADDR_IN addr; int idCnt = 0; int iPort = mnPort; Blob options; + char zTmpPath[MAX_PATH]; if( zStopper ) file_delete(zStopper); blob_zero(&options); if( zNotFound ){ blob_appendf(&options, " --notfound %s", zNotFound); @@ -189,31 +191,46 @@ }else{ fossil_fatal("unable to open listening socket on any" " port in the range %d..%d", mnPort, mxPort); } } - zTempPrefix = mprintf("fossil_server_P%d_", iPort); + if( !GetTempPath(sizeof(zTmpPath), zTmpPath) ){ + fossil_fatal("unable to get path to the temporary directory."); + } + zTempPrefix = mprintf("%sfossil_server_P%d_", zTmpPath, iPort); fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); if( zBrowser ){ zBrowser = mprintf(zBrowser, iPort); fossil_print("Launch webbrowser: %s\n", zBrowser); fossil_system(zBrowser); } fossil_print("Type Ctrl-C to stop the HTTP server\n"); + /* Set the service status to running and pass the listener socket to the + ** service handling procedures. */ + win32_http_service_running(s); for(;;){ SOCKET client; SOCKADDR_IN client_addr; HttpRequest *p; int len = sizeof(client_addr); + int wsaError; client = accept(s, (struct sockaddr*)&client_addr, &len); - if( zStopper && file_size(zStopper)>=0 ){ - break; - } if( client==INVALID_SOCKET ){ - closesocket(s); - fossil_fatal("error from accept()"); + /* If the service control handler has closed the listener socket, + ** cleanup and return, otherwise report a fatal error. */ + wsaError = WSAGetLastError(); + if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){ + WSACleanup(); + return; + }else{ + closesocket(s); + WSACleanup(); + fossil_fatal("error from accept()"); + } + }else if( zStopper && file_size(zStopper)>=0 ){ + break; } p = fossil_malloc( sizeof(*p) ); p->id = ++idCnt; p->s = client; p->addr = client_addr; @@ -221,7 +238,626 @@ _beginthread(win32_process_one_http_request, 0, (void*)p); } closesocket(s); WSACleanup(); } + +/* +** The HttpService structure is used to pass information to the service main +** function and to the service control handler function. +*/ +typedef struct HttpService HttpService; +struct HttpService { + int port; /* Port on which the http server should run */ + const char *zNotFound; /* The --notfound option, or NULL */ + int flags; /* One or more HTTP_SERVER_ flags */ + int isRunningAsService; /* Are we running as a service ? */ + const char *zServiceName; /* Name of the service */ + SOCKET s; /* Socket on which the http server listens */ +}; + +/* +** Variables used for running as windows service. +*/ +static HttpService hsData = {8080, NULL, 0, 0, NULL, INVALID_SOCKET}; +static SERVICE_STATUS ssStatus; +static SERVICE_STATUS_HANDLE sshStatusHandle; + +/* +** Get message string of the last system error. Return a pointer to the +** message string. Call fossil_mbcs_free() to deallocate any memory used +** to store the message string when done. +*/ +static char *win32_get_last_errmsg(void){ + DWORD nMsg; + LPTSTR tmp = NULL; + char *zMsg = NULL; + + nMsg = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPTSTR) &tmp, + 0, + NULL + ); + if( nMsg ){ + zMsg = fossil_mbcs_to_utf8(tmp); + }else{ + fossil_fatal("unable to get system error message."); + } + if( tmp ){ + LocalFree((HLOCAL) tmp); + } + return zMsg; +} + +/* +** Report the current status of the service to the service control manager. +** Make sure that during service startup no control codes are accepted. +*/ +static void win32_report_service_status( + DWORD dwCurrentState, /* The current state of the service */ + DWORD dwWin32ExitCode, /* The error code to report */ + DWORD dwWaitHint /* The estimated time for a pending operation */ +){ + if( dwCurrentState==SERVICE_START_PENDING) { + ssStatus.dwControlsAccepted = 0; + }else{ + ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + } + ssStatus.dwCurrentState = dwCurrentState; + ssStatus.dwWin32ExitCode = dwWin32ExitCode; + ssStatus.dwWaitHint = dwWaitHint; + + if( (dwCurrentState==SERVICE_RUNNING) || + (dwCurrentState==SERVICE_STOPPED) ){ + ssStatus.dwCheckPoint = 0; + }else{ + ssStatus.dwCheckPoint++; + } + SetServiceStatus(sshStatusHandle, &ssStatus); + return ; +} + +/* +** Handle control codes sent from the service control manager. +** The control dispatcher in the main thread of the service process invokes +** this function whenever it receives a control request from the service +** control manager. +*/ +static void WINAPI win32_http_service_ctrl( + DWORD dwCtrlCode +){ + switch( dwCtrlCode ){ + case SERVICE_CONTROL_STOP: { + win32_report_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0); + if( hsData.s != INVALID_SOCKET ){ + closesocket(hsData.s); + } + win32_report_service_status(ssStatus.dwCurrentState, NO_ERROR, 0); + break; + } + default: { + break; + } + } + return; +} + +/* +** This is the main entry point for the service. +** When the service control manager receives a request to start the service, +** it starts the service process (if it is not already running). The main +** thread of the service process calls the StartServiceCtrlDispatcher +** function with a pointer to an array of SERVICE_TABLE_ENTRY structures. +** Then the service control manager sends a start request to the service +** control dispatcher for this service process. The service control dispatcher +** creates a new thread to execute the ServiceMain function (this function) +** of the service being started. +*/ +static void WINAPI win32_http_service_main( + DWORD argc, /* Number of arguments in argv */ + LPTSTR *argv /* Arguments passed */ +){ + + /* Update the service information. */ + hsData.isRunningAsService = 1; + if( argc>0 ){ + hsData.zServiceName = argv[0]; + } + + /* Register the service control handler function */ + sshStatusHandle = RegisterServiceCtrlHandler("", win32_http_service_ctrl); + if( !sshStatusHandle ){ + win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0); + return; + } + + /* Set service specific data and report that the service is starting. */ + ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + ssStatus.dwServiceSpecificExitCode = 0; + win32_report_service_status(SERVICE_START_PENDING, NO_ERROR, 3000); + + /* Execute the http server */ + win32_http_server(hsData.port, hsData.port, + NULL, NULL, hsData.zNotFound, hsData.flags); + + /* Service has stopped now. */ + win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0); + return; +} + +/* +** When running as service, update the HttpService structure with the +** listener socket and update the service status. This procedure must be +** called from the http server when he is ready to accept connections. +*/ +LOCAL void win32_http_service_running(SOCKET s){ + if( hsData.isRunningAsService ){ + hsData.s = s; + win32_report_service_status(SERVICE_RUNNING, NO_ERROR, 0); + } +} + +/* +** Try to start the http server as a windows service. If we are running in +** a interactive console session, this routine fails and returns a non zero +** integer value. When running as service, this routine does not return until +** the service is stopped. In this case, the return value is zero. +*/ +int win32_http_service( + int nPort, /* TCP port number */ + const char *zNotFound, /* The --notfound option, or NULL */ + int flags /* One or more HTTP_SERVER_ flags */ +){ + /* Define the service table. */ + SERVICE_TABLE_ENTRY ServiceTable[] = + {{"", (LPSERVICE_MAIN_FUNCTION)win32_http_service_main}, {NULL, NULL}}; + + /* Initialize the HttpService structure. */ + hsData.port = nPort; + hsData.zNotFound = zNotFound; + hsData.flags = flags; + + /* Try to start the control dispatcher thread for the service. */ + if( !StartServiceCtrlDispatcher(ServiceTable) ){ + if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){ + return 1; + }else{ + fossil_fatal("error from StartServiceCtrlDispatcher()"); + } + } + return 0; +} + +/* +** COMMAND: service +** +** Usage: fossil service METHOD ?SERVICE-NAME? ?OPTIONS? +** +** Where METHOD is one of: create delete show start stop. +** +** The service command can be used to create and control instances of Fossil +** which are are running as Windows services. No user needs to be logged on +** when running Fossil as a service. In the following description of the +** methods, "Fossil-DSCM" will be used as the default SERVICE-NAME, if no +** SERVICE-NAME is specified. +** +** fossil service create ?SERVICE-NAME? ?OPTIONS? +** +** Creates a service. The following service specific options are +** available: +** +** -D|--display DISPLAY-NAME +** +** Sets the display name of the service. This name will be showed +** by graphical interface programs. By default, the display name +** equals to the service name. +** +** -S|--start TYPE +** +** Sets the start type of the service. TYPE can be "manual", +** which means you need to start the service yourself with the +** 'fossil service start' command or with the "net start" command +** from the operating system. If TYPE is set to "auto", the service +** will be started automatically by the system during startup. +** +** -U|--username USERNAME +** +** Specifies the user account which will be used to run the +** service. The account needs the "Logon as a service" right +** enabled in its profile. Specify local accounts as follows: +** ".\\USERNAME". By default, the "LocalSystem" account will be +** used. +** +** -W|--password PASSWORD +** +** Password for the user account. +** +** The following options are more or less the same as for the "server" +** command and influence the behaviour of the http server: +** +** -p|--port TCPPORT +** +** Specifies the TCP port (default port is 8080) on which the +** server should listen. +** +** -R|--repository REPOSITORY +** +** Specifies the name of the repository to be served. +** The repository option may be omitted if the working directory +** is within an open checkout. +** The REPOSITORY can be a directory (aka folder) that contains +** one or more respositories with names ending in ".fossil". +** In that case, the first element of the URL is used to select +** among the various repositories. +** +** --notfound URL +** +** If REPOSITORY is a directory that contains one or more +** respositories with names of the form "*.fossil" then the +** first element of the URL pathname selects among the various +** repositories. If the pathname does not select a valid +** repository and the --notfound option is available, +** then the server redirects (HTTP code 302) to the URL of +** --notfound. +** +** --localauth +** +** Enables automatic login if the --localauth option is present +** and the "localauth" setting is off and the connection is from +** localhost. +** +** +** fossil service delete ?SERVICE-NAME? +** +** Deletes a service. If the service is currently running, it will be +** stopped first and then deleted. +** +** +** fossil service show ?SERVICE-NAME? +** +** Shows how the service is configured and its current state. +** +** +** fossil service start ?SERVICE-NAME? +** +** Start the service. +** +** +** fossil service stop ?SERVICE-NAME? +** +** Stop the service. +** +** +** NOTE: This command is available on Windows operating systems only and +** requires administrative rights on the machine executed. +** +*/ +void cmd_win32_service(void){ + int n; + const char *zMethod; + const char *zSvcName = "Fossil-DSCM"; /* Default service name */ + + if( g.argc<3 ){ + usage("create|delete|show|start|stop ..."); + } + zMethod = g.argv[2]; + n = strlen(zMethod); + if( g.argc==4 ){ + zSvcName = g.argv[3]; + } + + if( strncmp(zMethod, "create", n)==0 ){ + SC_HANDLE hScm; + SC_HANDLE hSvc; + SERVICE_DESCRIPTION + svcDescr = {"Fossil - Distributed Software Configuration Management"}; + char *zErrFmt = "unable to create service '%s': %s"; + DWORD dwStartType = SERVICE_DEMAND_START; + const char *zDisplay; + const char *zStart; + const char *zUsername; + const char *zPassword; + const char *zPort; + const char *zNotFound; + const char *zLocalAuth; + const char *zRepository; + Blob binPath; + + /* Process service creation specific options. */ + zDisplay = find_option("display", "D", 1); + if( !zDisplay ){ + zDisplay = zSvcName; + } + zStart = find_option("start", "S", 1); + if( zStart ){ + if( strncmp(zStart, "auto", strlen(zStart))==0 ){ + dwStartType = SERVICE_AUTO_START; + }else if( strncmp(zStart, "manual", strlen(zStart))==0 ){ + dwStartType = SERVICE_DEMAND_START; + }else{ + fossil_fatal(zErrFmt, zSvcName, + "specify 'auto' or 'manual' for the '-S|--start' option"); + } + } + zUsername = find_option("username", "U", 1); + zPassword = find_option("password", "W", 1); + /* Process options for Fossil running as server. */ + zPort = find_option("port", "P", 1); + if( zPort && (atoi(zPort)<=0) ){ + fossil_fatal(zErrFmt, zSvcName, + "port number must be in the range 1 - 65535."); + } + zNotFound = find_option("notfound", 0, 1); + zLocalAuth = find_option("localauth", 0, 0); + zRepository = find_option("repository", "R", 1); + if( !zRepository ){ + db_must_be_within_tree(); + }else if( file_isdir(zRepository)==1 ){ + g.zRepositoryName = mprintf("%s", zRepository); + file_simplify_name(g.zRepositoryName, -1); + }else{ + db_open_repository(zRepository); + } + db_close(0); + verify_all_options(); + if( g.argc>4 ) fossil_fatal("to much arguments for create method."); + /* Build the fully-qualified path to the service binary file. */ + blob_zero(&binPath); + blob_appendf(&binPath, "\"%s\" server", fossil_nameofexe()); + if( zPort ) blob_appendf(&binPath, " --port %s", zPort); + if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound); + if( zLocalAuth ) blob_append(&binPath, " --localauth", -1); + blob_appendf(&binPath, " \"%s\"", g.zRepositoryName); + /* Create the service. */ + hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + hSvc = CreateService( + hScm, /* Handle to the SCM */ + fossil_utf8_to_mbcs(zSvcName), /* Name of the service */ + fossil_utf8_to_mbcs(zDisplay), /* Display name */ + SERVICE_ALL_ACCESS, /* Desired access */ + SERVICE_WIN32_OWN_PROCESS, /* Service type */ + dwStartType, /* Start type */ + SERVICE_ERROR_NORMAL, /* Error control */ + fossil_utf8_to_mbcs(blob_str(&binPath)), /* Binary path */ + NULL, /* Load ordering group */ + NULL, /* Tag value */ + NULL, /* Service dependencies */ + fossil_utf8_to_mbcs(zUsername), /* Service account */ + fossil_utf8_to_mbcs(zPassword) /* Account password */ + ); + if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + /* Set the service description. */ + ChangeServiceConfig2(hSvc, SERVICE_CONFIG_DESCRIPTION, &svcDescr); + fossil_print("Service '%s' successfully created.\n", zSvcName); + CloseServiceHandle(hSvc); + CloseServiceHandle(hScm); + }else + if( strncmp(zMethod, "delete", n)==0 ){ + SC_HANDLE hScm; + SC_HANDLE hSvc; + SERVICE_STATUS sstat; + char *zErrFmt = "unable to delete service '%s': %s"; + + verify_all_options(); + if( g.argc>4 ) fossil_fatal("to much arguments for delete method."); + hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), SERVICE_ALL_ACCESS); + if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + QueryServiceStatus(hSvc, &sstat); + if( sstat.dwCurrentState!=SERVICE_STOPPED ){ + fossil_print("Stopping service '%s'", zSvcName); + if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){ + if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){ + fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + } + } + while( sstat.dwCurrentState!=SERVICE_STOPPED ){ + Sleep(100); + fossil_print("."); + QueryServiceStatus(hSvc, &sstat); + } + fossil_print("\nService '%s' stopped.\n", zSvcName); + } + if( !DeleteService(hSvc) ){ + if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){ + fossil_warning("Service '%s' already marked for delete.\n", zSvcName); + }else{ + fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + } + }else{ + fossil_print("Service '%s' successfully deleted.\n", zSvcName); + } + CloseServiceHandle(hSvc); + CloseServiceHandle(hScm); + }else + if( strncmp(zMethod, "show", n)==0 ){ + SC_HANDLE hScm; + SC_HANDLE hSvc; + SERVICE_STATUS sstat; + LPQUERY_SERVICE_CONFIG pSvcConfig; + LPSERVICE_DESCRIPTION pSvcDescr; + BOOL bStatus; + DWORD nRequired; + char *zErrFmt = "unable to show service '%s': %s"; + static const char *zSvcTypes[] = { + "Driver service", + "File system driver service", + "Service runs in its own process", + "Service shares a process with other services", + "Service can interact with the desktop" + }; + const char *zSvcType = ""; + static char *zSvcStartTypes[] = { + "Started by the system loader", + "Started by the IoInitSystem function", + "Started automatically by the service control manager", + "Started manually", + "Service cannot be started" + }; + const char *zSvcStartType = ""; + static const char *zSvcStates[] = { + "Stopped", "Starting", "Stopping", "Running", + "Continue pending", "Pause pending", "Paused" + }; + const char *zSvcState = ""; + + verify_all_options(); + if( g.argc>4 ) fossil_fatal("to much arguments for show method."); + hScm = OpenSCManager(NULL, NULL, GENERIC_READ); + if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), GENERIC_READ); + if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + /* Get the service configuration */ + bStatus = QueryServiceConfig(hSvc, NULL, 0, &nRequired); + if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){ + fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + } + pSvcConfig = fossil_malloc(nRequired); + bStatus = QueryServiceConfig(hSvc, pSvcConfig, nRequired, &nRequired); + if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + /* Translate the service type */ + switch( pSvcConfig->dwServiceType ){ + case SERVICE_KERNEL_DRIVER: zSvcType = zSvcTypes[0]; break; + case SERVICE_FILE_SYSTEM_DRIVER: zSvcType = zSvcTypes[1]; break; + case SERVICE_WIN32_OWN_PROCESS: zSvcType = zSvcTypes[2]; break; + case SERVICE_WIN32_SHARE_PROCESS: zSvcType = zSvcTypes[3]; break; + case SERVICE_INTERACTIVE_PROCESS: zSvcType = zSvcTypes[4]; break; + } + /* Translate the service start type */ + switch( pSvcConfig->dwStartType ){ + case SERVICE_BOOT_START: zSvcStartType = zSvcStartTypes[0]; break; + case SERVICE_SYSTEM_START: zSvcStartType = zSvcStartTypes[1]; break; + case SERVICE_AUTO_START: zSvcStartType = zSvcStartTypes[2]; break; + case SERVICE_DEMAND_START: zSvcStartType = zSvcStartTypes[3]; break; + case SERVICE_DISABLED: zSvcStartType = zSvcStartTypes[4]; break; + } + /* Get the service description. */ + bStatus = QueryServiceConfig2(hSvc, SERVICE_CONFIG_DESCRIPTION, + NULL, 0, &nRequired); + if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){ + fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + } + pSvcDescr = fossil_malloc(nRequired); + bStatus = QueryServiceConfig2(hSvc, SERVICE_CONFIG_DESCRIPTION, + (LPBYTE)pSvcDescr, nRequired, &nRequired); + if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + /* Retrieves the current status of the specified service. */ + bStatus = QueryServiceStatus(hSvc, &sstat); + if( !bStatus ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + /* Translate the current state. */ + switch( sstat.dwCurrentState ){ + case SERVICE_STOPPED: zSvcState = zSvcStates[0]; break; + case SERVICE_START_PENDING: zSvcState = zSvcStates[1]; break; + case SERVICE_STOP_PENDING: zSvcState = zSvcStates[2]; break; + case SERVICE_RUNNING: zSvcState = zSvcStates[3]; break; + case SERVICE_CONTINUE_PENDING: zSvcState = zSvcStates[4]; break; + case SERVICE_PAUSE_PENDING: zSvcState = zSvcStates[5]; break; + case SERVICE_PAUSED: zSvcState = zSvcStates[6]; break; + } + /* Print service information to terminal */ + fossil_print("Service name .......: %s\n", zSvcName); + fossil_print("Display name .......: %s\n", + fossil_mbcs_to_utf8(pSvcConfig->lpDisplayName)); + fossil_print("Service description : %s\n", + fossil_mbcs_to_utf8(pSvcDescr->lpDescription)); + fossil_print("Service type .......: %s.\n", zSvcType); + fossil_print("Service start type .: %s.\n", zSvcStartType); + fossil_print("Binary path name ...: %s\n", + fossil_mbcs_to_utf8(pSvcConfig->lpBinaryPathName)); + fossil_print("Service username ...: %s\n", + fossil_mbcs_to_utf8(pSvcConfig->lpServiceStartName)); + fossil_print("Current state ......: %s.\n", zSvcState); + /* Cleanup */ + fossil_free(pSvcConfig); + fossil_free(pSvcDescr); + CloseServiceHandle(hSvc); + CloseServiceHandle(hScm); + }else + if( strncmp(zMethod, "start", n)==0 ){ + SC_HANDLE hScm; + SC_HANDLE hSvc; + SERVICE_STATUS sstat; + char *zErrFmt = "unable to start service '%s': %s"; + + verify_all_options(); + if( g.argc>4 ) fossil_fatal("to much arguments for start method."); + hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), SERVICE_ALL_ACCESS); + if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + QueryServiceStatus(hSvc, &sstat); + if( sstat.dwCurrentState!=SERVICE_RUNNING ){ + fossil_print("Starting service '%s'", zSvcName); + if( sstat.dwCurrentState!=SERVICE_START_PENDING ){ + if( !StartService(hSvc, 0, NULL) ){ + fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + } + } + while( sstat.dwCurrentState!=SERVICE_RUNNING ){ + Sleep(100); + fossil_print("."); + QueryServiceStatus(hSvc, &sstat); + } + fossil_print("\nService '%s' started.\n", zSvcName); + }else{ + fossil_print("Service '%s' is already started.\n", zSvcName); + } + CloseServiceHandle(hSvc); + CloseServiceHandle(hScm); + }else + if( strncmp(zMethod, "stop", n)==0 ){ + SC_HANDLE hScm; + SC_HANDLE hSvc; + SERVICE_STATUS sstat; + char *zErrFmt = "unable to stop service '%s': %s"; + + verify_all_options(); + if( g.argc>4 ) fossil_fatal("to much arguments for stop method."); + hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), SERVICE_ALL_ACCESS); + if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + QueryServiceStatus(hSvc, &sstat); + if( sstat.dwCurrentState!=SERVICE_STOPPED ){ + fossil_print("Stopping service '%s'", zSvcName); + if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){ + if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){ + fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); + } + } + while( sstat.dwCurrentState!=SERVICE_STOPPED ){ + Sleep(100); + fossil_print("."); + QueryServiceStatus(hSvc, &sstat); + } + fossil_print("\nService '%s' stopped.\n", zSvcName); + }else{ + fossil_print("Service '%s' is already stopped.\n", zSvcName); + } + CloseServiceHandle(hSvc); + CloseServiceHandle(hScm); + }else + { + fossil_fatal("METHOD should be one of:" + " create delete show start stop"); + } + return; +} + +#else /* _WIN32 -- This code is for win32 only */ +#include "winhttp.h" + +void cmd_win32_service(void){ + fossil_fatal("The service command is platform specific " + "and not available on this platform."); + return; +} #endif /* _WIN32 -- This code is for win32 only */ Index: win/Makefile.msc ================================================================== --- win/Makefile.msc +++ win/Makefile.msc @@ -31,11 +31,11 @@ INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR) CFLAGS = -nologo -MT -O2 BCC = $(CC) $(CFLAGS) TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) -LIBS = $(ZLIB) ws2_32.lib $(SSLLIB) +LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR) 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 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