#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <memory.h>
#include <sys/utsname.h>
#include <sys/tiuser.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <values.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <sys/ipc.h>
#include <sys/poll.h>
#include <sys/stropts.h>
#include <sac.h>
#include <utmpx.h>
#include "lsparam.h"
#include "lsfiles.h"
#include "lserror.h"
#include "lsnlsmsg.h"
#include "lssmbmsg.h"
#include "lsdbf.h"
#include "listen.h"
#define NAMESIZE (NAMEBUFSZ-1)
#define SPLhi() Splflag = 1
#define SPLlo() Splflag = 0
#define GEN 1
#define LOGIN 0
int NLPS_proc = 0;
pid_t Pid;
char *Progname;
static char Provbuf[PATHSIZE];
char *Provider = Provbuf;
char *Netspec = NETSPEC;
char *Minor_prefix;
int Dbf_entries;
int Valid_addrs;
struct pollfd *Pollfds;
dbf_t *Dbfhead;
dbf_t *Newdbf;
char *Server_cmd_lines;
char *New_cmd_lines;
long Ndesc;
int Readdb;
struct netconfig *Netconf;
struct call_list Free_call;
struct call_list *Free_call_p = &Free_call;
struct call_list *Priv_call;
#ifndef DEBUGMODE
#define USEDFDS 6
#else
#define USEDFDS 7
FILE *Debugfp;
#endif
int Acceptfd;
int Sacpipefd;
int Pmpipefd;
int Passfd;
int Pidfd;
FILE *Logfp;
struct pmmsg Pmmsg;
int State = PM_STARTING;
char Mytag[15];
char Lastmsg[BUFSIZ];
int Logmax = LOGMAX;
int Splflag;
static char *badnspmsg = "Bad netspec on command line ( Pathname too long )";
static char *badstart = "Listener failed to start properly";
static char *nologfile = "Unable to open listener log file during initialization";
static char *usage = "Usage: listen [ -m minor_prefix ] network_device";
static char *nopmtag = "Fatal error: Unable to get PMTAG from environment";
static char tzenv[BUFSIZ];
#define TZFILE "/etc/default/init"
#define TZSTR "TZ="
void check_sac_mesg();
void rpc_register();
void rpc_unregister();
extern struct netconfig *getnetconfigent();
extern char *t_alloc();
extern void logexit();
extern int t_errno;
extern int errno;
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
static void mod_prvaddr(void);
static void pitchcall(struct call_list *pending, struct t_discon *discon);
static void clr_call(struct t_call *call);
static void trycon(struct call_list *phead, int fd);
static void send_dis(struct call_list *phead, int fd);
static void doevent(struct call_list *phead, int fd);
static void listen(void);
static void rst_signals(void);
static void catch_signals(void);
static void net_open(void);
static void init_files(void);
static void pid_open(void);
int
main(int argc, char **argv)
{
struct stat buf;
int ret;
char scratch[BUFSIZ];
char log[BUFSIZ];
char olog[BUFSIZ];
char *scratch_p = scratch;
char *mytag_p;
FILE *fp;
extern char *getenv();
char *parse();
int c;
extern char *optarg;
extern int optind;
int i;
char *Mytag_p = Mytag;
if ((mytag_p = getenv("PMTAG")) == NULL) {
exit(1);
}
strcpy(Mytag, mytag_p);
sprintf(log, "%s/%s/%s", ALTDIR, Mytag_p, LOGNAME);
sprintf(olog, "%s/%s/%s", ALTDIR, Mytag_p, OLOGNAME);
if (stat(log, &buf) == 0) {
unlink(olog);
rename(log, olog);
}
if ((i = open(log, O_WRONLY|O_CREAT|O_APPEND, 0444)) < 0)
logexit(1, nologfile);
if ((ret = fcntl(i, F_DUPFD, 5)) != 5)
logexit(1, nologfile);
Logfp = fdopen(ret, "a+");
if ((mytag_p = getenv("PMTAG")) == NULL) {
logexit(1, nopmtag);
}
strcpy(Mytag, mytag_p);
(void) umask(022);
Readdb = FALSE;
if (geteuid() != (uid_t) 0) {
logmessage("Must be root to start listener");
logexit(1, badstart);
}
while ((c = getopt(argc, argv, "m:")) != EOF)
switch (c) {
case 'm':
Minor_prefix = optarg;
break;
default:
logexit(1, usage);
break;
}
if ((Netspec = argv[optind]) == NULL) {
logexit(1, usage);
}
if ((Netconf = getnetconfigent(Netspec)) == NULL) {
sprintf(scratch, "no netconfig entry for <%s>", Netspec);
logmessage(scratch);
logexit(1, badstart);
}
if (!Minor_prefix)
Minor_prefix = argv[optind];
if ((int) strlen(Netspec) > PATHSIZE) {
logmessage(badnspmsg);
logexit(1, badstart);
}
strcpy(Provbuf, "/dev/");
strcat(Provbuf, Netspec);
(void) umask(0);
init_files();
pid_open();
#ifdef DEBUGMODE
sprintf(scratch, "%s/%s/%s", ALTDIR, Mytag, DBGNAME);
Debugfp = fopen(scratch, "w");
#endif
#ifdef DEBUGMODE
if ((!Logfp) || (!Debugfp))
#else
if (!Logfp)
#endif
logexit(1, badstart);
if (getenv("TZ") == NULL) {
fp = fopen(TZFILE, "r");
if (fp) {
while (fgets(tzenv, BUFSIZ, fp)) {
if (tzenv[strlen(tzenv) - 1] == '\n')
tzenv[strlen(tzenv) - 1] = '\0';
if (!strncmp(TZSTR, tzenv, strlen(TZSTR))) {
putenv(parse(tzenv));
break;
}
}
fclose(fp);
}
else {
sprintf(scratch, "couldn't open %s, default to GMT",
TZFILE);
logmessage(scratch);
}
}
logmessage("@(#)listen:listen.c 1.19.9.1");
#ifdef DEBUGMODE
logmessage("Listener process with DEBUG capability");
#endif
sprintf(scratch, "Listener port monitor tag: %s", Mytag_p);
logmessage(scratch);
DEBUG((9, "Minor prefix: %s Netspec %s", Minor_prefix, Netspec));
Pmmsg.pm_maxclass = MAXCLASS;
strcpy(Pmmsg.pm_tag, Mytag_p);
Pmmsg.pm_size = 0;
if ((scratch_p = getenv("ISTATE")) == NULL)
logexit(1, "ERROR: ISTATE variable not set in environment");
if (!strcmp(scratch_p, "enabled")) {
State = PM_ENABLED;
logmessage("Starting state: ENABLED");
}
else {
State = PM_DISABLED;
logmessage("Starting state: DISABLED");
}
Progname = strrchr(argv[0], '/');
if (Progname && Progname[1])
++Progname;
else
Progname = argv[0];
catch_signals();
Ndesc = ulimit(4,0L);
read_dbf(DB_INIT);
net_open();
for (i = 3; i < Ndesc; i++) {
fcntl(i, F_SETFD, 1);
}
logmessage("Initialization Complete");
listen();
return (0);
}
static char *pidopenmsg ="Can't create process ID file in home directory";
static char *pidlockmsg ="Can't lock PID file: listener may already be running";
static void
pid_open(void)
{
int ret;
unsigned int i;
char pidstring[20];
if ((Pidfd = open(PIDNAME, PIDOFLAG, PIDMODE)) == -1) {
logmessage(pidopenmsg);
error(E_CREAT, EXIT | NOCORE | NO_MSG);
}
if (lockf(Pidfd, 2, 0L) == -1) {
logmessage(pidlockmsg);
logexit(1, badstart);
}
Pid = getpid();
i = sprintf(pidstring, "%ld", Pid) + 1;
ftruncate(Pidfd, 0);
while ((ret = write(Pidfd, pidstring, i)) != i) {
if (errno == EINTR)
continue;
if (ret < 0)
sys_error(E_PIDWRITE, EXIT);
else
error(E_PIDWRITE, EXIT);
}
}
static char *pmopenmsg = "Can't open pipe to read SAC messages";
static char *sacopenmsg = "Can't open pipe to respond to SAC messages";
static void
init_files(void)
{
close(0);
if ((Acceptfd = open("/dev/null", O_RDWR)) != 0) {
logmessage("Trouble opening /dev/null");
sys_error(E_SYS_ERROR, EXIT | NOCORE);
}
close(1);
if ((Sacpipefd = open(SACPIPE, O_RDWR|O_NDELAY)) != 1) {
logmessage(sacopenmsg);
error(E_CREAT, EXIT | NOCORE | NO_MSG);
}
close(2);
if ((Pmpipefd = open(PMPIPE, O_RDWR|O_NDELAY)) != 2) {
logmessage(pmopenmsg);
error(E_CREAT, EXIT | NOCORE | NO_MSG);
}
close(3);
if ((Passfd = dup(Acceptfd)) != 3) {
logmessage("Trouble duping /dev/null");
sys_error(E_SYS_ERROR, EXIT | NOCORE);
}
}
static void
net_open(void)
{
#ifdef CHARADDR
char pbuf[NAMEBUFSZ + 1];
#endif
int i;
dbf_t *dp;
char scratch[BUFSIZ];
DEBUG((9,"in net_open"));
Free_call_p->cl_head = (struct callsave *) NULL;
Free_call_p->cl_tail = (struct callsave *) NULL;
if ((Priv_call = (struct call_list *) malloc(Ndesc *(sizeof(
struct call_list)))) == NULL)
error(E_MALLOC,NOCORE | EXIT);
i = 0;
Valid_addrs = 0;
while ( (i < Dbf_entries) ) {
dp = &Dbfhead[i];
if (!(dp->dbf_sflags & DFLAG)) {
if (add_prvaddr(dp) == 0)
Valid_addrs++;
}
i++;
}
i = 0;
while ( (i < Dbf_entries) ) {
dp = &Dbfhead[i];
if (dp->dbf_sflags & DFLAG) {
if (add_prvaddr(dp) == 0)
Valid_addrs++;
}
i++;
}
sprintf(scratch, "Net opened, %d %s bound, %d fds free", Valid_addrs,
(Valid_addrs == 1) ? "address" : "addresses",
Ndesc-Valid_addrs-USEDFDS);
logmessage(scratch);
}
void
queue(head, cp)
struct call_list *head;
struct callsave *cp;
{
DEBUG((9,"in queue"));
if (head->cl_tail == (struct callsave *) NULL) {
cp->c_np = (struct callsave *) NULL;
head->cl_head = head->cl_tail = cp;
}
else {
cp->c_np = head->cl_tail->c_np;
head->cl_tail->c_np = cp;
head->cl_tail = cp;
}
}
void
pqueue(head, cp)
struct call_list *head;
struct callsave *cp;
{
if (head->cl_head == (struct callsave *) NULL) {
cp->c_np = (struct callsave *) NULL;
head->cl_head = head->cl_tail = cp;
}
else {
cp->c_np = head->cl_head;
head->cl_head = cp;
}
}
struct callsave *
dequeue(head)
struct call_list *head;
{
struct callsave *ret;
DEBUG((9,"in dequeue"));
if (head->cl_head == (struct callsave *) NULL) {
#ifdef OLD
DEBUG((9,"cl_head = null"));
error(E_CANT_HAPPEN, EXIT);
#endif
DEBUG((9, "NULL return"));
return((struct callsave *) NULL);
}
ret = head->cl_head;
head->cl_head = ret->c_np;
if (head->cl_head == (struct callsave *) NULL)
head->cl_tail = (struct callsave *) NULL;
return(ret);
}
int
open_bind(name, qlen, clen, conp, adrp)
char *name;
int qlen;
int clen;
unsigned int *conp;
char **adrp;
{
int fd;
int ret;
DEBUG((9,"in open_bind, qlen=%d clen=%d conp=%d",qlen,clen,conp));
while ((fd = t_open(Provider, NETOFLAG, NULL)) < 0) {
if (t_errno == TSYSERR) {
switch (errno) {
case EINTR:
continue;
case EMFILE:
return(-2);
break;
case ENXIO:
case ENOSR:
case ENOSPC:
case EAGAIN:
tli_error(E_FD1OPEN, CONTINUE);
logmessage("No network minor devices (ENXIO/ENOSR)");
return(-1);
break;
}
DEBUG((9,"problem in t_open"));
tli_error(E_FD1OPEN, EXIT);
}
}
ret = bind(fd, name, qlen, clen, adrp);
DEBUG((9, "bind returns %d", ret));
if (ret < 0) {
t_close(fd);
return(-3);
}
if (conp)
*conp = ret;
return(fd);
}
int
bind(fd, name, qlen, clen, ap)
int fd;
char *name;
int qlen;
int clen;
char **ap;
{
struct t_bind *req = (struct t_bind *)0;
struct t_bind *ret = (struct t_bind *)0;
char *p, *q;
unsigned int retval;
extern void nlsaddr2c();
extern int memcmp();
extern int errno;
#ifdef CHARADDR
char pbuf[NAMEBUFSZ + 1];
#endif
char scratch[BUFSIZ];
DEBUG((9,"in bind, fd = %d, clen = %d", fd, clen));
if (clen) {
errno = t_errno = 0;
while (!(req = (struct t_bind *)t_alloc(fd,T_BIND,T_ALL)) ) {
if ((t_errno != TSYSERR) || (errno != EAGAIN))
tli_error( E_T_ALLOC, EXIT);
else
tli_error( E_T_ALLOC, CONTINUE);
}
errno = t_errno = 0;
while (!(ret = (struct t_bind *)t_alloc(fd,T_BIND,T_ALL)) ) {
if ((t_errno != TSYSERR) || (errno != EAGAIN))
tli_error( E_T_ALLOC, EXIT);
else
tli_error( E_T_ALLOC, CONTINUE);
}
if (clen > (int) req->addr.maxlen) {
sprintf(scratch,"Truncating name size from %d to %d",
clen, req->addr.maxlen);
logmessage(scratch);
clen = req->addr.maxlen;
}
if (clen == -1) {
req->addr.len = 0;
}
else {
(void)memcpy(req->addr.buf, name, clen);
req->addr.len = clen;
}
req->qlen = qlen;
#if defined(CHARADDR) && defined(DEBUGMODE)
(void)memcpy(pbuf, req->addr.buf, req->addr.len);
pbuf[req->addr.len] = (char)0;
DEBUG((3,"bind: fd=%d, logical name=%c%s%c, len=%d",
fd, '\"',pbuf, '\"', req->addr.len));
#endif
#if defined(CHARADDR) && defined(DEBUGMODE)
(void)memcpy(pbuf, req->addr.buf, req->addr.len);
pbuf[req->addr.len] = (char)0;
DEBUG((3,"bind: fd=%d, address=%c%s%c, len=%d",
fd, '\"',pbuf, '\"', req->addr.len));
#endif
}
if (t_bind(fd, req, ret)) {
DEBUG((1,"t_bind failed; t_errno %d errno %d", t_errno, errno));
if (qlen)
tli_error(E_T_BIND, EXIT | NOCORE);
if ((t_errno == TNOADDR) || ((t_errno == TSYSERR) && (errno == EAGAIN))) {
tli_error(E_T_BIND, CONTINUE);
t_close(fd);
if (clen) {
if ( t_free((char *)req, T_BIND) )
tli_error(E_T_FREE, EXIT);
if ( t_free((char *)ret, T_BIND) )
tli_error(E_T_FREE, EXIT);
}
return(-1);
}
tli_error(E_T_BIND, EXIT | NOCORE);
}
DEBUG((9, "t_bind succeeded"));
if (clen) {
retval = ret->qlen;
if (clen == -1) {
*ap = (char *) malloc(((ret->addr.len) << 1) + 3);
if (*ap) {
(*ap)[0] = '\\';
(*ap)[1] = 'x';
nlsaddr2c(*ap+2,ret->addr.buf,(int)ret->addr.len);
}
}
else if ( (ret->addr.len != req->addr.len) ||
(memcmp( req->addr.buf, ret->addr.buf, (int) req->addr.len)) ) {
p = (char *) malloc(((ret->addr.len) << 1) + 1);
q = (char *) malloc(((req->addr.len) << 1) + 1);
if (p && q) {
nlsaddr2c(p, ret->addr.buf, (int)ret->addr.len);
nlsaddr2c(q, req->addr.buf, (int)req->addr.len);
sprintf(scratch, "Requested address \\x%s", q);
logmessage(scratch);
sprintf(scratch, "Actual address \\x%s", p);
logmessage(scratch);
free(p);
free(q);
}
DEBUG((9, "failed to bind requested address"));
t_unbind(fd);
t_close(fd);
if ( t_free((char *)req, T_BIND) )
tli_error(E_T_FREE, EXIT);
if ( t_free((char *)ret, T_BIND) )
tli_error(E_T_FREE, EXIT);
return(-1);
}
if ( t_free((char *)req, T_BIND) )
tli_error(E_T_FREE, EXIT);
if ( t_free((char *)ret, T_BIND) )
tli_error(E_T_FREE, EXIT);
return(retval);
}
return((unsigned int) 0);
}
sigset_t Oset;
struct sigaction Sigterm;
struct sigaction Sigcld;
static void
catch_signals(void)
{
sigset_t sset;
sigset_t eset;
struct sigaction sigact;
extern void sigterm();
(void) sigfillset(&sset);
(void) sigdelset(&sset, SIGTERM);
(void) sigdelset(&sset, SIGCLD);
(void) sigprocmask(SIG_SETMASK, &sset, &Oset);
sigact.sa_flags = 0;
sigact.sa_handler = sigterm;
sigact.sa_mask = sset;
sigaction(SIGTERM, &sigact, &Sigterm);
sigact.sa_flags = SA_NOCLDWAIT;
sigact.sa_handler = SIG_IGN;
sigact.sa_mask = sset;
sigaction(SIGCLD, &sigact, &Sigcld);
}
static void
rst_signals(void)
{
struct sigaction sigact;
sigaction(SIGTERM, &Sigterm, NULL);
sigaction(SIGCLD, &Sigcld, NULL);
sigprocmask(SIG_SETMASK, &Oset, NULL);
}
void
sigterm()
{
extern char *shaddr;
extern char *sh2addr;
error(E_SIGTERM, EXIT | NORMAL | NOCORE);
}
static char *dbfnewdmsg = "Using new data base file";
static void
listen(void)
{
int i;
dbf_t *dbp = Dbfhead;
struct pollfd *sp;
struct call_list *phead;
DEBUG((9,"in listen, tag %s", Pmmsg.pm_tag));
if ((Pollfds = (struct pollfd *) malloc(Ndesc * sizeof(struct pollfd)))
== NULL)
error(E_MALLOC,NOCORE | EXIT);
sp = Pollfds;
sp->fd = Pmpipefd;
sp->events = POLLIN;
sp->revents = 0;
sp++;
for (dbp = Dbfhead; dbp && dbp->dbf_svc_code; dbp++) {
if (dbp->dbf_fd >= 0) {
sp->fd = dbp->dbf_fd;
DEBUG((9, "adding %d to poll struct", dbp->dbf_fd));
sp->events = POLLIN;
sp->revents = 0;
sp++;
}
}
errno = t_errno = 0;
for (;;) {
DEBUG((9,"listen(): TOP of loop"));
if (poll(Pollfds, Valid_addrs + 1, -1) < 0) {
if (errno == EINTR)
continue;
sys_error(E_POLL, EXIT);
}
else {
for (i = 0, sp = Pollfds; i < Valid_addrs + 1; i++, sp++) {
switch (sp->revents) {
case POLLIN:
if (sp->fd == Pmpipefd) {
DEBUG((9,"sac message received"));
check_sac_mesg();
}
else {
DEBUG((9,"Connection requested "));
phead = ((sp->fd) + Priv_call);
doevent(phead, (sp->fd));
if (State == PM_ENABLED)
trycon(phead, (sp->fd));
else
send_dis(phead, (sp->fd));
}
break;
case 0:
break;
case POLLERR:
logmessage("poll() returned POLLERR");
error(E_SYS_ERROR, EXIT | NO_MSG);
break;
case POLLHUP:
logmessage("poll() returned POLLHUP");
error(E_SYS_ERROR, EXIT | NO_MSG);
break;
case POLLNVAL:
logmessage("poll() returned POLLNVAL");
error(E_SYS_ERROR, EXIT | NO_MSG);
break;
case POLLPRI:
logmessage("poll() returned POLLPRI");
error(E_SYS_ERROR, EXIT | NO_MSG);
break;
case POLLOUT:
logmessage("poll() returned POLLOUT");
error(E_SYS_ERROR, EXIT | NO_MSG);
break;
default:
logmessage("poll() returned unrecognized event");
error(E_SYS_ERROR, EXIT | NO_MSG);
}
sp->revents = 0;
}
}
if (Readdb) {
DEBUG((9,"dbf file has been modified"));
logmessage("Re-reading database");
close(Acceptfd);
if (!read_dbf(DB_REREAD)) {
dup(Passfd);
mod_prvaddr();
}
else {
dup(Passfd);
logmessage(dbfnewdmsg);
}
Readdb = FALSE;
}
}
}
void
check_sac_mesg()
{
int length;
struct sacmsg sacmsg;
DEBUG((9, "in check_sac_mesg..."));
while ((length = read(Pmpipefd, &sacmsg, sizeof(sacmsg))) != 0) {
if (length < 0) {
if (errno == EINTR)
continue;
DEBUG((9, "read of _pmpipe failed"));
return;
}
switch (sacmsg.sc_type) {
case SC_STATUS:
DEBUG((9, "Got SC_STATUS message"));
Pmmsg.pm_type = PM_STATUS;
Pmmsg.pm_state = State;
break;
case SC_ENABLE:
DEBUG((9, "Got SC_ENABLE message"));
if (State != PM_ENABLED)
logmessage("New state: ENABLED");
Pmmsg.pm_type = PM_STATUS;
State = PM_ENABLED;
Pmmsg.pm_state = PM_ENABLED;
break;
case SC_DISABLE:
DEBUG((9, "Got SC_DISABLE message"));
if (State != PM_DISABLED)
logmessage("New state: DISABLED");
Pmmsg.pm_type = PM_STATUS;
State = PM_DISABLED;
Pmmsg.pm_state = PM_DISABLED;
break;
case SC_READDB:
DEBUG((9, "Got SC_READDB message"));
Readdb = TRUE;
Pmmsg.pm_type = PM_STATUS;
Pmmsg.pm_state = State;
break;
default:
DEBUG((9, "Got UNKNOWN message"));
Pmmsg.pm_type = PM_UNKNOWN;
Pmmsg.pm_state = State;
logmessage("Received unknown message from sac -- ignored");
break;
}
DEBUG((9, "Responding with state %d", Pmmsg.pm_state));
while (write(Sacpipefd, &Pmmsg, sizeof(Pmmsg)) != sizeof(Pmmsg)) {
if (errno == EINTR)
continue;
DEBUG((9, "sanity response failed"));
break;
}
}
}
static void
doevent(struct call_list *phead, int fd)
{
static struct t_discon *disc;
struct callsave *current;
struct t_call *call;
char scratch[BUFSIZ];
DEBUG((9, "in doevent"));
switch (t_look(fd)) {
case 0:
sys_error(E_POLL, EXIT);
break;
case T_LISTEN:
DEBUG((9, "case t_listen "));
current = dequeue(Free_call_p);
call = current->c_cp;
if (t_listen(fd, call) < 0) {
tli_error(E_T_LISTEN, CONTINUE);
clr_call(call);
queue(Free_call_p, current);
return;
}
queue(phead, current);
DEBUG((9, "incoming call seq # %d", call->sequence));
break;
case T_DISCONNECT:
DEBUG((9, "case t_disconnect"));
if (disc == NULL) {
while (!(disc = (struct t_discon *)t_alloc(fd, T_DIS, T_ALL)) ) {
if (t_errno == TBADF)
DEBUG((9,"listen - fd not transport end point"));
if ((t_errno != TSYSERR) || (errno != EAGAIN))
tli_error(E_T_ALLOC, EXIT);
else
tli_error(E_T_ALLOC, CONTINUE);
}
}
if (t_rcvdis(fd, disc) < 0) {
tli_error(E_T_RCVDIS, EXIT);
}
sprintf(scratch, "Disconnect on fd %d, seq # %d", fd, disc->sequence);
logmessage(scratch);
DEBUG((9, "incoming disconnect seq # %d", disc->sequence));
pitchcall(phead, disc);
break;
default:
DEBUG((9, "case default"));
tli_error(E_T_LOOK, CONTINUE);
break;
}
}
static void
send_dis(struct call_list *phead, int fd)
{
struct t_call *call;
struct callsave *current;
char scratch[BUFSIZ];
DEBUG((9, "sending disconnect"));
while (!EMPTYLIST(phead)) {
current = dequeue(phead);
call = current->c_cp;
if (t_snddis(fd, call) < 0) {
if (t_errno == TLOOK) {
DEBUG((9, "collision during snddis"));
pqueue(phead, current);
return;
}
else
tli_error(E_T_SNDDIS, CONTINUE);
}
sprintf(scratch, "Incoming call while disabled: fd %d, seq %d", fd, call->sequence);
logmessage(scratch);
clr_call(call);
queue(Free_call_p, current);
}
return;
}
static void
trycon(struct call_list *phead, int fd)
{
struct callsave *current;
struct t_call *call;
int i;
pid_t pid;
dbf_t *dbp;
char scratch[BUFSIZ];
extern dbf_t *getentry();
DEBUG((9, "in trycon"));
while (!EMPTYLIST(phead)) {
current = dequeue(phead);
call = current->c_cp;
if ((dbp = getentry(fd)) == NULL) {
sprintf(scratch, "No service bound to incoming fd %d: call disconnected", fd);
logmessage(scratch);
t_snddis(fd, call);
clr_call(call);
queue(Free_call_p, current);
continue;
}
if (dbp->dbf_flags & DBF_OFF) {
sprintf(scratch, "Request for service on fd %d denied: disabled", fd);
logmessage(scratch);
t_snddis(fd, call);
clr_call(call);
queue(Free_call_p, current);
continue;
}
DEBUG((9, "try to accept #%d", call->sequence));
SPLhi();
close(Acceptfd);
if ((Acceptfd = open_bind(NULL, 0, 0, (unsigned int *) 0, NULL)) != 0) {
error(E_OPENBIND, CONTINUE);
clr_call(call);
queue(Free_call_p, current);
continue;
}
SPLlo();
if (t_accept(fd, Acceptfd, call) < 0) {
if (t_errno == TLOOK) {
t_close(Acceptfd);
SPLhi();
if (dup(Passfd) != 0)
logmessage("Trouble duping fd 0");
SPLlo();
logmessage("Incoming call during t_accept -- queueing current call");
DEBUG((9, "save call #%d", call->sequence));
pqueue(phead, current);
return;
}
else {
t_close(Acceptfd);
SPLhi();
if (dup(Passfd) != 0)
logmessage("Trouble duping fd 0");
SPLlo();
tli_error(E_T_ACCEPT, CONTINUE);
clr_call(call);
queue(Free_call_p, current);
continue;
}
}
sprintf(scratch, "Connect: fd %d, svctag %s, seq %d, type %s",
fd, dbp->dbf_svc_code, call->sequence,
(dbp->dbf_sflags & PFLAG) ? "passfd" : "exec");
logmessage(scratch);
DEBUG((9, "Accepted call %d", call->sequence));
if (dbp->dbf_sflags & PFLAG) {
close(Passfd);
if (pushmod(Acceptfd, dbp->dbf_modules)) {
sprintf(scratch, "Could not push modules: %s", dbp->dbf_modules);
logmessage(scratch);
goto cleanup;
}
DEBUG((9, "Running doconfig on %s", dbp->dbf_svc_code));
if ((i = doconfig(Acceptfd, dbp->dbf_svc_code, NOASSIGN|NORUN)) != 0) {
DEBUG((9, "doconfig exited with code %d", i));
sprintf(scratch, "doconfig failed on line %d of script %s", i, dbp->dbf_svc_code);
logmessage(scratch);
goto cleanup;
}
if ((Passfd = open(dbp->dbf_cmd_line, O_WRONLY)) < 0) {
sprintf(scratch,"Open failed: %s", dbp->dbf_cmd_line);
logmessage(scratch);
goto cleanup;
}
if (ioctl(Passfd, I_SENDFD, Acceptfd) < 0) {
sprintf(scratch,"Passfd failed: %s", dbp->dbf_cmd_line);
logmessage(scratch);
}
cleanup:
clr_call(call);
t_close(Acceptfd);
close(Passfd);
Acceptfd = open("/dev/null", O_RDWR);
Passfd = dup(Acceptfd);
queue(Free_call_p, current);
}
else {
if ((pid = fork()) < 0)
log(E_FORK_SERVICE);
else if (!pid) {
setpgrp();
Pid = getpid();
if (senviron(call)) {
logmessage("Can't expand server's environment");
}
start_server(Acceptfd, dbp);
#ifdef COREDUMP
abort();
#endif
exit(1);
}
clr_call(call);
t_close(Acceptfd);
queue(Free_call_p, current);
SPLhi();
if (dup(Passfd) != 0)
logmessage("Trouble duping fd 0");
SPLlo();
}
}
}
static char homeenv[BUFSIZ];
static char pathenv[BUFSIZ];
int
start_server(netfd, dbp)
int netfd;
dbf_t *dbp;
{
char *path;
char **argvp;
extern char **environ;
extern char **mkdbfargv();
struct passwd *pwdp;
struct group *grpp;
char msgbuf[256];
int i;
argvp = mkdbfargv(dbp);
path = *argvp;
(void) close(Sacpipefd);
(void) close(Pmpipefd);
if (dbp->dbf_flags & DBF_UTMP) {
pid_t tmp;
struct stat sbuf;
char device[20];
char dummy[PMTAGSIZE + 1];
struct utmpx utline;
DEBUG((9, "Creating a utmpx entry for this service "));
if ((tmp = fork()) < 0) {
logmessage("Can't fork to create utmpx entry");
exit(2);
}
if (tmp)
exit(0);
setpgrp();
if (fstat(0, &sbuf) < 0) {
logmessage("Stat failed on fd 0: no line field "
"available for utmpx entry");
*device = '\0';
}
else {
if (minor(sbuf.st_rdev) < 100)
sprintf(device, "%.9s%02d", Minor_prefix,
minor(sbuf.st_rdev));
else
sprintf(device, "%.8s%03d", Minor_prefix,
minor(sbuf.st_rdev));
DEBUG((9, "Device: %s", device));
}
sprintf(dummy, ".%s", Mytag);
strncpy(utline.ut_user, dummy, sizeof (utline.ut_user) - 1);
sprintf(utline.ut_id, "ls%c%c", SC_WILDC, SC_WILDC);
strncpy(utline.ut_line, device, sizeof (utline.ut_line) - 1);
utline.ut_pid = getpid();
utline.ut_type = USER_PROCESS;
utline.ut_exit.e_termination = 0;
utline.ut_exit.e_exit = 0;
utline.ut_xtime = (time_t) time((time_t *)0);
makeutx(&utline);
}
if (dup(0) != 1 || dup(0) != 2) {
logmessage("Dup of fd 0 failed");
exit(2);
}
if (pushmod(netfd, dbp->dbf_modules)) {
logmessage("Can't push server's modules: exit");
exit(2);
}
rst_signals();
DEBUG((9, "Running doconfig on %s", dbp->dbf_svc_code));
if ((i = doconfig(Acceptfd, dbp->dbf_svc_code, 0)) != 0) {
DEBUG((9, "doconfig exited with code %d", i));
sprintf(msgbuf, "doconfig failed on line %d of script %s", i, dbp->dbf_svc_code);
logmessage(msgbuf);
exit(2);
}
if ((pwdp = getpwnam(dbp->dbf_id)) == NULL) {
sprintf(msgbuf, "Missing or bad passwd entry for <%s>",dbp->dbf_id);
logmessage(msgbuf);
exit(2);
}
if (setgid(pwdp->pw_gid)) {
if ((grpp = getgrgid(pwdp->pw_gid)) == NULL) {
sprintf(msgbuf, "No group entry for %ld", pwdp->pw_gid);
logmessage(msgbuf);
exit(2);
}
sprintf(msgbuf, "Cannot set group id to %s", grpp->gr_name);
logmessage(msgbuf);
exit(2);
}
if (setuid(pwdp->pw_uid)) {
sprintf(msgbuf, "Cannot set user id to %s", dbp->dbf_id);
logmessage(msgbuf);
exit(2);
}
if (chdir(pwdp->pw_dir)) {
sprintf(msgbuf, "Cannot chdir to %s", pwdp->pw_dir);
logmessage(msgbuf);
exit(2);
}
DEBUG((9, "New uid %ld New gid %ld", getuid(), getgid()));
sprintf(homeenv, "HOME=%s", pwdp->pw_dir);
putenv(homeenv);
if (pwdp->pw_uid)
sprintf(pathenv, "PATH=/usr/bin:");
else
sprintf(pathenv, "PATH=/usr/sbin:/usr/bin");
putenv(pathenv);
endpwent();
execve(path, argvp, environ);
logmessage("ERROR: could not exec server");
sys_error(E_SYS_ERROR, CONTINUE);
return(-1);
}
static char provenv[2*PATHSIZE];
static char prefenv[2*PATHSIZE];
int
senviron(call)
struct t_call *call;
{
char *p;
extern void nlsaddr2c();
extern char *getenv();
if (getenv("PATH") == NULL)
putenv("PATH=/usr/sbin:/usr/bin");
if ((p = (char *)malloc(((call->addr.len)<<1) + 18)) == NULL)
return(-1);
strcpy(p, NLSADDR);
strcat(p, "=");
nlsaddr2c(p + strlen(p), call->addr.buf, (int)call->addr.len);
DEBUG((7, "Adding %s to server's environment", p));
putenv(p);
if ((p = (char *)malloc(((call->opt.len)<<1) + 16)) == NULL)
return(-1);
strcpy(p, NLSOPT);
strcat(p, "=");
nlsaddr2c(p + strlen(p), call->opt.buf, (int)call->opt.len);
DEBUG((7, "Adding %s to server's environment", p));
putenv(p);
p = provenv;
strcpy(p, NLSPROVIDER);
strcat(p, "=");
strcat(p, Netspec);
DEBUG((7, "Adding %s to environment", p));
putenv(p);
p = prefenv;
strcpy(p, "MPREFIX");
strcat(p, "=");
strcat(p, Minor_prefix);
DEBUG((7, "Adding %s to environment", p));
putenv(p);
if ((p = (char *)malloc(((call->udata.len)<<1) + 20)) == NULL)
return(-1);
strcpy(p, NLSUDATA);
strcat(p, "=");
if ((int)call->udata.len >= 0)
nlsaddr2c(p + strlen(p), call->udata.buf, (int)call->udata.len);
putenv(p);
return (0);
}
char *
parse(s)
char *s;
{
char *p;
char *tp;
char scratch[BUFSIZ];
int delim;
tp = p = s + strlen("TZ=");
if ((*p == '"') || (*p == '\'')) {
delim = *p++;
for (;;) {
if (*p == '\0') {
sprintf(scratch, "%s ill-formed", TZFILE);
logmessage(scratch);
strcpy(s, "TZ=");
return(s);
}
if (*p == delim) {
*tp = '\0';
return(s);
}
else {
*tp++ = *p++;
}
}
}
else {
for ( ; *p && !isspace(*p) && *p != '#'; ++p)
;
if (*p) {
*p = '\0';
}
return(s);
}
}
static void
clr_call(struct t_call *call)
{
call->sequence = 0;
call->addr.len = 0;
call->opt.len = 0;
call->udata.len = 0;
memset(call->addr.buf, 0, (int)call->addr.maxlen);
memset(call->opt.buf, 0, (int)call->opt.maxlen);
memset(call->udata.buf, 0, (int)call->udata.maxlen);
}
static void
pitchcall(struct call_list *pending, struct t_discon *discon)
{
struct callsave *p, *oldp;
DEBUG((9, "pitching call, sequence # is %d", discon->sequence));
if (EMPTYLIST(pending)) {
discon->sequence = -1;
return;
}
p = pending->cl_head;
oldp = (struct callsave *) NULL;
while (p) {
if (p->c_cp->sequence == discon->sequence) {
if (oldp == (struct callsave *) NULL) {
pending->cl_head = p->c_np;
if (pending->cl_head == (struct callsave *) NULL) {
pending->cl_tail = (struct callsave *) NULL;
}
}
else if (p == pending->cl_tail) {
oldp->c_np = p->c_np;
pending->cl_tail = oldp;
}
else {
oldp->c_np = p->c_np;
}
clr_call(p->c_cp);
queue(Free_call_p, p);
discon->sequence = -1;
return;
}
oldp = p;
p = p->c_np;
}
logmessage("received disconnect with no pending call");
discon->sequence = -1;
return;
}
int
add_prvaddr(dbp)
dbf_t *dbp;
{
extern char *t_alloc();
int j;
struct call_list *temp_pend;
struct callsave *tmp;
char scratch[BUFSIZ];
int bindfd;
extern struct netbuf *stoa();
char str[NAMEBUFSZ];
char *lstr = str;
struct netbuf netbuf;
int maxcon;
char *ap;
int clen;
DEBUG((9,"in add_prvaddr, addr %s, svc %s",
(dbp->dbf_sflags & DFLAG) ? "DYNAMIC" : dbp->dbf_prv_adr,
dbp->dbf_svc_code));
netbuf.buf = NULL;
netbuf.maxlen = 0;
netbuf.len = 0;
if (!(dbp->dbf_sflags & DFLAG)) {
strcpy(lstr, dbp->dbf_prv_adr);
if (stoa(lstr, &netbuf) == (struct netbuf *)NULL) {
DEBUG((9,"stoa returned null, errno = %d\n",errno));
error(1, E_MALLOC);
return(-1);
}
clen = netbuf.len;
}
else {
clen = -1;
}
if ((bindfd = open_bind(netbuf.buf, MAXCON, clen, &maxcon, &ap)) < 0) {
switch (bindfd) {
case -1:
return(-1);
break;
case -2:
sprintf(scratch, " Service %s ignored: out of file descriptors", dbp->dbf_svc_code);
logmessage(scratch);
return(-1);
break;
case -3:
sprintf(scratch, " Service %s ignored: unable to bind requested address", dbp->dbf_svc_code);
logmessage(scratch);
return(-1);
break;
default:
error(E_OPENBIND, EXIT);
}
}
if (clen == -1) {
sprintf(scratch,"Service %s: fd %d dynamic addr %s", dbp->dbf_svc_code, bindfd, ap);
dbp->dbf_prv_adr = ap;
}
else {
sprintf(scratch,"Service %s: fd %d addr %s", dbp->dbf_svc_code, bindfd, dbp->dbf_prv_adr);
}
logmessage(scratch);
rpc_register(dbp);
temp_pend = Priv_call + bindfd;
dbp->dbf_fd = bindfd;
dbp->dbf_maxcon = maxcon;
temp_pend->cl_head = (struct callsave *) NULL;
temp_pend->cl_tail = (struct callsave *) NULL;
for (j=0; j < maxcon; ++j) {
if ((tmp = (struct callsave *) malloc(sizeof(struct callsave))) == NULL) {
error (E_MALLOC, NOCORE | EXIT);
}
if ((tmp->c_cp = (struct t_call *) t_alloc(bindfd, T_CALL,
T_ALL)) == NULL) {
tli_error(E_T_ALLOC,EXIT);
}
queue(Free_call_p, tmp);
}
return(0);
}
static void
mod_prvaddr(void)
{
dbf_t *entry_p;
dbf_t *oldentry_p;
char scratch[BUFSIZ];
dbf_t *svc_code_match();
int bound;
struct pollfd *sp;
DEBUG((9, "in mod_prvaddr..."));
for (entry_p = Newdbf; entry_p && entry_p->dbf_svc_code; entry_p++) {
if ((oldentry_p = svc_code_match(entry_p->dbf_svc_code)) != NULL) {
DEBUG((9, "MATCHED service code"));
if ((strcmp(oldentry_p->dbf_prv_adr, entry_p->dbf_prv_adr) == 0) || ((oldentry_p->dbf_sflags & DFLAG) && (entry_p->dbf_sflags & DFLAG))) {
DEBUG((9, "SAME addresses, old %s, new %s",
oldentry_p->dbf_prv_adr, entry_p->dbf_prv_adr));
DEBUG((9, "Old fd %d", oldentry_p->dbf_fd));
entry_p->dbf_fd = oldentry_p->dbf_fd;
entry_p->dbf_maxcon = oldentry_p->dbf_maxcon;
oldentry_p->dbf_fd = -1;
if ((oldentry_p->dbf_sflags & DFLAG) && (entry_p->dbf_sflags & DFLAG)) {
entry_p->dbf_prv_adr = oldentry_p->dbf_prv_adr;
}
if (entry_p->dbf_fd != -1) {
sprintf(scratch, "Service %s: fd %d addr %s",
entry_p->dbf_svc_code, entry_p->dbf_fd,
entry_p->dbf_prv_adr);
logmessage(scratch);
}
if ((oldentry_p->dbf_version != entry_p->dbf_version) || (oldentry_p->dbf_prognum != entry_p->dbf_prognum)) {
rpc_unregister(oldentry_p);
rpc_register(entry_p);
}
}
}
}
for (oldentry_p = Dbfhead; oldentry_p && oldentry_p->dbf_svc_code; oldentry_p++) {
if (oldentry_p->dbf_fd != -1) {
DEBUG((9, "deleting %s", oldentry_p->dbf_svc_code));
if (del_prvaddr(oldentry_p) == 0)
Valid_addrs--;
}
}
for (entry_p = Newdbf; entry_p && entry_p->dbf_svc_code; entry_p++) {
if ((entry_p->dbf_fd == -1) && (!(entry_p->dbf_sflags & DFLAG))) {
DEBUG((9, "adding %s", entry_p->dbf_svc_code));
if (add_prvaddr(entry_p) == 0)
Valid_addrs++;
}
}
for (entry_p = Newdbf; entry_p && entry_p->dbf_svc_code; entry_p++) {
if ((entry_p->dbf_fd == -1) && (entry_p->dbf_sflags & DFLAG)) {
DEBUG((9, "adding %s", entry_p->dbf_svc_code));
if (add_prvaddr(entry_p) == 0)
Valid_addrs++;
}
}
free(Dbfhead);
free(Server_cmd_lines);
Dbfhead = Newdbf;
Newdbf = NULL;
Server_cmd_lines = New_cmd_lines;
sprintf(scratch, "Re-read complete, %d %s bound, %d fds free", Valid_addrs,
(Valid_addrs == 1) ? "address" : "addresses",
Ndesc-Valid_addrs-USEDFDS);
logmessage(scratch);
sp = &Pollfds[1];
for (entry_p = Dbfhead; entry_p && entry_p->dbf_svc_code; entry_p++) {
if (entry_p->dbf_fd >= 0) {
sp->fd = entry_p->dbf_fd;
DEBUG((9, "adding %d to poll struct", entry_p->dbf_fd));
sp->events = POLLIN;
sp->revents = 0;
sp++;
}
}
}
int
del_prvaddr(dbp)
dbf_t *dbp;
{
struct callsave *tmp;
struct call_list *q;
struct t_call *call;
int i;
char scratch[BUFSIZ];
DEBUG((9, "in del_prvaddr..."));
rpc_unregister(dbp);
if (dbp->dbf_fd < 0)
return -1;
q = Priv_call + dbp->dbf_fd;
i = 0;
while ((tmp = dequeue(q)) != NULL) {
i++;
call = tmp->c_cp;
t_snddis(dbp->dbf_fd, call);
t_free((char *)call, T_CALL);
free(tmp);
}
for ( ; i < dbp->dbf_maxcon; i++) {
tmp = dequeue(Free_call_p);
t_free((char *)tmp->c_cp, T_CALL);
free(tmp);
}
t_unbind(dbp->dbf_fd);
t_close(dbp->dbf_fd);
sprintf(scratch, "Unbind %s: fd %d addr %s", dbp->dbf_svc_code,
dbp->dbf_fd, dbp->dbf_prv_adr);
logmessage(scratch);
dbp->dbf_fd = -1;
return 0;
}
dbf_t *
svc_code_match(new_code)
char *new_code;
{
dbf_t *dbp;
for (dbp = Dbfhead; dbp && dbp->dbf_svc_code; dbp++) {
if (strcmp(dbp->dbf_svc_code, new_code) == 0)
return(dbp);
}
return((dbf_t *)NULL);
}
void
rpc_register(dbp)
dbf_t *dbp;
{
char str[NAMEBUFSZ];
char scratch[BUFSIZ];
char *lstr = str;
struct netbuf netbuf;
extern struct netbuf *stoa();
extern int errno;
DEBUG((9, "in rpc_register"));
if (dbp->dbf_prognum == -1 || dbp->dbf_version == -1)
return;
rpc_unregister(dbp);
netbuf.buf = NULL;
netbuf.maxlen = 0;
netbuf.len = 0;
strcpy(lstr, dbp->dbf_prv_adr);
if (stoa(lstr, &netbuf) == (struct netbuf *)NULL) {
DEBUG((9,"stoa returned null, errno = %d\n",errno));
error(1, E_MALLOC);
return;
}
if (rpcb_set(dbp->dbf_prognum, dbp->dbf_version, Netconf, &netbuf)) {
sprintf(scratch," registered with rpcbind, prognum %d version %d", dbp->dbf_prognum, dbp->dbf_version);
logmessage(scratch);
}
else {
logmessage("rpcb_set failed, service not registered with rpcbind");
}
return;
}
void
rpc_unregister(dbp)
dbf_t *dbp;
{
DEBUG((9, "in rpc_unregister"));
if (dbp->dbf_prognum == -1 || dbp->dbf_version == -1)
return;
(void) rpcb_unset(dbp->dbf_prognum, dbp->dbf_version, Netconf);
}