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