Fossil SCM

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

Keyboard Shortcuts

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