3 Written by Egor Duda <deo@logos-m.ru>
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
11 #ifdef __OUTSIDE_CYGWIN__
14 #include <sys/types.h>
26 #include "cygwin_version.h"
28 #include "cygserver.h"
30 #include "transport.h"
32 #include "cygserver_ipc.h"
33 #include "cygserver_msg.h"
34 #include "cygserver_sem.h"
36 #define DEF_CONFIG_FILE "" SYSCONFDIR "/cygserver.conf"
38 #define SERVER_VERSION "1.20"
40 GENERIC_MAPPING access_mapping
;
47 TOKEN_PRIVILEGES sPrivileges
;
49 rc
= OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS
, &hToken
) ;
52 debug ("error opening process token (err %u)", GetLastError ());
55 rc
= LookupPrivilegeValue (NULL
, SE_DEBUG_NAME
, &sPrivileges
.Privileges
[0].Luid
);
58 debug ("error getting privilege luid (err %u)", GetLastError ());
62 sPrivileges
.PrivilegeCount
= 1 ;
63 sPrivileges
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
64 rc
= AdjustTokenPrivileges (hToken
, FALSE
, &sPrivileges
, 0, NULL
, NULL
) ;
67 debug ("error adjusting privilege level. (err %u)", GetLastError ());
72 access_mapping
.GenericRead
= FILE_READ_DATA
;
73 access_mapping
.GenericWrite
= FILE_WRITE_DATA
;
74 access_mapping
.GenericExecute
= 0;
75 access_mapping
.GenericAll
= FILE_READ_DATA
| FILE_WRITE_DATA
;
85 check_and_dup_handle (HANDLE from_process
, HANDLE to_process
,
86 HANDLE from_process_token
,
89 HANDLE
*to_handle_ptr
, BOOL bInheritHandle
= FALSE
)
91 HANDLE local_handle
= NULL
;
94 PSECURITY_DESCRIPTOR sd
= (PSECURITY_DESCRIPTOR
) &sd_buf
;
97 DWORD ps_len
= sizeof (ps
);
100 if (from_process
!= GetCurrentProcess ())
102 if (!DuplicateHandle (from_process
, from_handle
,
103 GetCurrentProcess (), &local_handle
,
105 DUPLICATE_SAME_ACCESS
))
107 log (LOG_ERR
, "error getting handle(%p) to server (err %u)",
108 from_handle
, GetLastError ());
112 local_handle
= from_handle
;
114 if (!GetKernelObjectSecurity (local_handle
,
115 (OWNER_SECURITY_INFORMATION
116 | GROUP_SECURITY_INFORMATION
117 | DACL_SECURITY_INFORMATION
),
118 sd
, sizeof (sd_buf
), &bytes_needed
))
120 log (LOG_ERR
, "error getting handle SD (err %u)", GetLastError ());
124 MapGenericMask (&access
, &access_mapping
);
126 if (!AccessCheck (sd
, from_process_token
, access
, &access_mapping
,
127 &ps
, &ps_len
, &access
, &status
))
129 log (LOG_ERR
, "error checking access rights (err %u)", GetLastError ());
135 log (LOG_ERR
, "access to object denied");
139 if (!DuplicateHandle (from_process
, from_handle
,
140 to_process
, to_handle_ptr
,
141 access
, bInheritHandle
, 0))
143 log (LOG_ERR
, "error getting handle to client (err %u)", GetLastError ());
147 debug ("Duplicated %p to %p", from_handle
, *to_handle_ptr
);
152 if (local_handle
&& from_process
!= GetCurrentProcess ())
153 CloseHandle (local_handle
);
159 * client_request_attach_tty::serve ()
163 client_request_attach_tty::serve (transport_layer_base
*const conn
,
168 assert (!error_code ());
170 if (msglen () != sizeof (req
))
172 log (LOG_ERR
, "bad request body length: expecting %lu bytes, got %lu",
173 sizeof (req
), msglen ());
179 msglen (0); // Until we fill in some fields.
181 debug ("pid %d:(%p,%p) -> pid %d", req
.master_pid
, req
.from_master
,
182 req
.to_master
, req
.pid
);
184 debug ("opening process %d", req
.master_pid
);
186 const HANDLE from_process_handle
=
187 OpenProcess (PROCESS_DUP_HANDLE
, FALSE
, req
.master_pid
);
189 if (!from_process_handle
)
191 log (LOG_ERR
, "error opening `from' process (err %u)", GetLastError ());
196 debug ("opening process %d", req
.pid
);
198 const HANDLE to_process_handle
=
199 OpenProcess (PROCESS_DUP_HANDLE
, FALSE
, req
.pid
);
201 if (!to_process_handle
)
203 log (LOG_ERR
, "error opening `to' process (err %u)", GetLastError ());
204 CloseHandle (from_process_handle
);
209 debug ("Impersonating client");
210 if (!conn
->impersonate_client ())
212 CloseHandle (from_process_handle
);
213 CloseHandle (to_process_handle
);
218 HANDLE token_handle
= NULL
;
220 debug ("about to open thread token");
221 const DWORD rc
= OpenThreadToken (GetCurrentThread (),
226 debug ("opened thread token, rc=%u", rc
);
227 if (!conn
->revert_to_self ())
229 CloseHandle (from_process_handle
);
230 CloseHandle (to_process_handle
);
237 log (LOG_ERR
, "error opening thread token (err %u)", GetLastError ());
238 CloseHandle (from_process_handle
);
239 CloseHandle (to_process_handle
);
244 // From this point on, a reply body is returned to the client.
246 const HANDLE from_master
= req
.from_master
;
247 const HANDLE to_master
= req
.to_master
;
249 req
.from_master
= NULL
;
250 req
.to_master
= NULL
;
252 msglen (sizeof (req
));
255 if (check_and_dup_handle (from_process_handle
, to_process_handle
,
259 &req
.from_master
, TRUE
) != 0)
261 log (LOG_ERR
, "error duplicating from_master handle (err %u)",
267 if (check_and_dup_handle (from_process_handle
, to_process_handle
,
271 &req
.to_master
, TRUE
) != 0)
273 log (LOG_ERR
, "error duplicating to_master handle (err %u)",
278 CloseHandle (from_process_handle
);
279 CloseHandle (to_process_handle
);
280 CloseHandle (token_handle
);
282 debug ("%u(%p, %p) -> %u(%p,%p)", req
.master_pid
, from_master
, to_master
,
283 req
.pid
, req
.from_master
, req
.to_master
);
289 client_request_get_version::serve (transport_layer_base
*, process_cache
*)
291 assert (!error_code ());
294 log (LOG_ERR
, "unexpected request body ignored: %lu bytes", msglen ());
296 msglen (sizeof (version
));
298 version
.major
= CYGWIN_SERVER_VERSION_MAJOR
;
299 version
.api
= CYGWIN_SERVER_VERSION_API
;
300 version
.minor
= CYGWIN_SERVER_VERSION_MINOR
;
301 version
.patch
= CYGWIN_SERVER_VERSION_PATCH
;
304 class server_request
: public queue_request
307 server_request (transport_layer_base
*const conn
, process_cache
*const cache
)
308 : _conn (conn
), _cache (cache
)
311 virtual ~server_request ()
316 virtual void process ()
318 client_request::handle_request (_conn
, _cache
);
322 transport_layer_base
*const _conn
;
323 process_cache
*const _cache
;
326 class server_submission_loop
: public queue_submission_loop
329 server_submission_loop (threaded_queue
*const queue
,
330 transport_layer_base
*const transport
,
331 process_cache
*const cache
)
332 : queue_submission_loop (queue
, false),
333 _transport (transport
),
341 transport_layer_base
*const _transport
;
342 process_cache
*const _cache
;
344 virtual void request_loop ();
347 /* FIXME: this is a little ugly. What we really want is to wait on
348 * two objects: one for the pipe/socket, and one for being told to
349 * shutdown. Otherwise this will stay a problem (we won't actually
350 * shutdown until the request _AFTER_ the shutdown request. And
351 * sending ourselves a request is ugly
354 server_submission_loop::request_loop ()
356 /* I'd like the accepting thread's priority to be above any "normal"
357 * thread in the system to avoid overflowing the listen queue (for
358 * sockets; similar issues exist for named pipes); but, for example,
359 * a normal priority thread in a foregrounded process is boosted to
360 * THREAD_PRIORITY_HIGHEST (AFAICT). Thus try to set the current
361 * thread's priority to a level one above that. This fails on
362 * win9x/ME so assume any failure in that call is due to that and
363 * simply call again at one priority level lower.
364 * FIXME: This looks weird and is an issue on NT, too. Per MSDN,
365 * THREAD_PRIORITY_HIGHEST + 1 is only a valid priority level if
366 * the priority class is set to REALTIME_PRIORITY_CLASS.
368 if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST
+ 1))
369 if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST
))
370 debug ("failed to raise accept thread priority (err %u)",
375 bool recoverable
= false;
376 transport_layer_base
*const conn
= _transport
->accept (&recoverable
);
377 if (!conn
&& !recoverable
)
379 log (LOG_ERR
, "fatal error on IPC transport: closing down");
382 // EINTR probably implies a shutdown request; so back off for a
383 // moment to let the main thread take control, otherwise the
384 // server spins here receiving EINTR repeatedly since the signal
385 // handler in the main thread doesn't get a chance to be called.
386 if (!conn
&& errno
== EINTR
)
388 if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL
))
389 debug ("failed to reset thread priority (err %u)",
393 if (!SetThreadPriority (GetCurrentThread (),
394 THREAD_PRIORITY_HIGHEST
+ 1))
395 if (!SetThreadPriority (GetCurrentThread (),
396 THREAD_PRIORITY_HIGHEST
))
397 debug ("failed to raise thread priority (err %u)",
401 _queue
->add (new server_request (conn
, _cache
));
405 client_request_shutdown::client_request_shutdown ()
406 : client_request (CYGSERVER_REQUEST_SHUTDOWN
)
411 client_request_shutdown::serve (transport_layer_base
*, process_cache
*)
413 assert (!error_code ());
416 log (LOG_ERR
, "unexpected request body ignored: %lu bytes", msglen ());
418 /* FIXME: link upwards, and then this becomes a trivial method call to
419 * only shutdown _this queue_
422 kill (getpid (), SIGINT
);
427 static sig_atomic_t shutdown_server
= false;
430 handle_signal (const int signum
)
432 /* any signal makes us die :} */
434 shutdown_server
= true;
442 print_usage (const char *const pgm
)
444 log (LOG_NOTICE
, "Usage: %s [OPTIONS]\n"
446 "Cygwin background service daemon\n"
448 "Configuration option:\n"
450 " -f, --config-file <file> Use <file> as config file. Default is\n"
451 " " DEF_CONFIG_FILE
"\n"
453 "Performance options:\n"
455 " -c, --cleanup-threads <num> Number of cleanup threads to use.\n"
456 " -p, --process-cache <num> Size of process cache.\n"
457 " -r, --request-threads <num> Number of request threads to use.\n"
461 " -d, --debug Log debug messages to stderr.\n"
462 " -e, --stderr Log to stderr (default if stderr is a tty).\n"
463 " -E, --no-stderr Don't log to stderr (see -y, -Y options).\n"
464 " -l, --log-level <level> Verbosity of logging (1..7). Default: 6\n"
465 " -y, --syslog Log to syslog (default if stderr is no tty).\n"
466 " -Y, --no-syslog Don't log to syslog (See -e, -E options).\n"
470 " -m, --no-sharedmem Don't start XSI Shared Memory support.\n"
471 " -q, --no-msgqueues Don't start XSI Message Queue support.\n"
472 " -s, --no-semaphores Don't start XSI Semaphore support.\n"
476 " -S, --shutdown Shutdown the daemon.\n"
477 " -h, --help Output usage information and exit.\n"
478 " -V, --version Output version information and exit.\n"
490 "cygserver (cygwin) %d.%d.%d\n"
491 "Cygwin background service daemon\n"
492 "Copyright (C) 2001 - %s Cygwin Authors\n"
493 "This is free software; see the source for copying conditions. There is NO\n"
494 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.",
495 CYGWIN_VERSION_DLL_MAJOR
/ 1000,
496 CYGWIN_VERSION_DLL_MAJOR
% 1000,
497 CYGWIN_VERSION_DLL_MINOR
,
498 strrchr (__DATE__
, ' ') + 1);
506 main (const int argc
, char *argv
[])
508 const struct option longopts
[] = {
509 {"cleanup-threads", required_argument
, NULL
, 'c'},
510 {"debug", no_argument
, NULL
, 'd'},
511 {"stderr", no_argument
, NULL
, 'e'},
512 {"no-stderr", no_argument
, NULL
, 'E'},
513 {"config-file", required_argument
, NULL
, 'f'},
514 {"help", no_argument
, NULL
, 'h'},
515 {"log-level", required_argument
, NULL
, 'l'},
516 {"no-sharedmem", no_argument
, NULL
, 'm'},
517 {"process-cache", required_argument
, NULL
, 'p'},
518 {"no-msgqueues", no_argument
, NULL
, 'q'},
519 {"request-threads", required_argument
, NULL
, 'r'},
520 {"no-semaphores", no_argument
, NULL
, 's'},
521 {"shutdown", no_argument
, NULL
, 'S'},
522 {"version", no_argument
, NULL
, 'V'},
523 {"syslog", no_argument
, NULL
, 'y'},
524 {"no-syslog", no_argument
, NULL
, 'Y'},
525 {0, no_argument
, NULL
, 0}
528 const char opts
[] = "c:deEf:hl:mp:qr:sSVyY";
530 int32_t cleanup_threads
= 0;
531 int32_t request_threads
= 0;
532 int32_t process_cache_size
= 0;
533 bool shutdown
= false;
534 const char *config_file
= DEF_CONFIG_FILE
;
535 bool force_config_file
= false;
536 tun_bool_t option_log_stderr
= TUN_UNDEF
;
537 tun_bool_t option_log_syslog
= TUN_UNDEF
;
541 /* Check if we have a terminal. If so, default to stderr logging,
542 otherwise default to syslog logging. This must be done early
543 to allow default logging already in option processing state. */
544 openlog ("cygserver", LOG_PID
, LOG_KERN
);
546 log_stderr
= TUN_TRUE
;
548 log_syslog
= TUN_TRUE
;
555 while ((opt
= getopt_long (argc
, argv
, opts
, longopts
, NULL
)) != EOF
)
560 cleanup_threads
= strtol (optarg
, &c
, 10);
561 if (cleanup_threads
<= 0 || cleanup_threads
> 32 || (c
&& *c
))
562 panic ("Number of cleanup threads must be between 1 and 32");
566 log_debug
= TUN_TRUE
;
570 option_log_stderr
= TUN_TRUE
;
574 option_log_stderr
= TUN_FALSE
;
578 config_file
= optarg
;
579 force_config_file
= true;
583 print_usage (getprogname ());
588 log_level
= strtoul (optarg
, &c
, 10);
589 if (!log_level
|| log_level
> 7 || (c
&& *c
))
590 panic ("Log level must be between 1 and 7");
594 support_sharedmem
= TUN_FALSE
;
599 process_cache_size
= strtol (optarg
, &c
, 10);
600 if (process_cache_size
<= 0 || process_cache_size
> 310 || (c
&& *c
))
601 panic ("Size of process cache must be between 1 and 310");
605 support_msgqueues
= TUN_FALSE
;
610 request_threads
= strtol (optarg
, &c
, 10);
611 if (request_threads
<= 0 || request_threads
> 310 || (c
&& *c
))
612 panic ("Number of request threads must be between 1 and 310");
616 support_semaphores
= TUN_FALSE
;
628 option_log_syslog
= TUN_TRUE
;
632 option_log_syslog
= TUN_FALSE
;
636 panic ("unknown option -- %c\n"
637 "Try `%s --help' for more information.", optopt
, getprogname ());
641 panic ("Too many arguments");
645 /* Setting `cygserver_running' stops the request code making a
646 * version request, which is not much to the point.
648 cygserver_running
= CYGSERVER_OK
;
650 client_request_shutdown req
;
652 if (req
.make_request () == -1 || req
.error_code ())
653 panic("Shutdown request failed: %s", strerror (req
.error_code ()));
655 // FIXME: It would be nice to wait here for the daemon to exit.
664 tunable_param_init (config_file
, force_config_file
);
666 loginit (option_log_stderr
, option_log_syslog
);
668 log (LOG_INFO
, "daemon starting up");
670 if (!cleanup_threads
)
671 TUNABLE_INT_FETCH ("kern.srv.cleanup_threads", &cleanup_threads
);
672 if (!cleanup_threads
)
675 if (!request_threads
)
676 TUNABLE_INT_FETCH ("kern.srv.request_threads", &request_threads
);
677 if (!request_threads
)
678 request_threads
= 10;
680 if (!process_cache_size
)
681 TUNABLE_INT_FETCH ("kern.srv.process_cache_size", &process_cache_size
);
682 if (!process_cache_size
)
683 process_cache_size
= 62;
685 if (support_sharedmem
== TUN_UNDEF
)
686 TUNABLE_BOOL_FETCH ("kern.srv.sharedmem", &support_sharedmem
);
687 if (support_sharedmem
== TUN_UNDEF
)
688 support_sharedmem
= TUN_TRUE
;
690 if (support_msgqueues
== TUN_UNDEF
)
691 TUNABLE_BOOL_FETCH ("kern.srv.msgqueues", &support_msgqueues
);
692 if (support_msgqueues
== TUN_UNDEF
)
693 support_msgqueues
= TUN_TRUE
;
695 if (support_semaphores
== TUN_UNDEF
)
696 TUNABLE_BOOL_FETCH ("kern.srv.semaphores", &support_semaphores
);
697 if (support_semaphores
== TUN_UNDEF
)
698 support_semaphores
= TUN_TRUE
;
700 if (!setup_privileges ())
701 panic ("Setting process privileges failed.");
706 threaded_queue
request_queue (request_threads
);
708 transport_layer_base
*const transport
= create_server_transport ();
711 if (transport
->listen () == -1)
714 process_cache
cache (process_cache_size
, cleanup_threads
);
716 server_submission_loop
submission_loop (&request_queue
, transport
, &cache
);
718 request_queue
.add_submission_loop (&submission_loop
);
722 request_queue
.start ();
724 log (LOG_NOTICE
, "Initialization complete. Waiting for requests.");
726 /* TODO: wait on multiple objects - the thread handle for each
727 * request loop + all the process handles. This should be done by
728 * querying the request_queue and the process cache for all their
729 * handles, and then waiting for (say) 30 seconds. after that we
730 * recreate the list of handles to wait on, and wait again. the
731 * point of all this abstraction is that we can trivially server
732 * both sockets and pipes simply by making a new transport, and then
733 * calling request_queue.process_requests (transport2);
735 /* WaitForMultipleObjects abort && request_queue && process_queue && signal
736 -- if signal event then retrigger it
738 while (!shutdown_server
&& request_queue
.running () && cache
.running ())
743 shutdown_server
= false;
744 log (LOG_WARNING
, "Shutdown request received but ignored. "
745 "Dependent processes still running.");
749 log (LOG_INFO
, "Shutdown request received - new requests will be denied");
750 request_queue
.stop ();
751 log (LOG_INFO
, "All pending requests processed");
753 log (LOG_INFO
, "No longer accepting requests - cygwin will operate in daemonless mode");
755 log (LOG_INFO
, "All outstanding process-cache activities completed");
756 log (LOG_NOTICE
, "Shutdown finished.");
760 #endif /* __OUTSIDE_CYGWIN__ */