1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Bill Jolitz.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the University of California, Berkeley.  The name of the
14  * University may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  */
20 
21 #if defined(LIBC_SCCS) && !defined(lint)
22 static char sccsid[] = "@(#)externalconnect.c	5.1 (Berkeley) 05/16/89";
23 #endif /* LIBC_SCCS and not lint */
24 
25 /*
26  * externalconnect:
27  *	send a message to connection daemon via UNIX domain socket
28  *	containing a resource request and preparation instructions;
29  *	expect a response message containing either the file descriptor
30  *	of the resource and method of preparation, or an connection
31  *	error status explaining why it could not be done.
32  */
33 
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #include <sys/uio.h>
38 #include <connect.h>
39 
40 static struct fdsocket {
41 	int fd;
42 	int sock;
43 	int state ;
44 }	fdsockets[MAXCONNECTS];
45 static nfds = 0, inprocess = -1 ;
46 
47 externalconnect (cdp, opts, optlen, efd)
48 	struct connectdomain *cdp ;
49 	char *opts ; int optlen ; int efd ;
50 {
51 	int sock, n, i, fd, rv ;
52 	struct sockaddr_un rqsts;
53 	struct iovec iov[4];
54 	struct msghdr msg ;
55 	int constatus, rqstfmt;
56 
57 
58 	sock = socket (AF_UNIX, SOCK_STREAM, 0) ;
59 	if (sock < 0) {
60 		perror("externalconnect: stream socket") ;
61 		exit(1) ;
62 	}
63 	rqsts.sun_family = AF_UNIX ;
64 	strcpy (rqsts.sun_path,"/dev/connect") ;
65 	if (connect (sock, &rqsts, sizeof (rqsts)) < 0) {
66 		perror ("externalconnect: connect /dev/connect") ;
67 		exit (1) ;
68 	}
69 
70 	/* record evidence of communication, so that multiple
71 	   outstandings/aborts are possible */
72 	if (!nfds)
73 		for (i = 0; i < MAXCONNECTS ; i++) {
74 			fdsockets[i].fd = -1;
75 			fdsockets[i].sock = -1;
76 			fdsockets[i].state = -1; } ;
77 	fdsockets[nfds].sock = sock ;
78 	fdsockets[nfds].state = CDNEWREQUEST ;
79 	inprocess = nfds++ ;
80 
81 
82 	/* send connnection request message */
83 	rqstfmt = CDNEWREQUEST;
84 	msg.msg_name = "";
85 	msg.msg_namelen = 0 ;
86 	iov[0].iov_base = (caddr_t) &rqstfmt ;
87 	iov[0].iov_len = sizeof (rqstfmt) ;
88 	iov[1].iov_base = (caddr_t) cdp ;
89 	iov[1].iov_len = CDSIZE(cdp) ;
90 	iov[2].iov_base = (caddr_t) opts;
91 	iov[2].iov_len = optlen ;
92 	msg.msg_iov = iov;
93 	msg.msg_iovlen = 3;
94 	if (efd) {
95 		msg.msg_accrights = (caddr_t) &efd ;
96 		msg.msg_accrightslen = sizeof (efd) ;
97 	} else	msg.msg_accrightslen = 0;
98 
99 	if (sendmsg (sock, &msg, 0) < 0) {
100 		perror("externalconnection: sendmsg") ;
101 		exit(1) ;
102 	}
103 
104 	/* recieve message from connection daemon */
105 	msg.msg_name = "" ;
106 	msg.msg_namelen = 0 ;
107 	iov[0].iov_base = (caddr_t) &rqstfmt ;
108 	iov[0].iov_len = sizeof(rqstfmt) ;
109 	iov[1].iov_base = (caddr_t) &constatus ;
110 	iov[1].iov_len = sizeof(constatus) ;
111 	iov[2].iov_base = (caddr_t) opts;
112 	iov[2].iov_len = optlen ;
113 	msg.msg_iov = iov;
114 	msg.msg_iovlen = 3;
115 	msg.msg_accrights = (caddr_t) &fd ;
116 	msg.msg_accrightslen = sizeof (fd) ;
117 
118 	if (recvmsg (sock, &msg, 0) < 0) {
119 		perror("externalconnection: recvmsg") ;
120 		exit(1) ;
121 	}
122 
123 	/* did we succeed? */
124 	inprocess = -1 ;
125 	if (msg.msg_accrightslen >= sizeof (fd)) {
126 /* XXX needs more work */
127 		fdsockets[nfds-1].fd = fd ;
128 		fdsockets[nfds-1].state = CDNEWRESPONSE ;
129 		return (fd) ;
130 	} else {
131 		nfds--;
132 		close (sock) ;
133 		return (constatus) ;
134 	}
135 }
136 
137 /*
138  * externalfinish: send back file descriptor we got from
139  *	external connect for a well-behaved close. We
140  *	will wait for close just to be able to report
141  *	back any trouble.
142  */
143 externalfinish (fd)
144 	int fd ;
145 {
146 	int sock, n, i, rv ;
147 	struct iovec iov[2];
148 	struct msghdr msg ;
149 	int constatus, rqstfmt;
150 	struct fdsocket *fdp;
151 
152 
153 	fdp = fdsockets ;
154 	/* find socket associated with file descriptor */
155 	for (i = 0; i < nfds ; i++,fdp++)
156 		if (fdp->fd == fd) break;
157 
158 	/* not found at all */
159 	if (i > nfds || !nfds) {
160 		if (inprocess >= 0) externalabort(-1);
161 		return (-1) ;
162 	}
163 
164 	sock = fdp->sock ;
165 
166 	/* never was open */
167 	if (sock < 0) return (-2) ;
168 
169 	/* is there an outstanding request on this guy? */
170 	if (ISCDREQUEST(fdp->state)) externalabort(fdp->fd);
171 
172 	/* mark as closed */
173 	fdp->fd = -1 ;
174 
175 
176 	/* send finish request message */
177 	inprocess = rqstfmt = CDFINISHREQUEST;
178 	msg.msg_name = "";
179 	msg.msg_namelen = 0 ;
180 	iov[0].iov_base = (caddr_t) &rqstfmt ;
181 	iov[0].iov_len = sizeof (rqstfmt) ;
182 	msg.msg_iov = iov;
183 	msg.msg_iovlen = 1;
184 	msg.msg_accrights = (caddr_t) &fd ;
185 	msg.msg_accrightslen = sizeof (fd) ;
186 
187 	if (sendmsg (sock, &msg, 0) < 0) {
188 		perror("externalfinish: sendmsg") ;
189 		return (-3) ;
190 	}
191 
192 	/* recieve message from connection daemon */
193 	msg.msg_name = "" ;
194 	msg.msg_namelen = 0 ;
195 	iov[0].iov_base = (caddr_t) &rqstfmt ;
196 	iov[0].iov_len = sizeof(rqstfmt) ;
197 	iov[1].iov_base = (caddr_t) &constatus ;
198 	iov[1].iov_len = sizeof(constatus) ;
199 	msg.msg_iov = iov;
200 	msg.msg_iovlen = 2;
201 	msg.msg_accrights = 0 ;
202 	msg.msg_accrightslen = 0;
203 
204 	if (recvmsg (sock, &msg, 0) < 0) {
205 		perror("externalfinish: recvmsg") ;
206 		return (-4) ;
207 	}
208 	inprocess = -1 ;
209 	close (fd) ;
210 	close (sock) ;
211 
212 	if (rqstfmt != CDFINISHRESPONSE) return (-5) ;
213 	return (constatus) ;
214 }
215 
216 /*
217  * externalabort: if we have an outstanding request,
218  *	cancel it and return immediately. If the request
219  *	was the initial open, the connection will never
220  *	return a file descriptor, otherwise connection
221  *	status is unaffected. This  routine is mean to be
222  *	called from interrupt routines.
223  */
224 externalabort (fd)
225 	int fd ;
226 {
227 	int sock, n, i, rv ;
228 	struct iovec iov[2];
229 	struct msghdr msg ;
230 	int constatus, rqstfmt;
231 	struct fdsocket *fdp;
232 
233 
234 	fdp = fdsockets ;
235 	/* if we don't know who we are, abort current connection */
236 	if (fd < 0) {
237 		/* but nothing's going on... */
238 		if (inprocess < 0) return (-1) ;
239 		fdp += inprocess ;
240 	} else	{
241 		/* find socket associated with file descriptor */
242 		for (i = 0; i < nfds ; i++,fdp++)
243 			if (fdp->fd == fd) break;
244 
245 		/* not found at all */
246 		if (i > nfds || !nfds)
247 			return (-1) ;
248 	}
249 
250 	sock = fdp->sock ;
251 
252 	/* never was open */
253 	if (sock < 0) return (-2) ;
254 
255 	/* is there not an outstanding request on this guy? */
256 	if (!ISCDREQUEST(fdp->state)) return (-3) ;
257 
258 	/* send abort request message */
259 	rqstfmt = CDCANCELREQUEST;
260 	msg.msg_name = "";
261 	msg.msg_namelen = 0 ;
262 	iov[0].iov_base = (caddr_t) &rqstfmt ;
263 	iov[0].iov_len = sizeof (rqstfmt) ;
264 	msg.msg_iov = iov;
265 	msg.msg_iovlen = 1;
266 	msg.msg_accrights = (caddr_t) &fd ;
267 	msg.msg_accrightslen = sizeof (fd) ;
268 
269 	if (sendmsg (sock, &msg, MSG_OOB) < 0) {
270 		perror("externalabort: sendmsg") ;
271 		return (-4) ;
272 	}
273 	return (0) ;
274 }
275 
276 /*
277  * externaloption: send a bag of options to be done to the file descriptor
278  *	we got from externalconnect. Options are passed as value-result.
279  */
280 externaloption (fd, opts, optlen)
281 	int fd ;
282 	char *opts ;
283 	int *optlen ;
284 {
285 	int sock, n, i, rv ;
286 	struct iovec iov[3];
287 	struct msghdr msg ;
288 	int constatus, rqstfmt;
289 	struct fdsocket *fdp;
290 
291 
292 	fdp = fdsockets ;
293 	/* find socket associated with file descriptor */
294 	for (i = 0; i < nfds ; i++,fdp++)
295 		if (fdp->fd == fd) break;
296 
297 	/* not found at all */
298 	if (i > nfds || !nfds)
299 		return (-1) ;
300 
301 	sock = fdp->sock ;
302 
303 	/* never was open */
304 	if (sock < 0) return (-2) ;
305 
306 	/* is there an outstanding request on this guy? */
307 	if (ISCDREQUEST(fdp->state)) return (-3) ;
308 
309 	/* mark as closed */
310 	fdp->fd = -1 ;
311 
312 	/* send finish request message */
313 	inprocess = rqstfmt = CDOPTIONREQUEST;
314 	msg.msg_name = "";
315 	msg.msg_namelen = 0 ;
316 	iov[0].iov_base = (caddr_t) &rqstfmt ;
317 	iov[0].iov_len = sizeof (rqstfmt) ;
318 	iov[1].iov_base = (caddr_t) opts ;
319 	iov[1].iov_len = *optlen ;
320 	msg.msg_iov = iov;
321 	msg.msg_iovlen = 2;
322 	msg.msg_accrights = (caddr_t) &fd ;
323 	msg.msg_accrightslen = sizeof (fd) ;
324 
325 	if (sendmsg (sock, &msg, 0) < 0) {
326 		perror("externaloption: sendmsg") ;
327 		return (-3) ;
328 	}
329 
330 	/* recieve message from connection daemon */
331 	msg.msg_name = "" ;
332 	msg.msg_namelen = 0 ;
333 	iov[0].iov_base = (caddr_t) &rqstfmt ;
334 	iov[0].iov_len = sizeof(rqstfmt) ;
335 	iov[1].iov_base = (caddr_t) &constatus ;
336 	iov[1].iov_len = sizeof(constatus) ;
337 	iov[2].iov_base = (caddr_t) opts ;
338 	iov[2].iov_len = *optlen ;
339 	msg.msg_iov = iov;
340 	msg.msg_iovlen = 3;
341 	msg.msg_accrights = 0 ;
342 	msg.msg_accrightslen = 0;
343 
344 	if (recvmsg (sock, &msg, 0) < 0) {
345 		perror("externaloption: recvmsg") ;
346 		return (-4) ;
347 	}
348 	inprocess = -1 ;
349 
350 	if (rqstfmt != CDOPTIONRESPONSE) return (-5) ;
351 	return (constatus) ;
352 }
353