Fossil SCM
Enable Fossil to run as a Windows service and add a "fossil service" command.
Commit
91c2f65a6e705815627d088f7e2a6aa8aa0e5e8d
Parent
e14f0fe2f3d8a76…
2 files changed
+4
-1
+645
-9
+4
-1
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -1438,11 +1438,14 @@ | ||
| 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 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1438,11 +1438,14 @@ | |
| 1438 | if( isUiCmd ){ |
| 1439 | zBrowser = db_get("web-browser", "start"); |
| 1440 | zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser); |
| 1441 | } |
| 1442 | db_close(1); |
| 1443 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, flags); |
| 1444 | #endif |
| 1445 | } |
| 1446 | |
| 1447 | /* |
| 1448 | ** COMMAND: test-echo |
| 1449 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1438,11 +1438,14 @@ | |
| 1438 | if( isUiCmd ){ |
| 1439 | zBrowser = db_get("web-browser", "start"); |
| 1440 | zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser); |
| 1441 | } |
| 1442 | db_close(1); |
| 1443 | if( win32_http_service(iPort, zNotFound, flags) ){ |
| 1444 | win32_http_server(iPort, mxPort, zBrowserCmd, |
| 1445 | zStopperFile, zNotFound, flags); |
| 1446 | } |
| 1447 | #endif |
| 1448 | } |
| 1449 | |
| 1450 | /* |
| 1451 | ** COMMAND: test-echo |
| 1452 |
+645
-9
| --- src/winhttp.c | ||
| +++ src/winhttp.c | ||
| @@ -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 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -14,17 +14,18 @@ | |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file implements a very simple (and low-performance) HTTP server |
| 19 | ** for windows. |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #ifdef _WIN32 |
| 23 | /* This code is for win32 only */ |
| 24 | #include "winhttp.h" |
| 25 | #include <windows.h> |
| 26 | |
| 27 | /* |
| 28 | ** The HttpRequest structure holds information about each incoming |
| 29 | ** HTTP request. |
| 30 | */ |
| @@ -60,11 +61,11 @@ | |
| 60 | } |
| 61 | |
| 62 | /* |
| 63 | ** Process a single incoming HTTP request. |
| 64 | */ |
| 65 | void win32_process_one_http_request(void *pAppData){ |
| 66 | HttpRequest *p = (HttpRequest*)pAppData; |
| 67 | FILE *in = 0, *out = 0; |
| 68 | int amt, got; |
| 69 | int wanted = 0; |
| 70 | char *z; |
| @@ -145,10 +146,11 @@ | |
| 145 | SOCKET s = INVALID_SOCKET; |
| 146 | SOCKADDR_IN addr; |
| 147 | int idCnt = 0; |
| 148 | int iPort = mnPort; |
| 149 | Blob options; |
| 150 | |
| 151 | if( zStopper ) file_delete(zStopper); |
| 152 | blob_zero(&options); |
| 153 | if( zNotFound ){ |
| 154 | blob_appendf(&options, " --notfound %s", zNotFound); |
| @@ -189,31 +191,46 @@ | |
| 189 | }else{ |
| 190 | fossil_fatal("unable to open listening socket on any" |
| 191 | " port in the range %d..%d", mnPort, mxPort); |
| 192 | } |
| 193 | } |
| 194 | zTempPrefix = mprintf("fossil_server_P%d_", iPort); |
| 195 | fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); |
| 196 | if( zBrowser ){ |
| 197 | zBrowser = mprintf(zBrowser, iPort); |
| 198 | fossil_print("Launch webbrowser: %s\n", zBrowser); |
| 199 | fossil_system(zBrowser); |
| 200 | } |
| 201 | fossil_print("Type Ctrl-C to stop the HTTP server\n"); |
| 202 | for(;;){ |
| 203 | SOCKET client; |
| 204 | SOCKADDR_IN client_addr; |
| 205 | HttpRequest *p; |
| 206 | int len = sizeof(client_addr); |
| 207 | |
| 208 | client = accept(s, (struct sockaddr*)&client_addr, &len); |
| 209 | if( zStopper && file_size(zStopper)>=0 ){ |
| 210 | break; |
| 211 | } |
| 212 | if( client==INVALID_SOCKET ){ |
| 213 | closesocket(s); |
| 214 | fossil_fatal("error from accept()"); |
| 215 | } |
| 216 | p = fossil_malloc( sizeof(*p) ); |
| 217 | p->id = ++idCnt; |
| 218 | p->s = client; |
| 219 | p->addr = client_addr; |
| @@ -221,7 +238,626 @@ | |
| 221 | _beginthread(win32_process_one_http_request, 0, (void*)p); |
| 222 | } |
| 223 | closesocket(s); |
| 224 | WSACleanup(); |
| 225 | } |
| 226 | |
| 227 | #endif /* _WIN32 -- This code is for win32 only */ |
| 228 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -14,17 +14,18 @@ | |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file implements a very simple (and low-performance) HTTP server |
| 19 | ** for windows. It also implements a Windows Service which allows the HTTP |
| 20 | ** server to be run without any user logged on. |
| 21 | */ |
| 22 | #include "config.h" |
| 23 | #ifdef _WIN32 |
| 24 | /* This code is for win32 only */ |
| 25 | #include <windows.h> |
| 26 | #include "winhttp.h" |
| 27 | |
| 28 | /* |
| 29 | ** The HttpRequest structure holds information about each incoming |
| 30 | ** HTTP request. |
| 31 | */ |
| @@ -60,11 +61,11 @@ | |
| 61 | } |
| 62 | |
| 63 | /* |
| 64 | ** Process a single incoming HTTP request. |
| 65 | */ |
| 66 | static void win32_process_one_http_request(void *pAppData){ |
| 67 | HttpRequest *p = (HttpRequest*)pAppData; |
| 68 | FILE *in = 0, *out = 0; |
| 69 | int amt, got; |
| 70 | int wanted = 0; |
| 71 | char *z; |
| @@ -145,10 +146,11 @@ | |
| 146 | SOCKET s = INVALID_SOCKET; |
| 147 | SOCKADDR_IN addr; |
| 148 | int idCnt = 0; |
| 149 | int iPort = mnPort; |
| 150 | Blob options; |
| 151 | char zTmpPath[MAX_PATH]; |
| 152 | |
| 153 | if( zStopper ) file_delete(zStopper); |
| 154 | blob_zero(&options); |
| 155 | if( zNotFound ){ |
| 156 | blob_appendf(&options, " --notfound %s", zNotFound); |
| @@ -189,31 +191,46 @@ | |
| 191 | }else{ |
| 192 | fossil_fatal("unable to open listening socket on any" |
| 193 | " port in the range %d..%d", mnPort, mxPort); |
| 194 | } |
| 195 | } |
| 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); |
| 200 | fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); |
| 201 | if( zBrowser ){ |
| 202 | zBrowser = mprintf(zBrowser, iPort); |
| 203 | fossil_print("Launch webbrowser: %s\n", zBrowser); |
| 204 | fossil_system(zBrowser); |
| 205 | } |
| 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); |
| 210 | for(;;){ |
| 211 | SOCKET client; |
| 212 | SOCKADDR_IN client_addr; |
| 213 | HttpRequest *p; |
| 214 | int len = sizeof(client_addr); |
| 215 | int wsaError; |
| 216 | |
| 217 | client = accept(s, (struct sockaddr*)&client_addr, &len); |
| 218 | if( client==INVALID_SOCKET ){ |
| 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; |
| 232 | } |
| 233 | p = fossil_malloc( sizeof(*p) ); |
| 234 | p->id = ++idCnt; |
| 235 | p->s = client; |
| 236 | p->addr = client_addr; |
| @@ -221,7 +238,626 @@ | |
| 238 | _beginthread(win32_process_one_http_request, 0, (void*)p); |
| 239 | } |
| 240 | closesocket(s); |
| 241 | WSACleanup(); |
| 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 | } |
| 862 | |
| 863 | #endif /* _WIN32 -- This code is for win32 only */ |
| 864 |