Fossil SCM

Merge the windows-service command into trunk.

drh 2011-07-18 20:04 trunk merge
Commit 06e9ca23e70a59ffdf4d6ac8b06b1f51ce59677e
+4 -1
--- src/main.c
+++ src/main.c
@@ -1438,11 +1438,14 @@
14381438
if( isUiCmd ){
14391439
zBrowser = db_get("web-browser", "start");
14401440
zBrowserCmd = mprintf("%s http://127.0.0.1:%%d/", zBrowser);
14411441
}
14421442
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
+ }
14441447
#endif
14451448
}
14461449
14471450
/*
14481451
** COMMAND: test-echo
14491452
--- 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
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -653,11 +653,11 @@
653653
INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
654654
655655
CFLAGS = -nologo -MT -O2
656656
BCC = $(CC) $(CFLAGS)
657657
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
658
-LIBS = $(ZLIB) ws2_32.lib $(SSLLIB)
658
+LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
659659
LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
660660
}
661661
regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS
662662
writeln "SQLITE_OPTIONS = $MSC_SQLITE_OPTIONS\n"
663663
writeln -nonewline "SRC = "
664664
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -653,11 +653,11 @@
653 INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
654
655 CFLAGS = -nologo -MT -O2
656 BCC = $(CC) $(CFLAGS)
657 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
658 LIBS = $(ZLIB) ws2_32.lib $(SSLLIB)
659 LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
660 }
661 regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS
662 writeln "SQLITE_OPTIONS = $MSC_SQLITE_OPTIONS\n"
663 writeln -nonewline "SRC = "
664
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -653,11 +653,11 @@
653 INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
654
655 CFLAGS = -nologo -MT -O2
656 BCC = $(CC) $(CFLAGS)
657 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
658 LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
659 LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
660 }
661 regsub -all {[-]D} $SQLITE_OPTIONS {/D} MSC_SQLITE_OPTIONS
662 writeln "SQLITE_OPTIONS = $MSC_SQLITE_OPTIONS\n"
663 writeln -nonewline "SRC = "
664
+645 -9
--- src/winhttp.c
+++ src/winhttp.c
@@ -14,17 +14,18 @@
1414
** http://www.hwaci.com/drh/
1515
**
1616
*******************************************************************************
1717
**
1818
** 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.
2021
*/
2122
#include "config.h"
2223
#ifdef _WIN32
2324
/* This code is for win32 only */
24
-#include "winhttp.h"
2525
#include <windows.h>
26
+#include "winhttp.h"
2627
2728
/*
2829
** The HttpRequest structure holds information about each incoming
2930
** HTTP request.
3031
*/
@@ -60,11 +61,11 @@
6061
}
6162
6263
/*
6364
** Process a single incoming HTTP request.
6465
*/
65
-void win32_process_one_http_request(void *pAppData){
66
+static void win32_process_one_http_request(void *pAppData){
6667
HttpRequest *p = (HttpRequest*)pAppData;
6768
FILE *in = 0, *out = 0;
6869
int amt, got;
6970
int wanted = 0;
7071
char *z;
@@ -145,10 +146,11 @@
145146
SOCKET s = INVALID_SOCKET;
146147
SOCKADDR_IN addr;
147148
int idCnt = 0;
148149
int iPort = mnPort;
149150
Blob options;
151
+ char zTmpPath[MAX_PATH];
150152
151153
if( zStopper ) file_delete(zStopper);
152154
blob_zero(&options);
153155
if( zNotFound ){
154156
blob_appendf(&options, " --notfound %s", zNotFound);
@@ -189,31 +191,46 @@
189191
}else{
190192
fossil_fatal("unable to open listening socket on any"
191193
" port in the range %d..%d", mnPort, mxPort);
192194
}
193195
}
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);
195200
fossil_print("Listening for HTTP requests on TCP port %d\n", iPort);
196201
if( zBrowser ){
197202
zBrowser = mprintf(zBrowser, iPort);
198203
fossil_print("Launch webbrowser: %s\n", zBrowser);
199204
fossil_system(zBrowser);
200205
}
201206
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);
202210
for(;;){
203211
SOCKET client;
204212
SOCKADDR_IN client_addr;
205213
HttpRequest *p;
206214
int len = sizeof(client_addr);
215
+ int wsaError;
207216
208217
client = accept(s, (struct sockaddr*)&client_addr, &len);
209
- if( zStopper && file_size(zStopper)>=0 ){
210
- break;
211
- }
212218
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;
215232
}
216233
p = fossil_malloc( sizeof(*p) );
217234
p->id = ++idCnt;
218235
p->s = client;
219236
p->addr = client_addr;
@@ -221,7 +238,626 @@
221238
_beginthread(win32_process_one_http_request, 0, (void*)p);
222239
}
223240
closesocket(s);
224241
WSACleanup();
225242
}
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
+}
226862
227863
#endif /* _WIN32 -- This code is for win32 only */
228864
--- 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
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -31,11 +31,11 @@
3131
INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
3232
3333
CFLAGS = -nologo -MT -O2
3434
BCC = $(CC) $(CFLAGS)
3535
TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
36
-LIBS = $(ZLIB) ws2_32.lib $(SSLLIB)
36
+LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
3737
LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
3838
3939
SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 /DSQLITE_THREADSAFE=0 /DSQLITE_DEFAULT_FILE_FORMAT=4 /DSQLITE_ENABLE_STAT2 /Dlocaltime=fossil_localtime /DSQLITE_ENABLE_LOCKING_STYLE=0
4040
4141
SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
4242
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -31,11 +31,11 @@
31 INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
32
33 CFLAGS = -nologo -MT -O2
34 BCC = $(CC) $(CFLAGS)
35 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
36 LIBS = $(ZLIB) ws2_32.lib $(SSLLIB)
37 LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
38
39 SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 /DSQLITE_THREADSAFE=0 /DSQLITE_DEFAULT_FILE_FORMAT=4 /DSQLITE_ENABLE_STAT2 /Dlocaltime=fossil_localtime /DSQLITE_ENABLE_LOCKING_STYLE=0
40
41 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
42
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -31,11 +31,11 @@
31 INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR)
32
33 CFLAGS = -nologo -MT -O2
34 BCC = $(CC) $(CFLAGS)
35 TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
36 LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
37 LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
38
39 SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 /DSQLITE_THREADSAFE=0 /DSQLITE_DEFAULT_FILE_FORMAT=4 /DSQLITE_ENABLE_STAT2 /Dlocaltime=fossil_localtime /DSQLITE_ENABLE_LOCKING_STYLE=0
40
41 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
42

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button