1771 lines
37 KiB
C
1771 lines
37 KiB
C
/* point-to-point daemon utility code
|
|
*/
|
|
|
|
#ident "$Revision: 1.48 $"
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <paths.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <syslog.h>
|
|
#include <signal.h>
|
|
#include <values.h>
|
|
#include <math.h>
|
|
#include <pwd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/stropts.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/major.h>
|
|
|
|
#include <netinet/tcp.h>
|
|
#include <net/soioctl.h>
|
|
#include <net/if.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "pputil.h"
|
|
|
|
#if !defined(PPP) && !defined(SLIP)
|
|
#error "must be compiled for either SLIP or PPP"
|
|
#endif
|
|
|
|
#ifdef PPP
|
|
extern void proxy_unarp(void);
|
|
#endif
|
|
|
|
#if defined(PPP_IRIX_62) || defined(PPP_IRIX_53)
|
|
extern void setservice(char*);
|
|
extern void stlock(char*);
|
|
extern int conn_trycalls;
|
|
extern char lock_pid[SIZEOFPID+2]; /* account for \n and \0 */
|
|
extern int Nlocks;
|
|
extern char *AbortStr[];
|
|
extern int AbortSeen, Aborts;
|
|
extern int conn(char*);
|
|
#endif
|
|
|
|
static void stray_ctty(void);
|
|
|
|
static pid_t kludged;
|
|
|
|
#define USEC_PER_SEC 1000000
|
|
|
|
/* Update the nondecreasing clock that is not affected by time changes,
|
|
* as well as the system time-of-day clock. Use a value that is the
|
|
* number of microseconds since the system was started.
|
|
* Using only times() produces a clock that is too coarse, so combine
|
|
* the kernel's time-of-day clock with times()'s number of ticks since
|
|
* the system started.
|
|
*/
|
|
time_t /* return seconds */
|
|
get_clk(void)
|
|
{
|
|
# define FLOAT_TIME(t) (t.tv_sec*1.0 + t.tv_usec*1.0/USEC_PER_SEC)
|
|
static double sstart_d, prev_clk_d;
|
|
double d, cur_date_d;
|
|
clock_t ticks; /* HZ ticks since system started. */
|
|
struct tms tms;
|
|
|
|
ticks = times(&tms);
|
|
gettimeofday(&cur_date);
|
|
|
|
/* Notice changes in the apparent time-of-day when the system was
|
|
* started, and infer an adjustment in the system clock.
|
|
* Use that to keep the daemon's nondecreasing clock straight.
|
|
*/
|
|
cur_date_d = FLOAT_TIME(cur_date);
|
|
d = cur_date_d - ticks*1.0/HZ;
|
|
|
|
/* From the start of a tick until its end, the appearant system
|
|
* start time will appear to increase. At the start of the next
|
|
* tick, it will decrease by 1.0/HZ seconds. Consecutive
|
|
* nondecreasing clock values could jump forward or backwards
|
|
* by 1.0/HZ seconds as a result if the computed system start
|
|
* time were accept uncritically.
|
|
*/
|
|
if (fabs(d-sstart_d) > 1.0/HZ) {
|
|
if (d < sstart_d) {
|
|
/* do not let the clock jump backwards */
|
|
sstart_d = MIN(cur_date_d-prev_clk_d+1.0/USEC_PER_SEC,
|
|
d+1.0/HZ);
|
|
} else {
|
|
sstart_d = d-1.0/HZ;
|
|
}
|
|
}
|
|
|
|
prev_clk_d = cur_date_d-sstart_d;
|
|
clk.tv_sec = (long)floor(prev_clk_d);
|
|
clk.tv_usec = (long)((prev_clk_d-clk.tv_sec)*USEC_PER_SEC);
|
|
return clk.tv_sec;
|
|
}
|
|
|
|
|
|
void
|
|
timevalsub(struct timeval *t1,
|
|
struct timeval *t2,
|
|
struct timeval *t3)
|
|
{
|
|
register time_t sec, usec;
|
|
|
|
sec = t2->tv_sec - t3->tv_sec;
|
|
usec = t2->tv_usec - t3->tv_usec;
|
|
while (usec < 0) {
|
|
sec--;
|
|
usec += USEC_PER_SEC;
|
|
}
|
|
while (usec >= USEC_PER_SEC) {
|
|
sec++;
|
|
usec -= USEC_PER_SEC;
|
|
}
|
|
t1->tv_sec = sec;
|
|
t1->tv_usec = usec;
|
|
}
|
|
|
|
|
|
/* Arrange to make stderr point to something useful for the UUCP
|
|
* code, and otherwise start the error logging.
|
|
*/
|
|
void
|
|
kludge_stderr(void)
|
|
{
|
|
static char pidstr[16+1+16+1+1];
|
|
int fildes[2];
|
|
pid_t pid;
|
|
int i;
|
|
int serror;
|
|
char *es;
|
|
|
|
|
|
pid = getpid();
|
|
if (stderrfd < 0
|
|
|| (kludged > 0 && kludged != pid)) {
|
|
kludged = pid;
|
|
(void)sprintf(pidstr,"%0.16s[%d]",pgmname,kludged);
|
|
stderrfd = -1;
|
|
|
|
if (0 > pipe(fildes)) {
|
|
stderrpid = -1;
|
|
es = "pipe() failed for log";
|
|
} else {
|
|
stderrpid = fork();
|
|
es = "fork() failed for log";
|
|
}
|
|
if (stderrpid == 0) {
|
|
/* in the child */
|
|
no_signals(SIG_IGN);
|
|
|
|
if (0 <= dup2(fildes[0],0)) {
|
|
/* no controlly tty */
|
|
ctty = 0;
|
|
stray_ctty();
|
|
/* stderr is open, ensure it is file ID 2 */
|
|
if (stderrttyfd >= 0
|
|
&& stderrttyfd != 2
|
|
&& 0 <= dup2(stderrttyfd,2))
|
|
stderrttyfd = 2;
|
|
for (i = getdtablehi(); --i > 0; ) {
|
|
if (i != stderrttyfd)
|
|
(void)close(i);
|
|
}
|
|
(void)execlp("logger", "logger",
|
|
(stderrttyfd == 2) ? "-st" : "-t",
|
|
pidstr, "-p", "daemon.err", 0);
|
|
}
|
|
syslog(LOG_ERR, "failed to start logger: %m");
|
|
exit(-1);
|
|
|
|
} else if (stderrpid > 0) {
|
|
if (stderrttyfd == 2) {
|
|
stderrttyfd = dup(stderrttyfd);
|
|
(void)close(2);
|
|
}
|
|
(void)close(fildes[0]);
|
|
if (0 > dup2(fildes[1], 2)) {
|
|
es = "dup2()";
|
|
stderrpid = -1;
|
|
} else {
|
|
(void)dup2(2,1);
|
|
stderrfd = 2;
|
|
}
|
|
if (fildes[1] != 2)
|
|
(void)close(fildes[1]);
|
|
}
|
|
|
|
if (stderrpid < 0) {
|
|
/* fall back to the kludge if the fork fails */
|
|
serror = errno;
|
|
if (!freopen("/dev/log", "a", stderr))
|
|
return; /* huh? */
|
|
stderrfd = fileno(stderr);
|
|
errno = serror;
|
|
log_errno(es,"");
|
|
}
|
|
|
|
setlinebuf(stderr);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
call_system(char *link_name,
|
|
char *cmd_name,
|
|
char *cmd_base,
|
|
int note_exit)
|
|
{
|
|
#define CMD_NORM "1>/dev/null "
|
|
#define CMD_VERBOSE "set -x; 1>&2 "
|
|
int i;
|
|
char *cmd;
|
|
|
|
if (debug != 0) {
|
|
cmd = malloc(strlen(cmd_base) + sizeof(CMD_VERBOSE));
|
|
strcpy(cmd, CMD_VERBOSE);
|
|
} else {
|
|
cmd = malloc(strlen(cmd_base) + sizeof(CMD_NORM));
|
|
strcpy(cmd, CMD_NORM);
|
|
}
|
|
strcat(cmd, cmd_base);
|
|
|
|
kludge_stderr();
|
|
i = system(cmd);
|
|
if (0 > i) {
|
|
log_errno(cmd_name, cmd_base);
|
|
} else if (note_exit && (i = WEXITSTATUS(i)) != 0) {
|
|
log_complain(link_name,"%s`%s` exit=%d",
|
|
cmd_name, cmd_base, i);
|
|
}
|
|
|
|
free(cmd);
|
|
#undef CMD_STDIO
|
|
#undef CMD_VERBOSE
|
|
}
|
|
|
|
|
|
void
|
|
init_rand(void)
|
|
{
|
|
extern unsigned sysid(unsigned char id[16]);
|
|
|
|
srandom(sysid(0) ^ time(0) ^ gethostid());
|
|
srand48(random());
|
|
srand(random());
|
|
}
|
|
|
|
|
|
/* lock the device after answering the phone
|
|
*/
|
|
void
|
|
grab_dev(const char *hello_msg,
|
|
struct dev *dp)
|
|
{
|
|
FILE *lf;
|
|
pid_t pid;
|
|
char *p;
|
|
int i;
|
|
|
|
dp->devfd = 0;
|
|
if (!set_tty_modes(dp)) /* set async tty modes */
|
|
cleanup(1);
|
|
|
|
p = ttyname(dp->devfd);
|
|
if (!p) {
|
|
log_complain("","unable to discover device name (fd=%d)",
|
|
dp->devfd);
|
|
cleanup(1);
|
|
}
|
|
i = strlen(p);
|
|
if (i > sizeof(dp->nodename)-sizeof("/dev/")
|
|
|| i < sizeof("/dev/")) {
|
|
log_complain("","bogus device name \"%s\" (fd=%d)",
|
|
p,dp->devfd);
|
|
cleanup(1);
|
|
}
|
|
(void)strcpy(dp->nodename,p+sizeof("/dev/")-1);
|
|
(void)strcpy(dp->line,p);
|
|
if (!lockname(dp->nodename,dp->lockfile,
|
|
sizeof(dp->lockfile))) {
|
|
log_complain("","unable to compute lock file name for %s",
|
|
dp->nodename);
|
|
}
|
|
|
|
if (mmlock(dp->nodename)) { /* Try to lock the tty */
|
|
lf = fopen(dp->lockfile,"r+");
|
|
if (!lf) {
|
|
log_complain("","unable to open lockfile for %s",
|
|
dp->line);
|
|
cleanup(1);
|
|
}
|
|
if (1 != fscanf(lf, "%ld", &pid)) {
|
|
log_complain("","unable to parse lockfile for %s",
|
|
dp->line);
|
|
cleanup(1);
|
|
}
|
|
if (pid != getpid()) {
|
|
log_debug(1, "","strange PID %d in lockfile for %s"
|
|
" (my PPID=%d);"
|
|
" incoming/outgoing call collision?",
|
|
pid, dp->line, getppid());
|
|
/* Since things are confused, let the other guy know
|
|
* and then quit.
|
|
*/
|
|
(void)kill(pid, SIGINT);
|
|
cleanup(1);
|
|
}
|
|
(void)fclose(lf);
|
|
stlock(dp->lockfile);
|
|
}
|
|
|
|
if (dp->sync == SYNC_OFF) {
|
|
if (0 > write(dp->devfd, hello_msg, strlen(hello_msg)))
|
|
log_errno("write(hello)","");
|
|
if (0 > tcdrain(dp->devfd) && errno != EINTR)
|
|
log_errno("hello tcdrain","");
|
|
(void)sginap(HZ/5);
|
|
}
|
|
}
|
|
|
|
|
|
/* Relay between a socket and a pty for TCP testing.
|
|
* This wants a uucp/Devices entry like:
|
|
* PPPTCP - - Any TCP login
|
|
* and a uucp/Systems entry like:
|
|
* tcpgar Never PPPTCP,t Any garnet "" \0lnam\0rnam\0ppp/38400\0\c
|
|
* assword~2 tcpppp PPP~2
|
|
* PPP needs
|
|
* map_char_num=0xff
|
|
*/
|
|
static void
|
|
pty_relay(struct dev *dp,
|
|
int soc, /* socket being connected to pty */
|
|
int mpty) /* master side of pty */
|
|
{
|
|
int i, j;
|
|
char buf[2048];
|
|
fd_set rfds;
|
|
|
|
/* kludge to keep the PTY open while closing everything else */
|
|
dp->devfd = soc;
|
|
rendnode = mpty;
|
|
stderrfd = -1;
|
|
stderrpid = -1;
|
|
status_poke_fd = -1;
|
|
closefds();
|
|
modfd = -1;
|
|
rendnode = -1;
|
|
|
|
no_signals(SIG_IGN);
|
|
(void)signal(SIGPIPE, killed);
|
|
|
|
FD_ZERO(&rfds);
|
|
for (;;) {
|
|
FD_SET(mpty, &rfds);
|
|
FD_SET(soc, &rfds);
|
|
i = select(MAX(mpty,soc)+1, &rfds, 0, 0, 0);
|
|
if (i < 0) {
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
continue;
|
|
bad_errno("pty_relay() select()","");
|
|
}
|
|
|
|
if (FD_ISSET(mpty, &rfds)) {
|
|
i = read(mpty, buf, sizeof(buf));
|
|
if (i <= 0) {
|
|
if (i == 0)
|
|
log_debug(4,"","pty_relay mpty EOF");
|
|
else
|
|
bad_errno("pty_relay read(mpty)", "");
|
|
exit(0);
|
|
}
|
|
j = write(soc, buf, i);
|
|
if (i != j) {
|
|
if (j < 0) {
|
|
if (errno == EBADF || debug != 0)
|
|
bad_errno("pty_relay write(soc)",
|
|
"");
|
|
exit(0);
|
|
} else {
|
|
log_complain("",
|
|
"pty_relay write(soc)=%d"
|
|
" not %d", i, j);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FD_ISSET(soc, &rfds)) {
|
|
i = read(soc, buf, sizeof(buf));
|
|
if (i <= 0) {
|
|
if (i == 0)
|
|
log_debug(4,"","pty_relay soc EOF");
|
|
else
|
|
bad_errno("pty_relay read(soc)", "");
|
|
exit(0);
|
|
}
|
|
j = write(mpty, buf, i);
|
|
if (i != j) {
|
|
if (j < 0) {
|
|
if (errno == EBADF || debug != 0)
|
|
bad_errno("pty_relay write(mpty)",
|
|
"");
|
|
exit(0);
|
|
} else {
|
|
log_complain("",
|
|
"pty_relay write(mpty)=%d"
|
|
" not %d", i, j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int /* 0=failed */
|
|
set_tty_modes(struct dev *dp)
|
|
{
|
|
char lname[FMNAMESZ+1];
|
|
char *spty_name;
|
|
int soc, spty, mpty = -1;
|
|
int fl;
|
|
int val, val_len;
|
|
FILE *lf;
|
|
|
|
|
|
/* if we are talking to a socket, then assume this is a test
|
|
* over TCP and insert a pty in series.
|
|
* Just muddle through if we fail to make the pty.
|
|
*/
|
|
if (!strcmp(dp->nodename, "-")) {
|
|
soc = dp->devfd;
|
|
|
|
val_len = sizeof(val);
|
|
if (0 > getsockopt(soc,SOL_SOCKET,SO_SNDBUF, &val, &val_len)) {
|
|
log_errno("getsockopt(soc, SO_SNDBUF)","");
|
|
} else if (val < 60*1024) {
|
|
val = 60*1024;
|
|
if (0 > setsockopt(soc,SOL_SOCKET,SO_SNDBUF,
|
|
&val, sizeof(val)))
|
|
log_errno("setsockopt(soc, SO_SNDBUF)","");
|
|
}
|
|
val = 1;
|
|
if (0 > setsockopt(soc, IPPROTO_TCP, TCP_NODELAY,
|
|
&val, sizeof(val))) {
|
|
log_errno("setsockopt(soc, TCP_NODELAY)","");
|
|
return 0;
|
|
}
|
|
|
|
|
|
if (0 == (spty_name = _getpty(&mpty,O_RDWR|O_NOCTTY,0600,1))) {
|
|
log_complain("", "_getpty() failed");
|
|
return 0;
|
|
}
|
|
|
|
if (0 > (spty = open(spty_name, O_RDWR|O_NOCTTY))) {
|
|
log_errno("spty open() ", spty_name);
|
|
(void)close(mpty);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* switch to the pty
|
|
*/
|
|
(void)strcpy(dp->line, spty_name);
|
|
(void)strcpy(dp->nodename,
|
|
&spty_name[!strncmp(spty_name,"/dev/",5) ? 5:0]);
|
|
if (!lockname(dp->nodename,dp->lockfile,
|
|
sizeof(dp->lockfile))) {
|
|
log_complain("","unable to make lock file name for %s",
|
|
dp->nodename);
|
|
return 0;
|
|
}
|
|
|
|
lf = fopen(dp->lockfile,"w");
|
|
if (!lf) {
|
|
log_errno("fopen(pty) lock file ", dp->lockfile);
|
|
return 0;
|
|
}
|
|
(void)fprintf(lf, LOCKPID_PAT, getpid());
|
|
stlock(dp->lockfile);
|
|
(void)fclose(lf);
|
|
|
|
dp->devfd = spty;
|
|
dp->sync = SYNC_OFF;
|
|
|
|
} else {
|
|
if (0 > fstat(dp->devfd, &dp->dev_sbuf)) {
|
|
dp->dev_sbuf.st_ino = 0;
|
|
log_errno("fstat(dev)","");
|
|
return 0;
|
|
}
|
|
|
|
/* default line type */
|
|
#if defined(SPLI_MAJOR) || defined(WSTY_MAJOR)
|
|
if (dp->sync == SYNC_DEFAULT
|
|
&& (major(dp->dev_sbuf.st_rdev) == SPLI_MAJOR
|
|
#ifdef WSTY_MAJOR
|
|
|| major(dp->dev_sbuf.st_rdev) == WSTY_MAJOR
|
|
#endif
|
|
))
|
|
dp->sync = SYNC_ON;
|
|
#endif
|
|
if (dp->sync == SYNC_DEFAULT) {
|
|
if (0 > ioctl(dp->devfd,I_LOOK,lname)) {
|
|
if (errno != EINVAL) {
|
|
log_errno("ioctl(I_LOOK)","");
|
|
return 0;
|
|
}
|
|
/* assume EINVAL indicates a MUX and that
|
|
* all MUXes are for sync devices. sigh.
|
|
*/
|
|
dp->sync = SYNC_ON;
|
|
} else if (strcmp("stty_ld", lname)) {
|
|
log_debug(1,"",
|
|
"no line discipline STREAMS module"
|
|
"--assume sync");
|
|
dp->sync = SYNC_ON;
|
|
} else {
|
|
dp->sync = SYNC_OFF;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dp->sync == SYNC_OFF) {
|
|
if (0 > ioctl(dp->devfd, TCGETA, (char*)&dp->devtio)) {
|
|
log_errno("set_tty_modes TCGETA","");
|
|
return 0;
|
|
}
|
|
|
|
#if defined(PPP_IRIX_62) || defined(PPP_IRIX_53)
|
|
#ifdef CNEW_RTSCTS
|
|
dp->devtio.c_cflag &= (CBAUD | CNEW_RTSCTS);
|
|
#else
|
|
dp->devtio.c_cflag &= CBAUD;
|
|
#endif
|
|
#else
|
|
#ifdef CNEW_RTSCTS
|
|
dp->devtio.c_cflag &= CNEW_RTSCTS;
|
|
#else
|
|
dp->devtio.c_cflag = 0;
|
|
#endif
|
|
#endif
|
|
dp->devtio.c_cflag |= CS8|CREAD|HUPCL;
|
|
if (dp->xon_xoff)
|
|
dp->devtio.c_iflag = IGNPAR|IGNBRK | IXON|IXOFF;
|
|
else
|
|
dp->devtio.c_iflag = IGNPAR|IGNBRK;
|
|
dp->devtio.c_oflag = 0;
|
|
dp->devtio.c_lflag = 0;
|
|
dp->devtio.c_cc[VMIN] = 0;
|
|
dp->devtio.c_cc[VTIME] = 0;
|
|
if (0 > ioctl(dp->devfd, TCSETAW, (char*)&dp->devtio)) {
|
|
log_errno("TCSETAW","");
|
|
return 0;
|
|
}
|
|
|
|
/* after the pty is ready, start using it
|
|
*/
|
|
if (mpty >= 0) {
|
|
dp->pty_pid = fork();
|
|
if (0 > dp->pty_pid) {
|
|
log_errno("pty fork()","");
|
|
return 0;
|
|
}
|
|
if (dp->pty_pid == 0)
|
|
pty_relay(dp, soc, mpty);
|
|
|
|
log_debug(1,"","pty pid=%d", dp->pty_pid);
|
|
dp->dev_sbuf.st_ino = 0;
|
|
(void)close(soc);
|
|
(void)close(mpty);
|
|
}
|
|
|
|
} else {
|
|
/* Use non-blocking I/O on sync channels, since it is
|
|
* better to drop output packets than to wait forever for
|
|
* the ISDN daemon to work. Blocking forever in putmsg()
|
|
* causes lots of havoc.
|
|
*/
|
|
fl = fcntl(dp->devfd, F_GETFL, 0);
|
|
if (fl == -1) {
|
|
log_errno("fcntl(F_GETFL)","");
|
|
} else if (0 > fcntl(dp->devfd, F_SETFL, fl | FNDELAY)) {
|
|
log_errno("fcntl(F_SETFL, | FNDELAY)","");
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* get a tty ready
|
|
*/
|
|
int /* 0=failed */
|
|
rdy_tty_dev(struct dev *dp)
|
|
{
|
|
char lname[FMNAMESZ+1];
|
|
int i;
|
|
|
|
if (!set_tty_modes(dp))
|
|
return 0;
|
|
|
|
if (dp->dev_sbuf.st_ino != 0) {
|
|
if (dp->dev_sbuf.st_uid != 0
|
|
&& 0 > chown(dp->line, 0,0))
|
|
log_errno("chown(dev) ", dp->line);
|
|
if (0 > chmod(dp->line, S_IREAD|S_IWRITE))
|
|
log_errno("chmod(dev) ", dp->line);
|
|
}
|
|
|
|
/* strip line discipline */
|
|
if (dp->sync == SYNC_OFF) {
|
|
if (0 > ioctl(dp->devfd,I_LOOK,lname)) {
|
|
log_errno("ioctl(I_LOOK) ","");
|
|
strcpy(lname,"???");
|
|
}
|
|
if (0 > ioctl(dp->devfd,I_POP,0))
|
|
log_errno("ioctl(I_POP) ",lname);
|
|
if (0 > ioctl(dp->devfd, TCFLSH, 2))
|
|
log_errno("TCFLSH-2","");
|
|
}
|
|
|
|
/* install custom STREAMS modules
|
|
*/
|
|
for (i = 0; i < num_smods; i++) {
|
|
if (0 > ioctl(dp->devfd, I_PUSH, smods[i]))
|
|
bad_errno("ioctl(I_PUSH) ", smods[i]);
|
|
log_debug(6,"","ioctl(I_PUSH,%s) ok", smods[i]);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* set IP parameters
|
|
*/
|
|
void
|
|
set_ip(void)
|
|
{
|
|
int soc;
|
|
struct ifreq ifr;
|
|
|
|
soc = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (soc < 0)
|
|
bad_errno("set_ip() socket()","");
|
|
|
|
if (netmask.sin_addr.s_addr == 0) {
|
|
if (IN_CLASSA(remhost.sin_addr.s_addr))
|
|
netmask.sin_addr.s_addr = htonl(IN_CLASSA_NET);
|
|
else if (IN_CLASSB(remhost.sin_addr.s_addr))
|
|
netmask.sin_addr.s_addr = htonl(IN_CLASSB_NET);
|
|
else
|
|
netmask.sin_addr.s_addr = htonl(IN_CLASSC_NET);
|
|
}
|
|
(void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
|
ifr.ifr_addr = *(struct sockaddr *)&netmask;
|
|
#ifdef _HAVE_SIN_LEN
|
|
netmask.sin_len = _SIN_ADDR_SIZE;
|
|
#endif
|
|
netmask.sin_family = AF_INET;
|
|
if (0 > ioctl(soc, SIOCSIFNETMASK, (caddr_t)&ifr))
|
|
bad_errno("SIOCSIFNETMASK","");
|
|
|
|
if (metric != 0) {
|
|
(void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
|
ifr.ifr_metric = metric;
|
|
if (0 > ioctl(soc, SIOCSIFMETRIC, (char*)&ifr))
|
|
bad_errno("SIOCSIFMETRIC","");
|
|
}
|
|
|
|
(void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
|
ifr.ifr_addr = *(struct sockaddr *)&remhost;
|
|
if (0 > ioctl(soc, SIOCSIFDSTADDR, (char*)&ifr))
|
|
bad_errno("SIOCSIFDSTADDR","");
|
|
|
|
(void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
|
ifr.ifr_addr = *(struct sockaddr *)&lochost;
|
|
if (0 > ioctl(soc, SIOCSIFADDR, (char*)&ifr))
|
|
bad_errno("SIOCSIFADDR","");
|
|
|
|
(void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
|
if (0 > ioctl(soc, SIOCGIFFLAGS, (char*)&ifr))
|
|
bad_errno("SIOCGIFFLAGS","");
|
|
if (debug > 2) {
|
|
ifr.ifr_flags |= IFF_DEBUG;
|
|
} else {
|
|
ifr.ifr_flags &= ~IFF_DEBUG;
|
|
}
|
|
ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
|
|
(void)strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
|
|
if (0 > ioctl(soc, SIOCSIFFLAGS, (char*)&ifr))
|
|
bad_errno("SIOCSIFFLAGS","");
|
|
|
|
(void)close(soc);
|
|
}
|
|
|
|
|
|
|
|
/* create directory for rendezvous
|
|
*/
|
|
static int /* 0=failed */
|
|
rend_mkdir(void)
|
|
{
|
|
char rm_cmd[sizeof("/bin/rm -rf ")+RENDPATH_LEN];
|
|
struct stat sbuf;
|
|
|
|
for (;;) {
|
|
if (0 > lstat(RENDDIR, &sbuf)) {
|
|
if (errno != ENOENT)
|
|
bad_errno("lstat() ", RENDDIR);
|
|
if (0 > mkdir(RENDDIR, 0755)
|
|
&& errno != EEXIST) {
|
|
log_errno("mkdir() ", RENDDIR);
|
|
return 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (S_ISDIR(sbuf.st_mode)
|
|
&& sbuf.st_uid == 0
|
|
&& (sbuf.st_mode & (S_IWGRP | S_IWOTH)) == 0)
|
|
break;
|
|
|
|
log_complain("","stray %s", RENDDIR);
|
|
(void)sprintf(rm_cmd, "/bin/rm -rf %s", RENDDIR);
|
|
(void)system(rm_cmd);
|
|
if (0 <= lstat(RENDDIR, &sbuf)) {
|
|
log_complain("", "failed to `%s`", rm_cmd);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
/* define another rendezvous
|
|
*/
|
|
int /* 0=too many names */
|
|
add_rend_name(char *prefix, /* "" or "ep-" */
|
|
char *str) /* hostname, MAC address, etc */
|
|
{
|
|
int i;
|
|
struct rend *rp;
|
|
char path[RENDPATH_LEN];
|
|
|
|
if (!rend_mkdir())
|
|
return 0;
|
|
|
|
(void)sprintf(path, RENDPATH_PAT, prefix, str);
|
|
for (rp = rends, i = 0; i < MAX_RENDPATHS; i++, rp++) {
|
|
/* add the path to the list if it is new
|
|
*/
|
|
if (rp->path[0] == '\0') {
|
|
(void)strcpy(rp->path, path);
|
|
rp->made = 0;
|
|
rp->st.st_uid = -1;
|
|
num_rends++;
|
|
return 1;
|
|
}
|
|
|
|
/* quit if the path is already known
|
|
*/
|
|
if (!strcmp(path,rp->path))
|
|
return 1;
|
|
}
|
|
|
|
log_complain("","too many rendezvous names with \"%s\"", str);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* mark all rendezvous names removed so that they will not be removed.
|
|
*/
|
|
void
|
|
unmade_rend(void)
|
|
{
|
|
int i;
|
|
struct rend *rp;
|
|
|
|
for (rp = rends, i = 0; i < MAX_RENDPATHS; i++, rp++) {
|
|
rp->made = 0;
|
|
rp->st.st_uid = -1;
|
|
}
|
|
status_poke_path[0] = '\0';
|
|
status_path[0] = '\0';
|
|
}
|
|
|
|
|
|
/* remove one or all rendezvous file names
|
|
*/
|
|
void
|
|
rm_rend(char *prefix, /* "" or "ep-" */
|
|
char *str) /* hostname, MAC address, etc */
|
|
{
|
|
int i;
|
|
struct rend *rp;
|
|
char path[RENDPATH_LEN];
|
|
|
|
if (str)
|
|
(void)sprintf(path, RENDPATH_PAT, prefix, str);
|
|
for (rp = rends, i = 0; i < MAX_RENDPATHS; i++, rp++) {
|
|
if (rp->path[0] == '\0')
|
|
continue;
|
|
if (rp->made && (!str || !strcmp(path, rp->path))) {
|
|
(void)unlink(rp->path);
|
|
rp->made = 0;
|
|
rp->st.st_uid = -1;
|
|
num_rends--;
|
|
if (str) {
|
|
rp->path[0] = '\0';
|
|
return;
|
|
}
|
|
rp->path[0] = '\0';
|
|
}
|
|
}
|
|
if (str) {
|
|
(void)unlink(path);
|
|
return;
|
|
}
|
|
if (status_poke_path[0] != '\0') {
|
|
(void)unlink(status_poke_path);
|
|
status_poke_path[0] = '\0';
|
|
}
|
|
if (status_path[0] != '\0') {
|
|
(void)unlink(status_poke_path);
|
|
status_path[0] = '\0';
|
|
}
|
|
}
|
|
|
|
|
|
/* Create the rendezvous FIFO, if it does not already exist.
|
|
* Must only be called after link_fifo() so that rp->st.st_uid is valid.
|
|
*/
|
|
static void
|
|
make_fifo(int tgt) /* this name */
|
|
{
|
|
struct rend *rp = &rends[tgt];
|
|
|
|
|
|
if (rp->st.st_uid == 0) {
|
|
if (0 <= utime(rp->path, 0))
|
|
return;
|
|
if (errno != ENOENT)
|
|
bad_errno("lstat() ", rp->path);
|
|
log_complain("","%s disappeared",rp->path);
|
|
}
|
|
|
|
(void)unlink(rp->path);
|
|
rp->st.st_uid = -1;
|
|
if (!rend_mkdir())
|
|
return;
|
|
if (0 > mknod(rp->path, S_IFIFO|S_IRUSR|S_IWUSR,0)) {
|
|
log_errno("mknod() ", rp->path);
|
|
rendnode = -1;
|
|
} else {
|
|
rp->made = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* Check that the rendezvous FIFO exits
|
|
* Close its file descriptor if it is wrong.
|
|
* Create its links if ok.
|
|
*/
|
|
static int /* return good name index */
|
|
link_fifo(int mode) /* 0=create nothing,
|
|
* 1=create only if rendnode ok
|
|
* 2=create all */
|
|
{
|
|
static nlink_t complain_num_rends;
|
|
struct rend *rp, *good_rp;
|
|
int i, good, bad;
|
|
char *p1, *p2;
|
|
struct stat sfd;
|
|
|
|
|
|
/* Check all of the names to see that they are the same inode.
|
|
* If not, remove the oldest.
|
|
*/
|
|
good_rp = &rends[good = 0];
|
|
for (rp = rends, i = 0; i < MAX_RENDPATHS; i++, rp++) {
|
|
if (rp->path[0] == '\0')
|
|
continue;
|
|
|
|
if (0 > lstat(rp->path, &rp->st)) {
|
|
/* bad node if non-existent or cannot stat() it */
|
|
if (errno != ENOENT)
|
|
bad_errno("lstat() ", rp->path);
|
|
if (rp->made)
|
|
log_complain("","%s disappeared",rp->path);
|
|
rp->made = 0;
|
|
rp->st.st_uid = -1;
|
|
continue;
|
|
}
|
|
|
|
if (!S_ISFIFO(rp->st.st_mode)
|
|
|| rp->st.st_uid != 0) {
|
|
/* Bad node if not one of our FIFOs.
|
|
*/
|
|
log_complain("","stray %s", rp->path);
|
|
(void)unlink(rp->path);
|
|
rp->made = 0;
|
|
rp->st.st_uid = -1;
|
|
continue;
|
|
}
|
|
|
|
/* note the first good name
|
|
*/
|
|
while (good < i
|
|
&& rends[good].st.st_uid != 0)
|
|
good_rp = &rends[++good];
|
|
|
|
/* bad if not the same as the other FIFOs
|
|
*/
|
|
if (good != i
|
|
&& (good_rp->st.st_dev != rp->st.st_dev
|
|
|| good_rp->st.st_ino != rp->st.st_ino)) {
|
|
p1 = good_rp->path;
|
|
p2 = rp->path;
|
|
if (good_rp->st.st_mtime >= rp->st.st_mtime) {
|
|
bad = i;
|
|
} else {
|
|
bad = good;
|
|
good_rp = &rends[good = i];
|
|
}
|
|
if (mode != 0)
|
|
log_complain("","conflicting %s and %s;"
|
|
" unlink %s",
|
|
p1, p2, rends[bad].path);
|
|
rends[bad].made = 0;
|
|
rends[bad].st.st_uid = -1;
|
|
}
|
|
}
|
|
|
|
/* If no good path exists, then start creating with the first one.
|
|
*/
|
|
if (good_rp->st.st_uid != 0) {
|
|
good_rp = &rends[good = 0];
|
|
(void)close(rendnode);
|
|
rendnode = -1;
|
|
|
|
} else if (rendnode >= 0) {
|
|
if (0 > fstat(rendnode,&sfd)) {
|
|
log_errno("rendezvous fstat() ","rendnode");
|
|
(void)close(rendnode);
|
|
rendnode = -1;
|
|
} else if (good_rp->st.st_dev != sfd.st_dev
|
|
|| good_rp->st.st_ino != sfd.st_ino) {
|
|
log_complain("","rendezvous %s replaced",
|
|
good_rp->path);
|
|
(void)close(rendnode);
|
|
rendnode = -1;
|
|
}
|
|
}
|
|
|
|
/* if only checking (and so do not have the lock), quit now.
|
|
*/
|
|
if (mode == 0 || (mode == 1 && rendnode < 0))
|
|
return (good_rp->st.st_uid != 0) ? -1 : good;
|
|
|
|
/* create the node and the links
|
|
*/
|
|
make_fifo(good);
|
|
for (rp = rends, i = 0; i < MAX_RENDPATHS; i++, rp++) {
|
|
if (rp->path[0] == '\0')
|
|
continue;
|
|
|
|
rp->made = 1;
|
|
if (i != good && rp->st.st_uid != 0) {
|
|
(void)unlink(rp->path);
|
|
if (0 > link(good_rp->path,rp->path)) {
|
|
log_errno("link() ", rp->path);
|
|
rendnode = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for stray links
|
|
*/
|
|
if (0 > stat(good_rp->path, &good_rp->st)) {
|
|
log_errno("stat() ", good_rp->path);
|
|
rendnode = -1;
|
|
}
|
|
if (good_rp->st.st_nlink != num_rends
|
|
&& good_rp->st.st_nlink != complain_num_rends) {
|
|
log_debug(1,"","%s has %d instead of %d links",
|
|
good_rp->path, good_rp->st.st_nlink, num_rends);
|
|
complain_num_rends = good_rp->st.st_nlink;
|
|
}
|
|
return good;
|
|
}
|
|
|
|
|
|
/* create the rendezvous FIFO if necessary
|
|
* The IP address of the remote host must be known.
|
|
*/
|
|
int /* 1=need to pass on FD */
|
|
make_rend(int make_nodes) /* 1=names are known so make nodes */
|
|
{
|
|
# define REND_LOCK "pputil"
|
|
# define REND_LOCK2 (LOCKPRE "." REND_LOCK)
|
|
int good, i;
|
|
struct rend *good_rp;
|
|
|
|
|
|
if (num_rends == 0) {
|
|
log_debug(6,"","peer name and address unknown for rendezvous");
|
|
return 0;
|
|
}
|
|
|
|
/* If the FIFO already exists, then just check to see that it has
|
|
* not been deleted by an overeager /tmp cleaner.
|
|
*/
|
|
if (rendnode >= 0) {
|
|
(void)link_fifo(1);
|
|
if (rendnode >= 0)
|
|
return 0;
|
|
}
|
|
|
|
/* We do not have the FIFO open. Check to see if it exists
|
|
*/
|
|
good = link_fifo(0);
|
|
|
|
/* If we are not sure of the right names, do not create any nodes.
|
|
*/
|
|
if (!make_nodes) {
|
|
if (good < 0)
|
|
return 0;
|
|
good_rp = &rends[good];
|
|
/* if this succeeds, then a PPP process is already
|
|
* in charge of the link.
|
|
*/
|
|
rendnode = open(good_rp->path, O_WRONLY|O_NDELAY, 0);
|
|
return (rendnode >= 0);
|
|
}
|
|
|
|
/* Lock to mostly fix race during creation of the pipe.
|
|
* Try only for a while, and then just give up and hope
|
|
* for the best.
|
|
*/
|
|
for (i = 0; mmlock(REND_LOCK) && i < 5*4; i++) {
|
|
log_debug(2,"","waiting to make rendezvous node");
|
|
sginap(HZ/4);
|
|
}
|
|
|
|
/* Now that we have the lock, see if some other process
|
|
* was quicker and created the FIFO
|
|
*/
|
|
good = link_fifo(0);
|
|
if (good < 0)
|
|
good = 0;
|
|
good_rp = &rends[good];
|
|
|
|
/* Create a FIFO for this connection.
|
|
* If FIFO already exists, check to see the connection is
|
|
* already active. If so, poke the existing daemon and quit.
|
|
* Open an existing name if there is one to avoid creating
|
|
* links that the existing daemon might object to.
|
|
*/
|
|
make_fifo(good);
|
|
rendnode = open(good_rp->path, O_WRONLY|O_NDELAY, 0);
|
|
if (rendnode >= 0) {
|
|
/* No error means some other daemon is in charge.
|
|
* Give away the line or quit.
|
|
*/
|
|
rmlock(REND_LOCK2);
|
|
return 1;
|
|
}
|
|
|
|
if (errno != ENXIO)
|
|
bad_errno("open(O_WRONLY) ",good_rp->path);
|
|
/* ENXIO means we are in charge.
|
|
*
|
|
* Re-open the FIFO for reading and writing while keeping it open.
|
|
*/
|
|
i = rendnode;
|
|
rendnode = open(good_rp->path, O_RDWR | O_NDELAY, 0);
|
|
if (rendnode < 0)
|
|
bad_errno("open(O_RDWR) ", good_rp->path);
|
|
(void)close(i);
|
|
|
|
rmlock(REND_LOCK2);
|
|
|
|
/* check again, to resolve the race getting the lock.
|
|
* While we were waiting for the lock
|
|
*/
|
|
(void)link_fifo(2);
|
|
if (rendnode < 0) {
|
|
log_complain("", "failed to reliably create rendezvous");
|
|
unmade_rend();
|
|
cleanup(1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* common error exit */
|
|
void
|
|
cleanup(int code)
|
|
{
|
|
struct dev *dp;
|
|
pid_t pid;
|
|
int st;
|
|
|
|
for (dp = dp0; dp != 0; dp = dp->next)
|
|
rel_dev(dp);
|
|
|
|
rm_rend(0,0);
|
|
if (rendnode >= 0) {
|
|
(void)close(rendnode);
|
|
rendnode = -1;
|
|
}
|
|
if (modfd >= 0) {
|
|
(void)close(modfd);
|
|
modfd = -1;
|
|
}
|
|
|
|
/* delete added route */
|
|
if (del_route != 0)
|
|
call_system("", "del_route: ", del_route, 1);
|
|
|
|
/* optional ending script */
|
|
if (end_cmd != 0)
|
|
call_system("", "end_cmd: ", end_cmd, 1);
|
|
|
|
#ifdef PPP
|
|
/* Delete proxy-ARP table entry. */
|
|
proxy_unarp();
|
|
#endif /* PPP */
|
|
|
|
/* wait for children */
|
|
if ((pid = add_pid) > 0) {
|
|
add_pid = -1;
|
|
log_debug(7,"","kill pid %d", pid);
|
|
(void)kill(pid, SIGTERM);
|
|
(void)waitpid(pid,&st,0);
|
|
}
|
|
|
|
/* tell about any attempted calls */
|
|
if (dp0->acct.attempts
|
|
&& add_pid != 0)
|
|
ck_acct(dp0,1);
|
|
|
|
log_debug(1,"","exiting with %d", code);
|
|
|
|
stderrfd = -1;
|
|
stderrttyfd = -1;
|
|
closefds();
|
|
if (stderrpid > 0) {
|
|
(void)waitpid(stderrpid,&st,0);
|
|
stderrpid = -1;
|
|
}
|
|
exit(code);
|
|
}
|
|
|
|
|
|
/* get rid of signals */
|
|
void
|
|
no_signals(__sigret_t (*ksig)())
|
|
{
|
|
(void)alarm(0);
|
|
|
|
(void)signal(SIGHUP, SIG_IGN);
|
|
(void)signal(SIGINT, ksig);
|
|
(void)signal(SIGTERM, ksig);
|
|
(void)signal(SIGPIPE, ksig);
|
|
(void)signal(SIGUSR1, SIG_IGN);
|
|
(void)signal(SIGUSR2, SIG_IGN);
|
|
(void)signal(SIGCHLD, SIG_DFL);
|
|
}
|
|
|
|
|
|
/* (probably) killed by resident daemon */
|
|
void
|
|
killed(int code)
|
|
|
|
{
|
|
log_debug(1,"","killed by signal %d", code);
|
|
exit(code);
|
|
}
|
|
|
|
|
|
/* clear accounting
|
|
* get_clk() must have been called recently.
|
|
*/
|
|
void
|
|
clr_acct(struct dev *dp)
|
|
{
|
|
dp->acct.last_date = cur_date.tv_sec;
|
|
dp->acct.last_secs = clk.tv_sec;
|
|
dp->acct.call_start = 0;
|
|
dp->acct.calls = 0;
|
|
dp->acct.attempts = 0;
|
|
dp->acct.failures = 0;
|
|
dp->acct.answered = 0;
|
|
dp->acct.min_setup = MAXINT;
|
|
dp->acct.max_setup = 0;
|
|
dp->acct.setup = 0;
|
|
if (dp->acct.sconn == 0)
|
|
dp->acct.sconn = clk.tv_sec;
|
|
dp->acct.orig_conn = 0;
|
|
dp->acct.orig_idle = 0;
|
|
dp->acct.ans_conn = 0;
|
|
dp->acct.ans_idle = 0;
|
|
}
|
|
|
|
|
|
static char*
|
|
hms(int secs)
|
|
{
|
|
# define NUM_BUFS 10
|
|
static int n;
|
|
static struct {
|
|
char c[11]; /* hhhh:mm:ss */
|
|
} bufs[NUM_BUFS];
|
|
char *p;
|
|
|
|
p = bufs[n].c;
|
|
if (++n >= NUM_BUFS)
|
|
n = 0;
|
|
if (secs < 0)
|
|
secs = 0;
|
|
sprintf(p, "%d:%02d:%02d",
|
|
(secs/(60*60)) % 10000, (secs/60) % 60, secs % 60);
|
|
return p;
|
|
}
|
|
|
|
|
|
/* account for phone line use
|
|
*/
|
|
void
|
|
ck_acct(struct dev *dp,
|
|
int force) /* 1=do it anyway */
|
|
{
|
|
static char tbuf[] = "dayweek month dd hh:mm:ss ";
|
|
static char tpat[] = "%a %b %e %T";
|
|
struct tm lt;
|
|
|
|
|
|
if (!demand_dial && (!force || !camping))
|
|
return;
|
|
|
|
get_clk();
|
|
|
|
/* Report for the log once a day, after midnight and before 3 am.
|
|
* This minimizes problems with daylight savings time.
|
|
*/
|
|
if (!force) {
|
|
if (cur_date.tv_sec < dp->acct.next_date)
|
|
return;
|
|
lt = *localtime(&cur_date.tv_sec);
|
|
if (lt.tm_hour >= 3)
|
|
return;
|
|
}
|
|
|
|
syslog(LOG_INFO, "%s: %d answered, %d links originated,"
|
|
" %d calls dialed, %d gave up link",
|
|
remote,
|
|
dp->acct.answered,
|
|
dp->acct.calls, dp->acct.attempts, dp->acct.failures);
|
|
if (dp->acct.calls != 0)
|
|
syslog(LOG_INFO, "%s: %d/%d/%d min/avg/max seconds call setup",
|
|
remote,
|
|
dp->acct.min_setup,
|
|
dp->acct.setup/dp->acct.calls,
|
|
dp->acct.max_setup);
|
|
(void)cftime(tbuf,tpat,&dp->acct.last_date);
|
|
syslog(LOG_INFO,"%s: calling %s,idle %s; answering %s,idle %s;"
|
|
" disconncted %s since %s",
|
|
remote,
|
|
hms(dp->acct.orig_conn), hms(dp->acct.orig_idle),
|
|
hms(dp->acct.ans_conn), hms(dp->acct.ans_idle),
|
|
hms(dp->acct.sconn - dp->acct.last_secs
|
|
- dp->acct.orig_conn-dp->acct.ans_conn),
|
|
tbuf);
|
|
|
|
/* if not forcing things, reset the counters */
|
|
if (!force) {
|
|
clr_acct(dp);
|
|
lt.tm_sec = 1;
|
|
lt.tm_min = 1;
|
|
lt.tm_hour = 0;
|
|
dp->acct.next_date = mktime(<) + 24*60*60;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* get rid of stray controlling TTY
|
|
* We do not want control characters to give us signals.
|
|
*/
|
|
static void
|
|
stray_ctty(void)
|
|
{
|
|
register int i;
|
|
|
|
if (ctty <= 0) {
|
|
i = open("/dev/tty", O_RDWR|O_NDELAY);
|
|
if (i >= 0) {
|
|
ioctl(i, TIOCNOTTY, (char *)0);
|
|
(void)close(i);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/* stop being interactive
|
|
*/
|
|
void
|
|
no_interact(void)
|
|
{
|
|
interact = 0;
|
|
stderrfd = -1;
|
|
stderrttyfd = -1;
|
|
|
|
/* If not interactive, avoid controlling tty to avoid stray
|
|
* signals from the keyboard.
|
|
*/
|
|
ctty = 0;
|
|
stray_ctty();
|
|
|
|
(void)setsid();
|
|
|
|
(void)chdir("/");
|
|
}
|
|
|
|
|
|
|
|
/* see if a file descriptor is important
|
|
*/
|
|
static int /* 1=save it */
|
|
savefd(int fd)
|
|
{
|
|
struct dev *dp;
|
|
|
|
if (fd < 0)
|
|
return 1;
|
|
|
|
if (fd == stderrfd || fd == stderrttyfd
|
|
|| fd == modfd || fd == rendnode
|
|
|| fd == status_poke_fd)
|
|
return 1;
|
|
|
|
for (dp = dp0; dp != 0; dp = dp->next) {
|
|
if (fd == dp->devfd || fd == dp->devfd_save)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* garbage collect stray file descriptors from UUCP and friends
|
|
*/
|
|
void
|
|
closefds(void)
|
|
{
|
|
extern void _yp_unbind_all(void);
|
|
extern void _res_close(void);
|
|
int i;
|
|
|
|
stray_ctty();
|
|
|
|
/* Close all stray files and tell YP and DNS.
|
|
* The UUCP code likes to leave things open.
|
|
*/
|
|
for (i = getdtablehi(); --i > 2; ) {
|
|
if (!savefd(i))
|
|
(void)close(i);
|
|
}
|
|
_yp_unbind_all(); /* tell NIS its FD is dead */
|
|
_res_close(); /* tell resolver its FD is dead */
|
|
closelog();
|
|
|
|
i = open(_PATH_DEVNULL, O_RDWR, 0);
|
|
if (i >= 0) {
|
|
if (!savefd(0))
|
|
(void)dup2(i, 0);
|
|
if (!savefd(1))
|
|
(void)dup2(i, 1);
|
|
if (!savefd(2))
|
|
(void)dup2(i, 2);
|
|
if (i > 2)
|
|
(void)close(i);
|
|
}
|
|
|
|
openlog(pgmname, LOG_PID | LOG_ODELAY | LOG_NOWAIT, LOG_DAEMON);
|
|
}
|
|
|
|
|
|
char *
|
|
ascii_str(u_char *s,
|
|
int len)
|
|
{
|
|
static char buf[200];
|
|
char *p;
|
|
u_char *s2, c;
|
|
|
|
for (p = buf; len > 0 && p < &buf[sizeof(buf)-1]; len--) {
|
|
c = *s++;
|
|
if (c == '\0') {
|
|
for (s2 = s+1; s2 < &s[len]; s2++) {
|
|
if (*s2 != '\0')
|
|
break;
|
|
}
|
|
if (s2 >= &s[len])
|
|
break;
|
|
}
|
|
|
|
if (c >= ' ' && c < 0x7f && c != '\\') {
|
|
*p++ = c;
|
|
continue;
|
|
}
|
|
*p++ = '\\';
|
|
switch (c) {
|
|
case '\\':
|
|
*p++ = '\\';
|
|
break;
|
|
case '\n':
|
|
*p++= 'n';
|
|
break;
|
|
case '\r':
|
|
*p++= 'r';
|
|
break;
|
|
case '\t':
|
|
*p++ = 't';
|
|
break;
|
|
case '\b':
|
|
*p++ = 'b';
|
|
break;
|
|
default:
|
|
p += sprintf(p,"%o",c);
|
|
break;
|
|
}
|
|
}
|
|
*p = '\0';
|
|
return buf;
|
|
}
|
|
|
|
|
|
void
|
|
log_errno(char *s1, char *s2)
|
|
{
|
|
int serrno = errno;
|
|
|
|
kludge_stderr();
|
|
(void)fprintf(stderr, "%s: %s%s: %s\n",
|
|
remote, s1,s2, strerror(serrno));
|
|
(void)fflush(stderr);
|
|
errno = serrno;
|
|
}
|
|
|
|
|
|
void
|
|
bad_errno(char *s1, char *s2)
|
|
{
|
|
log_errno(s1,s2);
|
|
#ifdef DEBUG
|
|
(void)fflush(stderr);
|
|
abort();
|
|
#endif
|
|
cleanup(1);
|
|
}
|
|
|
|
|
|
void
|
|
log_debug(int lvl, char* name, char *p, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (debug < lvl)
|
|
return;
|
|
|
|
kludge_stderr();
|
|
|
|
va_start(args, p);
|
|
(void)fprintf(stderr, "%s%s: ", remote,name);
|
|
(void)vfprintf(stderr, p, args);
|
|
(void)putc('\n',stderr);
|
|
(void)fflush(stderr);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
void
|
|
log_cd(int sw, int lvl, char* name, char *p, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (!sw && debug < lvl)
|
|
return;
|
|
|
|
va_start(args, p);
|
|
kludge_stderr();
|
|
(void)fprintf(stderr, "%s%s: ", remote,name);
|
|
(void)vfprintf(stderr, p, args);
|
|
(void)putc('\n',stderr);
|
|
(void)fflush(stderr);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
void
|
|
log_complain(char* name, char *p, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, p);
|
|
kludge_stderr();
|
|
(void)fprintf(stderr, "%s%s: ", remote,name);
|
|
(void)vfprintf(stderr, p, args);
|
|
(void)putc('\n',stderr);
|
|
(void)fflush(stderr);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
/* Touch the devices to make them appear active to `w`.
|
|
*/
|
|
void
|
|
act_devs(int weak, /* 0=TCP/IP active, 1=IP active, 2=? */
|
|
int touch) /* 1=set the mtime on the device */
|
|
{
|
|
struct dev *dp;
|
|
time_t t0, t, b, d;
|
|
|
|
t0 = clk.tv_sec + ((weak >= 1) ? sactime : lactime);
|
|
for (dp = dp0; dp != 0; dp = dp->next) {
|
|
if (dp->line[0] == '\0')
|
|
continue;
|
|
t = t0;
|
|
b = dp->acct.toll_bound;
|
|
if (b > 0
|
|
&& dp->acct.call_start != 0) {
|
|
/* Round up connect-time to close to a multiple
|
|
* of the boundary. Do not get too close to
|
|
* the boundary to avoid paying for an extra
|
|
* minute.
|
|
*/
|
|
d = b - ((t0 - dp->acct.call_start) % b);
|
|
if (d > 1 && d < b-1)
|
|
t += d;
|
|
}
|
|
if (weak <= 1)
|
|
dp->active = t;
|
|
else if (dp->active < t)
|
|
dp->active = t;
|
|
|
|
if (touch
|
|
&& dp->touched+HEARTBEAT < get_clk()) {
|
|
if (0 > utime(dp->line, 0))
|
|
log_errno("touch() ", dp->line);
|
|
dp->touched = clk.tv_sec;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* finish with the device
|
|
*/
|
|
void
|
|
rel_dev(struct dev *dp)
|
|
{
|
|
int st;
|
|
|
|
if (modfd >= 0
|
|
&& dp->dev_index != -1) {
|
|
if (0 > ioctl(modfd,I_UNLINK,dp->dev_index)) /* unlink MUX */
|
|
log_errno("I_UNLINK","");
|
|
dp->devfd = dp->devfd_save;
|
|
}
|
|
dp->dev_index = -1;
|
|
dp->devfd_save = -1;
|
|
dp->devfd = -1;
|
|
|
|
/* restore the owner and permissions of the device
|
|
*/
|
|
if (dp->dev_sbuf.st_ino != 0) {
|
|
if (dp->dev_sbuf.st_uid != 0
|
|
&& 0 > chown(dp->line, dp->dev_sbuf.st_uid,
|
|
dp->dev_sbuf.st_gid))
|
|
log_errno("chown(restore) ", dp->line);
|
|
if (0 > chmod(dp->line, dp->dev_sbuf.st_mode))
|
|
log_errno("chmod(restore) ", dp->line);
|
|
dp->dev_sbuf.st_ino = 0;
|
|
}
|
|
|
|
dp->line[0] = '\0';
|
|
dp->nodename[0] = '\0';
|
|
closefds();
|
|
|
|
dologout(dp);
|
|
if (dp->lockfile[0] != '\0') {
|
|
rmlock(dp->lockfile);
|
|
dp->lockfile[0] = '\0';
|
|
}
|
|
|
|
/* get rid of rendezvousing process */
|
|
if (dp->rendpid > 0) {
|
|
if (0 > kill(dp->rendpid, SIGINT)
|
|
&& (errno != ESRCH || debug >= 1))
|
|
log_errno("kill(rel_dev rendezvous)","");
|
|
(void)waitpid(dp->rendpid,&st,0);
|
|
}
|
|
dp->rendpid = -1;
|
|
|
|
if (dp->pty_pid > 0) {
|
|
if (0 > kill(dp->pty_pid, SIGPIPE)
|
|
&& errno != ESRCH)
|
|
log_errno("kill(rel_dev pty_pid)","");
|
|
(void)waitpid(dp->pty_pid,&st,0);
|
|
dp->pty_pid = -1;
|
|
}
|
|
}
|
|
|
|
|
|
/* connect to the other side
|
|
*/
|
|
int /* 0=failed, 1=done, 2=rendezvous */
|
|
get_conn(struct dev *dp,
|
|
int rend_ok) /* 1=try to rendezvous */
|
|
{
|
|
char ebuf[40+2+80+1];
|
|
fd_set in;
|
|
int i, tries, delay;
|
|
struct timeval timer;
|
|
time_t start;
|
|
|
|
/* Try not to keep the modem busy in case the other end
|
|
* is trying to call us.
|
|
*/
|
|
conn_trycalls = 1;
|
|
/* reset the locking mechanisms
|
|
*/
|
|
lock_pid[0] = '\0';
|
|
Nlocks = 0;
|
|
|
|
get_clk();
|
|
start = clk.tv_sec;
|
|
if (Debug)
|
|
kludge_stderr();
|
|
|
|
tries = 0;
|
|
delay = 0;
|
|
for (;;) {
|
|
tries++;
|
|
dp->acct.attempts++;
|
|
|
|
/* Use the UUCP dialing code. */
|
|
dp->sync = dp->conf_sync;
|
|
setservice(pgmname);
|
|
dp->acct.call_start = clk.tv_sec;
|
|
dp->devfd = conn(dp->uucp_nam);
|
|
alarm(0); /* in case UUCP forgets */
|
|
(void)signal(SIGALRM, SIG_IGN);
|
|
|
|
if (dp->devfd != FAIL) {
|
|
(void)strncpy(dp->nodename,Dc,sizeof(dp->nodename));
|
|
(void)sprintf(dp->line,"/dev/%s",dp->nodename);
|
|
if (!lockname(dp->nodename,dp->lockfile,
|
|
sizeof(dp->lockfile))) {
|
|
log_complain("",
|
|
"unable to make lock file name"
|
|
" for %s",
|
|
dp->nodename);
|
|
}
|
|
closefds();
|
|
get_clk();
|
|
dp->acct.sconn = clk.tv_sec;
|
|
i = dp->acct.sconn - start;
|
|
dp->acct.setup += i;
|
|
if (i < dp->acct.min_setup
|
|
|| dp->acct.min_setup == 0)
|
|
dp->acct.min_setup = i;
|
|
if (i > dp->acct.max_setup)
|
|
dp->acct.max_setup = i;
|
|
dp->acct.calls++;
|
|
return 1;
|
|
}
|
|
|
|
dp->devfd = -1;
|
|
rel_dev(dp);
|
|
|
|
sprintf(ebuf, ((Uerror == SS_CHAT_FAILED
|
|
&& AbortSeen < Aborts)
|
|
? "%.40s--%.80s" : "%.40s"),
|
|
UERRORTEXT, AbortStr[AbortSeen]);
|
|
|
|
/* Give up if not trying hard and not interested in retrying,
|
|
*/
|
|
if (!camping && !demand_dial) {
|
|
log_complain("","fatal error %s", ebuf);
|
|
dp->acct.failures++;
|
|
return 0;
|
|
}
|
|
/* or if the cause of failure is strange.
|
|
*/
|
|
if (Uerror != SS_DIAL_FAILED
|
|
&& Uerror != SS_LOGIN_FAILED
|
|
&& Uerror != SS_CANT_ACCESS_DEVICE
|
|
&& Uerror != SS_CHAT_FAILED
|
|
&& Uerror != SS_LOCKED_DEVICE) {
|
|
log_complain("","fatal error %s on try #%d",
|
|
ebuf, tries);
|
|
dp->acct.failures++;
|
|
if (dp->modpause > 0)
|
|
sginap(dp->modpause*HZ);
|
|
return 0;
|
|
}
|
|
|
|
/* Random backoff in case the other machine
|
|
* is trying to call us at the same time.
|
|
* Quick delay if the modem is busy, to let getty
|
|
* either answer a call or finish initializing the modem,
|
|
* but exponentially increasing delay otherwise.
|
|
*/
|
|
timer.tv_sec = ((dp->modwait > 0)
|
|
? dp->modwait
|
|
: DEFAULT_ASYNC_MODWAIT);
|
|
timer.tv_usec = 0;
|
|
if (Uerror != SS_LOCKED_DEVICE) {
|
|
i = rint(++delay * drand48());
|
|
timer.tv_sec <<= i;
|
|
}
|
|
log_debug(demand_dial ? 1 : 0,
|
|
"","%s on try #%d, back off %d seconds",
|
|
ebuf,tries,timer.tv_sec);
|
|
if (rendnode >= 0) {
|
|
FD_ZERO(&in);
|
|
FD_SET(rendnode, &in);
|
|
switch (select(rendnode+1, &in,0,0,&timer)) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
if (!rend_ok)
|
|
return 2;
|
|
if (rendezvous(1))
|
|
return 2;
|
|
break;
|
|
default:
|
|
if (errno != EINTR && errno != EAGAIN)
|
|
bad_errno("select(rendnode) "
|
|
"in get_conn()","");
|
|
break;
|
|
}
|
|
} else {
|
|
sginap(timer.tv_sec*HZ);
|
|
}
|
|
if (tries > dp->modtries) {
|
|
dp->acct.failures++;
|
|
if (dp->modpause > 0)
|
|
sginap(dp->modpause*HZ);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|