/* $NetBSD: iosignal.c,v 1.5 2020/05/25 20:47:24 christos Exp $ */
/*
* iosignal.c - input/output routines for ntpd. The socket-opening code
* was shamelessly stolen from ntpd.
*/
/*
* [Bug 158]
* Do the #includes differently, as under some versions of Linux
* sys/param.h has a #undef CONFIG_PHONE line in it.
*
* As we have ~40 CONFIG_ variables, I don't feel like renaming them
* every time somebody adds a new macro to some system header.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <signal.h>
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif /* HAVE_SYS_PARAM_H */
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <arpa/inet.h>
#if _BSDI_VERSION >= 199510
# include <ifaddrs.h>
#endif
# ifdef __QNXNTO__
# include <fcntl.h>
# include <unix.h>
# define FNDELAY O_NDELAY
# endif
#include "ntp_machine.h"
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_if.h"
#include "ntp_stdlib.h"
#include "iosignal.h"
#if defined(HAVE_SIGNALED_IO)
static RETSIGTYPE sigio_handler (int);
/* consistency safegurad to catch BLOCK/UNBLOCK oversights */
static int sigio_block_count = 0;
/* main inputhandler to be called on SIGIO */
static input_handler_t *input_handler_callback = NULL;
# if defined(HAVE_SIGACTION)
/*
* If sigaction() is used for signal handling and a signal is
* pending then the kernel blocks the signal before it calls
* the signal handler.
*
* The variable below is used to take care that the SIGIO signal
* is not unintentionally unblocked inside the sigio_handler()
* if the handler executes a piece of code that is normally
* bracketed by BLOCKIO()/UNBLOCKIO() calls.
*/
static int sigio_handler_active = 0;
# endif
/*
* SIGPOLL and SIGIO ROUTINES.
*/
/*
* TTY initialization routines.
*/
int
init_clock_sig(
struct refclockio *rio
)
{
# ifdef USE_TTY_SIGPOLL
{
/* DO NOT ATTEMPT TO MAKE CLOCK-FD A CTTY: not portable, unreliable */
if (ioctl(rio->fd, I_SETSIG, S_INPUT) < 0)
{
msyslog(LOG_ERR,
"init_clock_sig: ioctl(I_SETSIG, S_INPUT) failed: %m");
return 1;
}
return 0;
}
# else
/*
* Special cases first!
*/
/* Was: defined(SYS_HPUX) */
# if defined(FIOSSAIOOWN) && defined(FIOSNBIO) && defined(FIOSSAIOSTAT)
#define CLOCK_DONE
{
int pgrp, on = 1;
/* DO NOT ATTEMPT TO MAKE CLOCK-FD A CTTY: not portable, unreliable */
pgrp = getpid();
if (ioctl(rio->fd, FIOSSAIOOWN, (char *)&pgrp) == -1)
{
msyslog(LOG_ERR, "ioctl(FIOSSAIOOWN) fails for clock I/O: %m - EXITING");
exit(1);
/*NOTREACHED*/
}
/*
* set non-blocking, async I/O on the descriptor
*/
if (ioctl(rio->fd, FIOSNBIO, (char *)&on) == -1)
{
msyslog(LOG_ERR, "ioctl(FIOSNBIO) fails for clock I/O: %m - EXITING");
exit(1);
/*NOTREACHED*/
}
if (ioctl(rio->fd, FIOSSAIOSTAT, (char *)&on) == -1)
{
msyslog(LOG_ERR, "ioctl(FIOSSAIOSTAT) fails for clock I/O: %m - EXITING");
exit(1);
/*NOTREACHED*/
}
return 0;
}
# endif /* SYS_HPUX: FIOSSAIOOWN && FIOSNBIO && FIOSSAIOSTAT */
/* Was: defined(SYS_AIX) && !defined(_BSD) */
# if !defined(_BSD) && defined(_AIX) && defined(FIOASYNC) && defined(FIOSETOWN)
/*
* SYSV compatibility mode under AIX.
*/
#define CLOCK_DONE
{
int pgrp, on = 1;
/* DO NOT ATTEMPT TO MAKE CLOCK-FD A CTTY: not portable, unreliable */
if (ioctl(rio->fd, FIOASYNC, (char *)&on) == -1)
{
msyslog(LOG_ERR, "ioctl(FIOASYNC) fails for clock I/O: %m");
return 1;
}
pgrp = -getpid();
if (ioctl(rio->fd, FIOSETOWN, (char*)&pgrp) == -1)
{
msyslog(LOG_ERR, "ioctl(FIOSETOWN) fails for clock I/O: %m");
return 1;
}
if (fcntl(rio->fd, F_SETFL, FNDELAY|FASYNC) < 0)
{
msyslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails for clock I/O: %m");
return 1;
}
return 0;
}
# endif /* AIX && !BSD: !_BSD && FIOASYNC && FIOSETOWN */
# ifndef CLOCK_DONE
{
/* DO NOT ATTEMPT TO MAKE CLOCK-FD A CTTY: not portable, unreliable */
# if defined(TIOCSCTTY) && defined(USE_FSETOWNCTTY)
/*
* there are, however, always exceptions to the rules
* one is, that OSF accepts SETOWN on TTY fd's only, iff they are
* CTTYs. SunOS and HPUX do not semm to have this restriction.
* another question is: how can you do multiple SIGIO from several
* ttys (as they all should be CTTYs), wondering...
*
* kd 95-07-16
*/
if (ioctl(rio->fd, TIOCSCTTY, 0) == -1)
{
msyslog(LOG_ERR, "ioctl(TIOCSCTTY, 0) fails for clock I/O: %m");
return 1;
}
# endif /* TIOCSCTTY && USE_FSETOWNCTTY */
if (fcntl(rio->fd, F_SETOWN, getpid()) == -1)
{
msyslog(LOG_ERR, "fcntl(F_SETOWN) fails for clock I/O: %m");
return 1;
}
if (fcntl(rio->fd, F_SETFL, FNDELAY|FASYNC) < 0)
{
msyslog(LOG_ERR,
"fcntl(FNDELAY|FASYNC) fails for clock I/O: %m");
return 1;
}
return 0;
}
# endif /* CLOCK_DONE */
# endif /* !USE_TTY_SIGPOLL */
}
void
init_socket_sig(
int fd
)
{
# ifdef USE_UDP_SIGPOLL
{
if (ioctl(fd, I_SETSIG, S_INPUT) < 0)
{
msyslog(LOG_ERR,
"init_socket_sig: ioctl(I_SETSIG, S_INPUT) failed: %m - EXITING");
exit(1);
}
}
# else /* USE_UDP_SIGPOLL */
{
int pgrp;
# ifdef FIOASYNC
int on = 1;
# endif
# if defined(FIOASYNC)
if (ioctl(fd, FIOASYNC, (char *)&on) == -1)
{
msyslog(LOG_ERR, "ioctl(FIOASYNC) fails: %m - EXITING");
exit(1);
/*NOTREACHED*/
}
# elif defined(FASYNC)
{
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
{
msyslog(LOG_ERR, "fcntl(F_GETFL) fails: %m - EXITING");
exit(1);
/*NOTREACHED*/
}
if (fcntl(fd, F_SETFL, flags|FASYNC) < 0)
{
msyslog(LOG_ERR, "fcntl(...|FASYNC) fails: %m - EXITING");
exit(1);
/*NOTREACHED*/
}
}
# else
# include "Bletch: Need asynchronous I/O!"
# endif
# ifdef UDP_BACKWARDS_SETOWN
pgrp = -getpid();
# else
pgrp = getpid();
# endif
# if defined(SIOCSPGRP)
if (ioctl(fd, SIOCSPGRP, (char *)&pgrp) == -1)
{
msyslog(LOG_ERR, "ioctl(SIOCSPGRP) fails: %m - EXITING");
exit(1);
/*NOTREACHED*/
}
# elif defined(FIOSETOWN)
if (ioctl(fd, FIOSETOWN, (char*)&pgrp) == -1)
{
msyslog(LOG_ERR, "ioctl(FIOSETOWN) fails: %m - EXITING");
exit(1);
/*NOTREACHED*/
}
# elif defined(F_SETOWN)
if (fcntl(fd, F_SETOWN, pgrp) == -1)
{
msyslog(LOG_ERR, "fcntl(F_SETOWN) fails: %m - EXITING");
exit(1);
/*NOTREACHED*/
}
# else
# include "Bletch: Need to set process(group) to receive SIG(IO|POLL)"
# endif
}
# endif /* USE_UDP_SIGPOLL */
}
static RETSIGTYPE
sigio_handler(
int sig
)
{
int saved_errno = errno;
l_fp ts;
get_systime(&ts);
# if defined(HAVE_SIGACTION)
sigio_handler_active++;
if (sigio_handler_active != 1) /* This should never happen! */
msyslog(LOG_ERR, "sigio_handler: sigio_handler_active != 1");
# endif
INSIST(input_handler_callback != NULL);
(*input_handler_callback)(&ts);
# if defined(HAVE_SIGACTION)
sigio_handler_active--;
if (sigio_handler_active != 0) /* This should never happen! */
msyslog(LOG_ERR, "sigio_handler: sigio_handler_active != 0");
# endif
errno = saved_errno;
}
/*
* Signal support routines.
*/
# ifdef HAVE_SIGACTION
void
set_signal(input_handler_t *input)
{
INSIST(input != NULL);
input_handler_callback = input;
using_sigio = TRUE;
# ifdef USE_SIGIO
(void) signal_no_reset(SIGIO, sigio_handler);
# endif
# ifdef USE_SIGPOLL
(void) signal_no_reset(SIGPOLL, sigio_handler);
# endif
}
void
block_io_and_alarm(void)
{
sigset_t set;
if (sigemptyset(&set))
msyslog(LOG_ERR, "block_io_and_alarm: sigemptyset() failed: %m");
# if defined(USE_SIGIO)
if (sigaddset(&set, SIGIO))
msyslog(LOG_ERR, "block_io_and_alarm: sigaddset(SIGIO) failed: %m");
# endif
# if defined(USE_SIGPOLL)
if (sigaddset(&set, SIGPOLL))
msyslog(LOG_ERR, "block_io_and_alarm: sigaddset(SIGPOLL) failed: %m");
# endif
if (sigaddset(&set, SIGALRM))
msyslog(LOG_ERR, "block_io_and_alarm: sigaddset(SIGALRM) failed: %m");
if (sigprocmask(SIG_BLOCK, &set, NULL))
msyslog(LOG_ERR, "block_io_and_alarm: sigprocmask() failed: %m");
}
void
block_sigio(void)
{
if ( sigio_handler_active == 0 ) /* not called from within signal handler */
{
sigset_t set;
++sigio_block_count;
if (sigio_block_count > 1)
msyslog(LOG_INFO, "block_sigio: sigio_block_count > 1");
if (sigio_block_count < 1)
msyslog(LOG_INFO, "block_sigio: sigio_block_count < 1");
if (sigemptyset(&set))
msyslog(LOG_ERR, "block_sigio: sigemptyset() failed: %m");
# if defined(USE_SIGIO)
if (sigaddset(&set, SIGIO))
msyslog(LOG_ERR, "block_sigio: sigaddset(SIGIO) failed: %m");
# endif
# if defined(USE_SIGPOLL)
if (sigaddset(&set, SIGPOLL))
msyslog(LOG_ERR, "block_sigio: sigaddset(SIGPOLL) failed: %m");
# endif
if (sigprocmask(SIG_BLOCK, &set, NULL))
msyslog(LOG_ERR, "block_sigio: sigprocmask() failed: %m");
}
}
void
unblock_io_and_alarm(void)
{
sigset_t unset;
if (sigemptyset(&unset))
msyslog(LOG_ERR, "unblock_io_and_alarm: sigemptyset() failed: %m");
# if defined(USE_SIGIO)
if (sigaddset(&unset, SIGIO))
msyslog(LOG_ERR, "unblock_io_and_alarm: sigaddset(SIGIO) failed: %m");
# endif
# if defined(USE_SIGPOLL)
if (sigaddset(&unset, SIGPOLL))
msyslog(LOG_ERR, "unblock_io_and_alarm: sigaddset(SIGPOLL) failed: %m");
# endif
if (sigaddset(&unset, SIGALRM))
msyslog(LOG_ERR, "unblock_io_and_alarm: sigaddset(SIGALRM) failed: %m");
if (sigprocmask(SIG_UNBLOCK, &unset, NULL))
msyslog(LOG_ERR, "unblock_io_and_alarm: sigprocmask() failed: %m");
}
void
unblock_sigio(void)
{
if ( sigio_handler_active == 0 ) /* not called from within signal handler */
{
sigset_t unset;
--sigio_block_count;
if (sigio_block_count > 0)
msyslog(LOG_INFO, "unblock_sigio: sigio_block_count > 0");
if (sigio_block_count < 0)
msyslog(LOG_INFO, "unblock_sigio: sigio_block_count < 0");
if (sigemptyset(&unset))
msyslog(LOG_ERR, "unblock_sigio: sigemptyset() failed: %m");
# if defined(USE_SIGIO)
if (sigaddset(&unset, SIGIO))
msyslog(LOG_ERR, "unblock_sigio: sigaddset(SIGIO) failed: %m");
# endif
# if defined(USE_SIGPOLL)
if (sigaddset(&unset, SIGPOLL))
msyslog(LOG_ERR, "unblock_sigio: sigaddset(SIGPOLL) failed: %m");
# endif
if (sigprocmask(SIG_UNBLOCK, &unset, NULL))
msyslog(LOG_ERR, "unblock_sigio: sigprocmask() failed: %m");
}
}
void
wait_for_signal(void)
{
sigset_t old;
if (sigprocmask(SIG_UNBLOCK, NULL, &old))
msyslog(LOG_ERR, "wait_for_signal: sigprocmask() failed: %m");
# if defined(USE_SIGIO)
if (sigdelset(&old, SIGIO))
msyslog(LOG_ERR, "wait_for_signal: sigdelset(SIGIO) failed: %m");
# endif
# if defined(USE_SIGPOLL)
if (sigdelset(&old, SIGPOLL))
msyslog(LOG_ERR, "wait_for_signal: sigdelset(SIGPOLL) failed: %m");
# endif
if (sigdelset(&old, SIGALRM))
msyslog(LOG_ERR, "wait_for_signal: sigdelset(SIGALRM) failed: %m");
if (sigsuspend(&old) && (errno != EINTR))
msyslog(LOG_ERR, "wait_for_signal: sigsuspend() failed: %m");
}
# else /* !HAVE_SIGACTION */
/*
* Must be an old bsd system.
* We assume there is no SIGPOLL.
*/
void
block_io_and_alarm(void)
{
int mask;
mask = sigmask(SIGIO) | sigmask(SIGALRM);
if (sigblock(mask))
msyslog(LOG_ERR, "block_io_and_alarm: sigblock() failed: %m");
}
void
block_sigio(void)
{
int mask;
++sigio_block_count;
if (sigio_block_count > 1)
msyslog(LOG_INFO, "block_sigio: sigio_block_count > 1");
if (sigio_block_count < 1)
msyslog(LOG_INFO, "block_sigio: sigio_block_count < 1");
mask = sigmask(SIGIO);
if (sigblock(mask))
msyslog(LOG_ERR, "block_sigio: sigblock() failed: %m");
}
void
set_signal(input_handler_t *input)
{
INSIST(input != NULL);
input_handler_callback = input;
using_sigio = TRUE;
(void) signal_no_reset(SIGIO, sigio_handler);
}
void
unblock_io_and_alarm(void)
{
int mask, omask;
mask = sigmask(SIGIO) | sigmask(SIGALRM);
omask = sigblock(0);
omask &= ~mask;
(void) sigsetmask(omask);
}
void
unblock_sigio(void)
{
int mask, omask;
--sigio_block_count;
if (sigio_block_count > 0)
msyslog(LOG_INFO, "unblock_sigio: sigio_block_count > 0");
if (sigio_block_count < 0)
msyslog(LOG_INFO, "unblock_sigio: sigio_block_count < 0");
mask = sigmask(SIGIO);
omask = sigblock(0);
omask &= ~mask;
(void) sigsetmask(omask);
}
void
wait_for_signal(void)
{
int mask, omask;
mask = sigmask(SIGIO) | sigmask(SIGALRM);
omask = sigblock(0);
omask &= ~mask;
if (sigpause(omask) && (errno != EINTR))
msyslog(LOG_ERR, "wait_for_signal: sigspause() failed: %m");
}
# endif /* HAVE_SIGACTION */
#else
int NotAnEmptyCompilationUnit;
#endif