]>
Commit | Line | Data |
---|---|---|
89c4ed63 A |
1 | /* |
2 | * winrc/w_inst.h - install and remove functions | |
3 | * | |
4 | * Copyright (c) 2009, NLnet Labs. All rights reserved. | |
5 | * | |
6 | * This software is open source. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * Redistributions of source code must retain the above copyright notice, | |
13 | * this list of conditions and the following disclaimer. | |
14 | * | |
15 | * Redistributions in binary form must reproduce the above copyright notice, | |
16 | * this list of conditions and the following disclaimer in the documentation | |
17 | * and/or other materials provided with the distribution. | |
18 | * | |
19 | * Neither the name of the NLNET LABS nor the names of its contributors may | |
20 | * be used to endorse or promote products derived from this software without | |
21 | * specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
26 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
27 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |
29 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
30 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
31 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
32 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
34 | */ | |
35 | ||
36 | /** | |
37 | * \file | |
38 | * | |
39 | * Contains install and remove functions that manipulate the | |
40 | * windows services API and windows registry. | |
41 | */ | |
42 | #include "config.h" | |
43 | #include "winrc/w_inst.h" | |
44 | #include "winrc/win_svc.h" | |
45 | ||
46 | void wsvc_err2str(char* str, size_t len, const char* fixed, DWORD err) | |
47 | { | |
48 | LPTSTR buf; | |
49 | if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | | |
50 | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, | |
51 | NULL, err, 0, (LPTSTR)&buf, 0, NULL) == 0) { | |
52 | /* could not format error message */ | |
53 | snprintf(str, len, "%s GetLastError=%d", fixed, (int)err); | |
54 | return; | |
55 | } | |
56 | snprintf(str, len, "%s (err=%d): %s", fixed, (int)err, buf); | |
57 | LocalFree(buf); | |
58 | } | |
59 | ||
60 | /** exit with windows error */ | |
61 | static void | |
62 | fatal_win(FILE* out, const char* str) | |
63 | { | |
64 | char e[256]; | |
65 | wsvc_err2str(e, sizeof(e), str, (int)GetLastError()); | |
66 | if(out) fprintf(out, "%s\n", e); | |
67 | else fprintf(stderr, "%s\n", e); | |
68 | exit(1); | |
69 | } | |
70 | ||
71 | /** install registry entries for eventlog */ | |
72 | static void | |
73 | event_reg_install(FILE* out, const char* pathname) | |
74 | { | |
75 | char buf[1024]; | |
76 | HKEY hk; | |
77 | DWORD t; | |
78 | if(out) fprintf(out, "install reg entries for %s\n", pathname); | |
79 | snprintf(buf, sizeof(buf), "SYSTEM\\CurrentControlSet\\Services" | |
80 | "\\EventLog\\Application\\%s", SERVICE_NAME); | |
81 | if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)buf, | |
82 | 0, /* reserved, mustbezero */ | |
83 | NULL, /* class of key, ignored */ | |
84 | REG_OPTION_NON_VOLATILE, /* values saved on disk */ | |
85 | KEY_WRITE, /* we want write permission */ | |
86 | NULL, /* use default security descriptor */ | |
87 | &hk, /* result */ | |
88 | NULL)) /* not interested if key new or existing */ | |
89 | fatal_win(out, "could not create registry key"); | |
90 | ||
91 | /* message file */ | |
92 | if(RegSetValueEx(hk, (LPCTSTR)"EventMessageFile", | |
93 | 0, /* reserved, mustbezero */ | |
94 | REG_EXPAND_SZ, /* value type (string w env subst) */ | |
95 | (BYTE*)pathname, /* data */ | |
96 | (DWORD)strlen(pathname)+1)) /* length of data */ | |
97 | { | |
98 | RegCloseKey(hk); | |
99 | fatal_win(out, "could not registry set EventMessageFile"); | |
100 | } | |
101 | ||
102 | /* event types */ | |
103 | t = EVENTLOG_SUCCESS | EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | |
104 | | EVENTLOG_INFORMATION_TYPE; | |
105 | if(RegSetValueEx(hk, (LPCTSTR)"TypesSupported", 0, REG_DWORD, | |
106 | (LPBYTE)&t, sizeof(t))) { | |
107 | RegCloseKey(hk); | |
108 | fatal_win(out, "could not registry set TypesSupported"); | |
109 | } | |
110 | ||
111 | /* category message file */ | |
112 | if(RegSetValueEx(hk, (LPCTSTR)"CategoryMessageFile", 0, REG_EXPAND_SZ, | |
113 | (BYTE*)pathname, (DWORD)strlen(pathname)+1)) { | |
114 | RegCloseKey(hk); | |
115 | fatal_win(out, "could not registry set CategoryMessageFile"); | |
116 | } | |
117 | t = 1; | |
118 | if(RegSetValueEx(hk, (LPCTSTR)"CategoryCount", 0, REG_DWORD, | |
119 | (LPBYTE)&t, sizeof(t))) { | |
120 | RegCloseKey(hk); | |
121 | fatal_win(out, "could not registry set CategoryCount"); | |
122 | } | |
123 | ||
124 | ||
125 | RegCloseKey(hk); | |
126 | if(out) fprintf(out, "installed reg entries\n"); | |
127 | } | |
128 | ||
129 | /** remove registry entries for eventlog */ | |
130 | static void | |
131 | event_reg_remove(FILE* out) | |
132 | { | |
133 | char buf[1024]; | |
134 | HKEY hk; | |
135 | if(out) fprintf(out, "remove reg entries\n"); | |
136 | snprintf(buf, sizeof(buf), "SYSTEM\\CurrentControlSet\\Services" | |
137 | "\\EventLog\\Application"); | |
138 | if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)buf, | |
139 | 0, /* reserved, mustbezero */ | |
140 | NULL, /* class of key, ignored */ | |
141 | REG_OPTION_NON_VOLATILE, /* values saved on disk */ | |
142 | DELETE, /* we want key delete permission */ | |
143 | NULL, /* use default security descriptor */ | |
144 | &hk, /* result */ | |
145 | NULL)) /* not interested if key new or existing */ | |
146 | fatal_win(out, "could not open registry key"); | |
147 | if(RegDeleteKey(hk, (LPCTSTR)SERVICE_NAME)) { | |
148 | RegCloseKey(hk); | |
149 | fatal_win(out, "could not delete registry key"); | |
150 | } | |
151 | RegCloseKey(hk); | |
152 | if(out) fprintf(out, "removed reg entries\n"); | |
153 | } | |
154 | ||
155 | /** | |
156 | * put quotes around string. Needs one space in front | |
157 | * @param out: debugfile | |
158 | * @param str: to be quoted. | |
159 | * @param maxlen: max length of the string buffer. | |
160 | */ | |
161 | static void | |
162 | quote_it(FILE* out, char* str, size_t maxlen) | |
163 | { | |
164 | if(strlen(str) == maxlen) { | |
165 | if(out) fprintf(out, "string too long %s", str); | |
166 | exit(1); | |
167 | } | |
168 | str[0]='"'; | |
169 | str[strlen(str)+1]=0; | |
170 | str[strlen(str)]='"'; | |
171 | } | |
172 | ||
173 | /** change suffix */ | |
174 | static void | |
175 | change(FILE* out, char* path, size_t max, const char* from, const char* to) | |
176 | { | |
177 | size_t fromlen = strlen(from); | |
178 | size_t tolen = strlen(to); | |
179 | size_t pathlen = strlen(path); | |
180 | if(pathlen - fromlen + tolen >= max) { | |
181 | if(out) fprintf(out, "string too long %s", path); | |
182 | exit(1); | |
183 | } | |
184 | snprintf(path+pathlen-fromlen, max-(pathlen-fromlen), "%s", to); | |
185 | } | |
186 | ||
187 | /* Install service in servicecontrolmanager */ | |
188 | void | |
189 | wsvc_install(FILE* out, const char* rename) | |
190 | { | |
191 | SC_HANDLE scm; | |
192 | SC_HANDLE sv; | |
193 | TCHAR path[2*MAX_PATH+4+256]; | |
194 | TCHAR path_config[2*MAX_PATH+4+256]; | |
195 | if(out) fprintf(out, "installing unbound service\n"); | |
196 | if(!GetModuleFileName(NULL, path+1, MAX_PATH)) | |
197 | fatal_win(out, "could not GetModuleFileName"); | |
198 | /* change 'unbound-service-install' to 'unbound' */ | |
199 | if(rename) { | |
200 | change(out, path+1, sizeof(path)-1, rename, "unbound.exe"); | |
201 | memmove(path_config+1, path+1, sizeof(path)-1); | |
202 | change(out, path_config+1, sizeof(path_config)-1, | |
203 | "unbound.exe", "service.conf"); | |
204 | } | |
205 | ||
206 | event_reg_install(out, path+1); | |
207 | ||
208 | /* have to quote it because of spaces in directory names */ | |
209 | /* could append arguments to be sent to ServiceMain */ | |
210 | quote_it(out, path, sizeof(path)); | |
211 | ||
212 | /* if we started in a different directory, also read config from it. */ | |
213 | if(rename) { | |
214 | quote_it(out, path_config, sizeof(path_config)); | |
215 | strcat(path, " -c "); | |
216 | strcat(path, path_config); | |
217 | } | |
218 | ||
219 | strcat(path, " -w service"); | |
220 | scm = OpenSCManager(NULL, NULL, (int)SC_MANAGER_CREATE_SERVICE); | |
221 | if(!scm) fatal_win(out, "could not OpenSCManager"); | |
222 | sv = CreateService( | |
223 | scm, | |
224 | SERVICE_NAME, /* name of service */ | |
225 | "Unbound DNS validator", /* display name */ | |
226 | SERVICE_ALL_ACCESS, /* desired access */ | |
227 | SERVICE_WIN32_OWN_PROCESS, /* service type */ | |
228 | SERVICE_AUTO_START, /* start type */ | |
229 | SERVICE_ERROR_NORMAL, /* error control type */ | |
230 | path, /* path to service's binary */ | |
231 | NULL, /* no load ordering group */ | |
232 | NULL, /* no tag identifier */ | |
233 | NULL, /* no deps */ | |
234 | NULL, /* on LocalSystem */ | |
235 | NULL /* no password */ | |
236 | ); | |
237 | if(!sv) { | |
238 | CloseServiceHandle(scm); | |
239 | fatal_win(out, "could not CreateService"); | |
240 | } | |
241 | CloseServiceHandle(sv); | |
242 | CloseServiceHandle(scm); | |
243 | if(out) fprintf(out, "unbound service installed\n"); | |
244 | } | |
245 | ||
246 | ||
247 | /* Remove installed service from servicecontrolmanager */ | |
248 | void | |
249 | wsvc_remove(FILE* out) | |
250 | { | |
251 | SC_HANDLE scm; | |
252 | SC_HANDLE sv; | |
253 | if(out) fprintf(out, "removing unbound service\n"); | |
254 | scm = OpenSCManager(NULL, NULL, (int)SC_MANAGER_ALL_ACCESS); | |
255 | if(!scm) fatal_win(out, "could not OpenSCManager"); | |
256 | sv = OpenService(scm, SERVICE_NAME, DELETE); | |
257 | if(!sv) { | |
258 | CloseServiceHandle(scm); | |
259 | fatal_win(out, "could not OpenService"); | |
260 | } | |
261 | if(!DeleteService(sv)) { | |
262 | CloseServiceHandle(sv); | |
263 | CloseServiceHandle(scm); | |
264 | fatal_win(out, "could not DeleteService"); | |
265 | } | |
266 | CloseServiceHandle(sv); | |
267 | CloseServiceHandle(scm); | |
268 | event_reg_remove(out); | |
269 | if(out) fprintf(out, "unbound service removed\n"); | |
270 | } | |
271 | ||
272 | ||
273 | /* Start daemon */ | |
274 | void | |
275 | wsvc_rc_start(FILE* out) | |
276 | { | |
277 | SC_HANDLE scm; | |
278 | SC_HANDLE sv; | |
279 | if(out) fprintf(out, "start unbound service\n"); | |
280 | scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); | |
281 | if(!scm) fatal_win(out, "could not OpenSCManager"); | |
282 | sv = OpenService(scm, SERVICE_NAME, SERVICE_START); | |
283 | if(!sv) { | |
284 | CloseServiceHandle(scm); | |
285 | fatal_win(out, "could not OpenService"); | |
286 | } | |
287 | if(!StartService(sv, 0, NULL)) { | |
288 | CloseServiceHandle(sv); | |
289 | CloseServiceHandle(scm); | |
290 | fatal_win(out, "could not StartService"); | |
291 | } | |
292 | CloseServiceHandle(sv); | |
293 | CloseServiceHandle(scm); | |
294 | if(out) fprintf(out, "unbound service started\n"); | |
295 | } | |
296 | ||
297 | ||
298 | /* Stop daemon */ | |
299 | void | |
300 | wsvc_rc_stop(FILE* out) | |
301 | { | |
302 | SC_HANDLE scm; | |
303 | SC_HANDLE sv; | |
304 | SERVICE_STATUS st; | |
305 | if(out) fprintf(out, "stop unbound service\n"); | |
306 | scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); | |
307 | if(!scm) fatal_win(out, "could not OpenSCManager"); | |
308 | sv = OpenService(scm, SERVICE_NAME, SERVICE_STOP); | |
309 | if(!sv) { | |
310 | CloseServiceHandle(scm); | |
311 | fatal_win(out, "could not OpenService"); | |
312 | } | |
313 | if(!ControlService(sv, SERVICE_CONTROL_STOP, &st)) { | |
314 | CloseServiceHandle(sv); | |
315 | CloseServiceHandle(scm); | |
316 | fatal_win(out, "could not ControlService"); | |
317 | } | |
318 | CloseServiceHandle(sv); | |
319 | CloseServiceHandle(scm); | |
320 | if(out) fprintf(out, "unbound service stopped\n"); | |
321 | } |