diff options
Diffstat (limited to 'ntpd/ntpd.c')
-rw-r--r-- | ntpd/ntpd.c | 1274 |
1 files changed, 1274 insertions, 0 deletions
diff --git a/ntpd/ntpd.c b/ntpd/ntpd.c new file mode 100644 index 0000000..0b05253 --- /dev/null +++ b/ntpd/ntpd.c @@ -0,0 +1,1274 @@ +/* + * ntpd.c - main program for the fixed point NTP daemon + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ntp_machine.h" +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_stdlib.h" + +#ifdef SIM +#include "ntpsim.h" +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#include <stdio.h> +#ifndef SYS_WINNT +# if !defined(VMS) /*wjm*/ +# ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +# endif +# endif /* VMS */ +# ifdef HAVE_SYS_SIGNAL_H +# include <sys/signal.h> +# else +# include <signal.h> +# endif +# ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +# endif /* HAVE_SYS_IOCTL_H */ +# ifdef HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +# endif /* HAVE_SYS_RESOURCE_H */ +#else +# include <signal.h> +# include <process.h> +# include <io.h> +# include "../libntp/log.h" +# include <clockstuff.h> +# include <crtdbg.h> +#endif /* SYS_WINNT */ +#if defined(HAVE_RTPRIO) +# ifdef HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +# endif +# ifdef HAVE_SYS_LOCK_H +# include <sys/lock.h> +# endif +# include <sys/rtprio.h> +#else +# ifdef HAVE_PLOCK +# ifdef HAVE_SYS_LOCK_H +# include <sys/lock.h> +# endif +# endif +#endif +#if defined(HAVE_SCHED_SETSCHEDULER) +# ifdef HAVE_SCHED_H +# include <sched.h> +# else +# ifdef HAVE_SYS_SCHED_H +# include <sys/sched.h> +# endif +# endif +#endif +#if defined(HAVE_SYS_MMAN_H) +# include <sys/mman.h> +#endif + +#ifdef HAVE_TERMIOS_H +# include <termios.h> +#endif + +#ifdef SYS_DOMAINOS +# include <apollo/base.h> +#endif /* SYS_DOMAINOS */ + +#include "recvbuff.h" +#include "ntp_cmdargs.h" + +#if 0 /* HMS: I don't think we need this. 961223 */ +#ifdef LOCK_PROCESS +# ifdef SYS_SOLARIS +# include <sys/mman.h> +# else +# include <sys/lock.h> +# endif +#endif +#endif + +#ifdef _AIX +# include <ulimit.h> +#endif /* _AIX */ + +#ifdef SCO5_CLOCK +# include <sys/ci/ciioctl.h> +#endif + +#ifdef HAVE_CLOCKCTL +# include <ctype.h> +# include <grp.h> +# include <pwd.h> +#endif + +/* + * Signals we catch for debugging. If not debugging we ignore them. + */ +#define MOREDEBUGSIG SIGUSR1 +#define LESSDEBUGSIG SIGUSR2 + +/* + * Signals which terminate us gracefully. + */ +#ifndef SYS_WINNT +# define SIGDIE1 SIGHUP +# define SIGDIE3 SIGQUIT +# define SIGDIE2 SIGINT +# define SIGDIE4 SIGTERM +#endif /* SYS_WINNT */ + +#if defined SYS_WINNT +/* handles for various threads, process, and objects */ +HANDLE ResolverThreadHandle = NULL; +/* variables used to inform the Service Control Manager of our current state */ +BOOL NoWinService = FALSE; +SERVICE_STATUS ssStatus; +SERVICE_STATUS_HANDLE sshStatusHandle; +HANDLE WaitHandles[3] = { NULL, NULL, NULL }; +char szMsgPath[255]; +static BOOL WINAPI OnConsoleEvent(DWORD dwCtrlType); +BOOL init_randfile(); +#endif /* SYS_WINNT */ + +/* + * Scheduling priority we run at + */ +#define NTPD_PRIO (-12) + +int priority_done = 2; /* 0 - Set priority */ + /* 1 - priority is OK where it is */ + /* 2 - Don't set priority */ + /* 1 and 2 are pretty much the same */ + +/* + * Debugging flag + */ +volatile int debug; + +/* + * Set the processing not to be in the forground + */ +int forground_process = FALSE; + +/* + * No-fork flag. If set, we do not become a background daemon. + */ +int nofork; + +#ifdef HAVE_CLOCKCTL +char *user = NULL; /* User to switch to */ +char *group = NULL; /* group to switch to */ +char *chrootdir = NULL; /* directory to chroot to */ +int sw_uid; +int sw_gid; +char *endp; +struct group *gr; +struct passwd *pw; +#endif /* HAVE_CLOCKCTL */ + +/* + * Initializing flag. All async routines watch this and only do their + * thing when it is clear. + */ +int initializing; + +/* + * Version declaration + */ +extern const char *Version; + +int was_alarmed; + +#ifdef DECL_SYSCALL +/* + * We put this here, since the argument profile is syscall-specific + */ +extern int syscall P((int, ...)); +#endif /* DECL_SYSCALL */ + + +#ifdef SIGDIE2 +static RETSIGTYPE finish P((int)); +#endif /* SIGDIE2 */ + +#ifdef DEBUG +#ifndef SYS_WINNT +static RETSIGTYPE moredebug P((int)); +static RETSIGTYPE lessdebug P((int)); +#endif +#else /* not DEBUG */ +static RETSIGTYPE no_debug P((int)); +#endif /* not DEBUG */ + +int ntpdmain P((int, char **)); +static void set_process_priority P((void)); + +#ifdef SIM +int +main( + int argc, + char *argv[] + ) +{ + return ntpsim(argc, argv); +} +#else /* SIM */ +#ifdef NO_MAIN_ALLOWED +CALL(ntpd,"ntpd",ntpdmain); +#else +int +main( + int argc, + char *argv[] + ) +{ + return ntpdmain(argc, argv); +} +#endif +#endif /* SIM */ + +#ifdef _AIX +/* + * OK. AIX is different than solaris in how it implements plock(). + * If you do NOT adjust the stack limit, you will get the MAXIMUM + * stack size allocated and PINNED with you program. To check the + * value, use ulimit -a. + * + * To fix this, we create an automatic variable and set our stack limit + * to that PLUS 32KB of extra space (we need some headroom). + * + * This subroutine gets the stack address. + * + * Grover Davidson and Matt Ladendorf + * + */ +static char * +get_aix_stack(void) +{ + char ch; + return (&ch); +} + +/* + * Signal handler for SIGDANGER. + */ +static void +catch_danger(int signo) +{ + msyslog(LOG_INFO, "ntpd: setpgid(): %m"); + /* Make the system believe we'll free something, but don't do it! */ + return; +} +#endif /* _AIX */ + +/* + * Set the process priority + */ +static void +set_process_priority(void) +{ + +#ifdef DEBUG + if (debug > 1) + msyslog(LOG_DEBUG, "set_process_priority: %s: priority_done is <%d>", + ((priority_done) + ? "Leave priority alone" + : "Attempt to set priority" + ), + priority_done); +#endif /* DEBUG */ + +#ifdef SYS_WINNT + priority_done += NT_set_process_priority(); +#endif + +#if defined(HAVE_SCHED_SETSCHEDULER) + if (!priority_done) { + extern int config_priority_override, config_priority; + int pmax, pmin; + struct sched_param sched; + + pmax = sched_get_priority_max(SCHED_FIFO); + sched.sched_priority = pmax; + if ( config_priority_override ) { + pmin = sched_get_priority_min(SCHED_FIFO); + if ( config_priority > pmax ) + sched.sched_priority = pmax; + else if ( config_priority < pmin ) + sched.sched_priority = pmin; + else + sched.sched_priority = config_priority; + } + if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) + msyslog(LOG_ERR, "sched_setscheduler(): %m"); + else + ++priority_done; + } +#endif /* HAVE_SCHED_SETSCHEDULER */ +#if defined(HAVE_RTPRIO) +# ifdef RTP_SET + if (!priority_done) { + struct rtprio srtp; + + srtp.type = RTP_PRIO_REALTIME; /* was: RTP_PRIO_NORMAL */ + srtp.prio = 0; /* 0 (hi) -> RTP_PRIO_MAX (31,lo) */ + + if (rtprio(RTP_SET, getpid(), &srtp) < 0) + msyslog(LOG_ERR, "rtprio() error: %m"); + else + ++priority_done; + } +# else /* not RTP_SET */ + if (!priority_done) { + if (rtprio(0, 120) < 0) + msyslog(LOG_ERR, "rtprio() error: %m"); + else + ++priority_done; + } +# endif /* not RTP_SET */ +#endif /* HAVE_RTPRIO */ +#if defined(NTPD_PRIO) && NTPD_PRIO != 0 +# ifdef HAVE_ATT_NICE + if (!priority_done) { + errno = 0; + if (-1 == nice (NTPD_PRIO) && errno != 0) + msyslog(LOG_ERR, "nice() error: %m"); + else + ++priority_done; + } +# endif /* HAVE_ATT_NICE */ +# ifdef HAVE_BSD_NICE + if (!priority_done) { + if (-1 == setpriority(PRIO_PROCESS, 0, NTPD_PRIO)) + msyslog(LOG_ERR, "setpriority() error: %m"); + else + ++priority_done; + } +# endif /* HAVE_BSD_NICE */ +#endif /* NTPD_PRIO && NTPD_PRIO != 0 */ + if (!priority_done) + msyslog(LOG_ERR, "set_process_priority: No way found to improve our priority"); +} + + +/* + * Main program. Initialize us, disconnect us from the tty if necessary, + * and loop waiting for I/O and/or timer expiries. + */ +int +ntpdmain( + int argc, + char *argv[] + ) +{ + l_fp now; + char *cp; + struct recvbuf *rbuflist; + struct recvbuf *rbuf; +#ifdef _AIX /* HMS: ifdef SIGDANGER? */ + struct sigaction sa; +#endif + + initializing = 1; /* mark that we are initializing */ + debug = 0; /* no debugging by default */ + nofork = 0; /* will fork by default */ + +#ifdef HAVE_UMASK + { + mode_t uv; + + uv = umask(0); + if(uv) + (void) umask(uv); + else + (void) umask(022); + } +#endif + +#if defined(HAVE_GETUID) && !defined(MPE) /* MPE lacks the concept of root */ + { + uid_t uid; + + uid = getuid(); + if (uid) + { + msyslog(LOG_ERR, "ntpd: must be run as root, not uid %ld", (long)uid); + exit(1); + } + } +#endif + +#ifdef SYS_WINNT + /* Set the Event-ID message-file name. */ + if (!GetModuleFileName(NULL, szMsgPath, sizeof(szMsgPath))) { + msyslog(LOG_ERR, "GetModuleFileName(PGM_EXE_FILE) failed: %m\n"); + exit(1); + } + addSourceToRegistry("NTP", szMsgPath); +#endif + getstartup(argc, argv); /* startup configuration, may set debug */ + + if (debug) + printf("%s\n", Version); + + /* + * Initialize random generator and public key pair + */ +#ifdef SYS_WINNT + /* Initialize random file before OpenSSL checks */ + if(!init_randfile()) + msyslog(LOG_ERR, "Unable to initialize .rnd file\n"); +#endif + get_systime(&now); + SRANDOM((int)(now.l_i * now.l_uf)); + +#if !defined(VMS) +# ifndef NODETACH + /* + * Detach us from the terminal. May need an #ifndef GIZMO. + */ +# ifdef DEBUG + if (!debug && !nofork) +# else /* DEBUG */ + if (!nofork) +# endif /* DEBUG */ + { +# ifndef SYS_WINNT +# ifdef HAVE_DAEMON + daemon(0, 0); +# else /* not HAVE_DAEMON */ + if (fork()) /* HMS: What about a -1? */ + exit(0); + + { +#if !defined(F_CLOSEM) + u_long s; + int max_fd; +#endif /* not F_CLOSEM */ + +#if defined(F_CLOSEM) + /* + * From 'Writing Reliable AIX Daemons,' SG24-4946-00, + * by Eric Agar (saves us from doing 32767 system + * calls) + */ + if (fcntl(0, F_CLOSEM, 0) == -1) + msyslog(LOG_ERR, "ntpd: failed to close open files(): %m"); +#else /* not F_CLOSEM */ + +# if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX) + max_fd = sysconf(_SC_OPEN_MAX); +# else /* HAVE_SYSCONF && _SC_OPEN_MAX */ + max_fd = getdtablesize(); +# endif /* HAVE_SYSCONF && _SC_OPEN_MAX */ + for (s = 0; s < max_fd; s++) + (void) close((int)s); +#endif /* not F_CLOSEM */ + (void) open("/", 0); + (void) dup2(0, 1); + (void) dup2(0, 2); +#ifdef SYS_DOMAINOS + { + uid_$t puid; + status_$t st; + + proc2_$who_am_i(&puid); + proc2_$make_server(&puid, &st); + } +#endif /* SYS_DOMAINOS */ +#if defined(HAVE_SETPGID) || defined(HAVE_SETSID) +# ifdef HAVE_SETSID + if (setsid() == (pid_t)-1) + msyslog(LOG_ERR, "ntpd: setsid(): %m"); +# else + if (setpgid(0, 0) == -1) + msyslog(LOG_ERR, "ntpd: setpgid(): %m"); +# endif +#else /* HAVE_SETPGID || HAVE_SETSID */ + { +# if defined(TIOCNOTTY) + int fid; + + fid = open("/dev/tty", 2); + if (fid >= 0) + { + (void) ioctl(fid, (u_long) TIOCNOTTY, (char *) 0); + (void) close(fid); + } +# endif /* defined(TIOCNOTTY) */ +# ifdef HAVE_SETPGRP_0 + (void) setpgrp(); +# else /* HAVE_SETPGRP_0 */ + (void) setpgrp(0, getpid()); +# endif /* HAVE_SETPGRP_0 */ + } +#endif /* HAVE_SETPGID || HAVE_SETSID */ +#ifdef _AIX + /* Don't get killed by low-on-memory signal. */ + sa.sa_handler = catch_danger; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + (void) sigaction(SIGDANGER, &sa, NULL); +#endif /* _AIX */ + } +# endif /* not HAVE_DAEMON */ +# else /* SYS_WINNT */ + + { + if (NoWinService == FALSE) { + SERVICE_TABLE_ENTRY dispatchTable[] = { + { TEXT("NetworkTimeProtocol"), (LPSERVICE_MAIN_FUNCTION)service_main }, + { NULL, NULL } + }; + + /* daemonize */ + if (!StartServiceCtrlDispatcher(dispatchTable)) + { + msyslog(LOG_ERR, "StartServiceCtrlDispatcher: %m"); + ExitProcess(2); + } + } + else { + service_main(argc, argv); + return 0; + } + } +# endif /* SYS_WINNT */ + } +# endif /* NODETACH */ +# if defined(SYS_WINNT) && !defined(NODETACH) + else + service_main(argc, argv); + return 0; /* must return a value */ +} /* end main */ + +/* + * If this runs as a service under NT, the main thread will block at + * StartServiceCtrlDispatcher() and another thread will be started by the + * Service Control Dispatcher which will begin execution at the routine + * specified in that call (viz. service_main) + */ +void +service_main( + DWORD argc, + LPTSTR *argv + ) +{ + char *cp; + struct recvbuf *rbuflist; + struct recvbuf *rbuf; + + if(!debug && NoWinService == FALSE) + { + /* register our service control handler */ + sshStatusHandle = RegisterServiceCtrlHandler( TEXT("NetworkTimeProtocol"), + (LPHANDLER_FUNCTION)service_ctrl); + if(sshStatusHandle == 0) + { + msyslog(LOG_ERR, "RegisterServiceCtrlHandler failed: %m"); + return; + } + + /* report pending status to Service Control Manager */ + ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + ssStatus.dwCurrentState = SERVICE_START_PENDING; + ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + ssStatus.dwWin32ExitCode = NO_ERROR; + ssStatus.dwServiceSpecificExitCode = 0; + ssStatus.dwCheckPoint = 1; + ssStatus.dwWaitHint = 5000; + if (!SetServiceStatus(sshStatusHandle, &ssStatus)) + { + msyslog(LOG_ERR, "SetServiceStatus: %m"); + ssStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(sshStatusHandle, &ssStatus); + return; + } + + } /* debug */ +# endif /* defined(SYS_WINNT) && !defined(NODETACH) */ +#endif /* VMS */ + + /* + * Logging. This may actually work on the gizmo board. Find a name + * to log with by using the basename of argv[0] + */ + cp = strrchr(argv[0], '/'); + if (cp == 0) + cp = argv[0]; + else + cp++; + + debug = 0; /* will be immediately re-initialized 8-( */ + getstartup(argc, argv); /* startup configuration, catch logfile this time */ + +#if !defined(VMS) + +# ifndef LOG_DAEMON + openlog(cp, LOG_PID); +# else /* LOG_DAEMON */ + +# ifndef LOG_NTP +# define LOG_NTP LOG_DAEMON +# endif + openlog(cp, LOG_PID | LOG_NDELAY, LOG_NTP); +# ifdef DEBUG + if (debug) + setlogmask(LOG_UPTO(LOG_DEBUG)); + else +# endif /* DEBUG */ + setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */ +# endif /* LOG_DAEMON */ +#endif /* !SYS_WINNT && !VMS */ + + NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */ + msyslog(LOG_NOTICE, "%s", Version); + +#ifdef SYS_WINNT + /* GMS 1/18/1997 + * TODO: lock the process in memory using SetProcessWorkingSetSize() and VirtualLock() functions + * + process_handle = GetCurrentProcess(); + if (SetProcessWorkingSetSize(process_handle, 2097152 , 4194304 ) == TRUE) { + if (VirtualLock(0 , 4194304) == FALSE) + msyslog(LOG_ERR, "VirtualLock() failed: %m"); + } else { + msyslog(LOG_ERR, "SetProcessWorkingSetSize() failed: %m"); + } + */ +#endif /* SYS_WINNT */ + +#ifdef SCO5_CLOCK + /* + * SCO OpenServer's system clock offers much more precise timekeeping + * on the base CPU than the other CPUs (for multiprocessor systems), + * so we must lock to the base CPU. + */ + { + int fd = open("/dev/at1", O_RDONLY); + if (fd >= 0) { + int zero = 0; + if (ioctl(fd, ACPU_LOCK, &zero) < 0) + msyslog(LOG_ERR, "cannot lock to base CPU: %m\n"); + close( fd ); + } /* else ... + * If we can't open the device, this probably just isn't + * a multiprocessor system, so we're A-OK. + */ + } +#endif + +#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && defined(MCL_FUTURE) +# ifdef HAVE_SETRLIMIT + /* + * Set the stack limit to something smaller, so that we don't lock a lot + * of unused stack memory. + */ + { + struct rlimit rl; + + if (getrlimit(RLIMIT_STACK, &rl) != -1 + && (rl.rlim_cur = 20 * 4096) < rl.rlim_max) + { + if (setrlimit(RLIMIT_STACK, &rl) == -1) + { + msyslog(LOG_ERR, + "Cannot adjust stack limit for mlockall: %m"); + } + } + } +# endif /* HAVE_SETRLIMIT */ + /* + * lock the process into memory + */ + if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) + msyslog(LOG_ERR, "mlockall(): %m"); +#else /* not (HAVE_MLOCKALL && MCL_CURRENT && MCL_FUTURE) */ +# ifdef HAVE_PLOCK +# ifdef PROCLOCK +# ifdef _AIX + /* + * set the stack limit for AIX for plock(). + * see get_aix_stack for more info. + */ + if (ulimit(SET_STACKLIM, (get_aix_stack() - 8*4096)) < 0) + { + msyslog(LOG_ERR,"Cannot adjust stack limit for plock on AIX: %m"); + } +# endif /* _AIX */ + /* + * lock the process into memory + */ + if (plock(PROCLOCK) < 0) + msyslog(LOG_ERR, "plock(PROCLOCK): %m"); +# else /* not PROCLOCK */ +# ifdef TXTLOCK + /* + * Lock text into ram + */ + if (plock(TXTLOCK) < 0) + msyslog(LOG_ERR, "plock(TXTLOCK) error: %m"); +# else /* not TXTLOCK */ + msyslog(LOG_ERR, "plock() - don't know what to lock!"); +# endif /* not TXTLOCK */ +# endif /* not PROCLOCK */ +# endif /* HAVE_PLOCK */ +#endif /* not (HAVE_MLOCKALL && MCL_CURRENT && MCL_FUTURE) */ + + /* + * Set up signals we pay attention to locally. + */ +#ifdef SIGDIE1 + (void) signal_no_reset(SIGDIE1, finish); +#endif /* SIGDIE1 */ +#ifdef SIGDIE2 + (void) signal_no_reset(SIGDIE2, finish); +#endif /* SIGDIE2 */ +#ifdef SIGDIE3 + (void) signal_no_reset(SIGDIE3, finish); +#endif /* SIGDIE3 */ +#ifdef SIGDIE4 + (void) signal_no_reset(SIGDIE4, finish); +#endif /* SIGDIE4 */ + +#ifdef SIGBUS + (void) signal_no_reset(SIGBUS, finish); +#endif /* SIGBUS */ + +#if !defined(SYS_WINNT) && !defined(VMS) +# ifdef DEBUG + (void) signal_no_reset(MOREDEBUGSIG, moredebug); + (void) signal_no_reset(LESSDEBUGSIG, lessdebug); +# else + (void) signal_no_reset(MOREDEBUGSIG, no_debug); + (void) signal_no_reset(LESSDEBUGSIG, no_debug); +# endif /* DEBUG */ +#endif /* !SYS_WINNT && !VMS */ + + /* + * Set up signals we should never pay attention to. + */ +#if defined SIGPIPE + (void) signal_no_reset(SIGPIPE, SIG_IGN); +#endif /* SIGPIPE */ + +#if defined SYS_WINNT + if (!SetConsoleCtrlHandler(OnConsoleEvent, TRUE)) { + msyslog(LOG_ERR, "Can't set console control handler: %m"); + } +#endif + + /* + * Call the init_ routines to initialize the data structures. + */ +#if defined (HAVE_IO_COMPLETION_PORT) + init_io_completion_port(); + init_winnt_time(); +#endif + init_auth(); + init_util(); + init_restrict(); + init_mon(); + init_timer(); + init_lib(); + init_random(); + init_request(); + init_control(); + init_peer(); +#ifdef REFCLOCK + init_refclock(); +#endif + set_process_priority(); + init_proto(); /* Call at high priority */ + init_io(); + init_loopfilter(); + mon_start(MON_ON); /* monitor on by default now */ + /* turn off in config if unwanted */ + + /* + * Get configuration. This (including argument list parsing) is + * done in a separate module since this will definitely be different + * for the gizmo board. While at it, save the host name for later + * along with the length. The crypto needs this. + */ +#ifdef DEBUG + debug = 0; +#endif + getconfig(argc, argv); +#ifdef OPENSSL + crypto_setup(); +#endif /* OPENSSL */ + initializing = 0; + +#if defined(SYS_WINNT) && !defined(NODETACH) +# if defined(DEBUG) + if(!debug) + { +# endif + if (NoWinService == FALSE) { + /* report to the service control manager that the service is running */ + ssStatus.dwCurrentState = SERVICE_RUNNING; + ssStatus.dwWin32ExitCode = NO_ERROR; + if (!SetServiceStatus(sshStatusHandle, &ssStatus)) + { + msyslog(LOG_ERR, "SetServiceStatus: %m"); + if (ResolverThreadHandle != NULL) + CloseHandle(ResolverThreadHandle); + ssStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(sshStatusHandle, &ssStatus); + return; + } + } +# if defined(DEBUG) + } +# endif +#endif + +#ifdef HAVE_CLOCKCTL + /* + * Drop super-user privileges and chroot now if the OS supports + * non root clock control (only NetBSD for now). + */ + if (user != NULL) { + if (isdigit((unsigned char)*user)) { + sw_uid = (uid_t)strtoul(user, &endp, 0); + if (*endp != '\0') + goto getuser; + } else { +getuser: + if ((pw = getpwnam(user)) != NULL) { + sw_uid = pw->pw_uid; + } else { + errno = 0; + msyslog(LOG_ERR, "Cannot find user `%s'", user); + exit (-1); + } + } + } + if (group != NULL) { + if (isdigit((unsigned char)*group)) { + sw_gid = (gid_t)strtoul(group, &endp, 0); + if (*endp != '\0') + goto getgroup; + } else { +getgroup: + if ((gr = getgrnam(group)) != NULL) { + sw_gid = pw->pw_gid; + } else { + errno = 0; + msyslog(LOG_ERR, "Cannot find group `%s'", group); + exit (-1); + } + } + } + if (chrootdir && chroot(chrootdir)) { + msyslog(LOG_ERR, "Cannot chroot to `%s': %m", chrootdir); + exit (-1); + } + if (group && setgid(sw_gid)) { + msyslog(LOG_ERR, "Cannot setgid() to group `%s': %m", group); + exit (-1); + } + if (group && setegid(sw_gid)) { + msyslog(LOG_ERR, "Cannot setegid() to group `%s': %m", group); + exit (-1); + } + if (user && setuid(sw_uid)) { + msyslog(LOG_ERR, "Cannot setuid() to user `%s': %m", user); + exit (-1); + } + if (user && seteuid(sw_uid)) { + msyslog(LOG_ERR, "Cannot seteuid() to user `%s': %m", user); + exit (-1); + } +#endif + /* + * Report that we're up to any trappers + */ + report_event(EVNT_SYSRESTART, (struct peer *)0); + + /* + * Use select() on all on all input fd's for unlimited + * time. select() will terminate on SIGALARM or on the + * reception of input. Using select() means we can't do + * robust signal handling and we get a potential race + * between checking for alarms and doing the select(). + * Mostly harmless, I think. + */ + /* On VMS, I suspect that select() can't be interrupted + * by a "signal" either, so I take the easy way out and + * have select() time out after one second. + * System clock updates really aren't time-critical, + * and - lacking a hardware reference clock - I have + * yet to learn about anything else that is. + */ +#if defined(HAVE_IO_COMPLETION_PORT) + WaitHandles[0] = CreateEvent(NULL, FALSE, FALSE, NULL); /* exit reques */ + WaitHandles[1] = get_timer_handle(); + WaitHandles[2] = get_io_event(); + + for (;;) { + DWORD Index = WaitForMultipleObjectsEx(sizeof(WaitHandles)/sizeof(WaitHandles[0]), WaitHandles, FALSE, 1000, TRUE); + switch (Index) { + case WAIT_OBJECT_0 + 0 : /* exit request */ + exit(0); + break; + + case WAIT_OBJECT_0 + 1 : /* timer */ + timer(); + break; + + case WAIT_OBJECT_0 + 2 : /* Io event */ +# ifdef DEBUG + if ( debug > 3 ) + { + printf( "IoEvent occurred\n" ); + } +# endif + break; + + case WAIT_IO_COMPLETION : /* loop */ + case WAIT_TIMEOUT : + break; + case WAIT_FAILED: + msyslog(LOG_ERR, "ntpdc: WaitForMultipleObjectsEx Failed: Error: %m"); + break; + + /* For now do nothing if not expected */ + default: + break; + + } /* switch */ + rbuflist = getrecvbufs(); /* get received buffers */ + +#else /* normal I/O */ + + was_alarmed = 0; + rbuflist = (struct recvbuf *)0; + for (;;) + { +# if !defined(HAVE_SIGNALED_IO) + extern fd_set activefds; + extern int maxactivefd; + + fd_set rdfdes; + int nfound; +# elif defined(HAVE_SIGNALED_IO) + block_io_and_alarm(); +# endif + + rbuflist = getrecvbufs(); /* get received buffers */ + if (alarm_flag) /* alarmed? */ + { + was_alarmed = 1; + alarm_flag = 0; + } + + if (!was_alarmed && rbuflist == (struct recvbuf *)0) + { + /* + * Nothing to do. Wait for something. + */ +# ifndef HAVE_SIGNALED_IO + rdfdes = activefds; +# if defined(VMS) || defined(SYS_VXWORKS) + /* make select() wake up after one second */ + { + struct timeval t1; + + t1.tv_sec = 1; t1.tv_usec = 0; + nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0, + (fd_set *)0, &t1); + } +# else + nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0, + (fd_set *)0, (struct timeval *)0); +# endif /* VMS */ + if (nfound > 0) + { + l_fp ts; + + get_systime(&ts); + + (void)input_handler(&ts); + } + else if (nfound == -1 && errno != EINTR) + msyslog(LOG_ERR, "select() error: %m"); +# ifdef DEBUG + else if (debug > 2) + msyslog(LOG_DEBUG, "select(): nfound=%d, error: %m", nfound); +# endif /* DEBUG */ +# else /* HAVE_SIGNALED_IO */ + + wait_for_signal(); +# endif /* HAVE_SIGNALED_IO */ + if (alarm_flag) /* alarmed? */ + { + was_alarmed = 1; + alarm_flag = 0; + } + rbuflist = getrecvbufs(); /* get received buffers */ + } +# ifdef HAVE_SIGNALED_IO + unblock_io_and_alarm(); +# endif /* HAVE_SIGNALED_IO */ + + /* + * Out here, signals are unblocked. Call timer routine + * to process expiry. + */ + if (was_alarmed) + { + timer(); + was_alarmed = 0; + } + +#endif /* HAVE_IO_COMPLETION_PORT */ + /* + * Call the data procedure to handle each received + * packet. + */ + while (rbuflist != (struct recvbuf *)0) + { + rbuf = rbuflist; + rbuflist = rbuf->next; + (rbuf->receiver)(rbuf); + freerecvbuf(rbuf); + } +#if defined DEBUG && defined SYS_WINNT + if (debug > 4) + printf("getrecvbufs: %ld handler interrupts, %ld frames\n", + handler_calls, handler_pkts); +#endif + + /* + * Go around again + */ + } +#ifndef SYS_WINNT + exit(1); /* unreachable */ +#endif +#ifndef SYS_WINNT + return 1; /* DEC OSF cc braindamage */ +#endif +} + + +#ifdef SIGDIE2 +/* + * finish - exit gracefully + */ +static RETSIGTYPE +finish( + int sig + ) +{ + + msyslog(LOG_NOTICE, "ntpd exiting on signal %d", sig); + + switch (sig) + { +# ifdef SIGBUS + case SIGBUS: + printf("\nfinish(SIGBUS)\n"); + exit(0); +# endif + case 0: /* Should never happen... */ + return; + default: + exit(0); + } +} +#endif /* SIGDIE2 */ + + +#ifdef DEBUG +#ifndef SYS_WINNT +/* + * moredebug - increase debugging verbosity + */ +static RETSIGTYPE +moredebug( + int sig + ) +{ + int saved_errno = errno; + + if (debug < 255) + { + debug++; + msyslog(LOG_DEBUG, "debug raised to %d", debug); + } + errno = saved_errno; +} + +/* + * lessdebug - decrease debugging verbosity + */ +static RETSIGTYPE +lessdebug( + int sig + ) +{ + int saved_errno = errno; + + if (debug > 0) + { + debug--; + msyslog(LOG_DEBUG, "debug lowered to %d", debug); + } + errno = saved_errno; +} +#endif +#else /* not DEBUG */ +#ifndef SYS_WINNT/* + * no_debug - We don't do the debug here. + */ +static RETSIGTYPE +no_debug( + int sig + ) +{ + int saved_errno = errno; + + msyslog(LOG_DEBUG, "ntpd not compiled for debugging (signal %d)", sig); + errno = saved_errno; +} +#endif /* not SYS_WINNT */ +#endif /* not DEBUG */ + +#ifdef SYS_WINNT +/* service_ctrl - control handler for NTP service + * signals the service_main routine of start/stop requests + * from the control panel or other applications making + * win32API calls + */ +void +service_ctrl( + DWORD dwCtrlCode + ) +{ + DWORD dwState = SERVICE_RUNNING; + + /* Handle the requested control code */ + switch(dwCtrlCode) + { + case SERVICE_CONTROL_PAUSE: + /* see no reason to support this */ + break; + + case SERVICE_CONTROL_CONTINUE: + /* see no reason to support this */ + break; + + case SERVICE_CONTROL_STOP: + dwState = SERVICE_STOP_PENDING; + /* + * Report the status, specifying the checkpoint and waithint, + * before setting the termination event. + */ + ssStatus.dwCurrentState = dwState; + ssStatus.dwWin32ExitCode = NO_ERROR; + ssStatus.dwWaitHint = 3000; + if (!SetServiceStatus(sshStatusHandle, &ssStatus)) + { + msyslog(LOG_ERR, "SetServiceStatus: %m"); + } + if (WaitHandles[0] != NULL) { + SetEvent(WaitHandles[0]); + } + return; + + case SERVICE_CONTROL_INTERROGATE: + /* Update the service status */ + break; + + default: + /* invalid control code */ + break; + + } + + ssStatus.dwCurrentState = dwState; + ssStatus.dwWin32ExitCode = NO_ERROR; + if (!SetServiceStatus(sshStatusHandle, &ssStatus)) + { + msyslog(LOG_ERR, "SetServiceStatus: %m"); + } +} + +static BOOL WINAPI +OnConsoleEvent( + DWORD dwCtrlType + ) +{ + switch (dwCtrlType) { + case CTRL_BREAK_EVENT : + if (debug > 0) { + debug <<= 1; + } + else { + debug = 1; + } + if (debug > 8) { + debug = 0; + } + printf("debug level %d\n", debug); + break ; + + case CTRL_C_EVENT : + case CTRL_CLOSE_EVENT : + case CTRL_SHUTDOWN_EVENT : + if (WaitHandles[0] != NULL) { + SetEvent(WaitHandles[0]); + } + break; + + default : + return FALSE; + + + } + return TRUE;; +} + + +/* + * NT version of exit() - all calls to exit() should be routed to + * this function. + */ +void +service_exit( + int status + ) +{ + if (!debug) { /* did not become a service, simply exit */ + /* service mode, need to have the service_main routine + * register with the service control manager that the + * service has stopped running, before exiting + */ + ssStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(sshStatusHandle, &ssStatus); + + } + uninit_io_completion_port(); + reset_winnt_time(); + +# if defined _MSC_VER + _CrtDumpMemoryLeaks(); +# endif +#undef exit + exit(status); +} + +#endif /* SYS_WINNT */ |