Fossil SCM

fossil-scm / src / winhttp.c
Blame History Raw 1448 lines
1
/*
2
** Copyright (c) 2008 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
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
# if !defined(_WIN32_WINNT)
26
# define _WIN32_WINNT 0x0501
27
# endif
28
#include <winsock2.h>
29
#include <ws2tcpip.h>
30
#include <windows.h>
31
#include <process.h>
32
#include "winhttp.h"
33
34
#ifndef IPV6_V6ONLY
35
# define IPV6_V6ONLY 27 /* Because this definition is missing in MinGW */
36
#endif
37
38
/*
39
** The SocketAddr structure holds a SOCKADDR_STORAGE and its content size.
40
*/
41
typedef struct SocketAddr SocketAddr;
42
struct SocketAddr {
43
SOCKADDR_STORAGE addr;
44
int len;
45
};
46
47
static char* SocketAddr_toString(const SocketAddr* pAddr){
48
SocketAddr addr;
49
char* zIp;
50
DWORD nIp = 50;
51
assert( pAddr!=NULL );
52
memcpy(&addr, pAddr, sizeof(SocketAddr));
53
if( addr.len==sizeof(SOCKADDR_IN6) ){
54
((SOCKADDR_IN6*)&addr)->sin6_port = 0;
55
}else{
56
((SOCKADDR_IN*)&addr)->sin_port = 0;
57
}
58
zIp = fossil_malloc(nIp);
59
if( WSAAddressToStringA((SOCKADDR*)&addr, addr.len, NULL, zIp, &nIp)!=0 ){
60
zIp[0] = 0;
61
}
62
return zIp;
63
}
64
65
/*
66
** The DualAddr structure holds two SocketAddr (one IPv4 and on IPv6).
67
*/
68
typedef struct DualAddr DualAddr;
69
struct DualAddr {
70
SocketAddr a4; /* IPv4 SOCKADDR_IN */
71
SocketAddr a6; /* IPv6 SOCKADDR_IN6 */
72
};
73
74
static void DualAddr_init(DualAddr* pDA){
75
assert( pDA!=NULL );
76
memset(pDA, 0, sizeof(DualAddr));
77
pDA->a4.len = sizeof(SOCKADDR_IN);
78
pDA->a6.len = sizeof(SOCKADDR_IN6);
79
}
80
81
/*
82
** The DualSocket structure holds two SOCKETs. One or both could be
83
** used or INVALID_SOCKET. One is dedicated to IPv4, the other to IPv6.
84
*/
85
typedef struct DualSocket DualSocket;
86
struct DualSocket {
87
SOCKET s4; /* IPv4 socket or INVALID_SOCKET */
88
SOCKET s6; /* IPv6 socket or INVALID_SOCKET */
89
};
90
91
/*
92
** Initializes a DualSocket.
93
*/
94
static void DualSocket_init(DualSocket* ds){
95
assert( ds!=NULL );
96
ds->s4 = INVALID_SOCKET;
97
ds->s6 = INVALID_SOCKET;
98
};
99
100
/*
101
** Close and reset a DualSocket.
102
*/
103
static void DualSocket_close(DualSocket* ds){
104
assert( ds!=NULL );
105
if( ds->s4!=INVALID_SOCKET ){
106
closesocket(ds->s4);
107
ds->s4 = INVALID_SOCKET;
108
}
109
if( ds->s6!=INVALID_SOCKET ){
110
closesocket(ds->s6);
111
ds->s6 = INVALID_SOCKET;
112
}
113
};
114
115
/*
116
** When ip is "W", listen to wildcard address (IPv4/IPv6 as available).
117
** When ip is "L", listen to loopback address (IPv4/IPv6 as available).
118
** Else listen only the specified ip, which is either IPv4 or IPv6 or invalid.
119
** Returns 1 on success, 0 on failure.
120
*/
121
static int DualSocket_listen(DualSocket* ds, const char* zIp, int iPort){
122
SOCKADDR_IN addr4;
123
SOCKADDR_IN6 addr6;
124
assert( ds!=NULL && zIp!=NULL && iPort!=0 );
125
DualSocket_close(ds);
126
memset(&addr4, 0, sizeof(addr4));
127
memset(&addr6, 0, sizeof(addr6));
128
if (strcmp(zIp, "W")==0 || strcmp(zIp, "L")==0 ){
129
ds->s4 = socket(AF_INET, SOCK_STREAM, 0);
130
ds->s6 = socket(AF_INET6, SOCK_STREAM, 0);
131
if( ds->s4==INVALID_SOCKET && ds->s6==INVALID_SOCKET ){
132
return 0;
133
}
134
if (ds->s4!=INVALID_SOCKET ) {
135
addr4.sin_family = AF_INET;
136
addr4.sin_port = htons(iPort);
137
if( strcmp(zIp, "L")==0 ){
138
addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
139
}else{
140
addr4.sin_addr.s_addr = INADDR_ANY;
141
}
142
}
143
if( ds->s6!=INVALID_SOCKET ) {
144
DWORD ipv6only = 1; /* don't want a dual-stack socket */
145
setsockopt(ds->s6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only,
146
sizeof(ipv6only));
147
addr6.sin6_family = AF_INET6;
148
addr6.sin6_port = htons(iPort);
149
if( strcmp(zIp, "L")==0 ){
150
memcpy(&addr6.sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback));
151
}else{
152
memcpy(&addr6.sin6_addr, &in6addr_any, sizeof(in6addr_any));
153
}
154
}
155
}else{
156
if( strstr(zIp, ".") ){
157
int addrlen = sizeof(addr4);
158
ds->s4 = socket(AF_INET, SOCK_STREAM, 0);
159
if( ds->s4==INVALID_SOCKET ){
160
return 0;
161
}
162
addr4.sin_family = AF_INET;
163
if (WSAStringToAddress((char*)zIp, AF_INET, NULL,
164
(struct sockaddr *)&addr4, &addrlen) != 0){
165
return 0;
166
}
167
addr4.sin_port = htons(iPort);
168
}else{
169
DWORD ipv6only = 1; /* don't want a dual-stack socket */
170
int addrlen = sizeof(addr6);
171
ds->s6 = socket(AF_INET6, SOCK_STREAM, 0);
172
if( ds->s6==INVALID_SOCKET ){
173
return 0;
174
}
175
setsockopt(ds->s6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only,
176
sizeof(ipv6only));
177
addr6.sin6_family = AF_INET6;
178
if (WSAStringToAddress((char*)zIp, AF_INET6, NULL,
179
(struct sockaddr *)&addr6, &addrlen) != 0){
180
return 0;
181
}
182
addr6.sin6_port = htons(iPort);
183
}
184
}
185
assert( ds->s4!=INVALID_SOCKET || ds->s6!=INVALID_SOCKET );
186
if( ds->s4!=INVALID_SOCKET && bind(ds->s4, (struct sockaddr*)&addr4,
187
sizeof(addr4))==SOCKET_ERROR ){
188
return 0;
189
}
190
if( ds->s6!=INVALID_SOCKET && bind(ds->s6, (struct sockaddr*)&addr6,
191
sizeof(addr6))==SOCKET_ERROR ){
192
return 0;
193
}
194
if( ds->s4!=INVALID_SOCKET && listen(ds->s4, SOMAXCONN)==SOCKET_ERROR ){
195
return 0;
196
}
197
if( ds->s6!=INVALID_SOCKET && listen(ds->s6, SOMAXCONN)==SOCKET_ERROR ){
198
return 0;
199
}
200
return 1;
201
};
202
203
/*
204
** Accepts connections on DualSocket.
205
*/
206
static void DualSocket_accept(DualSocket* pListen, DualSocket* pClient,
207
DualAddr* pClientAddr){
208
fd_set rs;
209
int rs_count = 0;
210
assert( pListen!=NULL && pClient!=NULL && pClientAddr!= NULL );
211
DualSocket_init(pClient);
212
DualAddr_init(pClientAddr);
213
FD_ZERO(&rs);
214
if( pListen->s4!=INVALID_SOCKET ){
215
FD_SET(pListen->s4, &rs);
216
++rs_count;
217
}
218
if( pListen->s6!=INVALID_SOCKET ){
219
FD_SET(pListen->s6, &rs);
220
++rs_count;
221
}
222
if( select(rs_count, &rs, 0, 0, 0 /*blocking*/)==SOCKET_ERROR ){
223
return;
224
}
225
if( FD_ISSET(pListen->s4, &rs) ){
226
pClient->s4 = accept(pListen->s4, (struct sockaddr*)&pClientAddr->a4.addr,
227
&pClientAddr->a4.len);
228
}
229
if( FD_ISSET(pListen->s6, &rs) ){
230
pClient->s6 = accept(pListen->s6, (struct sockaddr*)&pClientAddr->a6.addr,
231
&pClientAddr->a6.len);
232
}
233
}
234
235
/*
236
** The HttpServer structure holds information about an instance of
237
** the HTTP server itself.
238
*/
239
typedef struct HttpServer HttpServer;
240
struct HttpServer {
241
HANDLE hStoppedEvent; /* Event to signal when server is stopped,
242
** must be closed by callee. */
243
char *zStopper; /* The stopper file name, must be freed by
244
** callee. */
245
DualSocket listener; /* Sockets on which the server is listening,
246
** may be closed by callee. */
247
};
248
249
/*
250
** The HttpRequest structure holds information about each incoming
251
** HTTP request.
252
*/
253
typedef struct HttpRequest HttpRequest;
254
struct HttpRequest {
255
int id; /* ID counter */
256
SOCKET s; /* Socket on which to receive data */
257
SocketAddr addr; /* Address from which data is coming */
258
int flags; /* Flags passed to win32_http_server() */
259
const char *zOptions; /* --baseurl, --notfound, --localauth, --th-trace */
260
};
261
262
/*
263
** Prefix for a temporary file.
264
*/
265
static char *zTempPrefix;
266
267
/*
268
** Look at the HTTP header contained in zHdr. Find the content
269
** length and return it. Return 0 if there is no Content-Length:
270
** header line.
271
*/
272
static int find_content_length(const char *zHdr){
273
while( *zHdr ){
274
if( zHdr[0]=='\n' ){
275
if( zHdr[1]=='\r' ) return 0;
276
if( fossil_strnicmp(&zHdr[1], "content-length:", 15)==0 ){
277
return atoi(&zHdr[17]);
278
}
279
}
280
zHdr++;
281
}
282
return 0;
283
}
284
285
/*
286
** Issue a fatal error.
287
*/
288
static NORETURN void winhttp_fatal(
289
const char *zOp,
290
const char *zService,
291
const char *zErr
292
){
293
fossil_fatal("unable to %s service '%s': %s", zOp, zService, zErr);
294
}
295
296
/*
297
** Make sure the server stops as soon as possible after the stopper file
298
** is found. If there is no stopper file name, do nothing.
299
*/
300
static void win32_server_stopper(void *pAppData){
301
HttpServer *p = (HttpServer*)pAppData;
302
if( p!=0 ){
303
HANDLE hStoppedEvent = p->hStoppedEvent;
304
const char *zStopper = p->zStopper;
305
if( hStoppedEvent!=NULL && zStopper!=0 ){
306
while( 1 ){
307
DWORD dwResult = WaitForMultipleObjectsEx(1, &hStoppedEvent, FALSE,
308
1000, TRUE);
309
if( dwResult!=WAIT_IO_COMPLETION && dwResult!=WAIT_TIMEOUT ){
310
/* The event is either invalid, signaled, or abandoned. Bail
311
** out now because those conditions should indicate the parent
312
** thread is dead or dying. */
313
break;
314
}
315
if( file_size(zStopper, ExtFILE)>=0 ){
316
/* The stopper file has been found. Attempt to close the server
317
** listener socket now and then exit. */
318
DualSocket_close(&p->listener);
319
break;
320
}
321
}
322
}
323
if( hStoppedEvent!=NULL ){
324
CloseHandle(hStoppedEvent);
325
p->hStoppedEvent = NULL;
326
}
327
if( zStopper!=0 ){
328
fossil_free(p->zStopper);
329
p->zStopper = 0;
330
}
331
fossil_free(p);
332
}
333
}
334
335
/*
336
** Process a single incoming HTTP request.
337
*/
338
static void win32_http_request(void *pAppData){
339
HttpRequest *p = (HttpRequest*)pAppData;
340
FILE *in = 0, *out = 0, *aux = 0;
341
int amt, got, i;
342
int wanted = 0;
343
char *z;
344
char *zIp;
345
void *sslConn = 0;
346
char zCmdFName[MAX_PATH];
347
char zRequestFName[MAX_PATH];
348
char zReplyFName[MAX_PATH];
349
char zCmd[2000]; /* Command-line to process the request */
350
char zBuf[65536]; /* The HTTP request header */
351
const int szHdr = 4000; /* Reduced header size */
352
353
sqlite3_snprintf(MAX_PATH, zCmdFName,
354
"%s_%06d_cmd.txt", zTempPrefix, p->id);
355
sqlite3_snprintf(MAX_PATH, zRequestFName,
356
"%s_%06d_in.txt", zTempPrefix, p->id);
357
sqlite3_snprintf(MAX_PATH, zReplyFName,
358
"%s_%06d_out.txt", zTempPrefix, p->id);
359
amt = 0;
360
if( g.httpUseSSL ){
361
#ifdef FOSSIL_ENABLE_SSL
362
sslConn = ssl_new_server(p->s);
363
#endif
364
}
365
while( amt<szHdr ){
366
if( sslConn ){
367
#ifdef FOSSIL_ENABLE_SSL
368
got = ssl_read_server(sslConn, &zBuf[amt], szHdr-1-amt, 0);
369
#endif
370
}else{
371
got = recv(p->s, &zBuf[amt], szHdr-1-amt, 0);
372
if( got==SOCKET_ERROR ) goto end_request;
373
}
374
if( got==0 ){
375
wanted = 0;
376
break;
377
}
378
amt += got;
379
zBuf[amt] = 0;
380
z = strstr(zBuf, "\r\n\r\n");
381
if( z ){
382
wanted = find_content_length(zBuf) + (&z[4]-zBuf) - amt;
383
break;
384
}else{
385
z = strstr(zBuf, "\n\n");
386
if( z ){
387
wanted = find_content_length(zBuf) + (&z[2]-zBuf) - amt;
388
break;
389
}
390
}
391
}
392
if( amt>=szHdr ) goto end_request;
393
out = fossil_fopen(zRequestFName, "wb");
394
if( out==0 ) goto end_request;
395
fwrite(zBuf, 1, amt, out);
396
while( wanted>0 ){
397
if( sslConn ){
398
#ifdef FOSSIL_ENABLE_SSL
399
got = ssl_read_server(sslConn, zBuf, min(wanted, sizeof(zBuf)), 1);
400
#endif
401
}else{
402
got = recv(p->s, zBuf, sizeof(zBuf), 0);
403
if( got==SOCKET_ERROR ) goto end_request;
404
}
405
if( got>0 ){
406
fwrite(zBuf, 1, got, out);
407
}else{
408
break;
409
}
410
wanted -= got;
411
}
412
413
/*
414
** The repository name is only needed if there was no open check-out. This
415
** is designed to allow the open check-out for the interactive user to work
416
** with the local Fossil server started via the "ui" command.
417
*/
418
aux = fossil_fopen(zCmdFName, "wb");
419
if( aux==0 ) goto end_request;
420
fprintf(aux, "%s--in %s\n", get_utf8_bom(0), zRequestFName);
421
zIp = SocketAddr_toString(&p->addr);
422
fprintf(aux, "--out %s\n--ipaddr %s\n", zReplyFName, zIp);
423
fossil_free(zIp);
424
fprintf(aux, "--as %s\n", g.zCmdName);
425
if( g.zErrlog && g.zErrlog[0] ){
426
fprintf(aux,"--errorlog %s\n", g.zErrlog);
427
}
428
if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){
429
fprintf(aux,"%s",g.zRepositoryName);
430
}
431
432
sqlite3_snprintf(sizeof(zCmd), zCmd,
433
"\"%s\" http -args \"%s\"%s%s",
434
g.nameOfExe, zCmdFName,
435
g.httpUseSSL ? "" : " --nossl", p->zOptions
436
);
437
in = fossil_fopen(zReplyFName, "w+b");
438
fflush(out);
439
fflush(aux);
440
if( g.fHttpTrace ){
441
fossil_print("%s\n", zCmd);
442
}
443
fossil_system(zCmd);
444
if( in ){
445
while( (got = fread(zBuf, 1, sizeof(zBuf), in))>0 ){
446
if( sslConn ){
447
#ifdef FOSSIL_ENABLE_SSL
448
ssl_write_server(sslConn, zBuf, got);
449
#endif
450
}else{
451
send(p->s, zBuf, got, 0);
452
}
453
}
454
}
455
456
end_request:
457
if( out ) fclose(out);
458
if( aux ) fclose(aux);
459
if( in ) fclose(in);
460
/* Initiate shutdown prior to closing the socket */
461
if( sslConn!=0 ){
462
#ifdef FOSSIL_ENABLE_SSL
463
ssl_close_server(sslConn);
464
#endif
465
}
466
if( shutdown(p->s,1)==0 ) shutdown(p->s,0);
467
closesocket(p->s);
468
/* Make multiple attempts to delete the temporary files. Sometimes AV
469
** software keeps the files open for a few seconds, preventing the file
470
** from being deleted on the first try. */
471
if( !g.fHttpTrace ){
472
for(i=1; i<=10 && file_delete(zRequestFName); i++){ Sleep(1000*i); }
473
for(i=1; i<=10 && file_delete(zCmdFName); i++){ Sleep(1000*i); }
474
for(i=1; i<=10 && file_delete(zReplyFName); i++){ Sleep(1000*i); }
475
}
476
fossil_free(p);
477
}
478
479
/*
480
** Process a single incoming SCGI request.
481
*/
482
static void win32_scgi_request(void *pAppData){
483
HttpRequest *p = (HttpRequest*)pAppData;
484
FILE *in = 0, *out = 0;
485
int amt, got, nHdr, i;
486
int wanted = 0;
487
char *zIp;
488
char zRequestFName[MAX_PATH];
489
char zReplyFName[MAX_PATH];
490
char zCmd[2000]; /* Command-line to process the request */
491
char zHdr[4000]; /* The SCGI request header */
492
493
sqlite3_snprintf(MAX_PATH, zRequestFName,
494
"%s_%06d_in.txt", zTempPrefix, p->id);
495
sqlite3_snprintf(MAX_PATH, zReplyFName,
496
"%s_%06d_out.txt", zTempPrefix, p->id);
497
out = fossil_fopen(zRequestFName, "wb");
498
if( out==0 ) goto end_request;
499
amt = 0;
500
got = recv(p->s, zHdr, sizeof(zHdr), 0);
501
if( got==SOCKET_ERROR ) goto end_request;
502
amt = fwrite(zHdr, 1, got, out);
503
nHdr = 0;
504
for(i=0; zHdr[i]>='0' && zHdr[i]<='9'; i++){
505
nHdr = 10*nHdr + zHdr[i] - '0';
506
}
507
wanted = nHdr + i + 1;
508
if( strcmp(zHdr+i+1, "CONTENT_LENGTH")==0 ){
509
wanted += atoi(zHdr+i+15);
510
}
511
while( wanted>amt ){
512
got = recv(p->s, zHdr, wanted<sizeof(zHdr) ? wanted : sizeof(zHdr), 0);
513
if( got<=0 ) break;
514
fwrite(zHdr, 1, got, out);
515
wanted += got;
516
}
517
assert( g.zRepositoryName && g.zRepositoryName[0] );
518
zIp = SocketAddr_toString(&p->addr);
519
sqlite3_snprintf(sizeof(zCmd), zCmd,
520
"\"%s\" http --in \"%s\" --out \"%s\" --ipaddr %s \"%s\""
521
" --scgi --nossl%s",
522
g.nameOfExe, zRequestFName, zReplyFName, zIp,
523
g.zRepositoryName, p->zOptions
524
);
525
fossil_free(zIp);
526
in = fossil_fopen(zReplyFName, "w+b");
527
fflush(out);
528
fossil_system(zCmd);
529
if( in ){
530
while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){
531
send(p->s, zHdr, got, 0);
532
}
533
}
534
535
end_request:
536
if( out ) fclose(out);
537
if( in ) fclose(in);
538
/* Initiate shutdown prior to closing the socket */
539
if( shutdown(p->s,1)==0 ) shutdown(p->s,0);
540
closesocket(p->s);
541
/* Make multiple attempts to delete the temporary files. Sometimes AV
542
** software keeps the files open for a few seconds, preventing the file
543
** from being deleted on the first try. */
544
for(i=1; i<=10 && file_delete(zRequestFName); i++){ Sleep(1000*i); }
545
for(i=1; i<=10 && file_delete(zReplyFName); i++){ Sleep(1000*i); }
546
fossil_free(p);
547
}
548
549
/* forward reference */
550
static void win32_http_service_running(DualSocket* pS);
551
552
/*
553
** Start a listening socket and process incoming HTTP requests on
554
** that socket.
555
*/
556
void win32_http_server(
557
int mnPort, int mxPort, /* Range of allowed TCP port numbers */
558
const char *zBrowser, /* Command to launch browser. (Or NULL) */
559
const char *zStopper, /* Stop server when this file is exists (Or NULL) */
560
const char *zBaseUrl, /* The --baseurl option, or NULL */
561
const char *zNotFound, /* The --notfound option, or NULL */
562
const char *zFileGlob, /* The --fileglob option, or NULL */
563
const char *zIpAddr, /* Bind to this IP address, if not NULL */
564
int flags /* One or more HTTP_SERVER_ flags */
565
){
566
HANDLE hStoppedEvent;
567
WSADATA wd;
568
DualSocket ds;
569
int idCnt = 0;
570
int iPort = mnPort;
571
Blob options;
572
wchar_t zTmpPath[MAX_PATH];
573
char *zTempSubDirPath;
574
const char *zTempSubDir = "fossil";
575
const char *zSkin;
576
#if USE_SEE
577
const char *zSavedKey = 0;
578
size_t savedKeySize = 0;
579
#endif
580
581
blob_zero(&options);
582
if( PB("HTTPS") ){
583
blob_appendf(&options, " --https");
584
}
585
if( zBaseUrl ){
586
blob_appendf(&options, " --baseurl ");
587
blob_append_escaped_arg(&options, zBaseUrl, 0);
588
}
589
if( zNotFound ){
590
blob_appendf(&options, " --notfound ");
591
blob_append_escaped_arg(&options, zNotFound, 1);
592
}
593
if( g.zCkoutAlias ){
594
blob_appendf(&options, " --ckout-alias ");
595
blob_append_escaped_arg(&options, g.zCkoutAlias, 0);
596
}
597
if( zFileGlob ){
598
blob_appendf(&options, " --files-urlenc %T", zFileGlob);
599
}
600
if( g.useLocalauth ){
601
blob_appendf(&options, " --localauth");
602
}
603
if( g.thTrace ){
604
blob_appendf(&options, " --th-trace");
605
}
606
if( flags & HTTP_SERVER_REPOLIST ){
607
blob_appendf(&options, " --repolist");
608
}
609
if( g.zExtRoot && g.zExtRoot[0] ){
610
blob_appendf(&options, " --extroot");
611
blob_append_escaped_arg(&options, g.zExtRoot, 1);
612
}
613
zSkin = skin_in_use();
614
if( zSkin ){
615
blob_appendf(&options, " --skin %s", zSkin);
616
}
617
if( g.zMainMenuFile ){
618
blob_appendf(&options, " --mainmenu ");
619
blob_append_escaped_arg(&options, g.zMainMenuFile, 1);
620
}
621
if( builtin_get_js_delivery_mode()!=0 /* JS_INLINE==0 may change? */ ){
622
blob_appendf(&options, " --jsmode ");
623
blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0);
624
}
625
#if USE_SEE
626
zSavedKey = db_get_saved_encryption_key();
627
savedKeySize = db_get_saved_encryption_key_size();
628
if( db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ){
629
blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(),
630
zSavedKey, savedKeySize);
631
}
632
#endif
633
if( WSAStartup(MAKEWORD(2,0), &wd) ){
634
fossil_panic("unable to initialize winsock");
635
}
636
DualSocket_init(&ds);
637
while( iPort<=mxPort ){
638
if( zIpAddr ){
639
if( DualSocket_listen(&ds, zIpAddr, iPort)==0 ){
640
iPort++;
641
continue;
642
}
643
}else{
644
if( DualSocket_listen(&ds,
645
(flags & HTTP_SERVER_LOCALHOST) ? "L" : "W",
646
iPort
647
)==0 ){
648
iPort++;
649
continue;
650
}
651
}
652
break;
653
}
654
if( iPort>mxPort ){
655
/* These exits are merely fatal because firewall settings can cause them. */
656
if( mnPort==mxPort ){
657
fossil_fatal("unable to open listening socket on port %d", mnPort);
658
}else{
659
fossil_fatal("unable to open listening socket on any"
660
" port in the range %d..%d", mnPort, mxPort);
661
}
662
}
663
if( !GetTempPathW(MAX_PATH, zTmpPath) ){
664
fossil_panic("unable to get path to the temporary directory.");
665
}
666
/* Use a subdirectory for temp files (can then be excluded from virus scan) */
667
zTempSubDirPath = mprintf("%s%s\\",fossil_path_to_utf8(zTmpPath),zTempSubDir);
668
if ( !file_mkdir(zTempSubDirPath, ExtFILE, 0) ||
669
file_isdir(zTempSubDirPath, ExtFILE)==1 ){
670
wcscpy(zTmpPath, fossil_utf8_to_path(zTempSubDirPath, 1));
671
}
672
if( g.fHttpTrace ){
673
zTempPrefix = mprintf("httptrace");
674
}else{
675
zTempPrefix = mprintf("%sfossil_server_P%d",
676
fossil_unicode_to_utf8(zTmpPath), iPort);
677
}
678
fossil_print("Temporary files: %s*\n", zTempPrefix);
679
fossil_print("Listening for %s requests on TCP port %d\n",
680
(flags&HTTP_SERVER_SCGI)!=0 ? "SCGI" :
681
g.httpUseSSL ? "TLS-encrypted HTTPS" : "HTTP", iPort);
682
if( zBrowser ){
683
zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
684
fossil_print("Launch webbrowser: %s\n", zBrowser);
685
fossil_system(zBrowser);
686
}
687
fossil_print("Type Ctrl-C to stop the HTTP server\n");
688
/* Create an event used to signal when this server is exiting. */
689
hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
690
assert( hStoppedEvent!=NULL );
691
/* If there is a stopper file name, start the dedicated thread now.
692
** It will attempt to close the listener socket within 1 second of
693
** the stopper file being created. */
694
if( zStopper ){
695
HttpServer *pServer = fossil_malloc(sizeof(HttpServer));
696
memset(pServer, 0, sizeof(HttpServer));
697
DuplicateHandle(GetCurrentProcess(), hStoppedEvent,
698
GetCurrentProcess(), &pServer->hStoppedEvent,
699
0, FALSE, DUPLICATE_SAME_ACCESS);
700
assert( pServer->hStoppedEvent!=NULL );
701
pServer->zStopper = fossil_strdup(zStopper);
702
pServer->listener = ds;
703
file_delete(zStopper);
704
_beginthread(win32_server_stopper, 0, (void*)pServer);
705
}
706
/* Set the service status to running and pass the listener socket to the
707
** service handling procedures. */
708
win32_http_service_running(&ds);
709
for(;;){
710
DualSocket client;
711
DualAddr client_addr;
712
HttpRequest *pRequest;
713
int wsaError;
714
715
DualSocket_accept(&ds, &client, &client_addr);
716
if( client.s4==INVALID_SOCKET && client.s6==INVALID_SOCKET ){
717
/* If the service control handler has closed the listener socket,
718
** cleanup and return, otherwise report a fatal error. */
719
wsaError = WSAGetLastError();
720
DualSocket_close(&ds);
721
if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){
722
WSACleanup();
723
return;
724
}else{
725
WSACleanup();
726
fossil_panic("error from accept()");
727
}
728
}
729
if( client.s4!=INVALID_SOCKET ){
730
pRequest = fossil_malloc(sizeof(HttpRequest));
731
pRequest->id = ++idCnt;
732
pRequest->s = client.s4;
733
memcpy(&pRequest->addr, &client_addr.a4, sizeof(client_addr.a4));
734
pRequest->flags = flags;
735
pRequest->zOptions = blob_str(&options);
736
if( flags & HTTP_SERVER_SCGI ){
737
_beginthread(win32_scgi_request, 0, (void*)pRequest);
738
}else{
739
_beginthread(win32_http_request, 0, (void*)pRequest);
740
}
741
}
742
if( client.s6!=INVALID_SOCKET ){
743
pRequest = fossil_malloc(sizeof(HttpRequest));
744
pRequest->id = ++idCnt;
745
pRequest->s = client.s6;
746
memcpy(&pRequest->addr, &client_addr.a6, sizeof(client_addr.a6));
747
pRequest->flags = flags;
748
pRequest->zOptions = blob_str(&options);
749
if( flags & HTTP_SERVER_SCGI ){
750
_beginthread(win32_scgi_request, 0, (void*)pRequest);
751
}else{
752
_beginthread(win32_http_request, 0, (void*)pRequest);
753
}
754
}
755
}
756
DualSocket_close(&ds);
757
WSACleanup();
758
SetEvent(hStoppedEvent);
759
CloseHandle(hStoppedEvent);
760
}
761
762
/*
763
** The HttpService structure is used to pass information to the service main
764
** function and to the service control handler function.
765
*/
766
typedef struct HttpService HttpService;
767
struct HttpService {
768
int port; /* Port on which the http server should run */
769
const char *zBaseUrl; /* The --baseurl option, or NULL */
770
const char *zNotFound; /* The --notfound option, or NULL */
771
const char *zFileGlob; /* The --files option, or NULL */
772
int flags; /* One or more HTTP_SERVER_ flags */
773
int isRunningAsService; /* Are we running as a service ? */
774
const wchar_t *zServiceName;/* Name of the service */
775
DualSocket s; /* Sockets on which the http server listens */
776
};
777
778
/*
779
** Variables used for running as windows service.
780
*/
781
static HttpService hsData = {8080, NULL, NULL, NULL, 0, 0, NULL,
782
{INVALID_SOCKET, INVALID_SOCKET}};
783
static SERVICE_STATUS ssStatus;
784
static SERVICE_STATUS_HANDLE sshStatusHandle;
785
786
/*
787
** Get message string of the last system error. Return a pointer to the
788
** message string. Call fossil_unicode_free() to deallocate any memory used
789
** to store the message string when done.
790
*/
791
static char *win32_get_last_errmsg(void){
792
DWORD nMsg;
793
DWORD nErr = GetLastError();
794
LPWSTR tmp = NULL;
795
char *zMsg = NULL;
796
797
/* Try first to get the error text in English. */
798
nMsg = FormatMessageW(
799
FORMAT_MESSAGE_ALLOCATE_BUFFER |
800
FORMAT_MESSAGE_FROM_SYSTEM |
801
FORMAT_MESSAGE_IGNORE_INSERTS,
802
NULL,
803
nErr,
804
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
805
(LPWSTR) &tmp,
806
0,
807
NULL
808
);
809
if( !nMsg ){
810
/* No English, get what the system has available. */
811
nMsg = FormatMessageW(
812
FORMAT_MESSAGE_ALLOCATE_BUFFER |
813
FORMAT_MESSAGE_FROM_SYSTEM |
814
FORMAT_MESSAGE_IGNORE_INSERTS,
815
NULL,
816
nErr,
817
0,
818
(LPWSTR) &tmp,
819
0,
820
NULL
821
);
822
}
823
if( nMsg ){
824
zMsg = fossil_unicode_to_utf8(tmp);
825
}else{
826
fossil_panic("unable to get system error message.");
827
}
828
if( tmp ){
829
LocalFree((HLOCAL) tmp);
830
}
831
return zMsg;
832
}
833
834
/*
835
** Report the current status of the service to the service control manager.
836
** Make sure that during service startup no control codes are accepted.
837
*/
838
static void win32_report_service_status(
839
DWORD dwCurrentState, /* The current state of the service */
840
DWORD dwWin32ExitCode, /* The error code to report */
841
DWORD dwWaitHint /* The estimated time for a pending operation */
842
){
843
if( dwCurrentState==SERVICE_START_PENDING ){
844
ssStatus.dwControlsAccepted = 0;
845
}else{
846
ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
847
}
848
ssStatus.dwCurrentState = dwCurrentState;
849
ssStatus.dwWin32ExitCode = dwWin32ExitCode;
850
ssStatus.dwWaitHint = dwWaitHint;
851
852
if( (dwCurrentState==SERVICE_RUNNING) ||
853
(dwCurrentState==SERVICE_STOPPED) ){
854
ssStatus.dwCheckPoint = 0;
855
}else{
856
ssStatus.dwCheckPoint++;
857
}
858
SetServiceStatus(sshStatusHandle, &ssStatus);
859
return ;
860
}
861
862
/*
863
** Handle control codes sent from the service control manager.
864
** The control dispatcher in the main thread of the service process invokes
865
** this function whenever it receives a control request from the service
866
** control manager.
867
*/
868
static void WINAPI win32_http_service_ctrl(
869
DWORD dwCtrlCode
870
){
871
switch( dwCtrlCode ){
872
case SERVICE_CONTROL_STOP: {
873
DualSocket_close(&hsData.s);
874
win32_report_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
875
break;
876
}
877
default: {
878
break;
879
}
880
}
881
return;
882
}
883
884
/*
885
** This is the main entry point for the service.
886
** When the service control manager receives a request to start the service,
887
** it starts the service process (if it is not already running). The main
888
** thread of the service process calls the StartServiceCtrlDispatcher
889
** function with a pointer to an array of SERVICE_TABLE_ENTRY structures.
890
** Then the service control manager sends a start request to the service
891
** control dispatcher for this service process. The service control dispatcher
892
** creates a new thread to execute the ServiceMain function (this function)
893
** of the service being started.
894
*/
895
static void WINAPI win32_http_service_main(
896
DWORD argc, /* Number of arguments in argv */
897
LPWSTR *argv /* Arguments passed */
898
){
899
900
/* Update the service information. */
901
hsData.isRunningAsService = 1;
902
if( argc>0 ){
903
hsData.zServiceName = argv[0];
904
}
905
906
/* Register the service control handler function */
907
sshStatusHandle = RegisterServiceCtrlHandlerW(L"", win32_http_service_ctrl);
908
if( !sshStatusHandle ){
909
win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0);
910
return;
911
}
912
913
/* Set service specific data and report that the service is starting. */
914
ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
915
ssStatus.dwServiceSpecificExitCode = 0;
916
win32_report_service_status(SERVICE_START_PENDING, NO_ERROR, 3000);
917
918
/* Execute the http server */
919
win32_http_server(hsData.port, hsData.port,
920
NULL, NULL, hsData.zBaseUrl, hsData.zNotFound,
921
hsData.zFileGlob, 0, hsData.flags);
922
923
/* Service has stopped now. */
924
win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0);
925
return;
926
}
927
928
/*
929
** When running as service, update the HttpService structure with the
930
** listener socket and update the service status. This procedure must be
931
** called from the http server when he is ready to accept connections.
932
*/
933
static void win32_http_service_running(DualSocket *pS){
934
if( hsData.isRunningAsService ){
935
hsData.s = *pS;
936
win32_report_service_status(SERVICE_RUNNING, NO_ERROR, 0);
937
}
938
}
939
940
/*
941
** Try to start the http server as a windows service. If we are running in
942
** an interactive console session, this routine fails and returns a non zero
943
** integer value. When running as service, this routine does not return until
944
** the service is stopped. In this case, the return value is zero.
945
*/
946
int win32_http_service(
947
int nPort, /* TCP port number */
948
const char *zBaseUrl, /* The --baseurl option, or NULL */
949
const char *zNotFound, /* The --notfound option, or NULL */
950
const char *zFileGlob, /* The --files option, or NULL */
951
int flags /* One or more HTTP_SERVER_ flags */
952
){
953
/* Define the service table. */
954
SERVICE_TABLE_ENTRYW ServiceTable[] =
955
{{L"", (LPSERVICE_MAIN_FUNCTIONW)win32_http_service_main}, {NULL, NULL}};
956
957
/* Initialize the HttpService structure. */
958
hsData.port = nPort;
959
hsData.zBaseUrl = zBaseUrl;
960
hsData.zNotFound = zNotFound;
961
hsData.zFileGlob = zFileGlob;
962
hsData.flags = flags;
963
964
if( GetStdHandle(STD_INPUT_HANDLE)!=NULL ){ return 1; }
965
966
/* Try to start the control dispatcher thread for the service. */
967
if( !StartServiceCtrlDispatcherW(ServiceTable) ){
968
if( GetLastError()==ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ){
969
return 1;
970
}else{
971
fossil_fatal("error from StartServiceCtrlDispatcher()");
972
}
973
}
974
return 0;
975
}
976
977
/* Duplicate #ifdef needed for mkindex */
978
#ifdef _WIN32
979
/*
980
** COMMAND: winsrv*
981
**
982
** Usage: %fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS?
983
**
984
** Where METHOD is one of: create delete show start stop.
985
**
986
** The winsrv command manages Fossil as a Windows service. This allows
987
** (for example) Fossil to be running in the background when no user
988
** is logged in.
989
**
990
** In the following description of the methods, "Fossil-DSCM" will be
991
** used as the default SERVICE-NAME:
992
**
993
** %fossil winsrv create ?SERVICE-NAME? ?OPTIONS?
994
**
995
** Creates a service. Available options include:
996
**
997
** -D|--display DISPLAY-NAME
998
**
999
** Sets the display name of the service. This name is shown
1000
** by graphical interface programs. By default, the display name
1001
** is equal to the service name.
1002
**
1003
** -S|--start TYPE
1004
**
1005
** Sets the start type of the service. TYPE can be "manual",
1006
** which means you need to start the service yourself with the
1007
** 'fossil winsrv start' command or with the "net start" command
1008
** from the operating system. If TYPE is set to "auto", the service
1009
** will be started automatically by the system during startup.
1010
**
1011
** --username USERNAME
1012
**
1013
** Specifies the user account which will be used to run the
1014
** service. The account needs the "Logon as a service" right
1015
** enabled in its profile. Specify local accounts as follows:
1016
** ".\\USERNAME". By default, the "LocalSystem" account will be
1017
** used.
1018
**
1019
** -W|--password PASSWORD
1020
**
1021
** Password for the user account.
1022
**
1023
** The following options are more or less the same as for the "server"
1024
** command and influence the behavior of the http server:
1025
**
1026
** --baseurl URL
1027
**
1028
** Use URL as the base (useful for reverse proxies)
1029
**
1030
** -P|--port TCPPORT
1031
**
1032
** Specifies the TCP port (default port is 8080) on which the
1033
** server should listen.
1034
**
1035
** -R|--repository REPO
1036
**
1037
** Specifies the name of the repository to be served.
1038
** The repository option may be omitted if the working directory
1039
** is within an open check-out.
1040
** The REPOSITORY can be a directory (aka folder) that contains
1041
** one or more repositories with names ending in ".fossil".
1042
** In that case, the first element of the URL is used to select
1043
** among the various repositories.
1044
**
1045
** --notfound URL
1046
**
1047
** If REPOSITORY is a directory that contains one or more
1048
** repositories with names of the form "*.fossil" then the
1049
** first element of the URL pathname selects among the various
1050
** repositories. If the pathname does not select a valid
1051
** repository and the --notfound option is available,
1052
** then the server redirects (HTTP code 302) to the URL of
1053
** --notfound.
1054
**
1055
** --localauth
1056
**
1057
** Enables automatic login if the --localauth option is present
1058
** and the "localauth" setting is off and the connection is from
1059
** localhost.
1060
**
1061
** --repolist
1062
**
1063
** If REPOSITORY is directory, URL "/" lists all repositories.
1064
**
1065
** --scgi
1066
**
1067
** Create an SCGI server instead of an HTTP server
1068
**
1069
**
1070
** %fossil winsrv delete ?SERVICE-NAME?
1071
**
1072
** Deletes a service. If the service is currently running, it will be
1073
** stopped first and then deleted.
1074
**
1075
**
1076
** %fossil winsrv show ?SERVICE-NAME?
1077
**
1078
** Shows how the service is configured and its current state.
1079
**
1080
**
1081
** %fossil winsrv start ?SERVICE-NAME?
1082
**
1083
** Start the service.
1084
**
1085
**
1086
** %fossil winsrv stop ?SERVICE-NAME?
1087
**
1088
** Stop the service.
1089
**
1090
**
1091
** NOTE: This command is available on Windows operating systems only and
1092
** requires administrative rights on the machine executed.
1093
**
1094
*/
1095
void cmd_win32_service(void){
1096
int n;
1097
const char *zMethod;
1098
const char *zSvcName = "Fossil-DSCM"; /* Default service name */
1099
1100
if( g.argc<3 ){
1101
usage("create|delete|show|start|stop ...");
1102
}
1103
zMethod = g.argv[2];
1104
n = strlen(zMethod);
1105
1106
if( strncmp(zMethod, "create", n)==0 ){
1107
SC_HANDLE hScm;
1108
SC_HANDLE hSvc;
1109
SERVICE_DESCRIPTIONW
1110
svcDescr = {L"Fossil - Distributed Software Configuration Management"};
1111
DWORD dwStartType = SERVICE_DEMAND_START;
1112
const char *zAltBase = find_option("baseurl", 0, 1);
1113
const char *zDisplay = find_option("display", "D", 1);
1114
const char *zStart = find_option("start", "S", 1);
1115
const char *zUsername = find_option("username", 0, 1);
1116
const char *zPassword = find_option("password", "W", 1);
1117
const char *zPort = find_option("port", "P", 1);
1118
const char *zNotFound = find_option("notfound", 0, 1);
1119
const char *zFileGlob = find_option("files", 0, 1);
1120
const char *zLocalAuth = find_option("localauth", 0, 0);
1121
const char *zRepository = find_repository_option();
1122
int useSCGI = find_option("scgi", 0, 0)!=0;
1123
int allowRepoList = find_option("repolist",0,0)!=0;
1124
Blob binPath;
1125
1126
verify_all_options();
1127
if( g.argc==4 ){
1128
zSvcName = g.argv[3];
1129
}else if( g.argc>4 ){
1130
fossil_fatal("too many arguments for create method.");
1131
}
1132
/* Process service creation specific options. */
1133
if( !zDisplay ){
1134
zDisplay = zSvcName;
1135
}
1136
/* Per MSDN, the password parameter cannot be NULL. Must use empty
1137
** string instead (i.e. in the call to CreateServiceW). */
1138
if( !zPassword ){
1139
zPassword = "";
1140
}
1141
if( zStart ){
1142
if( strncmp(zStart, "auto", strlen(zStart))==0 ){
1143
dwStartType = SERVICE_AUTO_START;
1144
}else if( strncmp(zStart, "manual", strlen(zStart))==0 ){
1145
dwStartType = SERVICE_DEMAND_START;
1146
}else{
1147
winhttp_fatal("create", zSvcName,
1148
"specify 'auto' or 'manual' for the '-S|--start' option");
1149
}
1150
}
1151
/* Process options for Fossil running as server. */
1152
if( zPort && (atoi(zPort)<=0) ){
1153
winhttp_fatal("create", zSvcName,
1154
"port number must be in the range 1 - 65535.");
1155
}
1156
if( !zRepository ){
1157
db_must_be_within_tree();
1158
}else if( file_isdir(zRepository, ExtFILE)==1 ){
1159
g.zRepositoryName = fossil_strdup(zRepository);
1160
file_simplify_name(g.zRepositoryName, -1, 0);
1161
}else{
1162
db_open_repository(zRepository);
1163
}
1164
db_close(0);
1165
/* Build the fully-qualified path to the service binary file. */
1166
blob_zero(&binPath);
1167
blob_appendf(&binPath, "\"%s\" server", g.nameOfExe);
1168
if( zAltBase ) blob_appendf(&binPath, " --baseurl %s", zAltBase);
1169
if( zPort ) blob_appendf(&binPath, " --port %s", zPort);
1170
if( useSCGI ) blob_appendf(&binPath, " --scgi");
1171
if( allowRepoList ) blob_appendf(&binPath, " --repolist");
1172
if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound);
1173
if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob);
1174
if( zLocalAuth ) blob_append(&binPath, " --localauth", -1);
1175
blob_appendf(&binPath, " \"%s\"", g.zRepositoryName);
1176
/* Create the service. */
1177
hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1178
if( !hScm ) winhttp_fatal("create", zSvcName, win32_get_last_errmsg());
1179
hSvc = CreateServiceW(
1180
hScm, /* Handle to the SCM */
1181
fossil_utf8_to_unicode(zSvcName), /* Name of the service */
1182
fossil_utf8_to_unicode(zDisplay), /* Display name */
1183
SERVICE_ALL_ACCESS, /* Desired access */
1184
SERVICE_WIN32_OWN_PROCESS, /* Service type */
1185
dwStartType, /* Start type */
1186
SERVICE_ERROR_NORMAL, /* Error control */
1187
fossil_utf8_to_unicode(blob_str(&binPath)), /* Binary path */
1188
NULL, /* Load ordering group */
1189
NULL, /* Tag value */
1190
NULL, /* Service dependencies */
1191
zUsername ? fossil_utf8_to_unicode(zUsername) : 0, /* Account */
1192
fossil_utf8_to_unicode(zPassword) /* Account password */
1193
);
1194
if( !hSvc ) winhttp_fatal("create", zSvcName, win32_get_last_errmsg());
1195
/* Set the service description. */
1196
ChangeServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION, &svcDescr);
1197
fossil_print("Service '%s' successfully created.\n", zSvcName);
1198
CloseServiceHandle(hSvc);
1199
CloseServiceHandle(hScm);
1200
}else
1201
if( strncmp(zMethod, "delete", n)==0 ){
1202
SC_HANDLE hScm;
1203
SC_HANDLE hSvc;
1204
SERVICE_STATUS sstat;
1205
1206
verify_all_options();
1207
if( g.argc==4 ){
1208
zSvcName = g.argv[3];
1209
}else if( g.argc>4 ){
1210
fossil_fatal("too many arguments for delete method.");
1211
}
1212
hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1213
if( !hScm ) winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
1214
hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
1215
SERVICE_ALL_ACCESS);
1216
if( !hSvc ) winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
1217
QueryServiceStatus(hSvc, &sstat);
1218
if( sstat.dwCurrentState!=SERVICE_STOPPED ){
1219
fossil_print("Stopping service '%s'", zSvcName);
1220
if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
1221
if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
1222
winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
1223
}
1224
QueryServiceStatus(hSvc, &sstat);
1225
}
1226
while( sstat.dwCurrentState==SERVICE_STOP_PENDING ||
1227
sstat.dwCurrentState==SERVICE_RUNNING ){
1228
Sleep(100);
1229
fossil_print(".");
1230
QueryServiceStatus(hSvc, &sstat);
1231
}
1232
if( sstat.dwCurrentState==SERVICE_STOPPED ){
1233
fossil_print("\nService '%s' stopped.\n", zSvcName);
1234
}else{
1235
winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
1236
}
1237
}
1238
if( !DeleteService(hSvc) ){
1239
if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){
1240
fossil_warning("Service '%s' already marked for delete.\n", zSvcName);
1241
}else{
1242
winhttp_fatal("delete", zSvcName, win32_get_last_errmsg());
1243
}
1244
}else{
1245
fossil_print("Service '%s' successfully deleted.\n", zSvcName);
1246
}
1247
CloseServiceHandle(hSvc);
1248
CloseServiceHandle(hScm);
1249
}else
1250
if( strncmp(zMethod, "show", n)==0 ){
1251
SC_HANDLE hScm;
1252
SC_HANDLE hSvc;
1253
SERVICE_STATUS sstat;
1254
LPQUERY_SERVICE_CONFIGW pSvcConfig;
1255
LPSERVICE_DESCRIPTIONW pSvcDescr;
1256
BOOL bStatus;
1257
DWORD nRequired;
1258
static const char *const zSvcTypes[] = {
1259
"Driver service",
1260
"File system driver service",
1261
"Service runs in its own process",
1262
"Service shares a process with other services",
1263
"Service can interact with the desktop"
1264
};
1265
const char *zSvcType = "";
1266
static const char *const zSvcStartTypes[] = {
1267
"Started by the system loader",
1268
"Started by the IoInitSystem function",
1269
"Started automatically by the service control manager",
1270
"Started manually",
1271
"Service cannot be started"
1272
};
1273
const char *zSvcStartType = "";
1274
static const char *const zSvcStates[] = {
1275
"Stopped", "Starting", "Stopping", "Running",
1276
"Continue pending", "Pause pending", "Paused"
1277
};
1278
const char *zSvcState = "";
1279
1280
verify_all_options();
1281
if( g.argc==4 ){
1282
zSvcName = g.argv[3];
1283
}else if( g.argc>4 ){
1284
fossil_fatal("too many arguments for show method.");
1285
}
1286
hScm = OpenSCManagerW(NULL, NULL, GENERIC_READ);
1287
if( !hScm ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
1288
hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName), GENERIC_READ);
1289
if( !hSvc ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
1290
/* Get the service configuration */
1291
bStatus = QueryServiceConfigW(hSvc, NULL, 0, &nRequired);
1292
if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
1293
winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
1294
}
1295
pSvcConfig = fossil_malloc(nRequired);
1296
bStatus = QueryServiceConfigW(hSvc, pSvcConfig, nRequired, &nRequired);
1297
if( !bStatus ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
1298
/* Translate the service type */
1299
switch( pSvcConfig->dwServiceType ){
1300
case SERVICE_KERNEL_DRIVER: zSvcType = zSvcTypes[0]; break;
1301
case SERVICE_FILE_SYSTEM_DRIVER: zSvcType = zSvcTypes[1]; break;
1302
case SERVICE_WIN32_OWN_PROCESS: zSvcType = zSvcTypes[2]; break;
1303
case SERVICE_WIN32_SHARE_PROCESS: zSvcType = zSvcTypes[3]; break;
1304
case SERVICE_INTERACTIVE_PROCESS: zSvcType = zSvcTypes[4]; break;
1305
}
1306
/* Translate the service start type */
1307
switch( pSvcConfig->dwStartType ){
1308
case SERVICE_BOOT_START: zSvcStartType = zSvcStartTypes[0]; break;
1309
case SERVICE_SYSTEM_START: zSvcStartType = zSvcStartTypes[1]; break;
1310
case SERVICE_AUTO_START: zSvcStartType = zSvcStartTypes[2]; break;
1311
case SERVICE_DEMAND_START: zSvcStartType = zSvcStartTypes[3]; break;
1312
case SERVICE_DISABLED: zSvcStartType = zSvcStartTypes[4]; break;
1313
}
1314
/* Get the service description. */
1315
bStatus = QueryServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION,
1316
NULL, 0, &nRequired);
1317
if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){
1318
winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
1319
}
1320
pSvcDescr = fossil_malloc(nRequired);
1321
bStatus = QueryServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION,
1322
(LPBYTE)pSvcDescr, nRequired, &nRequired);
1323
if( !bStatus ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
1324
/* Retrieves the current status of the specified service. */
1325
bStatus = QueryServiceStatus(hSvc, &sstat);
1326
if( !bStatus ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg());
1327
/* Translate the current state. */
1328
switch( sstat.dwCurrentState ){
1329
case SERVICE_STOPPED: zSvcState = zSvcStates[0]; break;
1330
case SERVICE_START_PENDING: zSvcState = zSvcStates[1]; break;
1331
case SERVICE_STOP_PENDING: zSvcState = zSvcStates[2]; break;
1332
case SERVICE_RUNNING: zSvcState = zSvcStates[3]; break;
1333
case SERVICE_CONTINUE_PENDING: zSvcState = zSvcStates[4]; break;
1334
case SERVICE_PAUSE_PENDING: zSvcState = zSvcStates[5]; break;
1335
case SERVICE_PAUSED: zSvcState = zSvcStates[6]; break;
1336
}
1337
/* Print service information to terminal */
1338
fossil_print("Service name .......: %s\n", zSvcName);
1339
fossil_print("Display name .......: %s\n",
1340
fossil_unicode_to_utf8(pSvcConfig->lpDisplayName));
1341
fossil_print("Service description : %s\n",
1342
fossil_unicode_to_utf8(pSvcDescr->lpDescription));
1343
fossil_print("Service type .......: %s.\n", zSvcType);
1344
fossil_print("Service start type .: %s.\n", zSvcStartType);
1345
fossil_print("Binary path name ...: %s\n",
1346
fossil_unicode_to_utf8(pSvcConfig->lpBinaryPathName));
1347
fossil_print("Service username ...: %s\n",
1348
fossil_unicode_to_utf8(pSvcConfig->lpServiceStartName));
1349
fossil_print("Current state ......: %s.\n", zSvcState);
1350
/* Cleanup */
1351
fossil_free(pSvcConfig);
1352
fossil_free(pSvcDescr);
1353
CloseServiceHandle(hSvc);
1354
CloseServiceHandle(hScm);
1355
}else
1356
if( strncmp(zMethod, "start", n)==0 ){
1357
SC_HANDLE hScm;
1358
SC_HANDLE hSvc;
1359
SERVICE_STATUS sstat;
1360
1361
verify_all_options();
1362
if( g.argc==4 ){
1363
zSvcName = g.argv[3];
1364
}else if( g.argc>4 ){
1365
fossil_fatal("too many arguments for start method.");
1366
}
1367
hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1368
if( !hScm ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
1369
hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
1370
SERVICE_ALL_ACCESS);
1371
if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
1372
QueryServiceStatus(hSvc, &sstat);
1373
if( sstat.dwCurrentState!=SERVICE_RUNNING ){
1374
fossil_print("Starting service '%s'", zSvcName);
1375
if( sstat.dwCurrentState!=SERVICE_START_PENDING ){
1376
if( !StartServiceW(hSvc, 0, NULL) ){
1377
winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
1378
}
1379
QueryServiceStatus(hSvc, &sstat);
1380
}
1381
while( sstat.dwCurrentState==SERVICE_START_PENDING ||
1382
sstat.dwCurrentState==SERVICE_STOPPED ){
1383
Sleep(100);
1384
fossil_print(".");
1385
QueryServiceStatus(hSvc, &sstat);
1386
}
1387
if( sstat.dwCurrentState==SERVICE_RUNNING ){
1388
fossil_print("\nService '%s' started.\n", zSvcName);
1389
}else{
1390
winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
1391
}
1392
}else{
1393
fossil_print("Service '%s' is already started.\n", zSvcName);
1394
}
1395
CloseServiceHandle(hSvc);
1396
CloseServiceHandle(hScm);
1397
}else
1398
if( strncmp(zMethod, "stop", n)==0 ){
1399
SC_HANDLE hScm;
1400
SC_HANDLE hSvc;
1401
SERVICE_STATUS sstat;
1402
1403
verify_all_options();
1404
if( g.argc==4 ){
1405
zSvcName = g.argv[3];
1406
}else if( g.argc>4 ){
1407
fossil_fatal("too many arguments for stop method.");
1408
}
1409
hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1410
if( !hScm ) winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
1411
hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
1412
SERVICE_ALL_ACCESS);
1413
if( !hSvc ) winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
1414
QueryServiceStatus(hSvc, &sstat);
1415
if( sstat.dwCurrentState!=SERVICE_STOPPED ){
1416
fossil_print("Stopping service '%s'", zSvcName);
1417
if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){
1418
if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){
1419
winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
1420
}
1421
QueryServiceStatus(hSvc, &sstat);
1422
}
1423
while( sstat.dwCurrentState==SERVICE_STOP_PENDING ||
1424
sstat.dwCurrentState==SERVICE_RUNNING ){
1425
Sleep(100);
1426
fossil_print(".");
1427
QueryServiceStatus(hSvc, &sstat);
1428
}
1429
if( sstat.dwCurrentState==SERVICE_STOPPED ){
1430
fossil_print("\nService '%s' stopped.\n", zSvcName);
1431
}else{
1432
winhttp_fatal("stop", zSvcName, win32_get_last_errmsg());
1433
}
1434
}else{
1435
fossil_print("Service '%s' is already stopped.\n", zSvcName);
1436
}
1437
CloseServiceHandle(hSvc);
1438
CloseServiceHandle(hScm);
1439
}else
1440
{
1441
fossil_fatal("METHOD should be one of:"
1442
" create delete show start stop");
1443
}
1444
return;
1445
}
1446
#endif /* _WIN32 -- dupe needed for mkindex */
1447
#endif /* _WIN32 -- This code is for win32 only */
1448

Keyboard Shortcuts

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