/*-------------------------------------------------------*/
/* in.ybbsd.c ( NTHU CS MapleBBS Ver 2.37 ) */
/*-------------------------------------------------------*/
/* target : super telnet server for BBS */
/* create : 95/03/29 */
/* update : 96/01/06 */
/*-------------------------------------------------------*/
/* syntax : (1) login as root */
/* (2) in.ybbsd [low] [high] [port] [bbsuser] [bbsuser2] */
/*-------------------------------------------------------*/
/* (1) standalone telnet daemon */
/* (2) CPU/system loading daemon */
/* (3) replace /bin/login */
/* (4) replace ~bbs/bin/bbsrf */
/*-------------------------------------------------------*/
/*
woju
*/
#define PASSWD "按Enter繼續..."
#define BBSUSER "bbs"
#define BBSUSER2 "bbs"
#define BAD_HOST "/home/bbs/etc/bad_host"
#include <pwd.h>
char bbsprog[80];
char *bbsuser2, *bbsuser, *bbshome, *bbsshell;
int bbsuid, bbsgid;
#define LOW_THRESHOLD 16
#define HIGH_THRESHOLD 19
#define MYPORT 23
#undef AUTO_JUMPER
/*
woju
#include "config.h"
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/telnet.h>
/*
woju
*/
#ifndef LINUX
#include <ttyent.h>
#endif
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <termios.h>
#include <netdb.h>
#include <syslog.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#ifdef STREAM_PTY
#include <sys/stropts.h>
#include "../lib/tiocpkt.h"
extern char *ptsname();
#define PTY_PKT_READ pty_pkt_read
#else
#define PTY_PKT_READ read
#endif
#ifndef O_RDWR
#include <sys/fcntl.h>
#endif
#ifndef FIONBIO
#include <sys/filio.h>
#endif
#ifndef FIONBIO
#include <sys/ioctl.h>
#endif
#if !defined(STREAM_PTY) && !defined(TIOCPKT)
#include <sys/pty.h>
#endif
#include <string.h>
#define bcopy(s,d,l) memcpy(d,s,l)
#ifdef __STDC__
#define puts xputs /* prototype conflict */
#endif
/* Ultrix syslog(3) has no facility stuff. */
#ifndef LOG_DAEMON
#define LOG_DAEMON 0
#define LOG_ODELAY 0
#endif
#ifdef ultrix
#define setsid() setpgrp(0,0)
#endif
#define OPT_NO 0 /* won't do this option */
#define OPT_YES 1 /* will do this option */
#define OPT_YES_BUT_ALWAYS_LOOK 2
#define OPT_NO_BUT_ALWAYS_LOOK 3
char hisopts[256];
char myopts[256];
char doopt[] = {IAC, DO, '%', 'c', 0};
char dont[] = {IAC, DONT, '%', 'c', 0};
char will[] = {IAC, WILL, '%', 'c', 0};
char wont[] = {IAC, WONT, '%', 'c', 0};
/* I/O data buffers, pointers, and counters. */
char maple[80];
char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
char netibuf[BUFSIZ], *netip = netibuf;
#define NIACCUM(c) { *netip++ = c; \
ncc++; \
}
char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
char *neturg = 0; /* one past last bye of urgent data */
/* the remote system seems to NOT be an old 4.2 */
int not42 = 1;
#ifdef HAVE_CHKLOAD
#define BANNER "\r\n【批踢踢實業坊】◎ 台大資訊附屬地下轉播站 ◎ (ptt.m8.ntu.edu.tw)\r\nLinux 2.0.0 (系統負載 %s) [140.112.171.204:%s]\r\n\n"
#else
#define BANNER "\r\n【批踢踢實業坊】◎ 台大資訊附屬地下轉播站 ◎ (ptt.m8.ntu.edu.tw)\r\nLinux 2.0.0 [140.112.171.204]\r\n\n"
#endif
#ifdef SUNOS5
#define BANNER "\r\n\r\nUNIX(r) System V Release 4.0 (%s)\r\n\r\r\n\r"
#endif
#ifdef SUNOS4
#define BANNER "\r\n\r\nSunOS UNIX (%s)\r\n\r\r\n\r"
#endif
#ifdef ultrix
#define BANNER "\r\n\r\nULTRIX V4.2A (Rev. 47) (%s)\r\n\r\r\n\r"
#endif
#ifdef HPUX /* uname -s hostname uname -r uname -v uname
* -m (tty) */
#define BANNER "\r\n\r\nHP-UX %s\r\n\r\r\n\r"
#endif
/* buffer for sub-options */
char subbuffer[128], *subpointer = subbuffer, *subend = subbuffer;
#define SB_CLEAR() subpointer = subbuffer;
#define SB_TERM() { subend = subpointer; SB_CLEAR(); }
#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
*subpointer++ = (c); \
}
#define SB_GET() ((*subpointer++)&0xff)
#define SB_EOF() (subpointer >= subend)
int pcc, ncc;
int pty, net;
#ifdef STREAM_PTY
int pts;
#endif
int inter;
#ifndef SYSV_ENV
extern char **environ;
#endif
extern int errno;
char *line;
char *terminaltype = 0;
int SYNCHing = 0; /* we are in TELNET SYNCH mode */
#ifdef HAVE_UTMP
int utmp_slot;
#endif
gid_t mygid;
/*
* The following are some clocks used to decide how to interpret the
* relationship between various variables.
*/
struct
{
int
system, /* what the current time is */
echotoggle, /* last time user entered echo character */
modenegotiated, /* last time operating mode negotiated */
didnetreceive, /* last time we read data from network */
ttypeopt, /* ttype will/won't received */
ttypesubopt, /* ttype subopt is received */
getterminal, /* time started to get terminal information */
gotDM; /* when did we last see a data mark */
} clocks;
#define settimer(x) (clocks.x = ++clocks.system)
#define sequenceIs(x,y) (clocks.x < clocks.y)
time_t time();
/* ----------------------------------------------------- */
/* FSA (finite state automata) for telnet protocol */
/* ----------------------------------------------------- */
void ptyflush();
void dooption();
void telrcv();
void
cleanup()
{
char *p;
p = line + sizeof("/dev/") - 1;
#ifdef SYSV_UTMP
UTMP_LOGOUT(p);
(void) chown(line, 0, 0);
(void) chmod(line, 0644);
#else /* SYSV_UTMP */
#ifdef HAVE_UTMP
logout(p);
#endif
(void) chmod(line, 0666);
(void) chown(line, 0, 0);
*p = 'p';
(void) chmod(line, 0666);
(void) chown(line, 0, 0);
#endif /* SYSV_UTMP */
shutdown(net, 2);
exit(0);
}
void
fatal(f, msg)
int f;
char *msg;
{
char buf[BUFSIZ];
(void) sprintf(buf, "bbsd: %s.\r\n", msg);
(void) write(f, buf, strlen(buf));
exit(1);
}
void
fatalperror(f, msg)
int f;
char *msg;
{
char buf[BUFSIZ];
#ifndef LINUX
extern const char *const sys_errlist[];
#endif
(void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
fatal(f, buf);
}
#if 0
mode(on, off)
int on, off;
{
struct sgttyb b;
#ifdef STREAM_PTY
int pty = pts; /* XXX apply ioctl()s at slave end */
#endif
ptyflush();
ioctl(pty, TIOCGETP, &b);
b.sg_flags |= on;
b.sg_flags &= ~off;
ioctl(pty, TIOCSETP, &b);
}
#else
void
telopt_echo(on)
int on;
{
struct termios b;
#ifdef STREAM_PTY
int pty = pts;
#endif
ptyflush();
tcgetattr(pty, &b);
if (on)
{
b.c_lflag |= ECHO;
}
else
{
b.c_lflag &= ~ECHO;
}
tcsetattr(pty, TCSANOW, &b);
}
void
telopt_binary(on)
int on;
{
struct termios b;
#ifdef STREAM_PTY
int pty = pts;
#endif
ptyflush();
tcgetattr(pty, &b);
if (on)
{
b.c_oflag &= ~OPOST;
}
else
{
b.c_oflag |= OPOST;
}
tcsetattr(pty, TCSANOW, &b);
}
#endif
/*
* Send interrupt to process on other side of pty. If it is in raw mode, just
* write NULL; otherwise, write intr char.
*/
void
interrupt()
{
struct termios b;
#ifdef STREAM_PTY
int pty = pts; /* XXX apply ioctl()s at slave end */
#endif
ptyflush(); /* half-hearted */
tcgetattr(pty, &b);
if ((b.c_lflag & ICANON) == 0)
{
*pfrontp++ = '\0';
return;
}
if (b.c_cc[VINTR] != 0377)
*pfrontp++ = b.c_cc[VINTR];
}
/*
* Send quit to process on other side of pty. If it is in raw mode, just
* write NULL; otherwise, write quit char.
*/
void
sendbrk()
{
struct termios b;
#ifdef STREAM_PTY
int pty = pts; /* XXX apply ioctl()s at slave end */
#endif
ptyflush(); /* half-hearted */
tcgetattr(pty, &b);
if ((b.c_lflag & ICANON) == 0)
{
*pfrontp++ = '\0';
return;
}
if (b.c_cc[VQUIT] != 0377)
*pfrontp++ = b.c_cc[VQUIT];
}
void
ptyflush()
{
register int n;
if ((n = pfrontp - pbackp) > 0)
n = write(pty, pbackp, n);
if (n >= 0)
{
pbackp += n;
if (pbackp == pfrontp)
pbackp = pfrontp = ptyobuf;
}
}
/*
* nextitem()
*
* Return the address of the next "item" in the TELNET data stream. This will
* be the address of the next character if the current address is a user data
* character, or it will be the address of the character following the TELNET
* command if the current address is a TELNET IAC ("I Am a Command")
* character.
*/
char *
nextitem(current)
char *current;
{
if ((*current & 0xff) != IAC)
{
return current + 1;
}
switch (*(current + 1) & 0xff)
{
case DO:
case DONT:
case WILL:
case WONT:
return current + 3;
case SB: /* loop forever looking for the SE */
{
register char *look = current + 2;
for (;;)
{
if ((*look++ & 0xff) == IAC)
{
if ((*look++ & 0xff) == SE)
{
return look;
}
}
}
}
default:
return current + 2;
}
}
/*
* netclear()
*
* We are about to do a TELNET SYNCH operation. Clear the path to the network.
*
* Things are a bit tricky since we may have sent the first byte or so of a
* previous TELNET command into the network. So, we have to scan the network
* buffer from the beginning until we are up to where we want to be.
*
* A side effect of what we do, just to keep things simple, is to clear the
* urgent data pointer. The principal caller should be setting the urgent
* data pointer AFTER calling us in any case.
*/
void
netclear()
{
register char *thisitem, *next;
char *good;
#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
thisitem = netobuf;
while ((next = nextitem(thisitem)) <= nbackp)
{
thisitem = next;
}
/* Now, thisitem is first before/at boundary. */
good = netobuf; /* where the good bytes go */
while (nfrontp > thisitem)
{
if (wewant(thisitem))
{
int length;
next = thisitem;
do
{
next = nextitem(next);
} while (wewant(next) && (nfrontp > next));
length = next - thisitem;
bcopy(thisitem, good, length);
good += length;
thisitem = next;
}
else
{
thisitem = nextitem(thisitem);
}
}
nbackp = netobuf;
nfrontp = good; /* next byte to be sent */
neturg = 0;
}
/*
* netflush Send as much data as possible to the network, handling requests
* for urgent data.
*/
void
netflush()
{
register int n;
if ((n = nfrontp - nbackp) > 0)
{
/*
* if no urgent data, or if the other side appears to be an old 4.2
* client (and thus unable to survive TCP urgent data), write the entire
* buffer in non-OOB mode.
*/
if ((neturg == 0) || (not42 == 0))
{
n = write(net, nbackp, n);/* normal write */
}
else
{
n = neturg - nbackp;
/*
* In 4.2 (and 4.3) systems, there is some question about what byte in
* a sendOOB operation is the "OOB" data. To make ourselves compatible,
* we only send ONE byte out of band, the one WE THINK should be OOB
* (though we really have more the TCP philosophy of urgent data rather
* than the Unix philosophy of OOB data).
*/
if (n > 1)
{
n = send(net, nbackp, n - 1, 0); /* send URGENT all by itself */
}
else
{
n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
}
}
}
if (n < 0)
{
#if 0
if (errno == EWOULDBLOCK)
return;
#endif
/* should blow this guy away... */
return;
}
nbackp += n;
if (nbackp >= neturg)
{
neturg = 0;
}
if (nbackp == nfrontp)
{
nbackp = nfrontp = netobuf;
}
}
#ifdef VERBOSE
char editedhost[32];
void
edithost(pat, host)
register char *pat;
register char *host;
{
register char *res = editedhost;
if (!pat)
pat = "";
while (*pat)
{
switch (*pat)
{
case '#':
if (*host)
host++;
break;
case '@':
if (*host)
*res++ = *host++;
break;
default:
*res++ = *pat;
break;
}
if (res == &editedhost[sizeof editedhost - 1])
{
*res = '\0';
return;
}
pat++;
}
if (*host)
strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
else
*res = '\0';
editedhost[sizeof editedhost - 1] = '\0';
}
static char *putlocation;
void
putchr(cc)
{
*putlocation++ = cc;
}
void
puts(s)
register char *s;
{
while (*s)
putchr(*s++);
}
void
putf(cp, where)
register char *cp;
char *where;
{
char *slash;
putlocation = where;
while (*cp)
{
if (*cp != '%')
{
putchr(*cp++);
continue;
}
switch (*++cp)
{
case 't':
slash = strrchr(line, '/');
if (slash == (char *) 0)
puts(line);
else
puts(&slash[1]);
break;
case 'h':
puts(editedhost);
break;
case 'd':
#if 0
get_date(datebuffer);
puts(datebuffer);
#else
puts("hi there");
#endif
break;
case '%':
putchr('%');
break;
}
cp++;
}
}
#endif /* VERBOSE */
void
willoption(option)
int option;
{
char *fmt;
switch (option)
{
case TELOPT_BINARY:
telopt_binary(1);
fmt = doopt;
break;
case TELOPT_ECHO:
not42 = 0; /* looks like a 4.2 system */
/*
* Now, in a 4.2 system, to break them out of ECHOing (to the terminal)
* mode, we need to send a "WILL ECHO". Kludge upon kludge!
*/
if (myopts[TELOPT_ECHO] == OPT_YES)
{
dooption(TELOPT_ECHO);
}
fmt = dont;
break;
case TELOPT_TTYPE:
settimer(ttypeopt);
if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK)
{
hisopts[TELOPT_TTYPE] = OPT_YES;
return;
}
fmt = doopt;
break;
case TELOPT_SGA:
fmt = doopt;
break;
case TELOPT_TM:
fmt = dont;
break;
default:
fmt = dont;
break;
}
if (fmt == doopt)
{
hisopts[option] = OPT_YES;
}
else
{
hisopts[option] = OPT_NO;
}
(void) sprintf(nfrontp, fmt, option);
nfrontp += sizeof(dont) - 2;
}
void
wontoption(option)
int option;
{
char *fmt;
switch (option)
{
case TELOPT_ECHO:
not42 = 1; /* doesn't seem to be a 4.2 system */
break;
case TELOPT_BINARY:
telopt_binary(0);
break;
case TELOPT_TTYPE:
settimer(ttypeopt);
break;
}
fmt = dont;
hisopts[option] = OPT_NO;
(void) sprintf(nfrontp, fmt, option);
nfrontp += sizeof(doopt) - 2;
}
void
dooption(option)
int option;
{
char *fmt;
switch (option)
{
case TELOPT_ECHO:
telopt_echo(1);
fmt = will;
break;
case TELOPT_BINARY:
telopt_binary(1);
fmt = will;
break;
case TELOPT_SGA:
fmt = will;
break;
case TELOPT_TM:
default:
fmt = wont;
}
if (fmt == will)
{
myopts[option] = OPT_YES;
}
else
{
myopts[option] = OPT_NO;
}
(void) sprintf(nfrontp, fmt, option);
nfrontp += sizeof(doopt) - 2;
}
void
dontoption(option)
int option;
{
char *fmt;
switch (option)
{
case TELOPT_ECHO: /* we should stop echoing */
telopt_echo(0);
default:
fmt = wont;
}
if (fmt = wont)
{
myopts[option] = OPT_NO;
}
else
{
myopts[option] = OPT_YES;
}
(void) sprintf(nfrontp, fmt, option);
nfrontp += sizeof(wont) - 2;
}
/*
* suboption()
*
* Look at the sub-option buffer, and try to be helpful to the other side.
*
* Currently we recognize:
*
* Terminal type is
*/
void
suboption()
{
switch (SB_GET())
{
case TELOPT_TTYPE:
{ /* Yaaaay! */
static char terminalname[5 + 41] = "TERM=";
settimer(ttypesubopt);
if (SB_GET() != TELQUAL_IS)
{
return; /* ??? XXX but, this is the most robust */
}
terminaltype = terminalname + strlen(terminalname);
while ((terminaltype < (terminalname + sizeof terminalname - 1)) &&
!SB_EOF())
{
register int c;
c = SB_GET();
if (isupper(c))
{
c = tolower(c);
}
*terminaltype++ = c; /* accumulate name */
}
*terminaltype = 0;
terminaltype = terminalname;
break;
}
}
}
/*
* ttloop
*
* A small subroutine to flush the network output buffer, get some data from the
* network, and pass it through the telnet state machine. We also flush the
* pty input buffer (by dropping its data) if it becomes too full.
*/
void
ttloop()
{
if (nfrontp - nbackp)
{
netflush();
}
ncc = read(net, netibuf, sizeof netibuf);
if (ncc < 0)
{
syslog(LOG_INFO, "ttloop: read: %m\n");
cleanup(); /* exit(1); */
}
else if (ncc == 0)
{
syslog(LOG_INFO, "ttloop: peer died: %m\n");
cleanup(); /* exit(1); */
}
netip = netibuf;
telrcv(); /* state machine */
if (ncc > 0)
{
pfrontp = pbackp = ptyobuf;
telrcv();
}
}
/* Check a descriptor to see if out of band data exists on it. */
int
stilloob(s)
int s; /* socket number */
{
static struct timeval timeout = {0};
fd_set excepts;
int value;
do
{
FD_ZERO(&excepts);
FD_SET(s, &excepts);
value = select(s + 1, (fd_set *) 0, (fd_set *) 0, &excepts, &timeout);
} while ((value == -1) && (errno == EINTR));
if (value < 0)
{
fatalperror(pty, "select");
}
return (FD_ISSET(s, &excepts));
}
/*
* Main loop. Select from pty and network, and hand data to telnet receiver
* finite state machine.
*/
void
telnet(f, p)
{
int on = 1;
char hostname[MAXHOSTNAMELEN];
#ifdef VERBOSE
#define TABBUFSIZ 512
char defent[TABBUFSIZ];
char defstrs[TABBUFSIZ];
#undef TABBUFSIZ
#endif
char *HE;
char *HN;
char *IM;
ioctl(f, FIONBIO, &on);
ioctl(p, FIONBIO, &on);
#ifndef STREAM_PTY
ioctl(p, TIOCPKT, &on);
#endif
#if defined(SO_OOBINLINE)
setsockopt(net, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof on);
#endif /* defined(SO_OOBINLINE) */
signal(SIGTSTP, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
/*
* Ignoring SIGTTOU keeps the kernel from blocking us in ttioctl() in
* /sys/tty.c.
*/
signal(SIGTTOU, SIG_IGN);
signal(SIGCHLD, cleanup);
setsid();
/* Request to do remote echo and to suppress go ahead. */
if (!myopts[TELOPT_ECHO])
{
dooption(TELOPT_ECHO);
}
if (!myopts[TELOPT_SGA])
{
dooption(TELOPT_SGA);
}
/*
* Is the client side a 4.2 (NOT 4.3) system? We need to know this because
* 4.2 clients are unable to deal with TCP urgent data.
*
* To find out, we send out a "DO ECHO". If the remote system answers "WILL
* ECHO" it is probably a 4.2 client, and we note that fact ("WILL ECHO"
* ==> that the client will echo what WE, the server, sends it; it does NOT
* mean that the client will echo the terminal input).
*/
(void) sprintf(nfrontp, doopt, TELOPT_ECHO);
nfrontp += sizeof doopt - 2;
hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
/*
* Show banner that getty never gave.
*
* We put the banner in the pty input buffer. This way, it gets carriage
* return null processing, etc., just like all other pty --> client data.
*/
#ifdef VERBOSE
gethostname(hostname, sizeof(hostname));
if (getent(defent, "default") == 1)
{
char *getstr();
char *p = defstrs;
HE = getstr("he", &p);
HN = getstr("hn", &p);
IM = getstr("im", &p);
if (HN && *HN)
strcpy(hostname, HN);
edithost(HE, hostname);
if (IM && *IM)
putf(IM, ptyibuf + 1);
}
else
#endif
sprintf(ptyibuf + 1, BANNER /* ,maple, line + 5 */);
ptyip = ptyibuf + 1; /* Prime the pump */
pcc = strlen(ptyip); /* ditto */
/* Clear ptybuf[0] - where the packet information is received */
ptyibuf[0] = 0;
/*
* Call telrcv() once to pick up anything received during terminal type
* negotiation.
*/
telrcv();
for (;;)
{
fd_set ibits, obits, xbits;
register int c;
if (ncc < 0 && pcc < 0)
break;
FD_ZERO(&ibits);
FD_ZERO(&obits);
FD_ZERO(&xbits);
/*
* Never look for input if there's still stuff in the corresponding
* output buffer
*/
if (nfrontp - nbackp || pcc > 0)
{
FD_SET(f, &obits);
FD_SET(p, &xbits);
}
else
{
FD_SET(p, &ibits);
}
if (pfrontp - pbackp || ncc > 0)
{
FD_SET(p, &obits);
}
else
{
FD_SET(f, &ibits);
}
if (!SYNCHing)
{
FD_SET(f, &xbits);
}
if ((c = select(16, &ibits, &obits, &xbits,
(struct timeval *) 0)) < 1)
{
if (c == -1)
{
if (errno == EINTR)
{
continue;
}
}
sleep(3);
continue;
}
/* Any urgent data? */
if (FD_ISSET(net, &xbits))
{
SYNCHing = 1;
}
/* Something to read from the network... */
if (FD_ISSET(net, &ibits))
{
#if !defined(SO_OOBINLINE)
/*
* In 4.2 (and 4.3 beta) systems, the OOB indication and data handling
* in the kernel is such that if two separate TCP Urgent requests come
* in, one byte of TCP data will be overlaid. This is fatal for Telnet,
* but we try to live with it.
*
* In addition, in 4.2 (and...), a special protocol is needed to pick up
* the TCP Urgent data in the correct sequence.
*
* What we do is: if we think we are in urgent mode, we look to see if we
* are "at the mark". If we are, we do an OOB receive. If we run this
* twice, we will do the OOB receive twice, but the second will fail,
* since the second time we were "at the mark", but there wasn't any
* data there (the kernel doesn't reset "at the mark" until we do a
* normal read). Once we've read the OOB data, we go ahead and do
* normal reads.
*
* There is also another problem, which is that since the OOB byte we read
* doesn't put us out of OOB state, and since that byte is most likely
* the TELNET DM (data mark), we would stay in the TELNET SYNCH
* (SYNCHing) state. So, clocks to the rescue. If we've "just"
* received a DM, then we test for the presence of OOB data when the
* receive OOB fails (and AFTER we did the normal mode read to clear
* "at the mark").
*/
if (SYNCHing)
{
int atmark;
ioctl(net, SIOCATMARK, (char *) &atmark);
if (atmark)
{
ncc = recv(net, netibuf, sizeof(netibuf), MSG_OOB);
if ((ncc == -1) && (errno == EINVAL))
{
ncc = read(net, netibuf, sizeof(netibuf));
if (sequenceIs(didnetreceive, gotDM))
{
SYNCHing = stilloob(net);
}
}
}
else
{
ncc = read(net, netibuf, sizeof(netibuf));
}
}
else
{
ncc = read(net, netibuf, sizeof(netibuf));
}
settimer(didnetreceive);
#else /* !defined(SO_OOBINLINE)) */
ncc = read(net, netibuf, sizeof(netibuf));
#endif /* !defined(SO_OOBINLINE)) */
if (ncc < 0 && errno == EWOULDBLOCK)
ncc = 0;
else
{
if (ncc <= 0)
{
break;
}
netip = netibuf;
}
}
/* Something to read from the pty... */
if (FD_ISSET(p, &xbits))
{
if (PTY_PKT_READ(p, ptyibuf, 1) != 1)
{
break;
}
}
if (FD_ISSET(p, &ibits))
{
pcc = PTY_PKT_READ(p, ptyibuf, BUFSIZ);
if (pcc < 0 && errno == EWOULDBLOCK)
pcc = 0;
else
{
if (pcc <= 0)
break;
/* Skip past "packet" */
pcc--;
ptyip = ptyibuf + 1;
}
}
if (ptyibuf[0] & TIOCPKT_FLUSHWRITE)
{
netclear(); /* clear buffer back */
*nfrontp++ = IAC;
*nfrontp++ = DM;
neturg = nfrontp - 1; /* off by one XXX */
ptyibuf[0] = 0;
}
while (pcc > 0)
{
if ((&netobuf[BUFSIZ] - nfrontp) < 2)
break;
c = *ptyip++ & 0377, pcc--;
if (c == IAC)
*nfrontp++ = c;
*nfrontp++ = c;
/* Don't do CR-NUL if we are in binary mode */
if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO))
{
if (pcc > 0 && ((*ptyip & 0377) == '\n'))
{
*nfrontp++ = *ptyip++ & 0377;
pcc--;
}
else
*nfrontp++ = '\0';
}
}
if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
netflush();
if (ncc > 0)
telrcv();
if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
ptyflush();
}
cleanup();
}
/* State for recv fsm */
#define TS_DATA 0 /* base state */
#define TS_IAC 1 /* look for double IAC's */
#define TS_CR 2 /* CR-LF ->'s CR */
#define TS_SB 3 /* throw away begin's... */
#define TS_SE 4 /* ...end's (suboption negotiation) */
#define TS_WILL 5 /* will option negotiation */
#define TS_WONT 6 /* wont " */
#define TS_DO 7 /* do " */
#define TS_DONT 8 /* dont " */
void
telrcv()
{
register int c;
static int state = TS_DATA;
#ifdef STREAM_PTY
int pty = pts; /* XXX apply ioctl()s at slave end */
#endif
while (ncc > 0)
{
if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
return;
c = *netip++ & 0377, ncc--;
switch (state)
{
case TS_CR:
state = TS_DATA;
/* Strip off \n or \0 after a \r */
if ((c == 0) || (c == '\n'))
{
break;
}
/* FALL THROUGH */
case TS_DATA:
if (c == IAC)
{
state = TS_IAC;
break;
}
if (inter > 0)
break;
/*
* We now map \r\n ==> \r for pragmatic reasons. Many client
* implementations send \r\n when the user hits the CarriageReturn key.
*
* We USED to map \r\n ==> \n, since \r\n says that we want to be in
* column 1 of the next printable line, and \n is the standard unix way
* of saying that (\r is only good if CRMOD is set, which it normally
* is).
*/
if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO))
{
state = TS_CR;
}
*pfrontp++ = c;
break;
case TS_IAC:
switch (c)
{
/*
* Send the process on the pty side an interrupt. Do this with a
* NULL or interrupt char; depending on the tty mode.
*/
case IP:
interrupt();
break;
case BREAK:
sendbrk();
break;
/* Are You There? */
case AYT:
strcpy(nfrontp, "\r\n[Yes]\r\n");
nfrontp += 9;
break;
/* Abort Output */
case AO:
{
ptyflush(); /* half-hearted */
tcflush(pty, TCOFLUSH);
netclear(); /* clear buffer back */
*nfrontp++ = IAC;
*nfrontp++ = DM;
neturg = nfrontp - 1; /* off by one XXX */
break;
}
/* Erase Character and Erase Line */
case EC:
case EL:
{
struct termios b;
char ch;
ptyflush(); /* half-hearted */
tcgetattr(pty, &b);
ch = (c == EC) ?
b.c_cc[VERASE] : b.c_cc[VKILL];
if (ch != '\377')
{
*pfrontp++ = ch;
}
break;
}
/* Check for urgent data... */
case DM:
SYNCHing = stilloob(net);
settimer(gotDM);
break;
/* Begin option subnegotiation... */
case SB:
state = TS_SB;
continue;
case WILL:
state = TS_WILL;
continue;
case WONT:
state = TS_WONT;
continue;
case DO:
state = TS_DO;
continue;
case DONT:
state = TS_DONT;
continue;
case IAC:
*pfrontp++ = c;
break;
}
state = TS_DATA;
break;
case TS_SB:
if (c == IAC)
{
state = TS_SE;
}
else
{
SB_ACCUM(c);
}
break;
case TS_SE:
if (c != SE)
{
if (c != IAC)
{
SB_ACCUM(IAC);
}
SB_ACCUM(c);
state = TS_SB;
}
else
{
SB_TERM();
suboption(); /* handle sub-option */
state = TS_DATA;
}
break;
case TS_WILL:
if (hisopts[c] != OPT_YES)
willoption(c);
state = TS_DATA;
continue;
case TS_WONT:
if (hisopts[c] != OPT_NO)
wontoption(c);
state = TS_DATA;
continue;
case TS_DO:
if (myopts[c] != OPT_YES)
dooption(c);
state = TS_DATA;
continue;
case TS_DONT:
if (myopts[c] != OPT_NO)
{
dontoption(c);
}
state = TS_DATA;
continue;
default:
syslog(LOG_ERR, "panic state=%d\n", state);
cleanup(); /* exit(1) */
}
}
}
/*
* getterminaltype
*
* Ask the other end to send along its terminal type. Output is the variable
* terminaltype filled in.
*/
void
getterminaltype()
{
static char sbuf[] = {IAC, DO, TELOPT_TTYPE};
settimer(getterminal);
bcopy(sbuf, nfrontp, sizeof sbuf);
nfrontp += sizeof sbuf;
hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
while (sequenceIs(ttypeopt, getterminal))
{
ttloop();
}
if (hisopts[TELOPT_TTYPE] == OPT_YES)
{
static char sbbuf[] = {IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE};
bcopy(sbbuf, nfrontp, sizeof sbbuf);
nfrontp += sizeof sbbuf;
while (sequenceIs(ttypesubopt, getterminal))
{
ttloop();
}
}
}
/* --------------------------------- */
/* log in BBS */
/* --------------------------------- */
#include <sys/types.h>
#include <grp.h>
#include <sys/resource.h>
#include <utmp.h>
#ifndef _PATH_UTMP
#define _PATH_UTMP "/etc/utmp"
#endif
/* HP-UX 9.0 termios doesn't define these */
#ifndef FLUSHO
#define FLUSHO 0
#define XTABS 0
#endif
#ifndef OXTABS
#define OXTABS XTABS
#endif
#ifndef LINUX
#define _PATH_DEFPATH "/usr/ucb:/bin:/usr/bin:"
#endif
#ifdef SunOS
char* getpass(const char* prompt);
char* crypt(const char *key, const char *setting);
void
setenv(name, value, overwrite)
const char *name;
const char *value;
int overwrite;
{
if (overwrite || (getenv(name) == 0))
{
char *p;
if ((p = malloc(strlen(name) + strlen(value) + 2)) == 0)
{
syslog(LOG_ERR, "out of memory\n");
exit(1);
}
sprintf(p, "%s=%s", name, value);
putenv(p);
}
}
#endif
void
slave_termios()
{
struct termios termios;
/*
* Finalize the terminal settings. Some systems default to 8 bits, others
* to 7, so we should leave that alone.
*/
tcgetattr(0, &termios);
termios.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON | IMAXBEL);
termios.c_iflag &= ~IXANY;
termios.c_lflag |= (ISIG | IEXTEN | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL |
ECHOKE);
termios.c_lflag &= ~(ECHOPRT | TOSTOP | FLUSHO);
termios.c_oflag |= (OPOST | ONLCR);
termios.c_oflag &= ~OXTABS;
termios.c_cc[VEOF] = 4;
(void) tcsetattr(0, TCSANOW, &termios);
}
#ifdef HAVE_UTMP
/* --------------------------------- */
/* log out process */
/* --------------------------------- */
/* 0 on failure, 1 on success */
void
logout(line)
register char *line;
{
int fd;
if ((fd = open(_PATH_UTMP, O_RDWR, 0)) >= 0)
{
struct utmp ut;
(void) lseek(fd, (long) (utmp_slot), L_SET);
(void) read(fd, &ut, sizeof(struct utmp));
if (ut.ut_name[0] && !strncmp(ut.ut_line, line, sizeof(ut.ut_line)))
{
bzero(ut.ut_name, sizeof(ut.ut_name));
bzero(ut.ut_host, sizeof(ut.ut_host));
(void) time(&ut.ut_time);
(void) lseek(fd, (long) -sizeof(struct utmp), L_INCR);
(void) write(fd, &ut, sizeof(struct utmp));
}
(void) close(fd);
}
}
#endif
/* ----------------------------------------------- */
/* 取得 remote user name 以判定身份 */
/* ----------------------------------------------- */
/*
* rfc931() speaks a common subset of the RFC 931, AUTH, TAP, IDENT and RFC
* 1413 protocols. It queries an RFC 931 etc. compatible daemon on a remote
* host to look up the owner of a connection. The information should not be
* used for authentication purposes. This routine intercepts alarm signals.
*
* Diagnostics are reported through syslog(3).
*
* Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
*/
#include <setjmp.h>
#define STRN_CPY(d,s,l) { strncpy((d),(s),(l)); (d)[(l)-1] = 0; }
#define STRING_LENGTH 60
#define RFC931_TIMEOUT 10
#define RFC931_PORT 113 /* Semi-well-known port */
#define ANY_PORT 0 /* Any old port will do */
/* ------------------------- */
/* timeout - handle timeouts */
/* ------------------------- */
static jmp_buf timebuf;
static void
timeout(sig)
int sig;
{
longjmp(timebuf, sig);
}
void
getremotename(from, rname)
struct sockaddr_in *from;
char *rname;
{
struct sockaddr_in our_sin;
struct sockaddr_in rmt_sin;
unsigned rmt_port, rmt_pt;
unsigned our_port, our_pt;
FILE *fp;
char buffer[512], user[80], *cp;
int s;
/*
* Use one unbuffered stdio stream for writing to and for reading from the
* RFC931 etc. server. This is done because of a bug in the SunOS 4.1.x
* stdio library. The bug may live in other stdio implementations, too.
* When we use a single, buffered, bidirectional stdio stream ("r+" or "w+"
* mode) we read our own output. Such behaviour would make sense with
* resources that support random-access operations, but not with sockets.
*/
s = sizeof our_sin;
if (getsockname(0, (struct sockaddr*)&our_sin, &s) < 0)
return;
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("bbsd: socket in rfc931");
return;
}
if (!(fp = fdopen(s, "r+")))
{
close(s);
perror("bbsd:fdopen");
return;
}
/*
* Set up a timer so we won't get stuck while waiting for the server.
*/
if (setjmp(timebuf) == 0)
{
signal(SIGALRM, timeout);
alarm(RFC931_TIMEOUT);
/*
* Bind the local and remote ends of the query socket to the same IP
* addresses as the connection under investigation. We go through all
* this trouble because the local or remote system might have more than
* one network address. The RFC931 etc. client sends only port numbers;
* the server takes the IP addresses from the query socket.
*/
our_pt = ntohs(our_sin.sin_port);
our_sin.sin_port = htons(ANY_PORT);
rmt_sin = *from;
rmt_pt = ntohs(rmt_sin.sin_port);
rmt_sin.sin_port = htons(RFC931_PORT);
setbuf(fp, (char *) 0);
s = fileno(fp);
if (bind(s, (struct sockaddr *) & our_sin, sizeof(our_sin)) >= 0 &&
connect(s, (struct sockaddr *) & rmt_sin, sizeof(rmt_sin)) >= 0)
{
/*
* Send query to server. Neglect the risk that a 13-byte write would
* have to be fragmented by the local system and cause trouble with
* buggy System V stdio libraries.
*/
fprintf(fp, "%u,%u\r\n", rmt_pt, our_pt);
fflush(fp);
/*
* Read response from server. Use fgets()/sscanf() so we can work
* around System V stdio libraries that incorrectly assume EOF when a
* read from a socket returns less than requested.
*/
if (fgets(buffer, sizeof(buffer), fp) && !ferror(fp) && !feof(fp)
&& sscanf(buffer, "%u , %u : USERID :%*[^:]:%79s",
&rmt_port, &our_port, user) == 3
&& rmt_pt == rmt_port && our_pt == our_port)
{
/*
* Strip trailing carriage return. It is part of the protocol, not
* part of the data.
*/
if (cp = strchr(user, '\r'))
*cp = 0;
strcpy(rname, user);
}
}
alarm(0);
}
fclose(fp);
}
/*
woju
*/
bad_host(char* name)
{
FILE* list;
char buf[40];
if (list = fopen(BAD_HOST, "r")) {
while (fgets(buf, 40, list)) {
buf[strlen(buf) - 1] = '\0';
if (!strcmp(buf, name))
return 1;
if (buf[strlen(buf) - 1] == '.' && !strncmp(buf, name, strlen(buf)))
return 1;
if (*buf == '.' && strlen(buf) < strlen(name) &&
!strcmp(buf, name + strlen(name) - strlen(buf)))
return 1;
}
fclose(list);
}
return 0;
}
void
my_client()
{
char *host, *inet_ntoa();
static char rname[40] = "?";
struct hostent *hp;
struct sockaddr_in who;
int i, p, t;
openlog("bbsd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
net = sizeof(who);
if (getpeername(0, (struct sockaddr*)&who, &net) < 0)
{
perror("getpeername");
exit(1);
}
net = 1;
if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &net, sizeof(net)) < 0)
{
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
}
#ifdef __linux
setpgrp();
#else
setpgrp(0, 0);
#endif
net = 0;
/* get remote host name */
/*
woju
hp = gethostbyaddr((char *) &who.sin_addr, sizeof(struct in_addr),
who.sin_family);
*/
hp = 0;
if (setjmp(timebuf) == 0) {
signal(SIGALRM, timeout);
alarm(3);
hp = gethostbyaddr((char *) &who.sin_addr, sizeof(struct in_addr),
who.sin_family);
alarm(0);
}
if (hp)
host = (char*)hp->h_name;
else
host = inet_ntoa(who.sin_addr);
getremotename(&who, rname); /* RFC931 */
/* ban 掉 bad host / bad user */
if (bad_host(host) && !strcmp(rname, "?"))
exit(1);
/* ---------------------------- */
/* Get a pty, scan input lines. */
/* ---------------------------- */
#ifdef STREAM_PTY
if ((p = open("/dev/ptmx", O_RDWR)) < 0)
fatal(net, "All network ports in use");
if (grantpt(p) < 0 || unlockpt(p) < 0)
fatal(net, "Cannot initialize pty slave");
dup2(net, 0);
if ((line = ptsname(p)) == 0 || (t = open(line, O_RDWR)) < 0)
fatal(net, "Cannot find pty slave");
if (ioctl(t, I_PUSH, "ptem") < 0 || ioctl(t, I_PUSH, "ldterm") < 0
|| ioctl(t, I_PUSH, "ttcompat") < 0 || ioctl(p, I_PUSH, "pckt") < 0)
fatal(net, "Cannot push streams modules onto pty");
#else /* STREAM_PTY */
/* ----------------------------------------- */
/* hashing : increase the seaching pty speed */
/* ----------------------------------------- */
{
static char ptyname[] = "/dev/ptyXX";
line = ptyname;
}
i = getpid();
#ifdef BSD44
for (t = i + 256; i < t; i++)
{
line[sizeof("/dev/ptyp") - 2] =
"PqrsPQRS"[i & 7];
line[sizeof("/dev/ptyp") - 1] = "0123456789abcdefghijklmnopqrstuv"[(i >> 4)& 31];
#else
for (t = i + 512; i < t; i++)
{
line[sizeof("/dev/ptyp") - 2] =
"klmnopqrstuvwxyzKLMNOPQRSTUVWXYZ"[(i >> 4) & 31];
line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i & 15];
#endif
close(open(line, O_RDWR | O_NOCTTY));
p = open(line, O_RDWR | O_NOCTTY);
if(p > 0)
goto gotpty;
}
if(p <= 0)
{
fatal(net, "客滿了,請稍後再來 ...");
}
/* NOTREACHED */
gotpty:
/* dup2(net, 0); */
line[sizeof("/dev/") - 1] = 't';
/* close(open(line, O_RDWR)); */
t = open(line, O_RDWR | O_NOCTTY);
if (t < 0)
{
fatalperror(net, line);
}
#ifdef ultrix
{
struct termios b;
tcgetattr(t, &b);
b.c_iflag |= ICRNL;
b.c_oflag |= ONLCR;
tcsetattr(t, TCSANOW, &b);
}
#endif /* ultrix */
#endif /* STREAM_PTY */
pty = p;
#ifdef STREAM_PTY
pts = t;
#endif
/* get terminal type. */
getterminaltype();
i = fork();
if (i < 0)
fatalperror(net, "fork");
else if (i)
telnet(net, p);
/* Acquire a controlling terminal */
setsid();
#if defined(TIOCSCTTY) && !defined(BROKEN_TIOCSCTTY)
ioctl(t, TIOCSCTTY, (caddr_t) 0);
#else /* TIOCSCTTY */
i = t;
if ((t = open(line, O_RDWR)) < 0)
_exit(1);
close(i);
#endif /* TIOCSCTTY */
close(net);
close(p);
fchmod(t, 0620);
fchown(t, bbsuid, mygid);
dup2(t, 0);
dup2(t, 1);
dup2(t, 2);
close(t);
/* --------------- */
/* shortcut to BBS */
/* --------------- */
for (i = getdtablesize(); i > 2; i--)
close(i);
(void) setpriority(PRIO_PROCESS, 0, 0);
/*
woju
*/
{
char *salt, *p;
struct passwd* pwd;
if (pwd = getpwnam(bbsuser))
salt = pwd->pw_passwd;
else
salt = "xx";
if (*pwd->pw_passwd) {
p = getpass(PASSWD);
if (strcmp(crypt(p, salt), pwd->pw_passwd)) {
if (*bbsuser2) {
bbsuser = bbsuser2;
if (pwd = getpwnam(bbsuser)) {
bbsuid = pwd->pw_uid;
bbsgid = pwd->pw_gid;
bbshome =pwd->pw_dir;
bbsshell = pwd->pw_shell;
sprintf(bbsprog, "%s/bin/bbs", bbshome);
}
else {
sleep(5);
exit(1);
}
}
else {
sleep(5);
exit(1);
}
}
}
}
slave_termios();
#ifdef HAVE_UTMP
{
register int fd;
struct utmp utmp;
off_t lseek();
utmp_slot = ttyslot() * sizeof(struct utmp);
if (utmp_slot > 0 && (fd = open(_PATH_UTMP, O_WRONLY, 0)) >= 0)
{
memset((char *) &utmp, 0, sizeof(utmp));
(void) time(&utmp.ut_time);
strncpy(utmp.ut_name, bbsuser, sizeof(utmp.ut_name));
strncpy(utmp.ut_host, host, sizeof(utmp.ut_host));
strncpy(utmp.ut_line, line + sizeof("/dev/") - 1, sizeof(utmp.ut_line));
(void) lseek(fd, (long) (utmp_slot), L_SET);
(void) write(fd, &utmp, sizeof(struct utmp));
(void) close(fd);
}
}
#endif
/* Give up root privileges: no way back from here. */
if (setgid(bbsgid))
exit(1);
initgroups(bbsuser, bbsgid);
if (setuid(bbsuid))
exit(1);
/*
* Now that we have given up root privilege do the stuff that must be done
* as the real user: Kerberos or Secure RPC authentication, entering the
* (possibly remote) home directory.
*/
/* Change to home directory */
if (chdir(bbshome))
exit(1);
/*
* Set up a new environment. With SYSV, some variables are always
* preserved; some varables are never preserved, and some variables are
* always clobbered. With BSD, nothing is always preserved, and some
* variables are always clobbered. We add code to make sure that LD_* and
* IFS are never preserved.
*/
#if 0
#endif
environ[0] = 0;
if (terminaltype)
putenv(terminaltype);
/*
woju
(void) my_setenv("HOME", BBSHOME, 1);
(void) my_setenv("SHELL", BBSSHELL, 1);
(void) my_setenv("REMOTEHOST", host, 1);
(void) my_setenv("REMOTEUSERNAME", rname, 1);
(void) my_setenv("USER", BBSUSER, 1);
(void) my_setenv("PATH", _PATH_DEFPATH, 0);
*/
(void) setenv("HOME", bbshome, 1);
(void) setenv("SHELL", bbsshell, 1);
(void) setenv("REMOTEHOST", host, 1);
(void) setenv("REMOTEUSERNAME", rname, 1);
(void) setenv("USER", bbsuser, 1);
(void) setenv("PATH", _PATH_DEFPATH, 0);
/*
woju
*/
{
char RFC931[80];
sprintf(RFC931, "%s@%s", rname, host);
(void) setenv("RFC931", RFC931, 1);
}
execl(bbsprog, "bbs", host, line, rname, NULL);
syslog(LOG_ERR, "%s: %m", bbsprog);
exit(1);
}
/* --------------------------------- */
/* stand-alone daemon */
/* --------------------------------- */
void
reapchild()
{
int state, pid;
signal(SIGCHLD, reapchild);
while ((pid = waitpid(-1, &state, WNOHANG | WUNTRACED)) > 0);
}
/* ----------------------------------- */
/* check system / memory / CPU loading */
/* ----------------------------------- */
#ifdef HAVE_CHKLOAD
int
chkload(limit)
int limit;
{
double cpu_load[3];
#if defined(LINUX)
FILE *fp;
fp = fopen("/proc/loadavg", "r");
if (!fp)
cpu_load[0] = cpu_load[1] = cpu_load[2] = 0;
else
{
float av[3];
fscanf(fp, "%g %g %g", av, av + 1, av + 2);
fclose(fp);
cpu_load[0] = av[0];
cpu_load[1] = av[1];
cpu_load[2] = av[2];
}
#elif defined(BSD44)
getloadavg(cpu_load, 3);
#else
#include <nlist.h>
#define VMUNIX "/vmunix"
#define KMEM "/dev/kmem"
static struct nlist nlst[] = {
{"_avenrun"},
{0}
};
static long offset = -1;
long avenrun[3];
register int i;
int kmem;
if ((kmem = open(KMEM, O_RDONLY)) == -1)
return (1);
if (offset < 0)
{
(void) nlist(VMUNIX, nlst);
if (nlst[0].n_type == 0)
return (1);
offset = (long) nlst[0].n_value;
}
if (lseek(kmem, offset, L_SET) == -1)
{
close(kmem);
return (1);
}
if (read(kmem, (char *) avenrun, sizeof(avenrun)) == -1)
{
close(kmem);
return (1);
}
close(kmem);
#endif
#define loaddouble(la) ((double)(la) / (1 << 8))
for (i = 0; i < 3; i++)
cpu_load[i] = loaddouble(avenrun[i]);
if (cpu_load[0] > limit)
return 1;
sprintf(maple, "%.2f %.2f %.2f",
cpu_load[0], cpu_load[1], cpu_load[2]);
return 0;
}
#endif
void
dokill()
{
kill(0, SIGKILL);
}
void
standaloneinit(port)
int port;
{
int n, fp;
struct group *gr;
char buf[80];
/*
* More idiot speed-hacking --- the first time conversion makes the C
* library open the files containing the locale definition and time zone.
* If this hasn't happened in the parent process, it happens in the
* children, once per connection --- and it does add up.
*/
time_t dummy = time(NULL);
struct tm *dummy_time = localtime(&dummy);
struct tm *other_dummy_time = gmtime(&dummy);
strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", dummy_time);
gr = (struct group *) getgrnam("tty");
mygid = gr ? gr->gr_gid : bbsgid;
n = getdtablesize();
if (fork())
exit(0);
while (n)
(void) close(--n);
sprintf(buf, "/var/run/ybbsd-%d.pid", port);
if ((fp = open(buf, O_RDWR | O_CREAT, 0644)) >= 0)
{
memset(buf, 0, 6);
if (read(fp, buf, 5) == 5 && (n = atoi(buf)) > 2 && !kill(n, 0))
{
close(fp);
/* kill(n, SIGHUP); /* 睡著了 ==> wake up */
#if 0
fprintf(stderr, "ybbsd: I am strong enough (%s)\n", buf);
#endif
exit(0);
}
lseek(fp, 0, SEEK_SET);
sprintf(buf, "%5d", getpid());
write(fp, buf, 5);
close(fp);
}
else
{
fprintf(stderr, "ybbsd: cant log PID file [%s]\n", buf);
exit(-1);
}
(void) open("/", O_RDONLY);
(void) dup2(0, 1);
(void) dup2(0, 2);
n = open("/dev/tty", O_RDWR);
if (n > 0)
{
ioctl(n, TIOCNOTTY, (char *) 0);
(void) close(n);
}
openlog("bbsd", LOG_PID, LOG_LOCAL0);
syslog(LOG_NOTICE, "port=%d\n", port);
}
int
relisten(sin, port)
struct sockaddr_in *sin;
int port;
{
int s, on;
struct linger onn;
onn.l_onoff = 0;
onn.l_linger = 0;
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s < 0)
{
syslog(LOG_NOTICE, "socket\n");
exit(1);
}
/* ---------------- */
/* setup standalone */
/* ---------------- */
on = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0)
syslog(LOG_ERR, "(SO_REUSEADDR): %m");
on = 0;
if (setsockopt(s, SOL_SOCKET, SO_LINGER, (void *) &onn, sizeof(onn)) < 0)
syslog(LOG_ERR, "(SO_LINGER): %m");
bzero(sin, sizeof(*sin));
sin->sin_port = port;
sin->sin_family = AF_INET;
if (bind(s, (struct sockaddr*)sin, sizeof *sin) < 0)
{
perror("bind");
exit(1);
}
if (listen(s, 12) < 0)
{
perror("listen");
exit(1);
}
return s;
}
main(argc, argv)
char *argv[];
{
/* stand-alone */
static struct sockaddr_in sin = {AF_INET};
static int s;
int myport, high_threshold, low_threshold;
/*
woju
*/
struct passwd* pwd;
myport = MYPORT;
bbsuser = (argc > 4) ? argv[4] : BBSUSER;
bbsuser2 = (argc > 5) ? argv[5] : BBSUSER2;
if (!(pwd = getpwnam(bbsuser))) {
fprintf(stderr, "No such user: %s\n", bbsuser);
exit(1);
}
bbsuid = pwd->pw_uid;
bbsgid = pwd->pw_gid;
bbshome =pwd->pw_dir;
bbsshell = pwd->pw_shell;
sprintf(bbsprog, "%s/bin/bbs", bbshome);
low_threshold = (argc > 1) ? atoi(argv[1]) : LOW_THRESHOLD;
high_threshold = (argc > 2) ? atoi(argv[2]) : HIGH_THRESHOLD;
myport = (argc > 3) ? atoi(argv[3]) : MYPORT;
signal(SIGHUP, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, reapchild);
signal(SIGINT, dokill);
signal(SIGTERM, dokill);
chdir("/");
standaloneinit(myport);
myport = htons(myport);
s = relisten(&sin, myport);
for (;;)
{
pid_t pid;
int ns;
#ifdef HAVE_CHKLOAD
static time_t uptime = 0;
pid = time(0);
if (pid > uptime)
{
#ifdef AUTO_JUMPER
if (chkload(high_threshold))
{
close(s);
#if 0
syslog(LOG_NOTICE, "load too high\n");
#endif
do
{
sleep(23); /* 關閉 57 秒鐘以降低 system load */
} while (chkload(low_threshold));
#if 0
syslog(LOG_NOTICE, "load slow down\n");
#endif
s = relisten(&sin, myport);
pid = time(0);
}
uptime = pid + 43; /* 43 秒鐘內不再檢查 system load */
#else
uptime = pid + 43; /* 43 秒鐘內不再檢查 system load */
chkload(999);
#endif
}
#endif
do
{
int foo;
foo = sizeof sin;
errno = 0;
ns = accept(s, (struct sockaddr*)&sin, &foo);
} while (ns < 0 && errno == EINTR);
if (ns < 0 && errno != EINTR)
{
perror("accept");
continue;
}
pid = fork();
if (!pid)
{
close(s);
dup2(ns, 0);
dup2(ns, 1);
dup2(ns, 2);
my_client();
close(ns);
}
else
{
if (pid < 0)
{
perror("fork");
}
close(ns);
}
}
}