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