]>
Commit | Line | Data |
---|---|---|
f449bfef RC |
1 | /* cygserver.cc |
2 | ||
dafef5e2 | 3 | Copyright 2001, 2002, 2003, 2004, 2005 Red Hat Inc. |
f449bfef RC |
4 | |
5 | Written by Egor Duda <deo@logos-m.ru> | |
6 | ||
1c001dd2 | 7 | This file is part of Cygwin. |
f449bfef | 8 | |
1c001dd2 CS |
9 | This software is a copyrighted work licensed under the terms of the |
10 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
11 | details. */ | |
12 | ||
282113ba | 13 | #ifdef __OUTSIDE_CYGWIN__ |
1c001dd2 | 14 | #include "woutsup.h" |
f449bfef | 15 | |
f449bfef | 16 | #include <sys/types.h> |
1c001dd2 CS |
17 | |
18 | #include <assert.h> | |
1dcd520b | 19 | #include <errno.h> |
1c001dd2 | 20 | #include <ctype.h> |
1c001dd2 | 21 | #include <getopt.h> |
f449bfef | 22 | #include <signal.h> |
1c001dd2 | 23 | #include <stdio.h> |
59a2339f | 24 | #include <stdlib.h> |
1c001dd2 CS |
25 | #include <string.h> |
26 | #include <unistd.h> | |
f449bfef | 27 | |
1c001dd2 | 28 | #include "cygwin_version.h" |
f449bfef | 29 | |
56797078 | 30 | #include "cygserver.h" |
282113ba CV |
31 | #include "process.h" |
32 | #include "transport.h" | |
f449bfef | 33 | |
282113ba CV |
34 | #include "cygserver_ipc.h" |
35 | #include "cygserver_msg.h" | |
36 | #include "cygserver_sem.h" | |
1c001dd2 | 37 | |
282113ba | 38 | #define DEF_CONFIG_FILE "" SYSCONFDIR "/cygserver.conf" |
1c001dd2 | 39 | |
dafef5e2 | 40 | #define SERVER_VERSION "1.12" |
1c001dd2 CS |
41 | |
42 | GENERIC_MAPPING access_mapping; | |
43 | ||
282113ba | 44 | static bool |
f449bfef RC |
45 | setup_privileges () |
46 | { | |
47 | BOOL rc, ret_val; | |
48 | HANDLE hToken = NULL; | |
49 | TOKEN_PRIVILEGES sPrivileges; | |
50 | ||
1c001dd2 | 51 | rc = OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS , &hToken) ; |
73ea29f4 | 52 | if (!rc) |
f449bfef | 53 | { |
282113ba CV |
54 | debug ("error opening process token (%lu)", GetLastError ()); |
55 | return false; | |
f449bfef | 56 | } |
73ea29f4 CF |
57 | rc = LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid); |
58 | if (!rc) | |
f449bfef | 59 | { |
282113ba CV |
60 | debug ("error getting privilege luid (%lu)", GetLastError ()); |
61 | ret_val = false; | |
f449bfef RC |
62 | goto out; |
63 | } | |
64 | sPrivileges.PrivilegeCount = 1 ; | |
65 | sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ; | |
73ea29f4 CF |
66 | rc = AdjustTokenPrivileges (hToken, FALSE, &sPrivileges, 0, NULL, NULL) ; |
67 | if (!rc) | |
f449bfef | 68 | { |
282113ba CV |
69 | debug ("error adjusting privilege level. (%lu)", GetLastError ()); |
70 | ret_val = false; | |
f449bfef RC |
71 | goto out; |
72 | } | |
73 | ||
74 | access_mapping.GenericRead = FILE_READ_DATA; | |
75 | access_mapping.GenericWrite = FILE_WRITE_DATA; | |
76 | access_mapping.GenericExecute = 0; | |
77 | access_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA; | |
78 | ||
282113ba | 79 | ret_val = true; |
f449bfef RC |
80 | |
81 | out: | |
73ea29f4 | 82 | CloseHandle (hToken); |
f449bfef RC |
83 | return ret_val; |
84 | } | |
85 | ||
86 | int | |
87 | check_and_dup_handle (HANDLE from_process, HANDLE to_process, | |
88 | HANDLE from_process_token, | |
73ea29f4 CF |
89 | DWORD access, |
90 | HANDLE from_handle, | |
1c001dd2 | 91 | HANDLE *to_handle_ptr, BOOL bInheritHandle = FALSE) |
f449bfef RC |
92 | { |
93 | HANDLE local_handle = NULL; | |
94 | int ret_val = EACCES; | |
f449bfef RC |
95 | |
96 | if (from_process != GetCurrentProcess ()) | |
73ea29f4 | 97 | { |
1c001dd2 CS |
98 | if (!DuplicateHandle (from_process, from_handle, |
99 | GetCurrentProcess (), &local_handle, | |
100 | 0, bInheritHandle, | |
101 | DUPLICATE_SAME_ACCESS)) | |
102 | { | |
282113ba | 103 | log (LOG_ERR, "error getting handle(%u) to server (%lu)", |
1c001dd2 CS |
104 | (unsigned int)from_handle, GetLastError ()); |
105 | goto out; | |
106 | } | |
107 | } else | |
108 | local_handle = from_handle; | |
f449bfef | 109 | |
1c001dd2 CS |
110 | if (!wincap.has_security ()) |
111 | assert (!from_process_token); | |
112 | else | |
f449bfef | 113 | { |
1c001dd2 CS |
114 | char sd_buf [1024]; |
115 | PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf; | |
116 | DWORD bytes_needed; | |
117 | PRIVILEGE_SET ps; | |
118 | DWORD ps_len = sizeof (ps); | |
119 | BOOL status; | |
120 | ||
121 | if (!GetKernelObjectSecurity (local_handle, | |
122 | (OWNER_SECURITY_INFORMATION | |
123 | | GROUP_SECURITY_INFORMATION | |
124 | | DACL_SECURITY_INFORMATION), | |
125 | sd, sizeof (sd_buf), &bytes_needed)) | |
126 | { | |
282113ba | 127 | log (LOG_ERR, "error getting handle SD (%lu)", GetLastError ()); |
1c001dd2 CS |
128 | goto out; |
129 | } | |
f449bfef | 130 | |
1c001dd2 | 131 | MapGenericMask (&access, &access_mapping); |
f449bfef | 132 | |
1c001dd2 CS |
133 | if (!AccessCheck (sd, from_process_token, access, &access_mapping, |
134 | &ps, &ps_len, &access, &status)) | |
135 | { | |
282113ba | 136 | log (LOG_ERR, "error checking access rights (%lu)", |
1c001dd2 CS |
137 | GetLastError ()); |
138 | goto out; | |
139 | } | |
f449bfef | 140 | |
1c001dd2 CS |
141 | if (!status) |
142 | { | |
282113ba | 143 | log (LOG_ERR, "access to object denied"); |
1c001dd2 CS |
144 | goto out; |
145 | } | |
f449bfef RC |
146 | } |
147 | ||
148 | if (!DuplicateHandle (from_process, from_handle, | |
73ea29f4 CF |
149 | to_process, to_handle_ptr, |
150 | access, bInheritHandle, 0)) | |
f449bfef | 151 | { |
282113ba | 152 | log (LOG_ERR, "error getting handle to client (%lu)", GetLastError ()); |
f449bfef RC |
153 | goto out; |
154 | } | |
1c001dd2 | 155 | |
282113ba | 156 | debug ("Duplicated %p to %p", from_handle, *to_handle_ptr); |
f449bfef RC |
157 | |
158 | ret_val = 0; | |
73ea29f4 | 159 | |
1c001dd2 | 160 | out: |
f449bfef RC |
161 | if (local_handle && from_process != GetCurrentProcess ()) |
162 | CloseHandle (local_handle); | |
163 | ||
164 | return (ret_val); | |
165 | } | |
166 | ||
1c001dd2 CS |
167 | /* |
168 | * client_request_attach_tty::serve () | |
169 | */ | |
f449bfef RC |
170 | |
171 | void | |
1c001dd2 CS |
172 | client_request_attach_tty::serve (transport_layer_base *const conn, |
173 | process_cache *) | |
f449bfef | 174 | { |
1c001dd2 CS |
175 | assert (conn); |
176 | ||
177 | assert (!error_code ()); | |
f449bfef | 178 | |
1c001dd2 | 179 | if (!wincap.has_security ()) |
f449bfef | 180 | { |
282113ba | 181 | log (LOG_NOTICE, "operation only supported on systems with security"); |
1c001dd2 CS |
182 | error_code (EINVAL); |
183 | msglen (0); | |
f449bfef RC |
184 | return; |
185 | } | |
186 | ||
1c001dd2 | 187 | if (msglen () != sizeof (req)) |
f449bfef | 188 | { |
282113ba | 189 | log (LOG_ERR, "bad request body length: expecting %lu bytes, got %lu", |
1c001dd2 CS |
190 | sizeof (req), msglen ()); |
191 | error_code (EINVAL); | |
192 | msglen (0); | |
193 | return; | |
f449bfef RC |
194 | } |
195 | ||
1c001dd2 | 196 | msglen (0); // Until we fill in some fields. |
73ea29f4 | 197 | |
282113ba CV |
198 | debug ("pid %ld:(%p,%p) -> pid %ld", req.master_pid, req.from_master, |
199 | req.to_master, req.pid); | |
f449bfef | 200 | |
282113ba | 201 | debug ("opening process %ld", req.master_pid); |
f449bfef | 202 | |
1c001dd2 CS |
203 | const HANDLE from_process_handle = |
204 | OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid); | |
205 | ||
206 | if (!from_process_handle) | |
f449bfef | 207 | { |
282113ba | 208 | log (LOG_ERR, "error opening `from' process, error = %lu", |
1c001dd2 CS |
209 | GetLastError ()); |
210 | error_code (EACCES); | |
211 | return; | |
f449bfef RC |
212 | } |
213 | ||
282113ba | 214 | debug ("opening process %ld", req.pid); |
1c001dd2 CS |
215 | |
216 | const HANDLE to_process_handle = | |
217 | OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid); | |
218 | ||
219 | if (!to_process_handle) | |
f449bfef | 220 | { |
282113ba | 221 | log (LOG_ERR, "error opening `to' process, error = %lu", |
1c001dd2 CS |
222 | GetLastError ()); |
223 | CloseHandle (from_process_handle); | |
224 | error_code (EACCES); | |
225 | return; | |
f449bfef RC |
226 | } |
227 | ||
282113ba CV |
228 | debug ("Impersonating client"); |
229 | if (!conn->impersonate_client ()) | |
230 | { | |
231 | CloseHandle (from_process_handle); | |
232 | CloseHandle (to_process_handle); | |
233 | error_code (EACCES); | |
234 | return; | |
235 | } | |
1c001dd2 CS |
236 | |
237 | HANDLE token_handle = NULL; | |
238 | ||
282113ba | 239 | debug ("about to open thread token"); |
1c001dd2 CS |
240 | const DWORD rc = OpenThreadToken (GetCurrentThread (), |
241 | TOKEN_QUERY, | |
242 | TRUE, | |
243 | &token_handle); | |
244 | ||
282113ba CV |
245 | debug ("opened thread token, rc=%lu", rc); |
246 | if (!conn->revert_to_self ()) | |
247 | { | |
248 | CloseHandle (from_process_handle); | |
249 | CloseHandle (to_process_handle); | |
250 | error_code (EACCES); | |
251 | return; | |
252 | } | |
1c001dd2 CS |
253 | |
254 | if (!rc) | |
f449bfef | 255 | { |
282113ba | 256 | log (LOG_ERR, "error opening thread token, error = %lu", |
1c001dd2 CS |
257 | GetLastError ()); |
258 | CloseHandle (from_process_handle); | |
259 | CloseHandle (to_process_handle); | |
260 | error_code (EACCES); | |
261 | return; | |
f449bfef RC |
262 | } |
263 | ||
1c001dd2 | 264 | // From this point on, a reply body is returned to the client. |
f449bfef | 265 | |
1c001dd2 CS |
266 | const HANDLE from_master = req.from_master; |
267 | const HANDLE to_master = req.to_master; | |
f449bfef | 268 | |
1c001dd2 CS |
269 | req.from_master = NULL; |
270 | req.to_master = NULL; | |
271 | ||
272 | msglen (sizeof (req)); | |
273 | ||
274 | if (from_master) | |
275 | if (check_and_dup_handle (from_process_handle, to_process_handle, | |
276 | token_handle, | |
277 | GENERIC_READ, | |
278 | from_master, | |
279 | &req.from_master, TRUE) != 0) | |
280 | { | |
282113ba | 281 | log (LOG_ERR, "error duplicating from_master handle, error = %lu", |
1c001dd2 CS |
282 | GetLastError ()); |
283 | error_code (EACCES); | |
284 | } | |
285 | ||
286 | if (to_master) | |
287 | if (check_and_dup_handle (from_process_handle, to_process_handle, | |
288 | token_handle, | |
289 | GENERIC_WRITE, | |
290 | to_master, | |
291 | &req.to_master, TRUE) != 0) | |
292 | { | |
282113ba | 293 | log (LOG_ERR, "error duplicating to_master handle, error = %lu", |
1c001dd2 CS |
294 | GetLastError ()); |
295 | error_code (EACCES); | |
296 | } | |
297 | ||
298 | CloseHandle (from_process_handle); | |
299 | CloseHandle (to_process_handle); | |
300 | CloseHandle (token_handle); | |
301 | ||
282113ba | 302 | debug ("%lu(%lu, %lu) -> %lu(%lu,%lu)", |
1c001dd2 CS |
303 | req.master_pid, from_master, to_master, |
304 | req.pid, req.from_master, req.to_master); | |
305 | ||
306 | return; | |
f449bfef RC |
307 | } |
308 | ||
309 | void | |
1c001dd2 | 310 | client_request_get_version::serve (transport_layer_base *, process_cache *) |
f449bfef | 311 | { |
1c001dd2 CS |
312 | assert (!error_code ()); |
313 | ||
314 | if (msglen ()) | |
282113ba | 315 | log (LOG_ERR, "unexpected request body ignored: %lu bytes", msglen ()); |
1c001dd2 CS |
316 | |
317 | msglen (sizeof (version)); | |
318 | ||
f449bfef RC |
319 | version.major = CYGWIN_SERVER_VERSION_MAJOR; |
320 | version.api = CYGWIN_SERVER_VERSION_API; | |
321 | version.minor = CYGWIN_SERVER_VERSION_MINOR; | |
322 | version.patch = CYGWIN_SERVER_VERSION_PATCH; | |
323 | } | |
324 | ||
325 | class server_request : public queue_request | |
326 | { | |
1c001dd2 CS |
327 | public: |
328 | server_request (transport_layer_base *const conn, process_cache *const cache) | |
329 | : _conn (conn), _cache (cache) | |
330 | {} | |
f449bfef | 331 | |
1c001dd2 CS |
332 | virtual ~server_request () |
333 | { | |
282113ba | 334 | delete _conn; |
1c001dd2 | 335 | } |
f449bfef | 336 | |
1c001dd2 CS |
337 | virtual void process () |
338 | { | |
339 | client_request::handle_request (_conn, _cache); | |
340 | } | |
341 | ||
342 | private: | |
343 | transport_layer_base *const _conn; | |
344 | process_cache *const _cache; | |
f449bfef | 345 | }; |
f449bfef | 346 | |
1c001dd2 | 347 | class server_submission_loop : public queue_submission_loop |
f449bfef | 348 | { |
1c001dd2 CS |
349 | public: |
350 | server_submission_loop (threaded_queue *const queue, | |
351 | transport_layer_base *const transport, | |
352 | process_cache *const cache) | |
353 | : queue_submission_loop (queue, false), | |
354 | _transport (transport), | |
355 | _cache (cache) | |
f449bfef | 356 | { |
1c001dd2 CS |
357 | assert (_transport); |
358 | assert (_cache); | |
f449bfef | 359 | } |
f449bfef | 360 | |
1c001dd2 CS |
361 | private: |
362 | transport_layer_base *const _transport; | |
363 | process_cache *const _cache; | |
364 | ||
365 | virtual void request_loop (); | |
366 | }; | |
367 | ||
368 | /* FIXME: this is a little ugly. What we really want is to wait on | |
369 | * two objects: one for the pipe/socket, and one for being told to | |
370 | * shutdown. Otherwise this will stay a problem (we won't actually | |
371 | * shutdown until the request _AFTER_ the shutdown request. And | |
372 | * sending ourselves a request is ugly | |
373 | */ | |
f449bfef | 374 | void |
1c001dd2 | 375 | server_submission_loop::request_loop () |
f449bfef | 376 | { |
1c001dd2 CS |
377 | /* I'd like the accepting thread's priority to be above any "normal" |
378 | * thread in the system to avoid overflowing the listen queue (for | |
379 | * sockets; similar issues exist for named pipes); but, for example, | |
380 | * a normal priority thread in a foregrounded process is boosted to | |
381 | * THREAD_PRIORITY_HIGHEST (AFAICT). Thus try to set the current | |
382 | * thread's priority to a level one above that. This fails on | |
383 | * win9x/ME so assume any failure in that call is due to that and | |
384 | * simply call again at one priority level lower. | |
385 | */ | |
386 | if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST + 1)) | |
387 | if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST)) | |
282113ba CV |
388 | debug ("failed to raise accept thread priority, error = %lu", |
389 | GetLastError ()); | |
1c001dd2 CS |
390 | |
391 | while (_running) | |
392 | { | |
393 | bool recoverable = false; | |
394 | transport_layer_base *const conn = _transport->accept (&recoverable); | |
395 | if (!conn && !recoverable) | |
396 | { | |
282113ba | 397 | log (LOG_ERR, "fatal error on IPC transport: closing down"); |
1c001dd2 CS |
398 | return; |
399 | } | |
400 | // EINTR probably implies a shutdown request; so back off for a | |
401 | // moment to let the main thread take control, otherwise the | |
402 | // server spins here receiving EINTR repeatedly since the signal | |
403 | // handler in the main thread doesn't get a chance to be called. | |
404 | if (!conn && errno == EINTR) | |
405 | { | |
406 | if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL)) | |
282113ba CV |
407 | debug ("failed to reset thread priority, error = %lu", |
408 | GetLastError ()); | |
1c001dd2 CS |
409 | |
410 | Sleep (0); | |
411 | if (!SetThreadPriority (GetCurrentThread (), | |
412 | THREAD_PRIORITY_HIGHEST + 1)) | |
413 | if (!SetThreadPriority (GetCurrentThread (), | |
414 | THREAD_PRIORITY_HIGHEST)) | |
282113ba CV |
415 | debug ("failed to raise thread priority, error = %lu", |
416 | GetLastError ()); | |
1c001dd2 CS |
417 | } |
418 | if (conn) | |
282113ba | 419 | _queue->add (new server_request (conn, _cache)); |
1c001dd2 CS |
420 | } |
421 | } | |
422 | ||
423 | client_request_shutdown::client_request_shutdown () | |
424 | : client_request (CYGSERVER_REQUEST_SHUTDOWN) | |
425 | { | |
f449bfef RC |
426 | } |
427 | ||
428 | void | |
1c001dd2 | 429 | client_request_shutdown::serve (transport_layer_base *, process_cache *) |
f449bfef | 430 | { |
1c001dd2 CS |
431 | assert (!error_code ()); |
432 | ||
433 | if (msglen ()) | |
282113ba | 434 | log (LOG_ERR, "unexpected request body ignored: %lu bytes", msglen ()); |
1c001dd2 | 435 | |
f449bfef RC |
436 | /* FIXME: link upwards, and then this becomes a trivial method call to |
437 | * only shutdown _this queue_ | |
438 | */ | |
1c001dd2 CS |
439 | |
440 | kill (getpid (), SIGINT); | |
441 | ||
442 | msglen (0); | |
f449bfef RC |
443 | } |
444 | ||
1c001dd2 CS |
445 | static sig_atomic_t shutdown_server = false; |
446 | ||
447 | static void | |
448 | handle_signal (const int signum) | |
f449bfef | 449 | { |
1c001dd2 CS |
450 | /* any signal makes us die :} */ |
451 | ||
452 | shutdown_server = true; | |
f449bfef RC |
453 | } |
454 | ||
1c001dd2 CS |
455 | /* |
456 | * print_usage () | |
457 | */ | |
458 | ||
459 | static void | |
460 | print_usage (const char *const pgm) | |
f449bfef | 461 | { |
282113ba CV |
462 | log (LOG_NOTICE, "Usage: %s [OPTIONS]\n" |
463 | "Configuration option:\n" | |
464 | " -f, --config-file <file> Use <file> as config file. Default is\n" | |
e74c79a8 | 465 | " " DEF_CONFIG_FILE "\n" |
282113ba CV |
466 | "\n" |
467 | "Performance options:\n" | |
468 | " -c, --cleanup-threads <num> Number of cleanup threads to use.\n" | |
469 | " -r, --request-threads <num> Number of request threads to use.\n" | |
470 | "\n" | |
471 | "Logging options:\n" | |
472 | " -d, --debug Log debug messages to stderr.\n" | |
473 | " -e, --stderr Log to stderr (default if stderr is a tty).\n" | |
474 | " -E, --no-stderr Don't log to stderr (see -y, -Y options).\n" | |
282113ba CV |
475 | " -l, --log-level <level> Verbosity of logging (1..7). Default: 6\n" |
476 | " -y, --syslog Log to syslog (default if stderr is no tty).\n" | |
477 | " -Y, --no-syslog Don't log to syslog (See -e, -E options).\n" | |
478 | "\n" | |
479 | "Support options:\n" | |
480 | " -m, --no-sharedmem Don't start XSI Shared Memory support.\n" | |
481 | " -q, --no-msgqueues Don't start XSI Message Queue support.\n" | |
482 | " -s, --no-semaphores Don't start XSI Semaphore support.\n" | |
483 | "\n" | |
484 | "Miscellaneous:\n" | |
485 | " -S, --shutdown Shutdown the daemon.\n" | |
486 | " -h, --help Output usage information and exit.\n" | |
487 | " -v, --version Output version information and exit." | |
488 | , pgm); | |
1c001dd2 | 489 | } |
f449bfef | 490 | |
1c001dd2 CS |
491 | /* |
492 | * print_version () | |
493 | */ | |
f449bfef | 494 | |
1c001dd2 | 495 | static void |
282113ba | 496 | print_version () |
1c001dd2 | 497 | { |
1c001dd2 CS |
498 | char buf[200]; |
499 | snprintf (buf, sizeof (buf), "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s", | |
500 | cygwin_version.dll_major / 1000, | |
501 | cygwin_version.dll_major % 1000, | |
502 | cygwin_version.dll_minor, | |
503 | cygwin_version.api_major, | |
504 | cygwin_version.api_minor, | |
505 | cygwin_version.shared_data, | |
506 | CYGWIN_SERVER_VERSION_MAJOR, | |
507 | CYGWIN_SERVER_VERSION_API, | |
508 | CYGWIN_SERVER_VERSION_MINOR, | |
509 | CYGWIN_SERVER_VERSION_PATCH, | |
510 | cygwin_version.mount_registry, | |
511 | cygwin_version.dll_build_date); | |
512 | ||
282113ba CV |
513 | log (LOG_INFO, "(cygwin) %s\n" |
514 | "API version %s\n" | |
dafef5e2 | 515 | "Copyright 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.\n" |
282113ba CV |
516 | "Compiled on %s\n" |
517 | "Default configuration file is %s", | |
dafef5e2 | 518 | SERVER_VERSION, buf, __DATE__, DEF_CONFIG_FILE); |
f449bfef RC |
519 | } |
520 | ||
1c001dd2 CS |
521 | /* |
522 | * main () | |
523 | */ | |
f449bfef | 524 | |
1c001dd2 CS |
525 | int |
526 | main (const int argc, char *argv[]) | |
f449bfef | 527 | { |
1c001dd2 CS |
528 | const struct option longopts[] = { |
529 | {"cleanup-threads", required_argument, NULL, 'c'}, | |
282113ba CV |
530 | {"debug", no_argument, NULL, 'd'}, |
531 | {"stderr", no_argument, NULL, 'e'}, | |
532 | {"no-stderr", no_argument, NULL, 'E'}, | |
533 | {"config-file", required_argument, NULL, 'f'}, | |
1c001dd2 | 534 | {"help", no_argument, NULL, 'h'}, |
282113ba CV |
535 | {"log-level", required_argument, NULL, 'l'}, |
536 | {"no-sharedmem", no_argument, NULL, 'm'}, | |
537 | {"no-msgqueues", no_argument, NULL, 'q'}, | |
1c001dd2 | 538 | {"request-threads", required_argument, NULL, 'r'}, |
282113ba CV |
539 | {"no-semaphores", no_argument, NULL, 's'}, |
540 | {"shutdown", no_argument, NULL, 'S'}, | |
1c001dd2 | 541 | {"version", no_argument, NULL, 'v'}, |
282113ba CV |
542 | {"syslog", no_argument, NULL, 'y'}, |
543 | {"no-syslog", no_argument, NULL, 'Y'}, | |
1c001dd2 CS |
544 | {0, no_argument, NULL, 0} |
545 | }; | |
f449bfef | 546 | |
282113ba | 547 | const char opts[] = "c:deEf:hl:mqr:sSvyY"; |
f449bfef | 548 | |
282113ba CV |
549 | long cleanup_threads = 0; |
550 | long request_threads = 0; | |
1c001dd2 | 551 | bool shutdown = false; |
282113ba CV |
552 | const char *config_file = DEF_CONFIG_FILE; |
553 | bool force_config_file = false; | |
554 | tun_bool_t option_log_stderr = TUN_UNDEF; | |
555 | tun_bool_t option_log_syslog = TUN_UNDEF; | |
556 | ||
557 | char *c = NULL; | |
558 | ||
559 | /* Check if we have a terminal. If so, default to stderr logging, | |
560 | otherwise default to syslog logging. This must be done early | |
561 | to allow default logging already in option processing state. */ | |
562 | openlog ("cygserver", LOG_PID, LOG_KERN); | |
563 | if (isatty (2)) | |
564 | log_stderr = TUN_TRUE; | |
1c001dd2 | 565 | else |
282113ba | 566 | log_syslog = TUN_TRUE; |
1c001dd2 CS |
567 | |
568 | int opt; | |
569 | ||
282113ba | 570 | opterr = 0; |
1c001dd2 CS |
571 | while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) |
572 | switch (opt) | |
f449bfef | 573 | { |
1c001dd2 | 574 | case 'c': |
282113ba CV |
575 | c = NULL; |
576 | cleanup_threads = strtol (optarg, &c, 10); | |
577 | if (cleanup_threads <= 0 || cleanup_threads > 16 || (c && *c)) | |
578 | panic ("Number of cleanup threads must be between 1 and 16"); | |
579 | break; | |
580 | ||
581 | case 'd': | |
582 | log_debug = TUN_TRUE; | |
583 | break; | |
584 | ||
585 | case 'e': | |
586 | option_log_stderr = TUN_TRUE; | |
587 | break; | |
588 | ||
589 | case 'E': | |
590 | option_log_stderr = TUN_FALSE; | |
1c001dd2 CS |
591 | break; |
592 | ||
282113ba CV |
593 | case 'f': |
594 | config_file = optarg; | |
595 | force_config_file = true; | |
596 | break; | |
597 | ||
1c001dd2 | 598 | case 'h': |
282113ba | 599 | print_usage (getprogname ()); |
1c001dd2 CS |
600 | return 0; |
601 | ||
282113ba CV |
602 | case 'l': |
603 | c = NULL; | |
604 | log_level = strtoul (optarg, &c, 10); | |
605 | if (!log_level || log_level > 7 || (c && *c)) | |
606 | panic ("Log level must be between 1 and 7"); | |
607 | break; | |
608 | ||
609 | case 'm': | |
610 | support_sharedmem = TUN_FALSE; | |
611 | break; | |
612 | ||
613 | case 'q': | |
614 | support_msgqueues = TUN_FALSE; | |
615 | break; | |
616 | ||
1c001dd2 | 617 | case 'r': |
282113ba CV |
618 | c = NULL; |
619 | request_threads = strtol (optarg, &c, 10); | |
620 | if (request_threads <= 0 || request_threads > 64 || (c && *c)) | |
621 | panic ("Number of request threads must be between 1 and 64"); | |
73ea29f4 | 622 | break; |
1c001dd2 CS |
623 | |
624 | case 's': | |
282113ba CV |
625 | support_semaphores = TUN_FALSE; |
626 | break; | |
627 | ||
628 | case 'S': | |
1c001dd2 | 629 | shutdown = true; |
73ea29f4 | 630 | break; |
1c001dd2 CS |
631 | |
632 | case 'v': | |
282113ba | 633 | print_version (); |
1c001dd2 CS |
634 | return 0; |
635 | ||
282113ba CV |
636 | case 'y': |
637 | option_log_syslog = TUN_TRUE; | |
638 | break; | |
639 | ||
640 | case 'Y': | |
641 | option_log_syslog = TUN_FALSE; | |
642 | break; | |
643 | ||
1c001dd2 | 644 | case '?': |
282113ba CV |
645 | panic ("unknown option -- %c\n" |
646 | "Try `%s --help' for more information.", optopt, getprogname ()); | |
f449bfef RC |
647 | } |
648 | ||
1c001dd2 | 649 | if (optind != argc) |
282113ba | 650 | panic ("Too many arguments"); |
f449bfef RC |
651 | |
652 | if (shutdown) | |
653 | { | |
1c001dd2 CS |
654 | /* Setting `cygserver_running' stops the request code making a |
655 | * version request, which is not much to the point. | |
656 | */ | |
657 | cygserver_running = CYGSERVER_OK; | |
658 | ||
659 | client_request_shutdown req; | |
660 | ||
661 | if (req.make_request () == -1 || req.error_code ()) | |
282113ba | 662 | panic("Shutdown request failed: %s", strerror (req.error_code ())); |
1c001dd2 CS |
663 | |
664 | // FIXME: It would be nice to wait here for the daemon to exit. | |
665 | ||
666 | return 0; | |
f449bfef RC |
667 | } |
668 | ||
1c001dd2 CS |
669 | SIGHANDLE (SIGHUP); |
670 | SIGHANDLE (SIGINT); | |
671 | SIGHANDLE (SIGTERM); | |
672 | ||
282113ba CV |
673 | tunable_param_init (config_file, force_config_file); |
674 | ||
675 | loginit (option_log_stderr, option_log_syslog); | |
676 | ||
677 | log (LOG_INFO, "daemon starting up"); | |
678 | ||
679 | if (!cleanup_threads) | |
680 | TUNABLE_INT_FETCH ("kern.srv.cleanup_threads", &cleanup_threads); | |
681 | if (!cleanup_threads) | |
682 | cleanup_threads = 2; | |
1c001dd2 | 683 | |
282113ba CV |
684 | if (!request_threads) |
685 | TUNABLE_INT_FETCH ("kern.srv.request_threads", &request_threads); | |
686 | if (!request_threads) | |
687 | request_threads = 10; | |
688 | ||
689 | if (support_sharedmem == TUN_UNDEF) | |
690 | TUNABLE_BOOL_FETCH ("kern.srv.sharedmem", &support_sharedmem); | |
691 | if (support_sharedmem == TUN_UNDEF) | |
692 | support_sharedmem = TUN_TRUE; | |
693 | ||
694 | if (support_msgqueues == TUN_UNDEF) | |
695 | TUNABLE_BOOL_FETCH ("kern.srv.msgqueues", &support_msgqueues); | |
696 | if (support_msgqueues == TUN_UNDEF) | |
697 | support_msgqueues = TUN_TRUE; | |
698 | ||
699 | if (support_semaphores == TUN_UNDEF) | |
700 | TUNABLE_BOOL_FETCH ("kern.srv.semaphores", &support_semaphores); | |
701 | if (support_semaphores == TUN_UNDEF) | |
702 | support_semaphores = TUN_TRUE; | |
703 | ||
704 | wincap.init (); | |
705 | if (wincap.has_security () && !setup_privileges ()) | |
706 | panic ("Setting process privileges failed."); | |
707 | ||
1d88f8ce CV |
708 | ipcinit (); |
709 | ||
282113ba | 710 | /*XXXXX*/ |
1c001dd2 | 711 | threaded_queue request_queue (request_threads); |
1c001dd2 CS |
712 | |
713 | transport_layer_base *const transport = create_server_transport (); | |
714 | assert (transport); | |
1c001dd2 CS |
715 | |
716 | process_cache cache (cleanup_threads); | |
1c001dd2 CS |
717 | |
718 | server_submission_loop submission_loop (&request_queue, transport, &cache); | |
1c001dd2 CS |
719 | |
720 | request_queue.add_submission_loop (&submission_loop); | |
1c001dd2 CS |
721 | |
722 | if (transport->listen () == -1) | |
1d88f8ce | 723 | return 1; |
1c001dd2 CS |
724 | |
725 | cache.start (); | |
1c001dd2 CS |
726 | |
727 | request_queue.start (); | |
1c001dd2 | 728 | |
282113ba | 729 | log (LOG_NOTICE, "Initialization complete. Waiting for requests."); |
1c001dd2 CS |
730 | |
731 | /* TODO: wait on multiple objects - the thread handle for each | |
732 | * request loop + all the process handles. This should be done by | |
733 | * querying the request_queue and the process cache for all their | |
734 | * handles, and then waiting for (say) 30 seconds. after that we | |
735 | * recreate the list of handles to wait on, and wait again. the | |
736 | * point of all this abstraction is that we can trivially server | |
737 | * both sockets and pipes simply by making a new transport, and then | |
738 | * calling request_queue.process_requests (transport2); | |
f449bfef RC |
739 | */ |
740 | /* WaitForMultipleObjects abort && request_queue && process_queue && signal | |
741 | -- if signal event then retrigger it | |
1c001dd2 CS |
742 | */ |
743 | while (!shutdown_server && request_queue.running () && cache.running ()) | |
282113ba CV |
744 | { |
745 | pause (); | |
746 | if (ipcunload ()) | |
747 | { | |
748 | shutdown_server = false; | |
749 | log (LOG_WARNING, "Shutdown request received but ignored. " | |
750 | "Dependent processes still running."); | |
751 | } | |
752 | } | |
1c001dd2 | 753 | |
282113ba | 754 | log (LOG_INFO, "Shutdown request received - new requests will be denied"); |
1c001dd2 | 755 | request_queue.stop (); |
282113ba CV |
756 | log (LOG_INFO, "All pending requests processed"); |
757 | delete transport; | |
758 | log (LOG_INFO, "No longer accepting requests - cygwin will operate in daemonless mode"); | |
1c001dd2 | 759 | cache.stop (); |
282113ba CV |
760 | log (LOG_INFO, "All outstanding process-cache activities completed"); |
761 | log (LOG_NOTICE, "Shutdown finished."); | |
1c001dd2 CS |
762 | |
763 | return 0; | |
f449bfef | 764 | } |
282113ba | 765 | #endif /* __OUTSIDE_CYGWIN__ */ |