| | @@ -14,17 +14,18 @@ |
| 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 | */ |
| | @@ -60,11 +61,11 @@ |
| 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; |
| | @@ -145,10 +146,11 @@ |
| 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); |
| | @@ -189,31 +191,46 @@ |
| 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; |
| | @@ -221,7 +238,626 @@ |
| 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 */ |
| 228 | 864 | |