#ifndef lint
static char sccsid[] = "@(#)$Id: serve.c,v 1.41 1994/12/03 21:54:30 sob Exp sob $";
#endif
/*
* Main server routine
*/
#include "common.h"
#include <signal.h>
#ifdef USG
#include <sys/times.h>
#else
#include <sys/time.h>
#endif
#ifdef LOG
# ifndef USG
# include <sys/resource.h>
# endif
#endif
#ifdef TIMERS
#include "timer.h"
#endif
extern void ahbs(), dodate(), group(), help(), ihave();
extern void list(), newgroups(), newnews(), nextlast(), post();
extern void slave(), xlistgroup(), xhdr(), doxover(), xthread(), xindex();
extern void xgtitle(), doxrover(), doxmode();
extern int errno;
#ifdef AUTH
extern void doxauthcap(), doxauthinfo(), doxauthsys();
#endif
#ifdef GENAUTH
#define A 1
#else
#define A 0
#endif
static struct cmdent {
char *cmd_name;
int authreq; /* 0=none,1=userpass */
void (*cmd_fctn)();
} cmdtbl[] = {
#ifdef AUTH
"authcap", 0, doxauthcap,
"authinfo", 0, doxauthinfo,
"authsys", 0, doxauthsys,
#endif
"article", A, ahbs,
"body", A, ahbs,
"date", 0, dodate,
"group", A, group,
"head", A, ahbs,
"help", 0, help,
"ihave", 1, ihave,
"last", A, nextlast,
"list", A, list,
#ifdef LISTGROUP
"listgroup", A, xlistgroup,
#endif
"mode", 0, doxmode,
"newgroups", A, newgroups,
"newnews", A, newnews,
"next", A, nextlast,
"post", 1, post,
"slave", 0, slave,
"stat", A, ahbs,
"xgtitle", A, xgtitle,
#ifdef XHDR
"xhdr", A, xhdr,
#endif
#ifdef XINDEX
"xindex", A, xindex,
#endif
#ifdef XOVER
"xover", A, doxover,
#endif
#ifdef XTHREAD
"xthread", A, xthread,
#endif
#ifdef XROVER
"xrover", A, doxrover,
#endif
};
#define NUMCMDS (sizeof(cmdtbl) / sizeof(struct cmdent))
#ifdef TIMEOUT
static void timeout();
#endif
#ifdef LOGINCHECK
static void logincheck();
static int firstlogincheck;
#endif
#ifdef BATCHED_INPUT
static void batchcheck();
#endif
#ifdef TIMERS
static struct timer timers[] = {
#ifdef TIMEOUT
{ timeout, 1, TIMEOUT, 0 },
#endif
#ifdef LOGINCHECK
{ logincheck, 0, LOGINCHECK, 0 },
#endif
#ifdef BATCHCHECK
{ batchcheck, 1, BATCHCHECK, 0 },
#endif
};
#define NTIMERS (sizeof(timers) / sizeof(struct timer))
#endif
static char *stats_init();
#ifdef LOG
static void stats_finish();
#endif
#ifdef AUTH
extern int Needauth;
extern char User[];
#endif
/*
* serve -- given a connection on stdin/stdout, serve
* a client, executing commands until the client
* says goodbye.
*
* Parameters: None.
*
* Returns: Exits.
*
* Side effects: Talks to client, does a lot of
* stuff.
*/
void
serve()
{
char line[NNTP_STRLEN];
char host[MAXHOSTNAMELEN];
char gdbuf[MAXBUFLEN];
char **argp;
char *timeptr, *cp;
int argnum, i;
#ifdef POSTER
struct passwd *pp;
#endif
#ifdef LOG
grps_acsd = arts_acsd = 0;
#endif
/* Not all systems pass fd's 1 and 2 from inetd */
(void) close(1);
(void) close(2);
(void) dup(0);
(void) dup(0);
/* If we're ALONE, then we've already opened syslog */
#ifndef ALONE
# ifdef SYSLOG
# ifdef LOG_DAEMON
openlog("nntpd", LOG_PID, SYSLOG);
# else
openlog("nntpd", LOG_PID);
# endif
# endif
#endif
timeptr = stats_init();
#ifdef ALONE
#ifndef USG
(void) signal(SIGCHLD, SIG_IGN);
#endif
#endif
/* Ignore SIGPIPE, since we'll see closed connections with read */
(void) signal(SIGPIPE, SIG_IGN);
/* Get permissions and see if we can talk to this client */
#ifdef AUTH
Needauth = 1;
strcpy(User,"");
#endif
host_access(&canread, &canpost, &canxfer, gdbuf);
if (gethostname(host, sizeof(host)) < 0)
(void) strcpy(host, "Amnesiac");
#ifdef SETPROCTITLE
setproctitle("%s", hostname);
#endif
if (!canread && !canxfer) {
printf("%d %s NNTP server can't talk to you. Goodbye.\r\n",
ERR_ACCESS, host);
(void) fflush(stdout);
(void) fclose(stdout);
#ifdef SYSLOG
syslog(LOG_INFO, "%s refused connection", hostname);
#endif
exit(1);
}
#ifdef LOGINCHECK
firstlogincheck = 1;
logincheck();
firstlogincheck = 0;
#endif
if ( !canpost && !canread && !space(MINFREE)) {
printf("%d %s NNTP server out of space. Try later.\r\n",
ERR_GOODBYE, host);
(void) fflush(stdout);
#ifdef SYSLOG
syslog(LOG_INFO, "%s no space", hostname);
#endif
exit(1);
}
/* If we can talk, proceed with initialization */
ngpermcount = get_nglist(&ngpermlist, gdbuf);
#ifdef POSTER
pp = getpwnam(POSTER);
if (pp != NULL) {
uid_poster = pp->pw_uid;
gid_poster = pp->pw_gid;
home_poster = pp->pw_dir;
} else
#endif
{
uid_poster = gid_poster = 0;
home_poster = "/";
}
#ifndef FASTFORK
num_groups = 0;
num_groups = read_groups(); /* Read in the active file */
#else
signal(SIGALRM, SIG_IGN); /* Children don't deal with */
/* these things */
#endif
/*
* num_groups may be zero if expire is running and the active
* file is locked. (Under System V with lockf, for example.)
* Or, something may be really screwed up....
*/
if (num_groups == 0){ /* can't get a group list */
printf("%d %s NNTP server unavailable. Try later.\r\n",
ERR_FAULT, host);
(void) fflush(stdout);
#ifdef SYSLOG
syslog(LOG_INFO, "%s no groups", hostname);
#endif
exit(1);
}
art_fp = NULL;
argp = (char **) NULL; /* for first time */
if ((cp = index(timeptr, '\n')) != NULL)
*cp = '\0';
else
timeptr = "Unknown date";
#ifdef AUTH
printf("%d %s NNTP[auth] server version %s ready at %s (%s).\r\n",
#else
printf("%d %s NNTP server version %s ready at %s (%s).\r\n",
#endif
canpost ? OK_CANPOST : OK_NOPOST,
host, nntp_version,
timeptr,
canpost ? "posting ok" : "no posting");
(void) fflush(stdout);
/*
* Now get commands one at a time and execute the
* appropriate routine to deal with them.
*/
#ifdef TIMERS
timer_init(timers, NTIMERS);
#endif
for (;;) {
#ifdef TIMERS
/* Don't try to read input unless there is some */
if (!timer_sleep(timers, NTIMERS))
continue;
#endif
if (fgets(line, sizeof(line), stdin) == NULL)
break;
/* Strip trailing CR-LF */
cp = line + strlen(line) - 1;
while (cp >= line && (*cp == '\n' || *cp == '\r'))
*cp-- = '\0';
#ifdef DEBUG
if (debug)
syslog(LOG_DEBUG, "<- \"%s\"", line);
#endif
/* Null command */
if ((argnum = parsit(line, &argp)) == 0)
continue;
/* a motion to adjourn is always in order */
if (!strcasecmp(argp[0], "quit"))
break;
for (i = 0; i < NUMCMDS; ++i)
if (!strcasecmp(cmdtbl[i].cmd_name, argp[0]))
break;
if (i < NUMCMDS) {
#ifdef SETPROCTITLE
setproctitle("%s %s", hostname, argp[0]);
#endif
#ifdef AUTH
/* authentication required? */
if (cmdtbl[i].authreq == 1 && Needauth)
{
printf("%d Authentication required for command\r\n", ERR_NOAUTH);
(void) fflush(stdout);
continue;
}
#endif
(*cmdtbl[i].cmd_fctn)(argnum, argp);
} else {
#ifdef SYSLOG
syslog(LOG_INFO, "%s unrecognized %s", hostname, line);
#endif
printf("%d Command unrecognized.\r\n", ERR_COMMAND);
(void) fflush(stdout);
}
}
printf("%d %s closing connection. Goodbye.\r\n", OK_GOODBYE, host);
(void) fflush(stdout);
#ifdef BATCHED_INPUT
batchcheck();
#endif
#ifdef SYSLOG
if (ferror(stdout))
syslog(LOG_ERR, "%s disconnect: %m", hostname);
#ifdef LOG
stats_finish();
#endif
#endif
#ifdef PROFILE
profile();
#endif
exit(0);
}
#ifdef TIMEOUT
/*
* Called after TIMEOUT seconds of idle time to shut things down.
* XXX stats are not reported when this occurs
*/
static void
timeout()
{
printf("%d Timeout after %d seconds, closing connection.\r\n",
ERR_FAULT, TIMEOUT);
#ifdef SYSLOG
syslog(LOG_NOTICE, "%s timeout", hostname);
#endif
(void) fflush(stdout);
#ifdef BATCHED_INPUT
batchcheck();
#endif
#ifdef LOG
stats_finish();
#endif
#ifdef PROFILE
profile();
#endif
exit(1);
}
#endif
#ifdef LOGINCHECK
/*
* Called ever LOGINCHECK seconds to see if logins have been disabled.
* If so, shut down.
* XXX stats are not reported when this occurs
*/
static void
logincheck()
{
char host[MAXHOSTNAMELEN];
if (access(NOLOGIN, F_OK) < 0)
return;
if (gethostname(host, sizeof(host)) < 0)
(void) strcpy(host, "Amnesiac");
printf("%d Logins are disabled on NNTP server %s. Try again later.\r\n",
ERR_ACCESS, host);
(void) fflush(stdout);
#ifdef SYSLOG
syslog(LOG_INFO, "%s logins disabled%s",
hostname, firstlogincheck ? "" : " (kicked out)");
#endif
#ifdef BATCHED_INPUT
batchcheck();
#endif
#ifdef LOG
stats_finish();
#endif
#ifdef PROFILE
profile();
#endif
exit(1);
}
#endif
#ifdef BATCHED_INPUT
/*
* Called after BATCHCHECK seconds of idle time and at the end
* of a session to see if a batch needs to be launched.
*/
static void
batchcheck()
{
char errbuf[2 * NNTP_STRLEN];
enqpartbatch(CONT_XFER, ERR_XFERFAIL, errbuf);
}
#endif
/*
* Stats stuff
*/
static double Tstart, Tfinish;
static double user, sys;
#ifdef USG
static time_t start, finish;
#else
static struct timeval start, finish;
#endif
static char *
stats_init()
{
extern char *ctime();
#ifdef USG
(void) time(&start);
Tstart = (double) start;
return(ctime(&start));
#else
(void) gettimeofday(&start, (struct timezone *)NULL);
Tstart = (double) start.tv_sec + ((double)start.tv_usec)/1000000.0;
return(ctime(&start.tv_sec));
#endif
}
#ifdef LOG
static void
stats_finish()
{
char buf[NNTP_STRLEN];
# ifdef USG
struct tms cpu;
# else
struct rusage me, kids;
# endif
#ifdef USG
(void) time(&finish);
Tfinish = (double) finish;
#ifndef HZ
#define HZ 60.0 /* typical system clock ticks - param.h */
#endif
(void) times(&cpu);
user = (double)(cpu.tms_utime + cpu.tms_cutime) / HZ;
sys = (double)(cpu.tms_stime + cpu.tms_cstime) / HZ;
#else /* not USG */
(void) gettimeofday(&finish, (struct timezone *)NULL);
Tfinish = (double) finish.tv_sec + ((double)finish.tv_usec)/1000000.0;
(void) getrusage(RUSAGE_SELF, &me);
(void) getrusage(RUSAGE_CHILDREN, &kids);
user = (double) me.ru_utime.tv_sec + me.ru_utime.tv_usec/1000000.0 +
kids.ru_utime.tv_sec + kids.ru_utime.tv_usec/1000000.0;
sys = (double) me.ru_stime.tv_sec + me.ru_stime.tv_usec/1000000.0 +
kids.ru_stime.tv_sec + kids.ru_stime.tv_usec/1000000.0;
#endif /* not USG */
if (grps_acsd)
syslog(LOG_INFO, "%s exit %d articles %d groups",
hostname, arts_acsd, grps_acsd);
if (nn_told)
syslog(LOG_INFO, "%s newnews_stats told %d took %d",
hostname, nn_told, nn_took);
if (ih_accepted || ih_rejected || ih_failed)
syslog(LOG_INFO,
"%s ihave_stats accepted %d rejected %d failed %d",
hostname,
ih_accepted,
ih_rejected,
ih_failed);
(void) sprintf(buf, "user %.3f system %.3f elapsed %.3f",
user, sys, Tfinish - Tstart);
syslog(LOG_INFO, "%s times %s", hostname, buf);
}
#endif /* LOG */
void
doxmode(argc, argv)
int argc;
char *argv[];
{
if (argc == 2 && !strcasecmp(argv[1], "reader"))
printf("%d OK.\r\n", canpost ? OK_CANPOST : OK_NOPOST);
else
printf("%d Syntax error\r\n", ERR_CMDSYN);
(void) fflush(stdout);
}
syntax highlighted by Code2HTML, v. 0.9.1