3 Copyright 2001, 2002, 2003, 2004, 2005, 2007, 2011 Red Hat Inc.
5 Written by Egor Duda <deo@logos-m.ru>
7 This file is part of Cygwin.
9 This software is a copyrighted work licensed under the terms of the
10 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
13 #ifdef __OUTSIDE_CYGWIN__
16 #include <sys/types.h>
28 #include "cygwin_version.h"
30 #include "cygserver.h"
32 #include "transport.h"
34 #include "cygserver_ipc.h"
35 #include "cygserver_msg.h"
36 #include "cygserver_sem.h"
38 #define DEF_CONFIG_FILE "" SYSCONFDIR "/cygserver.conf"
40 #define SERVER_VERSION "1.20"
42 GENERIC_MAPPING access_mapping
;
49 TOKEN_PRIVILEGES sPrivileges
;
51 rc
= OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS
, &hToken
) ;
54 debug ("error opening process token (%lu)", GetLastError ());
57 rc
= LookupPrivilegeValue (NULL
, SE_DEBUG_NAME
, &sPrivileges
.Privileges
[0].Luid
);
60 debug ("error getting privilege luid (%lu)", GetLastError ());
64 sPrivileges
.PrivilegeCount
= 1 ;
65 sPrivileges
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
66 rc
= AdjustTokenPrivileges (hToken
, FALSE
, &sPrivileges
, 0, NULL
, NULL
) ;
69 debug ("error adjusting privilege level. (%lu)", GetLastError ());
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
;
87 check_and_dup_handle (HANDLE from_process
, HANDLE to_process
,
88 HANDLE from_process_token
,
91 HANDLE
*to_handle_ptr
, BOOL bInheritHandle
= FALSE
)
93 HANDLE local_handle
= NULL
;
96 PSECURITY_DESCRIPTOR sd
= (PSECURITY_DESCRIPTOR
) &sd_buf
;
99 DWORD ps_len
= sizeof (ps
);
102 if (from_process
!= GetCurrentProcess ())
104 if (!DuplicateHandle (from_process
, from_handle
,
105 GetCurrentProcess (), &local_handle
,
107 DUPLICATE_SAME_ACCESS
))
109 log (LOG_ERR
, "error getting handle(%u) to server (%lu)",
110 (unsigned int)from_handle
, GetLastError ());
114 local_handle
= from_handle
;
116 if (!GetKernelObjectSecurity (local_handle
,
117 (OWNER_SECURITY_INFORMATION
118 | GROUP_SECURITY_INFORMATION
119 | DACL_SECURITY_INFORMATION
),
120 sd
, sizeof (sd_buf
), &bytes_needed
))
122 log (LOG_ERR
, "error getting handle SD (%lu)", GetLastError ());
126 MapGenericMask (&access
, &access_mapping
);
128 if (!AccessCheck (sd
, from_process_token
, access
, &access_mapping
,
129 &ps
, &ps_len
, &access
, &status
))
131 log (LOG_ERR
, "error checking access rights (%lu)",
138 log (LOG_ERR
, "access to object denied");
142 if (!DuplicateHandle (from_process
, from_handle
,
143 to_process
, to_handle_ptr
,
144 access
, bInheritHandle
, 0))
146 log (LOG_ERR
, "error getting handle to client (%lu)", GetLastError ());
150 debug ("Duplicated %p to %p", from_handle
, *to_handle_ptr
);
155 if (local_handle
&& from_process
!= GetCurrentProcess ())
156 CloseHandle (local_handle
);
162 * client_request_attach_tty::serve ()
166 client_request_attach_tty::serve (transport_layer_base
*const conn
,
171 assert (!error_code ());
173 if (msglen () != sizeof (req
))
175 log (LOG_ERR
, "bad request body length: expecting %lu bytes, got %lu",
176 sizeof (req
), msglen ());
182 msglen (0); // Until we fill in some fields.
184 debug ("pid %ld:(%p,%p) -> pid %ld", req
.master_pid
, req
.from_master
,
185 req
.to_master
, req
.pid
);
187 debug ("opening process %ld", req
.master_pid
);
189 const HANDLE from_process_handle
=
190 OpenProcess (PROCESS_DUP_HANDLE
, FALSE
, req
.master_pid
);
192 if (!from_process_handle
)
194 log (LOG_ERR
, "error opening `from' process, error = %lu",
200 debug ("opening process %ld", req
.pid
);
202 const HANDLE to_process_handle
=
203 OpenProcess (PROCESS_DUP_HANDLE
, FALSE
, req
.pid
);
205 if (!to_process_handle
)
207 log (LOG_ERR
, "error opening `to' process, error = %lu",
209 CloseHandle (from_process_handle
);
214 debug ("Impersonating client");
215 if (!conn
->impersonate_client ())
217 CloseHandle (from_process_handle
);
218 CloseHandle (to_process_handle
);
223 HANDLE token_handle
= NULL
;
225 debug ("about to open thread token");
226 const DWORD rc
= OpenThreadToken (GetCurrentThread (),
231 debug ("opened thread token, rc=%lu", rc
);
232 if (!conn
->revert_to_self ())
234 CloseHandle (from_process_handle
);
235 CloseHandle (to_process_handle
);
242 log (LOG_ERR
, "error opening thread token, error = %lu",
244 CloseHandle (from_process_handle
);
245 CloseHandle (to_process_handle
);
250 // From this point on, a reply body is returned to the client.
252 const HANDLE from_master
= req
.from_master
;
253 const HANDLE to_master
= req
.to_master
;
255 req
.from_master
= NULL
;
256 req
.to_master
= NULL
;
258 msglen (sizeof (req
));
261 if (check_and_dup_handle (from_process_handle
, to_process_handle
,
265 &req
.from_master
, TRUE
) != 0)
267 log (LOG_ERR
, "error duplicating from_master handle, error = %lu",
273 if (check_and_dup_handle (from_process_handle
, to_process_handle
,
277 &req
.to_master
, TRUE
) != 0)
279 log (LOG_ERR
, "error duplicating to_master handle, error = %lu",
284 CloseHandle (from_process_handle
);
285 CloseHandle (to_process_handle
);
286 CloseHandle (token_handle
);
288 debug ("%lu(%lu, %lu) -> %lu(%lu,%lu)",
289 req
.master_pid
, from_master
, to_master
,
290 req
.pid
, req
.from_master
, req
.to_master
);
296 client_request_get_version::serve (transport_layer_base
*, process_cache
*)
298 assert (!error_code ());
301 log (LOG_ERR
, "unexpected request body ignored: %lu bytes", msglen ());
303 msglen (sizeof (version
));
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
;
311 class server_request
: public queue_request
314 server_request (transport_layer_base
*const conn
, process_cache
*const cache
)
315 : _conn (conn
), _cache (cache
)
318 virtual ~server_request ()
323 virtual void process ()
325 client_request::handle_request (_conn
, _cache
);
329 transport_layer_base
*const _conn
;
330 process_cache
*const _cache
;
333 class server_submission_loop
: public queue_submission_loop
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
),
348 transport_layer_base
*const _transport
;
349 process_cache
*const _cache
;
351 virtual void request_loop ();
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
361 server_submission_loop::request_loop ()
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.
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.
375 if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST
+ 1))
376 if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST
))
377 debug ("failed to raise accept thread priority, error = %lu",
382 bool recoverable
= false;
383 transport_layer_base
*const conn
= _transport
->accept (&recoverable
);
384 if (!conn
&& !recoverable
)
386 log (LOG_ERR
, "fatal error on IPC transport: closing down");
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
)
395 if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL
))
396 debug ("failed to reset thread priority, error = %lu",
400 if (!SetThreadPriority (GetCurrentThread (),
401 THREAD_PRIORITY_HIGHEST
+ 1))
402 if (!SetThreadPriority (GetCurrentThread (),
403 THREAD_PRIORITY_HIGHEST
))
404 debug ("failed to raise thread priority, error = %lu",
408 _queue
->add (new server_request (conn
, _cache
));
412 client_request_shutdown::client_request_shutdown ()
413 : client_request (CYGSERVER_REQUEST_SHUTDOWN
)
418 client_request_shutdown::serve (transport_layer_base
*, process_cache
*)
420 assert (!error_code ());
423 log (LOG_ERR
, "unexpected request body ignored: %lu bytes", msglen ());
425 /* FIXME: link upwards, and then this becomes a trivial method call to
426 * only shutdown _this queue_
429 kill (getpid (), SIGINT
);
434 static sig_atomic_t shutdown_server
= false;
437 handle_signal (const int signum
)
439 /* any signal makes us die :} */
441 shutdown_server
= true;
449 print_usage (const char *const pgm
)
451 log (LOG_NOTICE
, "Usage: %s [OPTIONS]\n"
453 "Cygwin background service daemon\n"
455 "Configuration option:\n"
457 " -f, --config-file <file> Use <file> as config file. Default is\n"
458 " " DEF_CONFIG_FILE
"\n"
460 "Performance options:\n"
462 " -c, --cleanup-threads <num> Number of cleanup threads to use.\n"
463 " -p, --process-cache <num> Size of process cache.\n"
464 " -r, --request-threads <num> Number of request threads to use.\n"
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"
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"
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"
483 " -S, --shutdown Shutdown the daemon.\n"
484 " -h, --help Output usage information and exit.\n"
485 " -V, --version Output version information and exit.\n"
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);
513 main (const int argc
, char *argv
[])
515 const struct option longopts
[] = {
516 {"cleanup-threads", required_argument
, NULL
, 'c'},
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'},
521 {"help", no_argument
, NULL
, 'h'},
522 {"log-level", required_argument
, NULL
, 'l'},
523 {"no-sharedmem", no_argument
, NULL
, 'm'},
524 {"process-cache", required_argument
, NULL
, 'p'},
525 {"no-msgqueues", no_argument
, NULL
, 'q'},
526 {"request-threads", required_argument
, NULL
, 'r'},
527 {"no-semaphores", no_argument
, NULL
, 's'},
528 {"shutdown", no_argument
, NULL
, 'S'},
529 {"version", no_argument
, NULL
, 'V'},
530 {"syslog", no_argument
, NULL
, 'y'},
531 {"no-syslog", no_argument
, NULL
, 'Y'},
532 {0, no_argument
, NULL
, 0}
535 const char opts
[] = "c:deEf:hl:mp:qr:sSVyY";
537 long cleanup_threads
= 0;
538 long request_threads
= 0;
539 long process_cache_size
= 0;
540 bool shutdown
= false;
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
;
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
);
553 log_stderr
= TUN_TRUE
;
555 log_syslog
= TUN_TRUE
;
562 while ((opt
= getopt_long (argc
, argv
, opts
, longopts
, NULL
)) != EOF
)
567 cleanup_threads
= strtol (optarg
, &c
, 10);
568 if (cleanup_threads
<= 0 || cleanup_threads
> 32 || (c
&& *c
))
569 panic ("Number of cleanup threads must be between 1 and 32");
573 log_debug
= TUN_TRUE
;
577 option_log_stderr
= TUN_TRUE
;
581 option_log_stderr
= TUN_FALSE
;
585 config_file
= optarg
;
586 force_config_file
= true;
590 print_usage (getprogname ());
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");
601 support_sharedmem
= TUN_FALSE
;
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");
612 support_msgqueues
= TUN_FALSE
;
617 request_threads
= strtol (optarg
, &c
, 10);
618 if (request_threads
<= 0 || request_threads
> 310 || (c
&& *c
))
619 panic ("Number of request threads must be between 1 and 310");
623 support_semaphores
= TUN_FALSE
;
635 option_log_syslog
= TUN_TRUE
;
639 option_log_syslog
= TUN_FALSE
;
643 panic ("unknown option -- %c\n"
644 "Try `%s --help' for more information.", optopt
, getprogname ());
648 panic ("Too many arguments");
652 /* Setting `cygserver_running' stops the request code making a
653 * version request, which is not much to the point.
655 cygserver_running
= CYGSERVER_OK
;
657 client_request_shutdown req
;
659 if (req
.make_request () == -1 || req
.error_code ())
660 panic("Shutdown request failed: %s", strerror (req
.error_code ()));
662 // FIXME: It would be nice to wait here for the daemon to exit.
671 tunable_param_init (config_file
, force_config_file
);
673 loginit (option_log_stderr
, option_log_syslog
);
675 log (LOG_INFO
, "daemon starting up");
677 if (!cleanup_threads
)
678 TUNABLE_INT_FETCH ("kern.srv.cleanup_threads", &cleanup_threads
);
679 if (!cleanup_threads
)
682 if (!request_threads
)
683 TUNABLE_INT_FETCH ("kern.srv.request_threads", &request_threads
);
684 if (!request_threads
)
685 request_threads
= 10;
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;
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
;
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
;
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
;
707 if (!setup_privileges ())
708 panic ("Setting process privileges failed.");
713 threaded_queue
request_queue (request_threads
);
715 transport_layer_base
*const transport
= create_server_transport ();
718 process_cache
cache (process_cache_size
, cleanup_threads
);
720 server_submission_loop
submission_loop (&request_queue
, transport
, &cache
);
722 request_queue
.add_submission_loop (&submission_loop
);
724 if (transport
->listen () == -1)
729 request_queue
.start ();
731 log (LOG_NOTICE
, "Initialization complete. Waiting for requests.");
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);
742 /* WaitForMultipleObjects abort && request_queue && process_queue && signal
743 -- if signal event then retrigger it
745 while (!shutdown_server
&& request_queue
.running () && cache
.running ())
750 shutdown_server
= false;
751 log (LOG_WARNING
, "Shutdown request received but ignored. "
752 "Dependent processes still running.");
756 log (LOG_INFO
, "Shutdown request received - new requests will be denied");
757 request_queue
.stop ();
758 log (LOG_INFO
, "All pending requests processed");
760 log (LOG_INFO
, "No longer accepting requests - cygwin will operate in daemonless mode");
762 log (LOG_INFO
, "All outstanding process-cache activities completed");
763 log (LOG_NOTICE
, "Shutdown finished.");
767 #endif /* __OUTSIDE_CYGWIN__ */