|
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 */ |