xref: /plan9/sys/src/ape/lib/bsd/listen.c (revision 781103c4074deb8af160e8a0da2742ba6b29dc2b)
1 /* posix */
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <fcntl.h>
7 #include <string.h>
8 #include <errno.h>
9 #include <sys/stat.h>
10 #include <signal.h>
11 
12 /* socket extensions */
13 #include <sys/uio.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <sys/un.h>
17 
18 /* plan 9 */
19 #include "lib.h"
20 #include "sys9.h"
21 
22 #include "priv.h"
23 
24 extern int	_muxsid;
25 extern void	_killmuxsid(void);
26 
27 /*
28  * replace the fd with a pipe and start a process to
29  * accept calls in.  this is all to make select work.
30  */
31 static int
listenproc(Rock * r,int fd)32 listenproc(Rock *r, int fd)
33 {
34 	Rock *nr;
35 	char *net;
36 	int cfd, nfd, dfd;
37 	int pfd[2];
38 	struct stat d;
39 	char *p;
40 	char listen[Ctlsize];
41 	char name[Ctlsize];
42 
43 	switch(r->stype){
44 	case SOCK_DGRAM:
45 		net = "udp";
46 		break;
47 	case SOCK_STREAM:
48 		net = "tcp";
49 		break;
50 	default:
51 		net = "gok";
52 		break;
53 	}
54 
55 	strcpy(listen, r->ctl);
56 	p = strrchr(listen, '/');
57 	if(p == 0)
58 		return -1;
59 	strcpy(p+1, "listen");
60 
61 	if(pipe(pfd) < 0)
62 		return -1;
63 
64 	/* replace fd with a pipe */
65 	nfd = dup(fd);
66 	dup2(pfd[0], fd);
67 	close(pfd[0]);
68 	fstat(fd, &d);
69 	r->inode = d.st_ino;
70 	r->dev = d.st_dev;
71 
72 	/* start listening process */
73 	switch(fork()){
74 	case -1:
75 		close(pfd[1]);
76 		close(nfd);
77 		return -1;
78 	case 0:
79 		if(_muxsid == -1) {
80 			_RFORK(RFNOTEG);
81 			_muxsid = getpgrp();
82 		} else
83 			setpgid(getpid(), _muxsid);
84 		_RENDEZVOUS(2, _muxsid);
85 		break;
86 	default:
87 		atexit(_killmuxsid);
88 		_muxsid = _RENDEZVOUS(2, 0);
89 		close(pfd[1]);
90 		close(nfd);
91 		return 0;
92 	}
93 
94 /*	for(fd = 0; fd < 30; fd++)
95 		if(fd != nfd && fd != pfd[1])
96 			close(fd);/**/
97 
98 	for(;;){
99 		cfd = open(listen, O_RDWR);
100 		if(cfd < 0)
101 			break;
102 
103 		dfd = _sock_data(cfd, net, r->domain, r->stype, r->protocol, &nr);
104 		if(dfd < 0)
105 			break;
106 
107 		if(write(pfd[1], nr->ctl, strlen(nr->ctl)) < 0)
108 			break;
109 		if(read(pfd[1], name, sizeof(name)) <= 0)
110 			break;
111 
112 		close(dfd);
113 	}
114 	exit(0);
115 	return 0;
116 }
117 
118 int
listen(int fd,int)119 listen(int fd, int)
120 {
121 	Rock *r;
122 	int n, cfd;
123 	char msg[128];
124 	struct sockaddr_in *lip;
125 	struct sockaddr_un *lunix;
126 
127 	r = _sock_findrock(fd, 0);
128 	if(r == 0){
129 		errno = ENOTSOCK;
130 		return -1;
131 	}
132 
133 	switch(r->domain){
134 	case PF_INET:
135 		cfd = open(r->ctl, O_RDWR);
136 		if(cfd < 0){
137 			errno = EBADF;
138 			return -1;
139 		}
140 		lip = (struct sockaddr_in*)&r->addr;
141 		if(1 || lip->sin_port >= 0) {	/* sin_port is unsigned */
142 			if(write(cfd, "bind 0", 6) < 0) {
143 				errno = EGREG;
144 				close(cfd);
145 				return -1;
146 			}
147 			snprintf(msg, sizeof msg, "announce %d",
148 				ntohs(lip->sin_port));
149 		}
150 		else
151 			strcpy(msg, "announce *");
152 		n = write(cfd, msg, strlen(msg));
153 		if(n < 0){
154 			errno = EOPNOTSUPP;	/* Improve error reporting!!! */
155 			close(cfd);
156 			return -1;
157 		}
158 		close(cfd);
159 
160 		return listenproc(r, fd);
161 	case PF_UNIX:
162 		if(r->other < 0){
163 			errno = EGREG;
164 			return -1;
165 		}
166 		lunix = (struct sockaddr_un*)&r->addr;
167 		if(_sock_srv(lunix->sun_path, r->other) < 0){
168 			_syserrno();
169 			r->other = -1;
170 			return -1;
171 		}
172 		r->other = -1;
173 		return 0;
174 	default:
175 		errno = EAFNOSUPPORT;
176 		return -1;
177 	}
178 }
179