📄 listen.c
字号:
* Following are some general queueing routines. The call list head contains * a pointer to the head of the queue and to the tail of the queue. Normally, * calls are added to the tail and removed from the head to ensure they are * processed in the order received, however, because of the possible interruption * of an acceptance with the resulting requeueing, it is necessary to have a * way to do a "priority queueing" which inserts at the head of the queue for * immediate processing *//* * queue: * * add calls to tail of queue */voidqueue(head, cp)register struct call_list *head;register struct callsave *cp;{ 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; }}/* * pqueue: * * priority queuer, add calls to head of queue */voidpqueue(head, cp)register struct call_list *head;register 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; }}/* * dequeue: * * remove a call from the head of queue */struct callsave *dequeue(head)register struct call_list *head;{ register struct callsave *ret; if (head->cl_head == (struct callsave *) NULL) error(E_CANT_HAPPEN, EXIT); 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);}/* * open_bind: * * open the network and bind the endpoint to 'name' * this routine is also used by listen(), so it can't exit * under all error conditions: specifically, if there are * no minor devices avaliable in the network driver, open_bind * returns -1. (error message will be logged). All other errors * cause an exit. * * If clen is zero, transport provider picks the name and these * routines (open_bind and bind) ignore name and qlen -- * this option is used when binding a name for accepting a connection * (not for listening.) You MUST supply a name, qlen and clen when * opening/binding a name for listening. * * Assumptions: driver returns ENXIO when all devices are allocated. */intopen_bind(name, qlen, clen, conp)char *name;int qlen;int clen;unsigned int *conp;{ register fd; struct t_info t_info; extern int t_errno, errno; unsigned int ret; extern unsigned int bind(); fd = t_open(Provider, NETOFLAG, &t_info); if (fd < 0) { if ( (t_errno == TSYSERR) && ((errno == ENXIO) || (errno == ENOSR) || (errno == EAGAIN) || (errno == ENOSPC)) ) { tli_error(E_FD1OPEN, CONTINUE); logmessage("No network minor devices (ENXIO/ENOSR)"); return(-1); } tli_error(E_FD1OPEN, EXIT); } DEBUG((7,"fd %d opened",fd)); ret = bind(fd, name, qlen, clen); if (conp) *conp = ret; return(fd);}unsigned intbind(fd, name, qlen, clen)register fd;char *name;int qlen;register int clen;{ register struct t_bind *req = (struct t_bind *)0; register struct t_bind *ret = (struct t_bind *)0; register char *p, *q; unsigned int retval; extern char *t_alloc(); extern void nlsaddr2c(); extern int memcmp(); extern int errno; extern char *memcpy();#ifdef S4 extern struct netbuf *lname2addr();#endif /* S4 */#ifdef CHARADDR char pbuf[NAMEBUFSZ + 1];#endif char scratch[BUFSIZ]; DEBUG((9,"in bind, clen = %d", 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 > req->addr.maxlen) { sprintf(scratch,"Truncating name size from %d to %d", clen, req->addr.maxlen); logmessage(scratch); clen = req->addr.maxlen; } (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 /* CHARADDR && DEBUGMODE */#ifdef S4 if (!lname2addr(fd, &(req->addr))) { sprintf(scratch, "lname2addr failed, errno %d", errno); logmessage(scratch); error(E_SYS_ERROR, EXIT | NO_MSG); }#endif /* S4 */#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 /* CHARADDR && DEBUGMODE */ } if (t_bind(fd, req, ret)) { DEBUG((1,"t_bind failed; t_errno %d errno %d", t_errno, errno)); if (qlen) /* starup only */ tli_error(E_T_BIND, EXIT | NOCORE); /* here during normal service */ if ((t_errno == TNOADDR) || ((t_errno == TSYSERR) && (errno == EAGAIN))) { /* our name space is all used up */ tli_error(E_T_BIND, CONTINUE); t_close(fd); return(-1); } /* otherwise, irrecoverable error */ tli_error(E_T_BIND, EXIT | NOCORE); }#if defined(S4) && defined(DEBUGMODE) t_dump(Debugfp); /* show TLI internal name structures on Debugfp */#endif if (clen) { retval = ret->qlen; if ( (ret->addr.len != req->addr.len) || (memcmp( req->addr.buf, ret->addr.buf, 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, ret->addr.len); nlsaddr2c(q, req->addr.buf, req->addr.len); sprintf(scratch, "attempted to bind address \\x%s", q); logmessage(scratch); sprintf(scratch, "actually bound address \\x%s", p); logmessage(scratch); } error(E_BIND_REQ, EXIT | NOCORE); } if ( t_free(req, T_BIND) ) tli_error(E_T_FREE, EXIT); if ( t_free(ret, T_BIND) ) tli_error(E_T_FREE, EXIT); return(retval); } return((unsigned int) 0);}/* * Clean-up server children corpses. Invoked on receiving the SIGCHLD signal. */void reap_child(){ wait(0);}/* * divorce_parent: fork a child, make child independent of parent. * parent writes child's pid to process id file * and exits. If it can't create or write pidfile, * child must be killed. * * Assumptions: directory previously changed to '/'. * * Notes: define FOREGROUND to inhibit the listener from running * in the background. Useful with DEBUGMODE. */divorce_parent(){ char pidstring[50]; /* holds childs pid in ascii decimal */ char scratch[128]; register ret, i; extern void exit(); struct sigvec sv; DEBUG((9,"in divorce_parent")); setpgrp(); /* listener + kids in own p-group */ if ( (Pid = fork()) < 0 ) sys_error(E_LSFORK, EXIT); if (Pid) { /* parent: open pid output file and * write childs process ID to it. * If it works, exit 0. * If it doesn't, kill the child and exit non-zero. */ i = sprintf(pidstring,"%d",Pid) + 1; /* add null */ if ( (ret = write(Pidfd,pidstring,(unsigned)i)) != i ) { if (ret < 0) sys_error(E_PIDWRITE, CONTINUE); signal(SIGCHLD, SIG_IGN); kill(Pid, SIGTERM); error(E_PIDWRITE, EXIT); /* exit with proper code */ } /* * Lock file will be unlocked when parent exits. */ exit( 0 ); /* parent exits -- child does the work */ } /* * child: close everything but the network fd's * but first, lock the lock file -- blocking lock will wait * for parent to exit. */ close(Lckfd); if ((Lckfd = open(LCKNAME, O_WRONLY | O_CREAT, 0666 )) == -1) { sprintf(scratch, "Error (%d) re-locking the lock file", errno); logmessage(scratch); error(E_SYS_ERROR, EXIT | NOCORE | NO_MSG); } if (locking(Lckfd, 1, 0L) == -1) { sprintf(scratch, "Error (%d) re-locking the lock file", errno); logmessage(scratch); error(E_SYS_ERROR, EXIT | NOCORE | NO_MSG); } /* Clean up server children corpses -- using sigvec like this prevents handler from getting reset when caught, whereas system V signal() routine resets handler when caught */ sv.sv_handler = (void (*)())reap_child; sv.sv_mask = 0; sv.sv_flags = SV_INTERRUPT; sigvec(SIGCHLD, &sv, (struct sigvec *) 0); Background = 1; close_files();}/* * close_files: * Close all files except what we opened explicitly * Including stdout, stderr and anything else * that may be open by whatever exec'ed me. * We also set the close on exec flag on all fd's * Note, that we keep fd's 0, 1, and 2 reserved for * servers by opening them to "/dev/null". */close_files(){ register int i; register int ndesc; fclose(stdin); fclose(stdout); fclose(stderr); if (((i = open("/dev/null", O_RDWR)) != 0) || (dup(i) != 1) || (dup(i) != 2)) { logmessage("Trouble opening/duping /dev/null"); sys_error(E_SYS_ERROR, EXIT | NOCORE); } ndesc = ulimit(4, 0L); /* get value for NOFILE */ for (i = 3; i < ndesc; i++) { /* leave stdout, stderr open */ fcntl(i, F_SETFD, 1); /* set close on exec flag*/ }}/* * catch_signals: * Ignore some, catch the rest. Use SIGTERM to kill me. */catch_signals(){ extern void sigterm(); register int i; struct sigvec sv; for (i = 1; i < SIGKILL; i++) signal(i, SIG_IGN); for (i = SIGKILL + 1; i < SIGTERM; i++) signal(i, SIG_IGN); sv.sv_handler = (void (*)())sigterm; sv.sv_mask = 0; sv.sv_flags = SV_INTERRUPT; sigvec(SIGTERM, &sv, (struct sigvec *) 0); for (i = SIGTERM + 1; i < NSIG; i++) signal(i, SIG_IGN); errno = 0; /* SIGWIND and SIGPHONE only on UNIX PC */}/* * rst_signals: * After forking but before exec'ing a server, * reset all signals to defaults. * exec automatically resets 'caught' sigs to SIG_DFL. * (This is quick and dirty for now.) */rst_signals(){ register int i; for (i = 1; i < SIGKILL; i++) signal(i, SIG_DFL); for (i = SIGKILL + 1; i < SIGTERM; i++) signal(i, SIG_DFL); for (i = SIGTERM + 1; i < NSIG; i++) signal(i, SIG_DFL); errno = 0; /* SIGWIND and SIGPHONE only on UNIX PC */}/* * sigterm: Clean up and exit. */voidsigterm(){ signal(SIGTERM, SIG_IGN); error(E_SIGTERM, EXIT | NORMAL | NOCORE); /* calls cleanup */}/* * listen: listen for and process connection requests. */static struct pollfd pollfds[2];static struct t_discon *disc;listen(){ register fd, i; int count; register struct pollfd *sp; struct call_list *fhead, *phead; /* free and pending heads */ DEBUG((9,"in listen")); if (Rxname) { count = 2; /* how many fd's are we listening on */ pollfds[0].fd = Nfd1; /* tells poll() what to do */ pollfds[1].fd = Nfd2; pollfds[0].events = pollfds[1].events = POLLIN; } else { count = 1; pollfds[0].fd = Nfd2; pollfds[0].events = POLLIN; } errno = t_errno = 0;/* * for receiving disconnects allocate one and be done with it */ while (!(disc = (struct t_discon *)t_alloc(Nfd2,T_DIS,T_ALL)) ) { if ((t_errno != TSYSERR) || (errno != EAGAIN)) tli_error(E_T_ALLOC, EXIT); else tli_error(E_T_ALLOC, CONTINUE); } for (;;) { DEBUG((9,"listen(): TOP of loop")); if (Rxname) { pollfds[0].revents = pollfds[1].revents = 0; } else { pollfds[0].revents = 0; } DEBUG((7,"waiting for a request (via poll)")); if (poll(pollfds, count, -1) == -1) { /* Assume due to a child server exiting, so just continue */ if (errno == EINTR) continue; else sys_error(E_POLL, EXIT); } for (i = 0, sp = pollfds; i < count; i++, sp++) { switch (sp->revents) { case POLLIN: fd = sp->fd; if (fd == Nfd1) { /* This can't happen if no remote login is available (Rxname == NULL, count == 1, Nfd1 == -1) */ fhead = &Rfree; phead = &Rpend; } else { fhead = &Lfree; phead = &Lpend; } doevent(fhead, phead, fd); trycon(fhead, phead, fd); break; case 0: break; /* distinguish the various errors for the user */ case POLLERR: logmessage("poll() returned POLLERR"); error(E_SYS_ERROR, EXIT | NO_MSG); case POLLHUP: logmessage("poll() returned POLLHUP"); error(E_SYS_ERROR, EXIT | NO_MSG); case POLLNVAL: logmessage("poll() returned POLLNVAL"); error(E_SYS_ERROR, EXIT | NO_MSG); case POLLPRI: logmessage("poll() returned POLLPRI");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -