3 Copyright 2001, 2002, 2003, 2004, 2005, 2007 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"
452 "Configuration option:\n"
453 " -f, --config-file <file> Use <file> as config file. Default is\n"
454 " " DEF_CONFIG_FILE "\n"
456 "Performance options:\n"
457 " -c, --cleanup-threads <num> Number of cleanup threads to use.\n"
458 " -p, --process-cache <num> Size of process cache.\n"
459 " -r, --request-threads <num> Number of request threads to use.\n"
462 " -d, --debug Log debug messages to stderr.\n"
463 " -e, --stderr Log to stderr (default if stderr is a tty).\n"
464 " -E, --no-stderr Don't log to stderr (see -y, -Y options).\n"
465 " -l, --log-level <level> Verbosity of logging (1..7). Default: 6\n"
466 " -y, --syslog Log to syslog (default if stderr is no tty).\n"
467 " -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"
475 " -S, --shutdown Shutdown the daemon.\n"
476 " -h, --help Output usage information and exit.\n"
477 " -v, --version Output version information and exit."
489 snprintf (buf, sizeof (buf), "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s",
490 cygwin_version.dll_major / 1000,
491 cygwin_version.dll_major % 1000,
492 cygwin_version.dll_minor,
493 cygwin_version.api_major,
494 cygwin_version.api_minor,
495 cygwin_version.shared_data,
496 CYGWIN_SERVER_VERSION_MAJOR,
497 CYGWIN_SERVER_VERSION_API,
498 CYGWIN_SERVER_VERSION_MINOR,
499 CYGWIN_SERVER_VERSION_PATCH,
500 cygwin_version.mount_registry,
501 cygwin_version.dll_build_date);
503 log (LOG_INFO, "(cygwin) %s\n"
505 "Copyright 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.\n"
507 "Default configuration file is %s",
508 SERVER_VERSION, buf, __DATE__, DEF_CONFIG_FILE);
516 main (const int argc, char *argv[])
518 const struct option longopts[] = {
519 {"cleanup-threads", required_argument, NULL, 'c'},
520 {"debug", no_argument, NULL, 'd'},
521 {"stderr", no_argument, NULL, 'e'},
522 {"no-stderr", no_argument, NULL, 'E'},
523 {"config-file", required_argument, NULL, 'f'},
524 {"help", no_argument, NULL, 'h'},
525 {"log-level", required_argument, NULL, 'l'},
526 {"no-sharedmem", no_argument, NULL, 'm'},
527 {"process-cache", required_argument, NULL, 'p'},
528 {"no-msgqueues", no_argument, NULL, 'q'},
529 {"request-threads", required_argument, NULL, 'r'},
530 {"no-semaphores", no_argument, NULL, 's'},
531 {"shutdown", no_argument, NULL, 'S'},
532 {"version", no_argument, NULL, 'v'},
533 {"syslog", no_argument, NULL, 'y'},
534 {"no-syslog", no_argument, NULL, 'Y'},
535 {0, no_argument, NULL, 0}
538 const char opts[] = "c:deEf:hl:mp:qr:sSvyY";
540 long cleanup_threads = 0;
541 long request_threads = 0;
542 long process_cache_size = 0;
543 bool shutdown = false;
544 const char *config_file = DEF_CONFIG_FILE;
545 bool force_config_file = false;
546 tun_bool_t option_log_stderr = TUN_UNDEF;
547 tun_bool_t option_log_syslog = TUN_UNDEF;
551 /* Check if we have a terminal. If so, default to stderr logging,
552 otherwise default to syslog logging. This must be done early
553 to allow default logging already in option processing state. */
554 openlog ("cygserver", LOG_PID, LOG_KERN);
556 log_stderr = TUN_TRUE;
558 log_syslog = TUN_TRUE;
565 while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
570 cleanup_threads = strtol (optarg, &c, 10);
571 if (cleanup_threads <= 0 || cleanup_threads > 32 || (c && *c))
572 panic ("Number of cleanup threads must be between 1 and 32");
576 log_debug = TUN_TRUE;
580 option_log_stderr = TUN_TRUE;
584 option_log_stderr = TUN_FALSE;
588 config_file = optarg;
589 force_config_file = true;
593 print_usage (getprogname ());
598 log_level = strtoul (optarg, &c, 10);
599 if (!log_level || log_level > 7 || (c && *c))
600 panic ("Log level must be between 1 and 7");
604 support_sharedmem = TUN_FALSE;
609 process_cache_size = strtol (optarg, &c, 10);
610 if (process_cache_size <= 0 || process_cache_size > 310 || (c && *c))
611 panic ("Size of process cache must be between 1 and 310");
615 support_msgqueues = TUN_FALSE;
620 request_threads = strtol (optarg, &c, 10);
621 if (request_threads <= 0 || request_threads > 310 || (c && *c))
622 panic ("Number of request threads must be between 1 and 310");
626 support_semaphores = TUN_FALSE;
638 option_log_syslog = TUN_TRUE;
642 option_log_syslog = TUN_FALSE;
646 panic ("unknown option -- %c\n"
647 "Try `%s --help' for more information.", optopt, getprogname ());
651 panic ("Too many arguments");
655 /* Setting `cygserver_running' stops the request code making a
656 * version request, which is not much to the point.
658 cygserver_running = CYGSERVER_OK;
660 client_request_shutdown req;
662 if (req.make_request () == -1 || req.error_code ())
663 panic("Shutdown request failed: %s", strerror (req.error_code ()));
665 // FIXME: It would be nice to wait here for the daemon to exit.
674 tunable_param_init (config_file, force_config_file);
676 loginit (option_log_stderr, option_log_syslog);
678 log (LOG_INFO, "daemon starting up");
680 if (!cleanup_threads)
681 TUNABLE_INT_FETCH ("kern.srv.cleanup_threads", &cleanup_threads);
682 if (!cleanup_threads)
685 if (!request_threads)
686 TUNABLE_INT_FETCH ("kern.srv.request_threads", &request_threads);
687 if (!request_threads)
688 request_threads = 10;
690 if (!process_cache_size)
691 TUNABLE_INT_FETCH ("kern.srv.process_cache_size", &process_cache_size);
692 if (!process_cache_size)
693 process_cache_size = 62;
695 if (support_sharedmem == TUN_UNDEF)
696 TUNABLE_BOOL_FETCH ("kern.srv.sharedmem", &support_sharedmem);
697 if (support_sharedmem == TUN_UNDEF)
698 support_sharedmem = TUN_TRUE;
700 if (support_msgqueues == TUN_UNDEF)
701 TUNABLE_BOOL_FETCH ("kern.srv.msgqueues", &support_msgqueues);
702 if (support_msgqueues == TUN_UNDEF)
703 support_msgqueues = TUN_TRUE;
705 if (support_semaphores == TUN_UNDEF)
706 TUNABLE_BOOL_FETCH ("kern.srv.semaphores", &support_semaphores);
707 if (support_semaphores == TUN_UNDEF)
708 support_semaphores = TUN_TRUE;
710 if (!setup_privileges ())
711 panic ("Setting process privileges failed.");
716 threaded_queue request_queue (request_threads);
718 transport_layer_base *const transport = create_server_transport ();
721 process_cache cache (process_cache_size, cleanup_threads);
723 server_submission_loop submission_loop (&request_queue, transport, &cache);
725 request_queue.add_submission_loop (&submission_loop);
727 if (transport->listen () == -1)
732 request_queue.start ();
734 log (LOG_NOTICE, "Initialization complete. Waiting for requests.");
736 /* TODO: wait on multiple objects - the thread handle for each
737 * request loop + all the process handles. This should be done by
738 * querying the request_queue and the process cache for all their
739 * handles, and then waiting for (say) 30 seconds. after that we
740 * recreate the list of handles to wait on, and wait again. the
741 * point of all this abstraction is that we can trivially server
742 * both sockets and pipes simply by making a new transport, and then
743 * calling request_queue.process_requests (transport2);
745 /* WaitForMultipleObjects abort && request_queue && process_queue && signal
746 -- if signal event then retrigger it
748 while (!shutdown_server && request_queue.running () && cache.running ())
753 shutdown_server = false;
754 log (LOG_WARNING, "Shutdown request received but ignored. "
755 "Dependent processes still running.");
759 log (LOG_INFO, "Shutdown request received - new requests will be denied");
760 request_queue.stop ();
761 log (LOG_INFO, "All pending requests processed");
763 log (LOG_INFO, "No longer accepting requests - cygwin will operate in daemonless mode");
765 log (LOG_INFO, "All outstanding process-cache activities completed");
766 log (LOG_NOTICE, "Shutdown finished.");
770 #endif /* __OUTSIDE_CYGWIN__ */