This is bbsdaemon.c in view mode; [Download] [Up]
/* $Id: bbsdaemon.c,v 1.2 1995/12/19 19:30:47 eilts Exp eilts $ */
#include "bbs.h"
sigjmp_buf env, env2, envsigint;
boolean bbsdaemon = TRUE;
SIGSET_T old_blocksigset;
sigmaskstatusenum blocksigsetstatus = SIGMASK_UNSET;
extern char **environ;
int main(int argc, char *argv[], char *envp[])
/*
Der Daemon (bbsd)
*/
{
PID_T dpid, dkillpid;
int k, sockd, len;
int status, logfilefd;
SIZE_T envlen;
char conffile[PATH_MAX+1], bbsdpath[PATH_MAX+1], tzenvstr[S_STRLEN], *sp, c;
struct sockaddr_un saun;
confrecordtyp confrecord;
sessionrecordtyp sessionrecords[MAXSESSIONS];
boolean argserror=FALSE, daemon=FALSE;
newsstattyp newsstat;
bbsddbtyp bbsddb;
extern int optind, opterr, optopt;
extern char *optarg;
*tzenvstr = '\0';
if ((sp=getenv("TZ")) != NULL) {
strncpy(tzenvstr,sp,S_STRLEN);
}
#ifdef BBSDNAME
/* Speicher vom alten Environment fuer setproctitle recyclen */
envlen = 0;
for (k=0; k<argc; k++) envlen += strlen(argv[k]);
for (k=0; envp[k]!=NULL; k++) envlen += strlen(envp[k]);
#endif
/* Environment loeschen, Variable vorbesetzen und Optionen einlesen */
environ[0] = (char *)NULL;
if (*tzenvstr != '\0') mdefenv("TZ",tzenvstr);
sessionrecords[0].pid = 0;
confrecord.curses_on = FALSE;
strcpy(conffile,CONFFILE);
strcpy(bbsdpath,BBSDPATH);
len = 0;
opterr = FALSE;
while ((c = getopt(argc, argv, "dc:p:")) != EOF) {
switch (c) {
case 'd':
daemon = TRUE;
break;
case 'c':
strmaxcpy(conffile,optarg,PATH_MAX);
len += strlen(optarg);
break;
case 'p':
strmaxcpy(bbsdpath,optarg,PATH_MAX);
len += strlen(optarg);
break;
case '?':
fprintf(stderr,"bbsd: unrecognized option: -%c\n", optopt);
argserror = TRUE;
}
len += 3;
}
if (argserror || (optind < argc)) {
fprintf(stderr,"usage: bbsd [-d] [-c conffile] [-p bbsdpath]\n");
exit(-1);
}
#ifdef BBSDNAME
/* alten Programmtitel loeschen */
argv[0][envlen-2] = '\0';
for (k=0; k<envlen-2; k++) argv[0][k] = ' ';
#endif
openlog("bbsd",LOG_PID,LOG_BBS);
if (readconffile(&confrecord,conffile) < 0) {
bgerror("bbsd","Terminated: cannot read conffile");
fprintf(stderr,"Terminated: cannot read conffile\n");
exit(1);
}
if (strcmp(confrecord.bbsdpath,bbsdpath) != 0) {
errormsg(E_SYSLOG|E_USER|E_CONSOLE,&confrecord,"bbsd","bbsd",
"Terminated: different pathes for bbsd between conffile(%s) and p-option(%s)",
confrecord.bbsdpath,bbsdpath);
exit(1);
}
/* Bei Bedarf effectiv-uid wechseln */
#ifdef BBSDUID
if (changeuid(confrecord.bbsduid) < 0) {
errormsg(E_SYSLOG|E_USER|E_CONSOLE,&confrecord,"bbsd","bbsd",
"Terminated: changeuid failed");
exit(1);
}
#endif
/* interne Datenbank initialisieren */
bbsddb.u_db = (DBASE_DB *)0;
if (updatebbsddb(&bbsddb,&confrecord) < 0) {
errormsg(E_SYSLOG|E_USER|E_CONSOLE,&confrecord,"bbsd","bbsd",
"cannot create bbsd_usr DB");
exit(1);
}
bbsddb.newsgrp_db = (DBASE_DB *)0;
#ifdef NNTPNEWS
if (updatenewsgroupsdb(&bbsddb,&newsstat,&confrecord) < 0) {
errormsg(E_SYSLOG|E_USER|E_CONSOLE,&confrecord,"bbsd","bbsd",
"cannot create newsgroups DB");
/*
exit(1);
*/
}
#endif
/* Socket zur Kommunikation mit den Clients einrichten */
if ((sockd=socket(AF_UNIX,SOCK_STREAM,0))<0) {
errormsg(E_SYSLOG|E_USER|E_CONSOLE,&confrecord,"bbsd","bbsd","socket: %m");
exit(1);
}
BZERO(&saun, sizeof(saun));
saun.sun_family = AF_UNIX;
strcpy(saun.sun_path,confrecord.sockpath);
len = SUN_LEN(&saun);
#ifdef SCM_RIGHTS
saun.sun_len = len;
#endif
if (testdaemon(confrecord.bbsdpidpath,&confrecord) < 0) {
unlink(confrecord.sockpath);
}
if (bind(sockd,(struct sockaddr *)&saun,len)<0) {
if (errno==EADDRINUSE) {
/* es laeuft bereits ein Daemon */
exit(0);
}
errormsg(E_SYSLOG|E_USER|E_CONSOLE,&confrecord,"bbsd","bbsd","bind %s: %m",
confrecord.sockpath);
unlink(confrecord.sockpath);
exit(1);
}
if (listen(sockd,5)<0) {
errormsg(E_SYSLOG|E_USER|E_CONSOLE,&confrecord,"bbsd","bbsd",
"listen %s: %m", confrecord.sockpath);
unlink(confrecord.sockpath);
exit(1);
}
/* Lockfile loeschen */
unlink(confrecord.lockpath);
/* Logfile oeffnen */
umask(022);
if ((logfilefd=open(confrecord.logfile,O_WRONLY|O_CREAT|O_APPEND,0644))<0) {
errormsg(E_SYSLOG|E_USER|E_CONSOLE,&confrecord,"bbsd","bbsd",
"open Logfile %s: %m", confrecord.logfile);
unlink(confrecord.sockpath);
exit(1);
}
dpid = getpid();
/* in den Hintergrund */
if (daemon) {
dpid = disconnect(&confrecord);
}
/* Daemonenkiller installieren */
dkillpid = -1;
#ifdef DAEMONENKILLER
if (confrecord.bbsdwatchtime > 0) {
dkillpid = daemonenkiller(daemon,argv,envlen,dpid,&confrecord);
}
#endif
/* Pidfiles anlegen */
writepidsfile(&confrecord,dpid,dkillpid);
bgerror("bbsd","Daemon started");
if (sigsetjmp(env,(int)TRUE)==0) { /* als Rueckkehrpunkt fuer manche Signale */
/* Signale in den Signalhaendler umlenken */
setsighandler(SIGUSR1, dsighandler);
setsighandler(SIGINT, dsighandler);
setsighandler(SIGTERM,dsighandler);
if (daemon) {
setsighandler(SIGHUP, SIG_IGN);
setsighandler(SIGQUIT, SIG_IGN);
}
else {
setsighandler(SIGHUP,dsighandler);
setsighandler(SIGQUIT,dsighandler);
}
if (sigsetjmp(env2,(int)TRUE) != 0) {
unlink(confrecord.lockpath);
rmdeadsessions(sessionrecords);
}
if (sigsetjmp(envsigint,(int)TRUE) != 0) {
dumpbbsdstatus(DUMPDIR,sessionrecords,&confrecord,conffile);
if (! daemon) siglongjmp(env,1);
}
setsighandler(SIGPIPE, dsighandler);
/* Anforderungen von den Clients abarbeiten */
do {
doaction(sockd,logfilefd,argv,envlen,sessionrecords,&bbsddb,&newsstat,
&confrecord);
} while (TRUE);
}
/* auf Daemonenkiller warten */
if (dkillpid > 0) {
/* Daemonenkiller killen */
kill(dkillpid,SIGTERM);
while (wait(&status)!=dkillpid);
bgerror("bbsd","Daemonenkiller exited normally");
}
closebbsddb(&bbsddb); /* Datenbank abspeichern */
close(logfilefd); /* Logfile schliessen */
unlink(confrecord.sockpath); /* Kommunikationssocket entfernen */
unlink(confrecord.lockpath); /* Lockfile entfernen */
removepidsfile(&confrecord); /* Pid-Files entfernen */
/* allen Clients ein SIGTERM senden */
for (k=0;sessionrecords[k].pid!=(PID_T)0;k++) {
kill(sessionrecords[k].pid,SIGTERM);
}
bgerror("bbsd","Daemon exited normally");
closelog();
return 0;
}
void dsighandler(int sig)
/*
Signale abarbeiten (Daemon)
*/
{
switch (sig) {
case SIGHUP:
case SIGQUIT:
case SIGTERM:
bgerror("dsighandler","got signal %i",sig);
siglongjmp(env,1);
exit(1);
break;
case SIGINT:
bgerror("dsighandler","got signal sigint, dumping status in %s",DUMPDIR);
siglongjmp(envsigint,1);
exit(1);
break;
case SIGPIPE:
bgerror("dsighandler","got signal sigpipe");
siglongjmp(env2,1);
exit(1);
break;
case SIGUSR1:
bgerror("dsighandler","got signal sigusr1, checking clinets");
siglongjmp(env2,1);
exit(1);
break;
}
}
int rmdeadsessions(sessionrecordtyp sessionrecords[])
/*
alle Sessions auf Existens pruefen und gegebenfalls entfernen.
Anzahl der Sessions wird zurueckgegeben.
*/
{
int k, n=0;
while (sessionrecords[n].pid!=0) {
if (kill(sessionrecords[n].pid,0)<0) {
if (errno==ESRCH) {
k = n;
while (sessionrecords[k].pid!=0) {
cpysr(&(sessionrecords[k]), &(sessionrecords[k+1]));
k++;
}
}
else {
n++;
}
}
else {
n++;
}
}
return(n);
}
void cpysr(sessionrecordtyp *srdest, const sessionrecordtyp *srsrc)
/*
Sessionrecord kopieren
*/
{
srdest->pid = srsrc->pid;
srdest->status = srsrc->status;
strcpy(srdest->tty,srsrc->tty);
strcpy(srdest->user,srsrc->user);
srdest->talking_to = srsrc->talking_to;
}
int dumpbbsdstatus(const char *dumpdir,const sessionrecordtyp sessionrecords[],
const confrecordtyp *confrecord, const char *conffile)
/*
Zustand vom Daemon (Daten) dumpen
*/
{
int n;
char dumppath[PATH_MAX+1];
FILE *fp;
sessionrecordtyp sr;
sprintf(dumppath,"%s/bbsddump.%ld", dumpdir, (long)getpid());
if ((fp=fopen(dumppath,"a")) == NULL) {
bgerror("dumpbbsdstatus","cannot open %s: %m",dumppath);
return(-1);
}
fprintf(fp,"*** BBSD (%ld) STATUS from %s ***\n\n", (long)getpid(),
gettime());
fprintf(fp,"CONFFILE: %s\n", conffile);
fprintf(fp,"Sessions:\n");
n = 0;
sr = sessionrecords[n++];
while (sr.pid != (PID_T)0) {
fprintf(fp,"PID %ld, USER %s at TTY %s STATUS %d, TALKING_TO %ld\n",
(long)sr.pid, sr.user, sr.tty, sr.status, (long)sr.talking_to);
sr = sessionrecords[n++];
}
fclose(fp);
return(0);
}
int changeuid(const char *uid_name)
/*
effective uid wechseln auf den User mit dem Namen uid_name
*/
{
UID_T neweuid, euid;
struct passwd *pwent;
if (uid_name == NULL) return 0;
if ((pwent=getpwnam(uid_name)) == NULL) {
bgerror("changuid","user %s not found",uid_name);
return(-1);
}
neweuid = pwent->pw_uid;
euid = geteuid();
if (euid == neweuid) return 0;
if (euid != 0) {
bgerror("changuid","bbsd must be suid root");
return(-1);
}
if (setuid(neweuid) < 0) {
bgerror("changuid","Changeing euid to %s failed",uid_name);
return(-1);
}
return 0;
}
PID_T disconnect(const confrecordtyp *confrecord)
/*
Programm in Daemon umwandeln
Zurueckgegeben wird der pid des Daemons
*/
{
PID_T pid;
#ifdef NO_SETSID
int fd;
#endif
if ((pid=fork())<0) {
errormsg(E_SYSLOG|E_USER,confrecord,"bbsd","disconnect","fork: %m");
unlink(confrecord->sockpath);
exit(1);
}
if (pid!=0) {
exit(0);
}
fflush(stdout);
close(0);
close(1);
close(2);
pid = getpid();
#ifdef NO_SETSID
setpgrp(0,pid);
if ((fd=open("/dev/tty",O_RDWR))<0) {
/*
bgerror("disconnect","/dev/tty");
exit(1);
*/
}
else {
ioctl(fd, (int) TIOCNOTTY, (char *) 0);
close(fd);
}
#else
setsid();
#endif
return pid;
}
PID_T daemonenkiller(const boolean daemon, char *argv[], const SIZE_T envlen,
const PID_T dpid, const confrecordtyp *confrecord)
/*
Daemonenkiller starten, das ist ein neues Programm, das regelmaessig
die Anzahl der Clients prueft, und den Daemon killt, wenn keine Clients
mehr vorhanden sind.
*/
{
PID_T pid;
sessionrecordtyp sr[1];
boolean startkiller = FALSE;
if ((pid=fork())<0) {
bgerror("daemonenkiller","fork: %m");
exit(1);
}
if (pid==0) {
bbsdaemon = FALSE;
#ifdef BBSDNAME
setproctitle(argv,envlen,"watching daemon (killer)");
#endif
if (sigsetjmp(env2,(int)TRUE)==0) { /* als Rueckkehrpunkt fuer manche Signale */
setsighandler(SIGHUP,kdsighandler);
setsighandler(SIGINT,kdsighandler);
setsighandler(SIGQUIT,kdsighandler);
setsighandler(SIGTERM,kdsighandler);
setsighandler(SIGUSR1,SIG_IGN);
while(! startkiller) {
if (access(confrecord->lockpath,F_OK)<0) {
startkiller = (getsessions(sr,0,confrecord)>0);
}
sleep(confrecord->bbsdwatchtime);
}
while (TRUE) {
/* Clients pruefen */
if (access(confrecord->lockpath,F_OK)<0 && daemon) {
if (getsessions(sr,0,confrecord)==0) {
bbslog(LOG_FILE, confrecord,"Daemon exited (no clients)");
kill(dpid,SIGTERM);
exit(0);
}
}
sleep(confrecord->bbsdwatchtime);
}
}
kill(dpid,SIGTERM);
exit(0);
}
return pid;
}
void kdsighandler(int sig)
/*
Signale abarbeiten (Daemonenkiller)
*/
{
switch (sig) {
case SIGHUP:
case SIGINT:
case SIGQUIT:
case SIGTERM:
bgerror("kdsighandler","got signal %i",sig);
siglongjmp(env2,1);
exit(1);
break;
}
}
int doaction(const int sockd, const int logfilefd, char *argv[],
const SIZE_T envlen, sessionrecordtyp sessionrecords[],
bbsddbtyp *bbsddb, newsstattyp *newsstat,
const confrecordtyp *confrecord)
/*
Abarbeitungsschleife fuer die Jobs von den Clients
*/
{
int flen, ns, n, befehl;
PID_T clientpid;
char str[STRLEN+1];
struct sockaddr_un fsaun;
SIGSET_T sigdoactionmask;
#ifdef BBSDNAME
const char *befnamen[] ={"","LOG","GETSESSIONS","ADDSESSION","REMOVESESSION",
"GETUSERRECORD","SAVEUSERRECORD","TALKINIT","TALKABORT","TALKREMOTE",
"SENDSTATUS","GETALLUSERNAMES","RECMESSAGE","SENDMESSAGE",
"REMOVEMESSAGE","LISTMESSAGES","RECEIVEMAIL","GETALLNEWSGOUPS",
"GETNEWSRC","SAVENEWSRC","CHECKGROUP","???"};
#endif
#ifdef BBSDNAME
setproctitle(argv,envlen,"waiting for connection");
#endif
/* Verbindungsanforderung vom Client annehmen */
BZERO(&fsaun, sizeof(fsaun));
fsaun.sun_family = AF_UNIX;
strcpy(fsaun.sun_path,confrecord->sockpath);
flen = SUN_LEN(&fsaun);
#ifdef SCM_RIGHTS
fsaun.sun_len = flen;
#endif
if ((ns=accept(sockd,(struct sockaddr *)&fsaun,&flen))<0) {
bgerror("doaction","accept %s: %m",confrecord->sockpath);
return -1;
}
/* Signale blockieren */
sigemptyset(&sigdoactionmask);
sigaddset(&sigdoactionmask, SIGUSR1);
sigaddset(&sigdoactionmask, SIGINT);
sigprocmask(SIG_BLOCK,&sigdoactionmask,NULL);
if (recvn(ns,(void *)&clientpid,(SIZE_T)sizeof(PID_T),0)<0) {
bgerror("doaction","recv %s: %m",confrecord->sockpath);
return -1;
}
/* Authorisierung */
if (recvn(ns,(void *)&n,(SIZE_T)sizeof(int),0)<0) {
bgerror("doaction","recv %s: %m",confrecord->sockpath);
return -1;
}
if (recvn(ns,(void *)str,(SIZE_T)n,0)<0) {
bgerror("doaction","recv %s: %m",confrecord->sockpath);
return -1;
}
if (strncmp(str,confrecord->authkey,STRLEN) != 0) {
sleep(2);
n = -1;
sendn(ns,(void *)&n,(SIZE_T)sizeof(int),0);
close(ns);
bgerror("doaction","wrong authorisation");
return n;
}
sendn(ns,(void *)&n,(SIZE_T)sizeof(int),0);
/* Befehl lesen */
if (recvn(ns,(void *)str,(SIZE_T)BEFSTRLEN,0)<0) {
bgerror("doaction","recv %s: %m",confrecord->sockpath);
return -1;
}
if (str[0]!='#' || str[3]!='\n') {
bgerror("doaction","wrong command-format");
return -1;
}
befehl = atoi(&(str[1]));
#ifdef BBSDNAME
setproctitle(argv,envlen,"serving client %ld: %s",(long)clientpid,
befnamen[befehl]);
#endif
/* Befehl ausfuehren, waehrend der Ausfuehrung locken wenn daemonenkiller
laeuft */
if (confrecord->bbsdwatchtime > 0) {
n = mklockfile(confrecord->lockpath);
}
else {
n = 0;
}
if (n == 0) {
switch (befehl) {
case DO_LOG:
n = dputinlog(logfilefd,ns);
break;
case DO_SENDSTATUS:
n = dsendstatustodaemon(sessionrecords,confrecord,ns);
break;
case DO_TALKINIT:
n = diniconnecttouser(sessionrecords,confrecord,ns);
break;
case DO_TALKABORT:
n = dabortconnecttouser(sessionrecords,confrecord,ns);
break;
case DO_TALKREMOTE:
n = dremconnecttouser(sessionrecords,confrecord,ns);
break;
case DO_GETSESSIONS:
n = dgetsessions(sessionrecords,ns);
break;
case DO_ADDSESSION:
n = daddsession(sessionrecords,ns);
break;
case DO_REMOVESESSION:
n = dremovesession(sessionrecords,ns);
break;
case DO_GETUSERRECORD:
updatebbsddb(bbsddb,confrecord);
n = dgetuserrecord(ns,confrecord);
break;
case DO_SAVEUSERRECORD:
n = dsaveuserrecord(ns,confrecord);
break;
case DO_GETALLUSERNAMES:
n = dgetallusernames(ns,bbsddb,confrecord);
break;
case DO_SENDMESSAGE:
updatebbsddb(bbsddb,confrecord);
n = dsendmessage(ns,bbsddb,confrecord);
break;
case DO_GETMESSAGE:
n = dgetmessage(ns,confrecord);
break;
case DO_REMOVEMESSAGE:
n = dremovemessage(ns,confrecord);
break;
case DO_LISTMESSAGES:
n = dlistmessages(ns,confrecord);
break;
case DO_RECEIVEMAIL:
updatebbsddb(bbsddb,confrecord);
n = dreceivemail(ns,bbsddb,confrecord);
break;
#ifdef NNTPNEWS
case DO_GETALLNEWSGROUPS:
if ((n=updatenewsgroupsdb(bbsddb,newsstat,confrecord)) >= 0) {
n = dgetallnewsgroups(ns,bbsddb,confrecord);
}
break;
case DO_GETNEWSRC:
n = dgetnewsrc(ns,confrecord);
break;
case DO_SAVENEWSRC:
n = dsavenewsrc(ns,confrecord);
break;
case DO_CHECKGROUP:
if ((n=updatenewsgroupsdb(bbsddb,newsstat,confrecord)) >= 0) {
n = dcheckgroup(ns,bbsddb,confrecord);
}
break;
#endif
default:
syslogn(LOG_ERR,"unknown command");
n = -2;
}
/* Befehl quittieren */
sendn(ns,(void *)&n,(SIZE_T)sizeof(int),0);
close(ns);
}
else {
bgerror("doaction","lock failed, command could not be performed");
}
if (confrecord->bbsdwatchtime > 0) {
unlink(confrecord->lockpath);
}
sigprocmask(SIG_UNBLOCK,&sigdoactionmask,NULL);
return n;
}
int writepidsfile(const confrecordtyp *confrecord, const PID_T dpid,
const PID_T kdpid)
/*
Liest eventuell vorhandene Pid-Files vom Daemon und Daemonenkiller
und sendet SIGTERM an sie.
Legt neue Pid-Files an
*/
{
PID_T pid;
char str[S_STRLEN+1];
FILE *fd;
if (access(confrecord->bbsdpidpath,F_OK)==0) {
if ((fd=fopen(confrecord->bbsdpidpath,"r")) != NULL) {
fgetnln(str,S_STRLEN,fd);
fclose(fd);
/* hier droht eine Race-Condition: SIGTERM killt den alten Daemon,
der dann seinerseits sein Pid-File loeschen will, das jedoch hier
neu angelegt wird ...
*/
pid = (PID_T)atoi(str);
if (kill(pid,SIGTERM) == 0) {
bgerror("writepidsfile","old Daemon[%i] killed",pid);
while(kill(pid,0)==0) sleep(2); /* wegen Race-Condition */
}
else {
bgerror("writepidsfile","stale pidfile %s[%i]",
confrecord->bbsdpidpath,pid);
}
}
else {
bgerror("writepidsfile","pidfile %s exists but is unreadable",
confrecord->bbsdpidpath);
}
unlink(confrecord->bbsdpidpath);
}
if ((fd=fopen(confrecord->bbsdpidpath,"w")) == NULL) {
bgerror("writepidsfile","cannot create pidsfile %s: %m",
confrecord->bbsdpidpath);
return(-1);
}
fprintf(fd,"%ld\n",(long)dpid);
fclose(fd);
if (access(confrecord->bbsdkpidpath,F_OK)==0) {
if ((fd=fopen(confrecord->bbsdkpidpath,"r")) != NULL) {
fgetnln(str,S_STRLEN,fd);
fclose(fd);
pid = (PID_T)atoi(str);
if (kill(pid,SIGTERM) == 0) {
bgerror("writepidsfile","old Daemonkiller[%i] killed",pid);
while(kill(pid,0)==0) sleep(2); /* wegen Race-Condition */
}
else {
bgerror("writepidsfile","stale pidfile (killer) %s[%i]",confrecord->bbsdkpidpath,pid);
}
}
else {
bgerror("writepidsfile","pidfile (killer) %s exists but unreadable",
confrecord->bbsdkpidpath);
}
unlink(confrecord->bbsdkpidpath);
}
if (kdpid > 0) {
if ((fd=fopen(confrecord->bbsdkpidpath,"w")) == NULL) {
bgerror("writepidsfile","cannot create pidsfile (killer) %s: %m",
confrecord->bbsdkpidpath);
return(-1);
}
fprintf(fd,"%ld\n",(long)kdpid);
fclose(fd);
}
return 0;
}
int removepidsfile(const confrecordtyp *confrecord)
{
unlink(confrecord->bbsdpidpath);
unlink(confrecord->bbsdkpidpath);
return 0;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.