plan9fox/sys/src/ape/lib/bsd/listen.c

172 lines
2.8 KiB
C

/* posix */
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <signal.h>
/* socket extensions */
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
/* plan 9 */
#include "lib.h"
#include "sys9.h"
#include "priv.h"
extern int _muxsid;
extern void _killmuxsid(void);
/*
* replace the fd with a pipe and start a process to
* accept calls in. this is all to make select work.
*/
static int
listenproc(Rock *r, int fd)
{
char listen[Ctlsize], name[Ctlsize], *net, *p;
int cfd, nfd, dfd, pfd[2];
struct stat d;
Rock *nr;
void *v;
switch(r->stype){
case SOCK_DGRAM:
net = "udp";
break;
case SOCK_STREAM:
net = "tcp";
break;
case SOCK_RDM:
net = "il";
break;
}
strcpy(listen, r->ctl);
p = strrchr(listen, '/');
if(p == 0)
return -1;
strcpy(p+1, "listen");
if(pipe(pfd) < 0)
return -1;
/* replace fd with a pipe */
nfd = dup(fd);
dup2(pfd[0], fd);
close(pfd[0]);
fstat(fd, &d);
r->inode = d.st_ino;
r->dev = d.st_dev;
/* start listening process */
switch(fork()){
case -1:
close(pfd[1]);
close(nfd);
return -1;
case 0:
if(_muxsid == -1) {
_RFORK(RFNOTEG);
_muxsid = getpgrp();
} else
setpgid(getpid(), _muxsid);
while(_RENDEZVOUS(r, (void*)_muxsid) == (void*)~0)
;
break;
default:
while((v = _RENDEZVOUS(r, 0)) == (void*)~0)
;
_muxsid = (int)v;
atexit(_killmuxsid);
close(pfd[1]);
close(nfd);
return 0;
}
/* for(fd = 0; fd < 30; fd++)
if(fd != nfd && fd != pfd[1])
close(fd);/**/
for(;;){
cfd = open(listen, O_RDWR);
if(cfd < 0)
break;
dfd = _sock_data(cfd, net, r->domain, r->stype, r->protocol, &nr);
if(dfd < 0)
break;
if(write(pfd[1], nr->ctl, strlen(nr->ctl)) < 0)
break;
if(read(pfd[1], name, sizeof(name)) <= 0)
break;
close(dfd);
}
exit(0);
return 0;
}
int
listen(fd, backlog)
int fd;
int backlog;
{
Rock *r;
int n, cfd;
char msg[128];
struct sockaddr_un *lunix;
r = _sock_findrock(fd, 0);
if(r == 0){
errno = ENOTSOCK;
return -1;
}
switch(r->domain){
case PF_INET:
case PF_INET6:
cfd = open(r->ctl, O_RDWR);
if(cfd < 0){
errno = EBADF;
return -1;
}
strcpy(msg, "announce ");
_sock_inaddr2string(r, msg + 9, sizeof msg - 9);
n = write(cfd, msg, strlen(msg));
if(n < 0){
_syserrno();
if(errno == EPLAN9)
errno = EOPNOTSUPP;
close(cfd);
return -1;
}
close(cfd);
return listenproc(r, fd);
case PF_UNIX:
if(r->other < 0){
errno = EGREG;
return -1;
}
lunix = (struct sockaddr_un*)&r->addr;
if(_sock_srv(lunix->sun_path, r->other) < 0){
r->other = -1;
return -1;
}
r->other = -1;
return 0;
default:
errno = EAFNOSUPPORT;
return -1;
}
}