OSDN Git Service

* sysv_msg.cc: Add fix from upstream version 1.65.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygserver / cygserver.cc
1 /* cygserver.cc
2
3    Copyright 2001, 2002, 2003, 2004, 2005, 2007 Red Hat Inc.
4
5    Written by Egor Duda <deo@logos-m.ru>
6
7 This file is part of Cygwin.
8
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
13 #ifdef __OUTSIDE_CYGWIN__
14 #include "woutsup.h"
15
16 #include <sys/types.h>
17
18 #include <assert.h>
19 #include <errno.h>
20 #include <ctype.h>
21 #include <getopt.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "cygwin_version.h"
29
30 #include "cygserver.h"
31 #include "process.h"
32 #include "transport.h"
33
34 #include "cygserver_ipc.h"
35 #include "cygserver_msg.h"
36 #include "cygserver_sem.h"
37
38 #define DEF_CONFIG_FILE "" SYSCONFDIR "/cygserver.conf"
39
40 #define SERVER_VERSION  "1.20"
41
42 GENERIC_MAPPING access_mapping;
43
44 static bool
45 setup_privileges ()
46 {
47   BOOL rc, ret_val;
48   HANDLE hToken = NULL;
49   TOKEN_PRIVILEGES sPrivileges;
50
51   rc = OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS , &hToken) ;
52   if (!rc)
53     {
54       debug ("error opening process token (%lu)", GetLastError ());
55       return false;
56     }
57   rc = LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid);
58   if (!rc)
59     {
60       debug ("error getting privilege luid (%lu)", GetLastError ());
61       ret_val = false;
62       goto out;
63     }
64   sPrivileges.PrivilegeCount = 1 ;
65   sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ;
66   rc = AdjustTokenPrivileges (hToken, FALSE, &sPrivileges, 0, NULL, NULL) ;
67   if (!rc)
68     {
69       debug ("error adjusting privilege level. (%lu)", GetLastError ());
70       ret_val = false;
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
79   ret_val = true;
80
81 out:
82   CloseHandle (hToken);
83   return ret_val;
84 }
85
86 int
87 check_and_dup_handle (HANDLE from_process, HANDLE to_process,
88                       HANDLE from_process_token,
89                       DWORD access,
90                       HANDLE from_handle,
91                       HANDLE *to_handle_ptr, BOOL bInheritHandle = FALSE)
92 {
93   HANDLE local_handle = NULL;
94   int ret_val = EACCES;
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;
101
102   if (from_process != GetCurrentProcess ())
103     {
104       if (!DuplicateHandle (from_process, from_handle,
105                             GetCurrentProcess (), &local_handle,
106                             0, bInheritHandle,
107                             DUPLICATE_SAME_ACCESS))
108         {
109           log (LOG_ERR, "error getting handle(%u) to server (%lu)",
110                          (unsigned int)from_handle, GetLastError ());
111           goto out;
112         }
113     } else
114       local_handle = from_handle;
115
116   if (!GetKernelObjectSecurity (local_handle,
117                                 (OWNER_SECURITY_INFORMATION
118                                  | GROUP_SECURITY_INFORMATION
119                                  | DACL_SECURITY_INFORMATION),
120                                 sd, sizeof (sd_buf), &bytes_needed))
121     {
122       log (LOG_ERR, "error getting handle SD (%lu)", GetLastError ());
123       goto out;
124     }
125
126   MapGenericMask (&access, &access_mapping);
127
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     }
135
136   if (!status)
137     {
138       log (LOG_ERR, "access to object denied");
139       goto out;
140     }
141
142   if (!DuplicateHandle (from_process, from_handle,
143                         to_process, to_handle_ptr,
144                         access, bInheritHandle, 0))
145     {
146       log (LOG_ERR, "error getting handle to client (%lu)", GetLastError ());
147       goto out;
148     }
149
150   debug ("Duplicated %p to %p", from_handle, *to_handle_ptr);
151
152   ret_val = 0;
153
154  out:
155   if (local_handle && from_process != GetCurrentProcess ())
156     CloseHandle (local_handle);
157
158   return (ret_val);
159 }
160
161 /*
162  * client_request_attach_tty::serve ()
163  */
164
165 void
166 client_request_attach_tty::serve (transport_layer_base *const conn,
167                                   process_cache *)
168 {
169   assert (conn);
170
171   assert (!error_code ());
172
173   if (msglen () != sizeof (req))
174     {
175       log (LOG_ERR, "bad request body length: expecting %lu bytes, got %lu",
176                       sizeof (req), msglen ());
177       error_code (EINVAL);
178       msglen (0);
179       return;
180     }
181
182   msglen (0);                   // Until we fill in some fields.
183
184   debug ("pid %ld:(%p,%p) -> pid %ld", req.master_pid, req.from_master,
185                                        req.to_master, req.pid);
186
187   debug ("opening process %ld", req.master_pid);
188
189   const HANDLE from_process_handle =
190     OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid);
191
192   if (!from_process_handle)
193     {
194       log (LOG_ERR, "error opening `from' process, error = %lu",
195                      GetLastError ());
196       error_code (EACCES);
197       return;
198     }
199
200   debug ("opening process %ld", req.pid);
201
202   const HANDLE to_process_handle =
203     OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid);
204
205   if (!to_process_handle)
206     {
207       log (LOG_ERR, "error opening `to' process, error = %lu",
208                      GetLastError ());
209       CloseHandle (from_process_handle);
210       error_code (EACCES);
211       return;
212     }
213
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     }
222
223   HANDLE token_handle = NULL;
224
225   debug ("about to open thread token");
226   const DWORD rc = OpenThreadToken (GetCurrentThread (),
227                                     TOKEN_QUERY,
228                                     TRUE,
229                                     &token_handle);
230
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     }
239
240   if (!rc)
241     {
242       log (LOG_ERR, "error opening thread token, error = %lu",
243                      GetLastError ());
244       CloseHandle (from_process_handle);
245       CloseHandle (to_process_handle);
246       error_code (EACCES);
247       return;
248     }
249
250   // From this point on, a reply body is returned to the client.
251
252   const HANDLE from_master = req.from_master;
253   const HANDLE to_master = req.to_master;
254
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       {
267         log (LOG_ERR, "error duplicating from_master handle, error = %lu",
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       {
279         log (LOG_ERR, "error duplicating to_master handle, error = %lu",
280                        GetLastError ());
281         error_code (EACCES);
282       }
283
284   CloseHandle (from_process_handle);
285   CloseHandle (to_process_handle);
286   CloseHandle (token_handle);
287
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);
291
292   return;
293 }
294
295 void
296 client_request_get_version::serve (transport_layer_base *, process_cache *)
297 {
298   assert (!error_code ());
299
300   if (msglen ())
301     log (LOG_ERR, "unexpected request body ignored: %lu bytes", msglen ());
302
303   msglen (sizeof (version));
304
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 {
313 public:
314   server_request (transport_layer_base *const conn, process_cache *const cache)
315     : _conn (conn), _cache (cache)
316   {}
317
318   virtual ~server_request ()
319   {
320     delete _conn;
321   }
322
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;
331 };
332
333 class server_submission_loop : public queue_submission_loop
334 {
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)
342   {
343     assert (_transport);
344     assert (_cache);
345   }
346
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  */
360 void
361 server_submission_loop::request_loop ()
362 {
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.
374    */
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",
378              GetLastError ());
379
380   while (_running)
381     {
382       bool recoverable = false;
383       transport_layer_base *const conn = _transport->accept (&recoverable);
384       if (!conn && !recoverable)
385         {
386           log (LOG_ERR, "fatal error on IPC transport: closing down");
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))
396             debug ("failed to reset thread priority, error = %lu",
397                    GetLastError ());
398
399           Sleep (0);
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",
405                      GetLastError ());
406         }
407       if (conn)
408         _queue->add (new server_request (conn, _cache));
409     }
410 }
411
412 client_request_shutdown::client_request_shutdown ()
413   : client_request (CYGSERVER_REQUEST_SHUTDOWN)
414 {
415 }
416
417 void
418 client_request_shutdown::serve (transport_layer_base *, process_cache *)
419 {
420   assert (!error_code ());
421
422   if (msglen ())
423     log (LOG_ERR, "unexpected request body ignored: %lu bytes", msglen ());
424
425   /* FIXME: link upwards, and then this becomes a trivial method call to
426    * only shutdown _this queue_
427    */
428
429   kill (getpid (), SIGINT);
430
431   msglen (0);
432 }
433
434 static sig_atomic_t shutdown_server = false;
435
436 static void
437 handle_signal (const int signum)
438 {
439   /* any signal makes us die :} */
440
441   shutdown_server = true;
442 }
443
444 /*
445  * print_usage ()
446  */
447
448 static void
449 print_usage (const char *const pgm)
450 {
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"
455 "\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"
460 "\n"
461 "Logging options:\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"
468 "\n"
469 "Support 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"
473 "\n"
474 "Miscellaneous:\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."
478 , pgm);
479 }
480
481 /*
482  * print_version ()
483  */
484
485 static void
486 print_version ()
487 {
488   char buf[200];
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);
502
503   log (LOG_INFO, "(cygwin) %s\n"
504                   "API version %s\n"
505                   "Copyright 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.\n"
506                   "Compiled on %s\n"
507                   "Default configuration file is %s",
508                   SERVER_VERSION, buf, __DATE__, DEF_CONFIG_FILE);
509 }
510
511 /*
512  * main ()
513  */
514
515 int
516 main (const int argc, char *argv[])
517 {
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}
536   };
537
538   const char opts[] = "c:deEf:hl:mp:qr:sSvyY";
539
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;
548
549   char *c = NULL;
550
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);
555   if (isatty (2))
556     log_stderr = TUN_TRUE;
557   else
558     log_syslog = TUN_TRUE;
559
560   int opt;
561
562   securityinit ();
563
564   opterr = 0;
565   while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
566     switch (opt)
567       {
568       case 'c':
569         c = NULL;
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");
573         break;
574
575       case 'd':
576         log_debug = TUN_TRUE;
577         break;
578
579       case 'e':
580         option_log_stderr = TUN_TRUE;
581         break;
582
583       case 'E':
584         option_log_stderr = TUN_FALSE;
585         break;
586
587       case 'f':
588         config_file = optarg;
589         force_config_file = true;
590         break;
591
592       case 'h':
593         print_usage (getprogname ());
594         return 0;
595
596       case 'l':
597         c = NULL;
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");
601         break;
602         
603       case 'm':
604         support_sharedmem = TUN_FALSE;
605         break;
606
607       case 'p':
608         c = NULL;
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");
612         break;
613
614       case 'q':
615         support_msgqueues = TUN_FALSE;
616         break;
617
618       case 'r':
619         c = NULL;
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");
623         break;
624
625       case 's':
626         support_semaphores = TUN_FALSE;
627         break;
628
629       case 'S':
630         shutdown = true;
631         break;
632
633       case 'v':
634         print_version ();
635         return 0;
636
637       case 'y':
638         option_log_syslog = TUN_TRUE;
639         break;
640
641       case 'Y':
642         option_log_syslog = TUN_FALSE;
643         break;
644
645       case '?':
646         panic ("unknown option -- %c\n"
647                "Try `%s --help' for more information.", optopt, getprogname ());
648       }
649
650   if (optind != argc)
651     panic ("Too many arguments");
652
653   if (shutdown)
654     {
655       /* Setting `cygserver_running' stops the request code making a
656        * version request, which is not much to the point.
657        */
658       cygserver_running = CYGSERVER_OK;
659
660       client_request_shutdown req;
661
662       if (req.make_request () == -1 || req.error_code ())
663         panic("Shutdown request failed: %s", strerror (req.error_code ()));
664
665       // FIXME: It would be nice to wait here for the daemon to exit.
666
667       return 0;
668     }
669
670   SIGHANDLE (SIGHUP);
671   SIGHANDLE (SIGINT);
672   SIGHANDLE (SIGTERM);
673
674   tunable_param_init (config_file, force_config_file);
675
676   loginit (option_log_stderr, option_log_syslog);
677
678   log (LOG_INFO, "daemon starting up");
679
680   if (!cleanup_threads)
681     TUNABLE_INT_FETCH ("kern.srv.cleanup_threads", &cleanup_threads);
682   if (!cleanup_threads)
683     cleanup_threads = 2;
684
685   if (!request_threads)
686     TUNABLE_INT_FETCH ("kern.srv.request_threads", &request_threads);
687   if (!request_threads)
688     request_threads = 10;
689
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;
694
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;
699
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;
704
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;
709
710   if (!setup_privileges ())
711     panic ("Setting process privileges failed.");
712
713   ipcinit ();
714
715   /*XXXXX*/
716   threaded_queue request_queue (request_threads);
717
718   transport_layer_base *const transport = create_server_transport ();
719   assert (transport);
720
721   process_cache cache (process_cache_size, cleanup_threads);
722
723   server_submission_loop submission_loop (&request_queue, transport, &cache);
724
725   request_queue.add_submission_loop (&submission_loop);
726
727   if (transport->listen () == -1)
728     return 1;
729
730   cache.start ();
731
732   request_queue.start ();
733
734   log (LOG_NOTICE, "Initialization complete.  Waiting for requests.");
735
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);
744    */
745   /* WaitForMultipleObjects abort && request_queue && process_queue && signal
746      -- if signal event then retrigger it
747   */
748   while (!shutdown_server && request_queue.running () && cache.running ())
749     {
750       pause ();
751       if (ipcunload ())
752         {
753           shutdown_server = false;
754           log (LOG_WARNING, "Shutdown request received but ignored.  "
755                              "Dependent processes still running.");
756         }
757     }
758
759   log (LOG_INFO, "Shutdown request received - new requests will be denied");
760   request_queue.stop ();
761   log (LOG_INFO, "All pending requests processed");
762   delete transport;
763   log (LOG_INFO, "No longer accepting requests - cygwin will operate in daemonless mode");
764   cache.stop ();
765   log (LOG_INFO, "All outstanding process-cache activities completed");
766   log (LOG_NOTICE, "Shutdown finished.");
767
768   return 0;
769 }
770 #endif /* __OUTSIDE_CYGWIN__ */