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.