2 * ntpd.c - main program for the fixed point NTP daemon
11 #include "ntp_stdlib.h"
16 #ifdef HAVE_SYS_STAT_H
17 # include <sys/stat.h>
21 # if !defined(VMS) /*wjm*/
22 # include <sys/param.h>
24 # include <sys/signal.h>
25 # ifdef HAVE_SYS_IOCTL_H
26 # include <sys/ioctl.h>
27 # endif /* HAVE_SYS_IOCTL_H */
28 # ifdef HAVE_SYS_RESOURCE_H
29 # include <sys/resource.h>
30 # endif /* HAVE_SYS_RESOURCE_H */
35 # include "../libntp/log.h"
37 #endif /* SYS_WINNT */
38 #if defined(HAVE_RTPRIO)
39 # ifdef HAVE_SYS_RESOURCE_H
40 # include <sys/resource.h>
42 # ifdef HAVE_SYS_LOCK_H
43 # include <sys/lock.h>
45 # include <sys/rtprio.h>
48 # ifdef HAVE_SYS_LOCK_H
49 # include <sys/lock.h>
53 #if defined(HAVE_SCHED_SETSCHEDULER)
57 # ifdef HAVE_SYS_SCHED_H
58 # include <sys/sched.h>
62 #if defined(HAVE_SYS_MMAN_H)
63 # include <sys/mman.h>
71 # include <apollo/base.h>
72 #endif /* SYS_DOMAINOS */
75 #include "ntp_cmdargs.h"
77 #if 0 /* HMS: I don't think we need this. 961223 */
80 # include <sys/mman.h>
82 # include <sys/lock.h>
92 # include <sys/ci/ciioctl.h>
96 #include "ntp_crypto.h"
100 * Signals we catch for debugging. If not debugging we ignore them.
102 #define MOREDEBUGSIG SIGUSR1
103 #define LESSDEBUGSIG SIGUSR2
106 * Signals which terminate us gracefully.
109 # define SIGDIE1 SIGHUP
110 # define SIGDIE3 SIGQUIT
111 # define SIGDIE2 SIGINT
112 # define SIGDIE4 SIGTERM
113 #endif /* SYS_WINNT */
115 #if defined SYS_WINNT
116 /* handles for various threads, process, and objects */
117 HANDLE ResolverThreadHandle = NULL;
118 /* variables used to inform the Service Control Manager of our current state */
119 SERVICE_STATUS ssStatus;
120 SERVICE_STATUS_HANDLE sshStatusHandle;
121 HANDLE WaitHandles[2] = { NULL, NULL };
123 static BOOL WINAPI OnConsoleEvent(DWORD dwCtrlType);
124 #endif /* SYS_WINNT */
127 * Scheduling priority we run at
129 #define NTPD_PRIO (-12)
131 int priority_done = 2; /* 0 - Set priority */
132 /* 1 - priority is OK where it is */
133 /* 2 - Don't set priority */
134 /* 1 and 2 are pretty much the same */
142 * No-fork flag. If set, we do not become a background daemon.
147 * Initializing flag. All async routines watch this and only do their
148 * thing when it is clear.
153 * Version declaration
155 extern const char *Version;
161 * We put this here, since the argument profile is syscall-specific
163 extern int syscall P((int, ...));
164 #endif /* DECL_SYSCALL */
168 static RETSIGTYPE finish P((int));
172 static RETSIGTYPE moredebug P((int));
173 static RETSIGTYPE lessdebug P((int));
174 #else /* not DEBUG */
175 static RETSIGTYPE no_debug P((int));
176 #endif /* not DEBUG */
178 int ntpdmain P((int, char **));
179 static void set_process_priority P((void));
182 #ifdef NO_MAIN_ALLOWED
183 CALL(ntpd,"ntpd",ntpdmain);
191 return ntpdmain(argc, argv);
197 * OK. AIX is different than solaris in how it implements plock().
198 * If you do NOT adjust the stack limit, you will get the MAXIMUM
199 * stack size allocated and PINNED with you program. To check the
200 * value, use ulimit -a.
202 * To fix this, we create an automatic variable and set our stack limit
203 * to that PLUS 32KB of extra space (we need some headroom).
205 * This subroutine gets the stack address.
207 * Grover Davidson and Matt Ladendorf
218 * Signal handler for SIGDANGER.
221 catch_danger(int signo)
223 msyslog(LOG_INFO, "ntpd: setpgid(): %m");
224 /* Make the system believe we'll free something, but don't do it! */
230 * Set the process priority
233 set_process_priority(void)
238 msyslog(LOG_DEBUG, "set_process_priority: %s: priority_done is <%d>",
240 ? "Leave priority alone"
241 : "Attempt to set priority"
247 priority_done += NT_set_process_priority();
250 #if defined(HAVE_SCHED_SETSCHEDULER)
251 if (!priority_done) {
252 extern int config_priority_override, config_priority;
254 struct sched_param sched;
256 pmax = sched_get_priority_max(SCHED_FIFO);
257 sched.sched_priority = pmax;
258 if ( config_priority_override ) {
259 pmin = sched_get_priority_min(SCHED_FIFO);
260 if ( config_priority > pmax )
261 sched.sched_priority = pmax;
262 else if ( config_priority < pmin )
263 sched.sched_priority = pmin;
265 sched.sched_priority = config_priority;
267 if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 )
268 msyslog(LOG_ERR, "sched_setscheduler(): %m");
272 #endif /* HAVE_SCHED_SETSCHEDULER */
273 #if defined(HAVE_RTPRIO)
275 if (!priority_done) {
278 srtp.type = RTP_PRIO_REALTIME; /* was: RTP_PRIO_NORMAL */
279 srtp.prio = 0; /* 0 (hi) -> RTP_PRIO_MAX (31,lo) */
281 if (rtprio(RTP_SET, getpid(), &srtp) < 0)
282 msyslog(LOG_ERR, "rtprio() error: %m");
286 # else /* not RTP_SET */
287 if (!priority_done) {
288 if (rtprio(0, 120) < 0)
289 msyslog(LOG_ERR, "rtprio() error: %m");
293 # endif /* not RTP_SET */
294 #endif /* HAVE_RTPRIO */
295 #if defined(NTPD_PRIO) && NTPD_PRIO != 0
296 # ifdef HAVE_ATT_NICE
297 if (!priority_done) {
299 if (-1 == nice (NTPD_PRIO) && errno != 0)
300 msyslog(LOG_ERR, "nice() error: %m");
304 # endif /* HAVE_ATT_NICE */
305 # ifdef HAVE_BSD_NICE
306 if (!priority_done) {
307 if (-1 == setpriority(PRIO_PROCESS, 0, NTPD_PRIO))
308 msyslog(LOG_ERR, "setpriority() error: %m");
312 # endif /* HAVE_BSD_NICE */
313 #endif /* NTPD_PRIO && NTPD_PRIO != 0 */
315 msyslog(LOG_ERR, "set_process_priority: No way found to improve our priority");
320 * Main program. Initialize us, disconnect us from the tty if necessary,
321 * and loop waiting for I/O and/or timer expiries.
333 char hostname[MAXFILENAME];
335 struct recvbuf *rbuflist;
336 struct recvbuf *rbuf;
337 #ifdef _AIX /* HMS: ifdef SIGDANGER? */
341 initializing = 1; /* mark that we are initializing */
342 debug = 0; /* no debugging by default */
343 nofork = 0; /* will fork by default */
364 msyslog(LOG_ERR, "ntpd: must be run as root, not uid %ld", (long)uid);
371 /* Set the Event-ID message-file name. */
372 if (!GetModuleFileName(NULL, szMsgPath, sizeof(szMsgPath))) {
373 msyslog(LOG_ERR, "GetModuleFileName(PGM_EXE_FILE) failed: %m\n");
376 addSourceToRegistry("NTP", szMsgPath);
378 getstartup(argc, argv); /* startup configuration, may set debug */
381 * Initialize random generator and public key pair
384 SRANDOM((int)(now.l_i * now.l_uf));
389 * Detach us from the terminal. May need an #ifndef GIZMO.
392 if (!debug && !nofork)
400 # else /* not HAVE_DAEMON */
401 if (fork()) /* HMS: What about a -1? */
405 #if !defined(F_CLOSEM)
408 #endif /* not F_CLOSEM */
411 * From 'Writing Reliable AIX Daemons,' SG24-4946-00,
412 * by Eric Agar (saves us from doing 32767 system
415 #if defined(F_CLOSEM)
416 if (fcntl(0, F_CLOSEM, 0) == -1)
417 msyslog(LOG_ERR, "ntpd: failed to close open files(): %m");
418 #else /* not F_CLOSEM */
420 # if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
421 max_fd = sysconf(_SC_OPEN_MAX);
422 # else /* HAVE_SYSCONF && _SC_OPEN_MAX */
423 max_fd = getdtablesize();
424 # endif /* HAVE_SYSCONF && _SC_OPEN_MAX */
425 for (s = 0; s < max_fd; s++)
426 (void) close((int)s);
427 #endif /* not F_CLOSEM */
436 proc2_$who_am_i(&puid);
437 proc2_$make_server(&puid, &st);
439 #endif /* SYS_DOMAINOS */
440 #if defined(HAVE_SETPGID) || defined(HAVE_SETSID)
442 if (setsid() == (pid_t)-1)
443 msyslog(LOG_ERR, "ntpd: setsid(): %m");
445 if (setpgid(0, 0) == -1)
446 msyslog(LOG_ERR, "ntpd: setpgid(): %m");
448 #else /* HAVE_SETPGID || HAVE_SETSID */
450 # if defined(TIOCNOTTY)
453 fid = open("/dev/tty", 2);
456 (void) ioctl(fid, (u_long) TIOCNOTTY, (char *) 0);
459 # endif /* defined(TIOCNOTTY) */
460 # ifdef HAVE_SETPGRP_0
462 # else /* HAVE_SETPGRP_0 */
463 (void) setpgrp(0, getpid());
464 # endif /* HAVE_SETPGRP_0 */
466 #endif /* HAVE_SETPGID || HAVE_SETSID */
468 /* Don't get killed by low-on-memory signal. */
469 sa.sa_handler = catch_danger;
470 sigemptyset(&sa.sa_mask);
471 sa.sa_flags = SA_RESTART;
473 (void) sigaction(SIGDANGER, &sa, NULL);
476 # endif /* not HAVE_DAEMON */
477 # else /* SYS_WINNT */
480 SERVICE_TABLE_ENTRY dispatchTable[] = {
481 { TEXT("NetworkTimeProtocol"), (LPSERVICE_MAIN_FUNCTION)service_main },
486 if (!StartServiceCtrlDispatcher(dispatchTable))
488 msyslog(LOG_ERR, "StartServiceCtrlDispatcher: %m");
492 # endif /* SYS_WINNT */
494 # endif /* NODETACH */
495 # if defined(SYS_WINNT) && !defined(NODETACH)
497 service_main(argc, argv);
498 return 0; /* must return a value */
502 * If this runs as a service under NT, the main thread will block at
503 * StartServiceCtrlDispatcher() and another thread will be started by the
504 * Service Control Dispatcher which will begin execution at the routine
505 * specified in that call (viz. service_main)
514 struct recvbuf *rbuflist;
515 struct recvbuf *rbuf;
518 char hostname[MAXFILENAME];
522 /* register our service control handler */
523 if (!(sshStatusHandle = RegisterServiceCtrlHandler( TEXT("NetworkTimeProtocol"),
524 (LPHANDLER_FUNCTION)service_ctrl)))
526 msyslog(LOG_ERR, "RegisterServiceCtrlHandler failed: %m");
530 /* report pending status to Service Control Manager */
531 ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
532 ssStatus.dwCurrentState = SERVICE_START_PENDING;
533 ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
534 ssStatus.dwWin32ExitCode = NO_ERROR;
535 ssStatus.dwServiceSpecificExitCode = 0;
536 ssStatus.dwCheckPoint = 1;
537 ssStatus.dwWaitHint = 5000;
538 if (!SetServiceStatus(sshStatusHandle, &ssStatus))
540 msyslog(LOG_ERR, "SetServiceStatus: %m");
541 ssStatus.dwCurrentState = SERVICE_STOPPED;
542 SetServiceStatus(sshStatusHandle, &ssStatus);
547 # endif /* defined(SYS_WINNT) && !defined(NODETACH) */
551 * Logging. This may actually work on the gizmo board. Find a name
552 * to log with by using the basename of argv[0]
554 cp = strrchr(argv[0], '/');
560 debug = 0; /* will be immediately re-initialized 8-( */
561 getstartup(argc, argv); /* startup configuration, catch logfile this time */
563 #if !defined(SYS_WINNT) && !defined(VMS)
566 openlog(cp, LOG_PID);
567 # else /* LOG_DAEMON */
570 # define LOG_NTP LOG_DAEMON
572 openlog(cp, LOG_PID | LOG_NDELAY, LOG_NTP);
575 setlogmask(LOG_UPTO(LOG_DEBUG));
578 setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
579 # endif /* LOG_DAEMON */
580 #endif /* !SYS_WINNT && !VMS */
582 NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */
583 msyslog(LOG_NOTICE, "%s", Version);
587 * TODO: lock the process in memory using SetProcessWorkingSetSize() and VirtualLock() functions
589 process_handle = GetCurrentProcess();
590 if (SetProcessWorkingSetSize(process_handle, 2097152 , 4194304 ) == TRUE) {
591 if (VirtualLock(0 , 4194304) == FALSE)
592 msyslog(LOG_ERR, "VirtualLock() failed: %m");
594 msyslog(LOG_ERR, "SetProcessWorkingSetSize() failed: %m");
597 #endif /* SYS_WINNT */
601 * SCO OpenServer's system clock offers much more precise timekeeping
602 * on the base CPU than the other CPUs (for multiprocessor systems),
603 * so we must lock to the base CPU.
606 int fd = open("/dev/at1", O_RDONLY);
609 if (ioctl(fd, ACPU_LOCK, &zero) < 0)
610 msyslog(LOG_ERR, "cannot lock to base CPU: %m\n");
613 * If we can't open the device, this probably just isn't
614 * a multiprocessor system, so we're A-OK.
619 #if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && defined(MCL_FUTURE)
621 * lock the process into memory
623 if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0)
624 msyslog(LOG_ERR, "mlockall(): %m");
625 #else /* not (HAVE_MLOCKALL && MCL_CURRENT && MCL_FUTURE) */
630 * set the stack limit for AIX for plock().
631 * see get_aix_stack for more info.
633 if (ulimit(SET_STACKLIM, (get_aix_stack() - 8*4096)) < 0)
635 msyslog(LOG_ERR,"Cannot adjust stack limit for plock on AIX: %m");
639 * lock the process into memory
641 if (plock(PROCLOCK) < 0)
642 msyslog(LOG_ERR, "plock(PROCLOCK): %m");
643 # else /* not PROCLOCK */
648 if (plock(TXTLOCK) < 0)
649 msyslog(LOG_ERR, "plock(TXTLOCK) error: %m");
650 # else /* not TXTLOCK */
651 msyslog(LOG_ERR, "plock() - don't know what to lock!");
652 # endif /* not TXTLOCK */
653 # endif /* not PROCLOCK */
654 # endif /* HAVE_PLOCK */
655 #endif /* not (HAVE_MLOCKALL && MCL_CURRENT && MCL_FUTURE) */
658 * Set up signals we pay attention to locally.
661 (void) signal_no_reset(SIGDIE1, finish);
664 (void) signal_no_reset(SIGDIE2, finish);
667 (void) signal_no_reset(SIGDIE3, finish);
670 (void) signal_no_reset(SIGDIE4, finish);
674 (void) signal_no_reset(SIGBUS, finish);
677 #if !defined(SYS_WINNT) && !defined(VMS)
679 (void) signal_no_reset(MOREDEBUGSIG, moredebug);
680 (void) signal_no_reset(LESSDEBUGSIG, lessdebug);
682 (void) signal_no_reset(MOREDEBUGSIG, no_debug);
683 (void) signal_no_reset(LESSDEBUGSIG, no_debug);
685 #endif /* !SYS_WINNT && !VMS */
688 * Set up signals we should never pay attention to.
691 (void) signal_no_reset(SIGPIPE, SIG_IGN);
694 #if defined SYS_WINNT
695 if (!SetConsoleCtrlHandler(OnConsoleEvent, TRUE)) {
696 msyslog(LOG_ERR, "Can't set console control handler: %m");
701 * Call the init_ routines to initialize the data structures.
703 #if defined (HAVE_IO_COMPLETION_PORT)
704 init_io_completion_port();
720 set_process_priority();
721 init_proto(); /* Call at high priority */
724 mon_start(MON_ON); /* monitor on by default now */
725 /* turn off in config if unwanted */
728 * Get configuration. This (including argument list parsing) is
729 * done in a separate module since this will definitely be different
730 * for the gizmo board. While at it, save the host name for later
731 * along with the length. The crypto needs this.
736 getconfig(argc, argv);
738 gethostname(hostname, MAXFILENAME);
739 n = strlen(hostname) + 1;
740 sys_hostname = emalloc(n);
741 memcpy(sys_hostname, hostname, n);
748 #if defined(SYS_WINNT) && !defined(NODETACH)
753 /* report to the service control manager that the service is running */
754 ssStatus.dwCurrentState = SERVICE_RUNNING;
755 ssStatus.dwWin32ExitCode = NO_ERROR;
756 if (!SetServiceStatus(sshStatusHandle, &ssStatus))
758 msyslog(LOG_ERR, "SetServiceStatus: %m");
759 if (ResolverThreadHandle != NULL)
760 CloseHandle(ResolverThreadHandle);
761 ssStatus.dwCurrentState = SERVICE_STOPPED;
762 SetServiceStatus(sshStatusHandle, &ssStatus);
771 * Report that we're up to any trappers
773 report_event(EVNT_SYSRESTART, (struct peer *)0);
776 * Use select() on all on all input fd's for unlimited
777 * time. select() will terminate on SIGALARM or on the
778 * reception of input. Using select() means we can't do
779 * robust signal handling and we get a potential race
780 * between checking for alarms and doing the select().
781 * Mostly harmless, I think.
783 /* On VMS, I suspect that select() can't be interrupted
784 * by a "signal" either, so I take the easy way out and
785 * have select() time out after one second.
786 * System clock updates really aren't time-critical,
787 * and - lacking a hardware reference clock - I have
788 * yet to learn about anything else that is.
790 #if defined(HAVE_IO_COMPLETION_PORT)
791 WaitHandles[0] = CreateEvent(NULL, FALSE, FALSE, NULL); /* exit reques */
792 WaitHandles[1] = get_timer_handle();
795 DWORD Index = WaitForMultipleObjectsEx(sizeof(WaitHandles)/sizeof(WaitHandles[0]), WaitHandles, FALSE, 1000, MWMO_ALERTABLE);
797 case WAIT_OBJECT_0 + 0 : /* exit request */
801 case WAIT_OBJECT_0 + 1 : /* timer */
804 case WAIT_OBJECT_0 + 2 : { /* Windows message */
806 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
807 if (msg.message == WM_QUIT) {
810 DispatchMessage(&msg);
815 case WAIT_IO_COMPLETION : /* loop */
820 rbuflist = getrecvbufs(); /* get received buffers */
822 #else /* normal I/O */
825 rbuflist = (struct recvbuf *)0;
828 # if !defined(HAVE_SIGNALED_IO)
829 extern fd_set activefds;
830 extern int maxactivefd;
834 # elif defined(HAVE_SIGNALED_IO)
835 block_io_and_alarm();
838 rbuflist = getrecvbufs(); /* get received buffers */
839 if (alarm_flag) /* alarmed? */
845 if (!was_alarmed && rbuflist == (struct recvbuf *)0)
848 * Nothing to do. Wait for something.
850 # ifndef HAVE_SIGNALED_IO
852 # if defined(VMS) || defined(SYS_VXWORKS)
853 /* make select() wake up after one second */
857 t1.tv_sec = 1; t1.tv_usec = 0;
858 nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0,
862 nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0,
863 (fd_set *)0, (struct timeval *)0);
871 (void)input_handler(&ts);
873 else if (nfound == -1 && errno != EINTR)
874 msyslog(LOG_ERR, "select() error: %m");
875 else if (debug > 2) {
876 msyslog(LOG_DEBUG, "select(): nfound=%d, error: %m", nfound);
878 # else /* HAVE_SIGNALED_IO */
881 # endif /* HAVE_SIGNALED_IO */
882 if (alarm_flag) /* alarmed? */
887 rbuflist = getrecvbufs(); /* get received buffers */
889 # ifdef HAVE_SIGNALED_IO
890 unblock_io_and_alarm();
891 # endif /* HAVE_SIGNALED_IO */
894 * Out here, signals are unblocked. Call timer routine
903 #endif /* HAVE_IO_COMPLETION_PORT */
905 * Call the data procedure to handle each received
908 while (rbuflist != (struct recvbuf *)0)
911 rbuflist = rbuf->next;
912 (rbuf->receiver)(rbuf);
915 #if defined DEBUG && defined SYS_WINNT
917 printf("getrecvbufs: %ld handler interrupts, %ld frames\n",
918 handler_calls, handler_pkts);
925 exit(1); /* unreachable */
926 return 1; /* DEC OSF cc braindamage */
932 * finish - exit gracefully
940 msyslog(LOG_NOTICE, "ntpd exiting on signal %d", sig);
946 printf("\nfinish(SIGBUS)\n");
949 case 0: /* Should never happen... */
960 * moredebug - increase debugging verbosity
967 int saved_errno = errno;
972 msyslog(LOG_DEBUG, "debug raised to %d", debug);
978 * lessdebug - decrease debugging verbosity
985 int saved_errno = errno;
990 msyslog(LOG_DEBUG, "debug lowered to %d", debug);
994 #else /* not DEBUG */
996 * no_debug - We don't do the debug here.
1003 int saved_errno = errno;
1005 msyslog(LOG_DEBUG, "ntpd not compiled for debugging (signal %d)", sig);
1006 errno = saved_errno;
1008 #endif /* not DEBUG */
1011 /* service_ctrl - control handler for NTP service
1012 * signals the service_main routine of start/stop requests
1013 * from the control panel or other applications making
1021 DWORD dwState = SERVICE_RUNNING;
1023 /* Handle the requested control code */
1026 case SERVICE_CONTROL_PAUSE:
1027 /* see no reason to support this */
1030 case SERVICE_CONTROL_CONTINUE:
1031 /* see no reason to support this */
1034 case SERVICE_CONTROL_STOP:
1035 dwState = SERVICE_STOP_PENDING;
1037 * Report the status, specifying the checkpoint and waithint,
1038 * before setting the termination event.
1040 ssStatus.dwCurrentState = dwState;
1041 ssStatus.dwWin32ExitCode = NO_ERROR;
1042 ssStatus.dwWaitHint = 3000;
1043 if (!SetServiceStatus(sshStatusHandle, &ssStatus))
1045 msyslog(LOG_ERR, "SetServiceStatus: %m");
1047 if (WaitHandles[0] != NULL) {
1048 SetEvent(WaitHandles[0]);
1052 case SERVICE_CONTROL_INTERROGATE:
1053 /* Update the service status */
1057 /* invalid control code */
1062 ssStatus.dwCurrentState = dwState;
1063 ssStatus.dwWin32ExitCode = NO_ERROR;
1064 if (!SetServiceStatus(sshStatusHandle, &ssStatus))
1066 msyslog(LOG_ERR, "SetServiceStatus: %m");
1075 switch (dwCtrlType) {
1076 case CTRL_BREAK_EVENT :
1086 printf("debug level %d\n", debug);
1090 case CTRL_CLOSE_EVENT :
1091 case CTRL_SHUTDOWN_EVENT :
1092 if (WaitHandles[0] != NULL) {
1093 SetEvent(WaitHandles[0]);
1107 * NT version of exit() - all calls to exit() should be routed to
1115 if (!debug) { /* did not become a service, simply exit */
1116 /* service mode, need to have the service_main routine
1117 * register with the service control manager that the
1118 * service has stopped running, before exiting
1120 ssStatus.dwCurrentState = SERVICE_STOPPED;
1121 SetServiceStatus(sshStatusHandle, &ssStatus);
1124 uninit_io_completion_port();
1127 # if defined _MSC_VER
1128 _CrtDumpMemoryLeaks();
1134 #endif /* SYS_WINNT */