3 Copyright 2001, 2002, 2003, 2004, 2005 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 if (from_process
!= GetCurrentProcess ())
98 if (!DuplicateHandle (from_process
, from_handle
,
99 GetCurrentProcess (), &local_handle
,
101 DUPLICATE_SAME_ACCESS
))
103 log (LOG_ERR
, "error getting handle(%u) to server (%lu)",
104 (unsigned int)from_handle
, GetLastError ());
108 local_handle
= from_handle
;
110 if (!wincap
.has_security ())
111 assert (!from_process_token
);
115 PSECURITY_DESCRIPTOR sd
= (PSECURITY_DESCRIPTOR
) &sd_buf
;
118 DWORD ps_len
= sizeof (ps
);
121 if (!GetKernelObjectSecurity (local_handle
,
122 (OWNER_SECURITY_INFORMATION
123 | GROUP_SECURITY_INFORMATION
124 | DACL_SECURITY_INFORMATION
),
125 sd
, sizeof (sd_buf
), &bytes_needed
))
127 log (LOG_ERR
, "error getting handle SD (%lu)", GetLastError ());
131 MapGenericMask (&access
, &access_mapping
);
133 if (!AccessCheck (sd
, from_process_token
, access
, &access_mapping
,
134 &ps
, &ps_len
, &access
, &status
))
136 log (LOG_ERR
, "error checking access rights (%lu)",
143 log (LOG_ERR
, "access to object denied");
148 if (!DuplicateHandle (from_process
, from_handle
,
149 to_process
, to_handle_ptr
,
150 access
, bInheritHandle
, 0))
152 log (LOG_ERR
, "error getting handle to client (%lu)", GetLastError ());
156 debug ("Duplicated %p to %p", from_handle
, *to_handle_ptr
);
161 if (local_handle
&& from_process
!= GetCurrentProcess ())
162 CloseHandle (local_handle
);
168 * client_request_attach_tty::serve ()
172 client_request_attach_tty::serve (transport_layer_base
*const conn
,
177 assert (!error_code ());
179 if (!wincap
.has_security ())
181 log (LOG_NOTICE
, "operation only supported on systems with security");
187 if (msglen () != sizeof (req
))
189 log (LOG_ERR
, "bad request body length: expecting %lu bytes, got %lu",
190 sizeof (req
), msglen ());
196 msglen (0); // Until we fill in some fields.
198 debug ("pid %ld:(%p,%p) -> pid %ld", req
.master_pid
, req
.from_master
,
199 req
.to_master
, req
.pid
);
201 debug ("opening process %ld", req
.master_pid
);
203 const HANDLE from_process_handle
=
204 OpenProcess (PROCESS_DUP_HANDLE
, FALSE
, req
.master_pid
);
206 if (!from_process_handle
)
208 log (LOG_ERR
, "error opening `from' process, error = %lu",
214 debug ("opening process %ld", req
.pid
);
216 const HANDLE to_process_handle
=
217 OpenProcess (PROCESS_DUP_HANDLE
, FALSE
, req
.pid
);
219 if (!to_process_handle
)
221 log (LOG_ERR
, "error opening `to' process, error = %lu",
223 CloseHandle (from_process_handle
);
228 debug ("Impersonating client");
229 if (!conn
->impersonate_client ())
231 CloseHandle (from_process_handle
);
232 CloseHandle (to_process_handle
);
237 HANDLE token_handle
= NULL
;
239 debug ("about to open thread token");
240 const DWORD rc
= OpenThreadToken (GetCurrentThread (),
245 debug ("opened thread token, rc=%lu", rc
);
246 if (!conn
->revert_to_self ())
248 CloseHandle (from_process_handle
);
249 CloseHandle (to_process_handle
);
256 log (LOG_ERR
, "error opening thread token, error = %lu",
258 CloseHandle (from_process_handle
);
259 CloseHandle (to_process_handle
);
264 // From this point on, a reply body is returned to the client.
266 const HANDLE from_master
= req
.from_master
;
267 const HANDLE to_master
= req
.to_master
;
269 req
.from_master
= NULL
;
270 req
.to_master
= NULL
;
272 msglen (sizeof (req
));
275 if (check_and_dup_handle (from_process_handle
, to_process_handle
,
279 &req
.from_master
, TRUE
) != 0)
281 log (LOG_ERR
, "error duplicating from_master handle, error = %lu",
287 if (check_and_dup_handle (from_process_handle
, to_process_handle
,
291 &req
.to_master
, TRUE
) != 0)
293 log (LOG_ERR
, "error duplicating to_master handle, error = %lu",
298 CloseHandle (from_process_handle
);
299 CloseHandle (to_process_handle
);
300 CloseHandle (token_handle
);
302 debug ("%lu(%lu, %lu) -> %lu(%lu,%lu)",
303 req
.master_pid
, from_master
, to_master
,
304 req
.pid
, req
.from_master
, req
.to_master
);
310 client_request_get_version::serve (transport_layer_base
*, process_cache
*)
312 assert (!error_code ());
315 log (LOG_ERR
, "unexpected request body ignored: %lu bytes", msglen ());
317 msglen (sizeof (version
));
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
;
325 class server_request
: public queue_request
328 server_request (transport_layer_base
*const conn
, process_cache
*const cache
)
329 : _conn (conn
), _cache (cache
)
332 virtual ~server_request ()
337 virtual void process ()
339 client_request::handle_request (_conn
, _cache
);
343 transport_layer_base
*const _conn
;
344 process_cache
*const _cache
;
347 class server_submission_loop
: public queue_submission_loop
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
),
362 transport_layer_base
*const _transport
;
363 process_cache
*const _cache
;
365 virtual void request_loop ();
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
375 server_submission_loop::request_loop ()
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.
386 if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST
+ 1))
387 if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST
))
388 debug ("failed to raise accept thread priority, error = %lu",
393 bool recoverable
= false;
394 transport_layer_base
*const conn
= _transport
->accept (&recoverable
);
395 if (!conn
&& !recoverable
)
397 log (LOG_ERR
, "fatal error on IPC transport: closing down");
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
)
406 if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL
))
407 debug ("failed to reset thread priority, error = %lu",
411 if (!SetThreadPriority (GetCurrentThread (),
412 THREAD_PRIORITY_HIGHEST
+ 1))
413 if (!SetThreadPriority (GetCurrentThread (),
414 THREAD_PRIORITY_HIGHEST
))
415 debug ("failed to raise thread priority, error = %lu",
419 _queue
->add (new server_request (conn
, _cache
));
423 client_request_shutdown::client_request_shutdown ()
424 : client_request (CYGSERVER_REQUEST_SHUTDOWN
)
429 client_request_shutdown::serve (transport_layer_base
*, process_cache
*)
431 assert (!error_code ());
434 log (LOG_ERR
, "unexpected request body ignored: %lu bytes", msglen ());
436 /* FIXME: link upwards, and then this becomes a trivial method call to
437 * only shutdown _this queue_
440 kill (getpid (), SIGINT
);
445 static sig_atomic_t shutdown_server
= false;
448 handle_signal (const int signum
)
450 /* any signal makes us die :} */
452 shutdown_server
= true;
460 print_usage (const char *const pgm
)
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"
465 " " DEF_CONFIG_FILE
"\n"
467 "Performance options:\n"
468 " -c, --cleanup-threads <num> Number of cleanup threads to use.\n"
469 " -p, --process-cache <num> Size of process cache.\n"
470 " -r, --request-threads <num> Number of request threads to use.\n"
473 " -d, --debug Log debug messages to stderr.\n"
474 " -e, --stderr Log to stderr (default if stderr is a tty).\n"
475 " -E, --no-stderr Don't log to stderr (see -y, -Y options).\n"
476 " -l, --log-level <level> Verbosity of logging (1..7). Default: 6\n"
477 " -y, --syslog Log to syslog (default if stderr is no tty).\n"
478 " -Y, --no-syslog Don't log to syslog (See -e, -E options).\n"
481 " -m, --no-sharedmem Don't start XSI Shared Memory support.\n"
482 " -q, --no-msgqueues Don't start XSI Message Queue support.\n"
483 " -s, --no-semaphores Don't start XSI Semaphore support.\n"
486 " -S, --shutdown Shutdown the daemon.\n"
487 " -h, --help Output usage information and exit.\n"
488 " -v, --version Output version information and exit."
500 snprintf (buf
, sizeof (buf
), "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s",
501 cygwin_version
.dll_major
/ 1000,
502 cygwin_version
.dll_major
% 1000,
503 cygwin_version
.dll_minor
,
504 cygwin_version
.api_major
,
505 cygwin_version
.api_minor
,
506 cygwin_version
.shared_data
,
507 CYGWIN_SERVER_VERSION_MAJOR
,
508 CYGWIN_SERVER_VERSION_API
,
509 CYGWIN_SERVER_VERSION_MINOR
,
510 CYGWIN_SERVER_VERSION_PATCH
,
511 cygwin_version
.mount_registry
,
512 cygwin_version
.dll_build_date
);
514 log (LOG_INFO
, "(cygwin) %s\n"
516 "Copyright 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.\n"
518 "Default configuration file is %s",
519 SERVER_VERSION
, buf
, __DATE__
, DEF_CONFIG_FILE
);
527 main (const int argc
, char *argv
[])
529 const struct option longopts
[] = {
530 {"cleanup-threads", required_argument
, NULL
, 'c'},
531 {"debug", no_argument
, NULL
, 'd'},
532 {"stderr", no_argument
, NULL
, 'e'},
533 {"no-stderr", no_argument
, NULL
, 'E'},
534 {"config-file", required_argument
, NULL
, 'f'},
535 {"help", no_argument
, NULL
, 'h'},
536 {"log-level", required_argument
, NULL
, 'l'},
537 {"no-sharedmem", no_argument
, NULL
, 'm'},
538 {"process-cache", required_argument
, NULL
, 'p'},
539 {"no-msgqueues", no_argument
, NULL
, 'q'},
540 {"request-threads", required_argument
, NULL
, 'r'},
541 {"no-semaphores", no_argument
, NULL
, 's'},
542 {"shutdown", no_argument
, NULL
, 'S'},
543 {"version", no_argument
, NULL
, 'v'},
544 {"syslog", no_argument
, NULL
, 'y'},
545 {"no-syslog", no_argument
, NULL
, 'Y'},
546 {0, no_argument
, NULL
, 0}
549 const char opts
[] = "c:deEf:hl:mp:qr:sSvyY";
551 long cleanup_threads
= 0;
552 long request_threads
= 0;
553 long process_cache_size
= 0;
554 bool shutdown
= false;
555 const char *config_file
= DEF_CONFIG_FILE
;
556 bool force_config_file
= false;
557 tun_bool_t option_log_stderr
= TUN_UNDEF
;
558 tun_bool_t option_log_syslog
= TUN_UNDEF
;
562 /* Check if we have a terminal. If so, default to stderr logging,
563 otherwise default to syslog logging. This must be done early
564 to allow default logging already in option processing state. */
565 openlog ("cygserver", LOG_PID
, LOG_KERN
);
567 log_stderr
= TUN_TRUE
;
569 log_syslog
= TUN_TRUE
;
577 while ((opt
= getopt_long (argc
, argv
, opts
, longopts
, NULL
)) != EOF
)
582 cleanup_threads
= strtol (optarg
, &c
, 10);
583 if (cleanup_threads
<= 0 || cleanup_threads
> 32 || (c
&& *c
))
584 panic ("Number of cleanup threads must be between 1 and 32");
588 log_debug
= TUN_TRUE
;
592 option_log_stderr
= TUN_TRUE
;
596 option_log_stderr
= TUN_FALSE
;
600 config_file
= optarg
;
601 force_config_file
= true;
605 print_usage (getprogname ());
610 log_level
= strtoul (optarg
, &c
, 10);
611 if (!log_level
|| log_level
> 7 || (c
&& *c
))
612 panic ("Log level must be between 1 and 7");
616 support_sharedmem
= TUN_FALSE
;
621 process_cache_size
= strtol (optarg
, &c
, 10);
622 if (process_cache_size
<= 0 || process_cache_size
> 310 || (c
&& *c
))
623 panic ("Size of process cache must be between 1 and 310");
627 support_msgqueues
= TUN_FALSE
;
632 request_threads
= strtol (optarg
, &c
, 10);
633 if (request_threads
<= 0 || request_threads
> 310 || (c
&& *c
))
634 panic ("Number of request threads must be between 1 and 310");
638 support_semaphores
= TUN_FALSE
;
650 option_log_syslog
= TUN_TRUE
;
654 option_log_syslog
= TUN_FALSE
;
658 panic ("unknown option -- %c\n"
659 "Try `%s --help' for more information.", optopt
, getprogname ());
663 panic ("Too many arguments");
667 /* Setting `cygserver_running' stops the request code making a
668 * version request, which is not much to the point.
670 cygserver_running
= CYGSERVER_OK
;
672 client_request_shutdown req
;
674 if (req
.make_request () == -1 || req
.error_code ())
675 panic("Shutdown request failed: %s", strerror (req
.error_code ()));
677 // FIXME: It would be nice to wait here for the daemon to exit.
686 tunable_param_init (config_file
, force_config_file
);
688 loginit (option_log_stderr
, option_log_syslog
);
690 log (LOG_INFO
, "daemon starting up");
692 if (!cleanup_threads
)
693 TUNABLE_INT_FETCH ("kern.srv.cleanup_threads", &cleanup_threads
);
694 if (!cleanup_threads
)
697 if (!request_threads
)
698 TUNABLE_INT_FETCH ("kern.srv.request_threads", &request_threads
);
699 if (!request_threads
)
700 request_threads
= 10;
702 if (!process_cache_size
)
703 TUNABLE_INT_FETCH ("kern.srv.process_cache_size", &process_cache_size
);
704 if (!process_cache_size
)
705 process_cache_size
= 62;
707 if (support_sharedmem
== TUN_UNDEF
)
708 TUNABLE_BOOL_FETCH ("kern.srv.sharedmem", &support_sharedmem
);
709 if (support_sharedmem
== TUN_UNDEF
)
710 support_sharedmem
= TUN_TRUE
;
712 if (support_msgqueues
== TUN_UNDEF
)
713 TUNABLE_BOOL_FETCH ("kern.srv.msgqueues", &support_msgqueues
);
714 if (support_msgqueues
== TUN_UNDEF
)
715 support_msgqueues
= TUN_TRUE
;
717 if (support_semaphores
== TUN_UNDEF
)
718 TUNABLE_BOOL_FETCH ("kern.srv.semaphores", &support_semaphores
);
719 if (support_semaphores
== TUN_UNDEF
)
720 support_semaphores
= TUN_TRUE
;
722 if (wincap
.has_security () && !setup_privileges ())
723 panic ("Setting process privileges failed.");
728 threaded_queue
request_queue (request_threads
);
730 transport_layer_base
*const transport
= create_server_transport ();
733 process_cache
cache (process_cache_size
, cleanup_threads
);
735 server_submission_loop
submission_loop (&request_queue
, transport
, &cache
);
737 request_queue
.add_submission_loop (&submission_loop
);
739 if (transport
->listen () == -1)
744 request_queue
.start ();
746 log (LOG_NOTICE
, "Initialization complete. Waiting for requests.");
748 /* TODO: wait on multiple objects - the thread handle for each
749 * request loop + all the process handles. This should be done by
750 * querying the request_queue and the process cache for all their
751 * handles, and then waiting for (say) 30 seconds. after that we
752 * recreate the list of handles to wait on, and wait again. the
753 * point of all this abstraction is that we can trivially server
754 * both sockets and pipes simply by making a new transport, and then
755 * calling request_queue.process_requests (transport2);
757 /* WaitForMultipleObjects abort && request_queue && process_queue && signal
758 -- if signal event then retrigger it
760 while (!shutdown_server
&& request_queue
.running () && cache
.running ())
765 shutdown_server
= false;
766 log (LOG_WARNING
, "Shutdown request received but ignored. "
767 "Dependent processes still running.");
771 log (LOG_INFO
, "Shutdown request received - new requests will be denied");
772 request_queue
.stop ();
773 log (LOG_INFO
, "All pending requests processed");
775 log (LOG_INFO
, "No longer accepting requests - cygwin will operate in daemonless mode");
777 log (LOG_INFO
, "All outstanding process-cache activities completed");
778 log (LOG_NOTICE
, "Shutdown finished.");
782 #endif /* __OUTSIDE_CYGWIN__ */