Improve the process for GNU tools
[minix3.git] / minix / net / uds / ioc_uds.c
blob3ac44084d76b45eb21c4bc555aa4a847e96eedbd
1 /*
2 * Unix Domain Sockets Implementation (PF_UNIX, PF_LOCAL)
3 * This code handles ioctl(2) commands to implement the socket API.
4 * Some helper functions are also present.
5 */
7 #include "uds.h"
9 static int
10 perform_connection(devminor_t minorx, devminor_t minory,
11 struct sockaddr_un *addr)
14 * There are several places were a connection is established, the
15 * initiating call being one of accept(2), connect(2), socketpair(2).
17 dprintf(("UDS: perform_connection(%d, %d)\n", minorx, minory));
20 * Only connection-oriented types are acceptable and only equal
21 * types can connect to each other.
23 if ((uds_fd_table[minorx].type != SOCK_SEQPACKET &&
24 uds_fd_table[minorx].type != SOCK_STREAM) ||
25 uds_fd_table[minorx].type != uds_fd_table[minory].type)
26 return EINVAL;
28 /* Connect the pair of sockets. */
29 uds_fd_table[minorx].peer = minory;
30 uds_fd_table[minory].peer = minorx;
32 /* Set the address of both sockets */
33 memcpy(&uds_fd_table[minorx].addr, addr, sizeof(struct sockaddr_un));
34 memcpy(&uds_fd_table[minory].addr, addr, sizeof(struct sockaddr_un));
36 return OK;
39 static int
40 do_accept(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
42 devminor_t minorparent; /* minor number of parent (server) */
43 devminor_t minorpeer;
44 int rc, i;
45 struct sockaddr_un addr;
47 dprintf(("UDS: do_accept(%d)\n", minor));
50 * Somewhat weird logic is used in this function, so here's an
51 * overview... The minor number is the server's client socket
52 * (the socket to be returned by accept()). The data waiting
53 * for us in the IO Grant is the address that the server is
54 * listening on. This function uses the address to find the
55 * server's descriptor. From there we can perform the
56 * connection or suspend and wait for a connect().
59 /* This IOCTL must be called on a 'fresh' socket. */
60 if (uds_fd_table[minor].type != -1)
61 return EINVAL;
63 /* Get the server's address */
64 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &addr,
65 sizeof(struct sockaddr_un))) != OK)
66 return rc;
68 /* Locate the server socket. */
69 for (i = 0; i < NR_FDS; i++) {
70 if (uds_fd_table[i].addr.sun_family == AF_UNIX &&
71 !strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path,
72 sizeof(uds_fd_table[i].addr.sun_path)) &&
73 uds_fd_table[i].listening == 1)
74 break;
77 if (i == NR_FDS)
78 return EINVAL;
80 minorparent = i; /* parent */
82 /* We are the parent's child. */
83 uds_fd_table[minorparent].child = minor;
86 * The peer has the same type as the parent. we need to be that
87 * type too.
89 uds_fd_table[minor].type = uds_fd_table[minorparent].type;
91 /* Locate the peer to accept in the parent's backlog. */
92 minorpeer = -1;
93 for (i = 0; i < uds_fd_table[minorparent].backlog_size; i++) {
94 if (uds_fd_table[minorparent].backlog[i] != -1) {
95 minorpeer = uds_fd_table[minorparent].backlog[i];
96 uds_fd_table[minorparent].backlog[i] = -1;
97 break;
101 if (minorpeer == -1) {
102 dprintf(("UDS: do_accept(%d): suspend\n", minor));
105 * There are no peers in the backlog, suspend and wait for one
106 * to show up.
108 uds_fd_table[minor].suspended = UDS_SUSPENDED_ACCEPT;
110 return EDONTREPLY;
113 dprintf(("UDS: connecting %d to %d -- parent is %d\n", minor,
114 minorpeer, minorparent));
116 if ((rc = perform_connection(minor, minorpeer, &addr)) != OK) {
117 dprintf(("UDS: do_accept(%d): connection failed\n", minor));
119 return rc;
122 uds_fd_table[minorparent].child = -1;
124 /* If the peer is blocked on connect() or write(), revive the peer. */
125 if (uds_fd_table[minorpeer].suspended == UDS_SUSPENDED_CONNECT ||
126 uds_fd_table[minorpeer].suspended == UDS_SUSPENDED_WRITE) {
127 dprintf(("UDS: do_accept(%d): revive %d\n", minor, minorpeer));
128 uds_unsuspend(minorpeer);
131 /* See if we can satisfy an ongoing select. */
132 if ((uds_fd_table[minorpeer].sel_ops & CDEV_OP_WR) &&
133 uds_fd_table[minorpeer].size < UDS_BUF) {
134 /* A write on the peer is possible now. */
135 chardriver_reply_select(uds_fd_table[minorpeer].sel_endpt,
136 minorpeer, CDEV_OP_WR);
137 uds_fd_table[minorpeer].sel_ops &= ~CDEV_OP_WR;
140 return OK;
143 static int
144 do_connect(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
146 int child, peer;
147 struct sockaddr_un addr;
148 int rc, i, j;
150 dprintf(("UDS: do_connect(%d)\n", minor));
152 /* Only connection oriented sockets can connect. */
153 if (uds_fd_table[minor].type != SOCK_STREAM &&
154 uds_fd_table[minor].type != SOCK_SEQPACKET)
155 return EINVAL;
157 /* The socket must not be connecting or connected already. */
158 peer = uds_fd_table[minor].peer;
159 if (peer != -1) {
160 if (uds_fd_table[peer].peer == -1)
161 return EALREADY; /* connecting */
162 else
163 return EISCONN; /* connected */
166 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &addr,
167 sizeof(struct sockaddr_un))) != OK)
168 return rc;
170 if ((rc = checkperms(uds_fd_table[minor].owner, addr.sun_path,
171 sizeof(addr.sun_path))) != OK)
172 return rc;
175 * Look for a socket of the same type that is listening on the
176 * address we want to connect to.
178 for (i = 0; i < NR_FDS; i++) {
179 if (uds_fd_table[minor].type != uds_fd_table[i].type)
180 continue;
181 if (!uds_fd_table[i].listening)
182 continue;
183 if (uds_fd_table[i].addr.sun_family != AF_UNIX)
184 continue;
185 if (strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path,
186 sizeof(uds_fd_table[i].addr.sun_path)))
187 continue;
189 /* Found a matching socket. */
190 break;
193 if (i == NR_FDS)
194 return ECONNREFUSED;
196 /* If the server is blocked on an accept, perform the connection. */
197 if ((child = uds_fd_table[i].child) != -1) {
198 rc = perform_connection(minor, child, &addr);
200 if (rc != OK)
201 return rc;
203 uds_fd_table[i].child = -1;
205 dprintf(("UDS: do_connect(%d): revive %d\n", minor, child));
207 /* Wake up the accepting party. */
208 uds_unsuspend(child);
210 return OK;
213 dprintf(("UDS: adding %d to %d's backlog\n", minor, i));
215 /* Look for a free slot in the backlog. */
216 rc = -1;
217 for (j = 0; j < uds_fd_table[i].backlog_size; j++) {
218 if (uds_fd_table[i].backlog[j] == -1) {
219 uds_fd_table[i].backlog[j] = minor;
221 rc = 0;
222 break;
226 if (rc == -1)
227 return ECONNREFUSED; /* backlog is full */
229 /* See if the server is blocked on select(). */
230 if (uds_fd_table[i].sel_ops & CDEV_OP_RD) {
231 /* Satisfy a read-type select on the server. */
232 chardriver_reply_select(uds_fd_table[i].sel_endpt, i,
233 CDEV_OP_RD);
235 uds_fd_table[i].sel_ops &= ~CDEV_OP_RD;
238 /* We found our server. */
239 uds_fd_table[minor].peer = i;
241 memcpy(&uds_fd_table[minor].addr, &addr, sizeof(struct sockaddr_un));
243 dprintf(("UDS: do_connect(%d): suspend\n", minor));
245 /* Suspend until the server side accepts the connection. */
246 uds_fd_table[minor].suspended = UDS_SUSPENDED_CONNECT;
248 return EDONTREPLY;
251 static int
252 do_listen(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
254 int rc;
255 int backlog_size;
257 dprintf(("UDS: do_listen(%d)\n", minor));
259 /* Ensure the socket has a type and is bound. */
260 if (uds_fd_table[minor].type == -1 ||
261 uds_fd_table[minor].addr.sun_family != AF_UNIX)
262 return EINVAL;
264 /* listen(2) supports only two socket types. */
265 if (uds_fd_table[minor].type != SOCK_STREAM &&
266 uds_fd_table[minor].type != SOCK_SEQPACKET)
267 return EOPNOTSUPP;
270 * The POSIX standard doesn't say what to do if listen() has
271 * already been called. Well, there isn't an errno. We silently
272 * let it happen, but if listen() has already been called, we
273 * don't allow the backlog to shrink.
275 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &backlog_size,
276 sizeof(backlog_size))) != OK)
277 return rc;
279 if (uds_fd_table[minor].listening == 0) {
280 /* Set the backlog size to a reasonable value. */
281 if (backlog_size <= 0 || backlog_size > UDS_SOMAXCONN)
282 backlog_size = UDS_SOMAXCONN;
284 uds_fd_table[minor].backlog_size = backlog_size;
285 } else {
286 /* Allow the user to expand the backlog size. */
287 if (backlog_size > uds_fd_table[minor].backlog_size &&
288 backlog_size < UDS_SOMAXCONN)
289 uds_fd_table[minor].backlog_size = backlog_size;
292 * Don't let the user shrink the backlog_size, as we might
293 * have clients waiting in those slots.
297 /* This socket is now listening. */
298 uds_fd_table[minor].listening = 1;
300 return OK;
303 static int
304 do_socket(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
306 int rc, type;
308 dprintf(("UDS: do_socket(%d)\n", minor));
310 /* The socket type can only be set once. */
311 if (uds_fd_table[minor].type != -1)
312 return EINVAL;
314 /* Get the requested type. */
315 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &type,
316 sizeof(type))) != OK)
317 return rc;
319 /* Assign the type if it is valid only. */
320 switch (type) {
321 case SOCK_STREAM:
322 case SOCK_DGRAM:
323 case SOCK_SEQPACKET:
324 uds_fd_table[minor].type = type;
325 return OK;
327 default:
328 return EINVAL;
332 static int
333 do_bind(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
335 struct sockaddr_un addr;
336 int rc, i;
338 dprintf(("UDS: do_bind(%d)\n", minor));
340 /* If the type hasn't been set by do_socket() yet, OR an attempt
341 * to re-bind() a non-SOCK_DGRAM socket is made, fail the call.
343 if ((uds_fd_table[minor].type == -1) ||
344 (uds_fd_table[minor].addr.sun_family == AF_UNIX &&
345 uds_fd_table[minor].type != SOCK_DGRAM))
346 return EINVAL;
348 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &addr,
349 sizeof(struct sockaddr_un))) != OK)
350 return rc;
352 /* Do some basic sanity checks on the address. */
353 if (addr.sun_family != AF_UNIX)
354 return EAFNOSUPPORT;
356 if (addr.sun_path[0] == '\0')
357 return ENOENT;
359 if ((rc = checkperms(uds_fd_table[minor].owner, addr.sun_path,
360 sizeof(addr.sun_path))) != OK)
361 return rc;
363 /* Make sure the address isn't already in use by another socket. */
364 for (i = 0; i < NR_FDS; i++) {
365 if (uds_fd_table[i].addr.sun_family == AF_UNIX &&
366 !strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path,
367 sizeof(uds_fd_table[i].addr.sun_path))) {
368 /* Another socket is bound to this sun_path. */
369 return EADDRINUSE;
373 /* Looks good, perform the bind(). */
374 memcpy(&uds_fd_table[minor].addr, &addr, sizeof(struct sockaddr_un));
376 return OK;
379 static int
380 do_getsockname(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
382 dprintf(("UDS: do_getsockname(%d)\n", minor));
385 * Unconditionally send the address we have assigned to this socket.
386 * The POSIX standard doesn't say what to do if the address hasn't been
387 * set. If the address isn't currently set, then the user will get
388 * NULL bytes. Note: libc depends on this behavior.
390 return sys_safecopyto(endpt, grant, 0,
391 (vir_bytes) &uds_fd_table[minor].addr, sizeof(struct sockaddr_un));
394 static int
395 do_getpeername(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
397 int peer_minor;
399 dprintf(("UDS: do_getpeername(%d)\n", minor));
401 /* Check that the socket is connected with a valid peer. */
402 if (uds_fd_table[minor].peer != -1) {
403 peer_minor = uds_fd_table[minor].peer;
405 /* Copy the address from the peer. */
406 return sys_safecopyto(endpt, grant, 0,
407 (vir_bytes) &uds_fd_table[peer_minor].addr,
408 sizeof(struct sockaddr_un));
409 } else if (uds_fd_table[minor].err == ECONNRESET) {
410 uds_fd_table[minor].err = 0;
412 return ECONNRESET;
413 } else
414 return ENOTCONN;
417 static int
418 do_shutdown(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
420 int rc, how;
422 dprintf(("UDS: do_shutdown(%d)\n", minor));
424 /* The socket must be connection oriented. */
425 if (uds_fd_table[minor].type != SOCK_STREAM &&
426 uds_fd_table[minor].type != SOCK_SEQPACKET)
427 return EINVAL;
429 if (uds_fd_table[minor].peer == -1) {
430 /* shutdown(2) is only valid for connected sockets. */
431 if (uds_fd_table[minor].err == ECONNRESET)
432 return ECONNRESET;
433 else
434 return ENOTCONN;
437 /* Get the 'how' parameter from the caller. */
438 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &how,
439 sizeof(how))) != OK)
440 return rc;
442 switch (how) {
443 case SHUT_RD: /* Take away read permission. */
444 uds_fd_table[minor].mode &= ~UDS_R;
445 break;
447 case SHUT_WR: /* Take away write permission. */
448 uds_fd_table[minor].mode &= ~UDS_W;
449 break;
451 case SHUT_RDWR: /* Shut down completely. */
452 uds_fd_table[minor].mode = 0;
453 break;
455 default:
456 return EINVAL;
459 return OK;
462 static int
463 do_socketpair(devminor_t minorx, endpoint_t endpt, cp_grant_id_t grant)
465 int rc;
466 dev_t minorin;
467 devminor_t minory;
468 struct sockaddr_un addr;
470 dprintf(("UDS: do_socketpair(%d)\n", minorx));
472 /* The ioctl argument is the minor number of the second socket. */
473 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &minorin,
474 sizeof(minorin))) != OK)
475 return rc;
477 minory = minor(minorin);
479 dprintf(("UDS: socketpair(%d, %d,)\n", minorx, minory));
481 /* Security check: both sockets must have the same owner endpoint. */
482 if (uds_fd_table[minorx].owner != uds_fd_table[minory].owner)
483 return EPERM;
485 addr.sun_family = AF_UNIX;
486 addr.sun_path[0] = 'X';
487 addr.sun_path[1] = '\0';
489 return perform_connection(minorx, minory, &addr);
492 static int
493 do_getsockopt_sotype(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
495 dprintf(("UDS: do_getsockopt_sotype(%d)\n", minor));
497 /* If the type hasn't been set yet, we fail the call. */
498 if (uds_fd_table[minor].type == -1)
499 return EINVAL;
501 return sys_safecopyto(endpt, grant, 0,
502 (vir_bytes) &uds_fd_table[minor].type, sizeof(int));
505 static int
506 do_getsockopt_peercred(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
508 int peer_minor;
509 int rc;
510 struct uucred cred;
512 dprintf(("UDS: do_getsockopt_peercred(%d)\n", minor));
514 if (uds_fd_table[minor].peer == -1) {
515 if (uds_fd_table[minor].err == ECONNRESET) {
516 uds_fd_table[minor].err = 0;
518 return ECONNRESET;
519 } else
520 return ENOTCONN;
523 peer_minor = uds_fd_table[minor].peer;
525 /* Obtain the peer's credentials and copy them out. */
526 if ((rc = getnucred(uds_fd_table[peer_minor].owner, &cred)) < 0)
527 return rc;
529 return sys_safecopyto(endpt, grant, 0, (vir_bytes) &cred,
530 sizeof(struct uucred));
533 static int
534 do_getsockopt_sndbuf(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
536 size_t sndbuf = UDS_BUF;
538 dprintf(("UDS: do_getsockopt_sndbuf(%d)\n", minor));
540 return sys_safecopyto(endpt, grant, 0, (vir_bytes) &sndbuf,
541 sizeof(sndbuf));
544 static int
545 do_setsockopt_sndbuf(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
547 int rc;
548 size_t sndbuf;
550 dprintf(("UDS: do_setsockopt_sndbuf(%d)\n", minor));
552 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &sndbuf,
553 sizeof(sndbuf))) != OK)
554 return rc;
556 /* The send buffer is limited to 32KB at the moment. */
557 if (sndbuf > UDS_BUF)
558 return ENOSYS;
560 /* FIXME: actually shrink the buffer. */
561 return OK;
564 static int
565 do_getsockopt_rcvbuf(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
567 size_t rcvbuf = UDS_BUF;
569 dprintf(("UDS: do_getsockopt_rcvbuf(%d)\n", minor));
571 return sys_safecopyto(endpt, grant, 0, (vir_bytes) &rcvbuf,
572 sizeof(rcvbuf));
575 static int
576 do_setsockopt_rcvbuf(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
578 int rc;
579 size_t rcvbuf;
581 dprintf(("UDS: do_setsockopt_rcvbuf(%d)\n", minor));
583 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &rcvbuf,
584 sizeof(rcvbuf))) != OK)
585 return rc;
587 /* The receive buffer is limited to 32KB at the moment. */
588 if (rcvbuf > UDS_BUF)
589 return ENOSYS;
591 /* FIXME: actually shrink the buffer. */
592 return OK;
595 static int
596 do_sendto(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
598 int rc;
599 struct sockaddr_un addr;
601 dprintf(("UDS: do_sendto(%d)\n", minor));
603 /* This IOCTL is only for SOCK_DGRAM sockets. */
604 if (uds_fd_table[minor].type != SOCK_DGRAM)
605 return EINVAL;
607 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &addr,
608 sizeof(struct sockaddr_un))) != OK)
609 return rc;
611 /* Do some basic sanity checks on the address. */
612 if (addr.sun_family != AF_UNIX || addr.sun_path[0] == '\0')
613 return EINVAL;
615 if ((rc = checkperms(uds_fd_table[minor].owner, addr.sun_path,
616 sizeof(addr.sun_path))) != OK)
617 return rc;
619 memcpy(&uds_fd_table[minor].target, &addr, sizeof(struct sockaddr_un));
621 return OK;
624 static int
625 do_recvfrom(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
627 dprintf(("UDS: do_recvfrom(%d)\n", minor));
629 return sys_safecopyto(endpt, grant, 0,
630 (vir_bytes) &uds_fd_table[minor].source,
631 sizeof(struct sockaddr_un));
634 static int
635 send_fds(devminor_t minor, struct msg_control *msg_ctrl,
636 struct ancillary *data)
638 int i, rc, nfds, totalfds;
639 endpoint_t from_ep;
640 struct msghdr msghdr;
641 struct cmsghdr *cmsg = NULL;
643 dprintf(("UDS: send_fds(%d)\n", minor));
645 from_ep = uds_fd_table[minor].owner;
647 /* Obtain this socket's credentials. */
648 if ((rc = getnucred(from_ep, &data->cred)) < 0)
649 return rc;
651 dprintf(("UDS: minor=%d cred={%d,%d,%d}\n", minor, data->cred.pid,
652 data->cred.uid, data->cred.gid));
654 totalfds = data->nfiledes;
656 memset(&msghdr, '\0', sizeof(struct msghdr));
657 msghdr.msg_control = msg_ctrl->msg_control;
658 msghdr.msg_controllen = msg_ctrl->msg_controllen;
660 for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg != NULL;
661 cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
662 if (cmsg->cmsg_level != SOL_SOCKET ||
663 cmsg->cmsg_type != SCM_RIGHTS)
664 continue;
666 nfds = MIN((cmsg->cmsg_len-CMSG_LEN(0))/sizeof(int), OPEN_MAX);
668 for (i = 0; i < nfds; i++) {
669 if (totalfds == OPEN_MAX)
670 return EOVERFLOW;
672 data->fds[totalfds] = ((int *) CMSG_DATA(cmsg))[i];
673 dprintf(("UDS: minor=%d fd[%d]=%d\n", minor, totalfds,
674 data->fds[totalfds]));
675 totalfds++;
679 for (i = data->nfiledes; i < totalfds; i++) {
680 if ((rc = copyfd(from_ep, data->fds[i], COPYFD_FROM)) < 0) {
681 printf("UDS: copyfd(COPYFD_FROM) failed: %d\n", rc);
683 /* Revert the successful copyfd() calls made so far. */
684 for (i--; i >= data->nfiledes; i--)
685 close(data->fds[i]);
687 return rc;
690 dprintf(("UDS: send_fds(): %d -> %d\n", data->fds[i], rc));
692 data->fds[i] = rc; /* this is now the local FD */
695 data->nfiledes = totalfds;
697 return OK;
701 * This function calls close() for all of the FDs in flight. This is used
702 * when a Unix Domain Socket is closed and there exists references to file
703 * descriptors that haven't been received with recvmsg().
706 uds_clear_fds(devminor_t minor, struct ancillary *data)
708 int i;
710 dprintf(("UDS: uds_clear_fds(%d)\n", minor));
712 for (i = 0; i < data->nfiledes; i++) {
713 dprintf(("UDS: uds_clear_fds() => %d\n", data->fds[i]));
715 close(data->fds[i]);
717 data->fds[i] = -1;
720 data->nfiledes = 0;
722 return OK;
725 static int
726 recv_fds(devminor_t minor, struct ancillary *data,
727 struct msg_control *msg_ctrl)
729 int rc, i, j, fds[OPEN_MAX];
730 struct msghdr msghdr;
731 struct cmsghdr *cmsg;
732 endpoint_t to_ep;
734 dprintf(("UDS: recv_fds(%d)\n", minor));
736 msghdr.msg_control = msg_ctrl->msg_control;
737 msghdr.msg_controllen = msg_ctrl->msg_controllen;
739 cmsg = CMSG_FIRSTHDR(&msghdr);
740 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * data->nfiledes);
741 cmsg->cmsg_level = SOL_SOCKET;
742 cmsg->cmsg_type = SCM_RIGHTS;
744 to_ep = uds_fd_table[minor].owner;
746 /* Copy to the target endpoint. */
747 for (i = 0; i < data->nfiledes; i++) {
748 if ((rc = copyfd(to_ep, data->fds[i], COPYFD_TO)) < 0) {
749 printf("UDS: copyfd(COPYFD_TO) failed: %d\n", rc);
751 /* Revert the successful copyfd() calls made so far. */
752 for (i--; i >= 0; i--)
753 (void) copyfd(to_ep, fds[i], COPYFD_CLOSE);
755 return rc;
758 fds[i] = rc; /* this is now the remote FD */
761 /* Close the local copies only once the entire procedure succeeded. */
762 for (i = 0; i < data->nfiledes; i++) {
763 dprintf(("UDS: recv_fds(): %d -> %d\n", data->fds[i], fds[i]));
765 ((int *)CMSG_DATA(cmsg))[i] = fds[i];
767 close(data->fds[i]);
769 data->fds[i] = -1;
772 data->nfiledes = 0;
774 return OK;
777 static int
778 recv_cred(devminor_t minor, struct ancillary *data,
779 struct msg_control *msg_ctrl)
781 struct msghdr msghdr;
782 struct cmsghdr *cmsg;
784 dprintf(("UDS: recv_cred(%d)\n", minor));
786 msghdr.msg_control = msg_ctrl->msg_control;
787 msghdr.msg_controllen = msg_ctrl->msg_controllen;
789 cmsg = CMSG_FIRSTHDR(&msghdr);
790 if (cmsg->cmsg_len > 0)
791 cmsg = CMSG_NXTHDR(&msghdr, cmsg);
793 cmsg->cmsg_len = CMSG_LEN(sizeof(struct uucred));
794 cmsg->cmsg_level = SOL_SOCKET;
795 cmsg->cmsg_type = SCM_CREDS;
796 memcpy(CMSG_DATA(cmsg), &data->cred, sizeof(struct uucred));
798 return OK;
801 static int
802 do_sendmsg(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
804 int peer, rc, i;
805 struct msg_control msg_ctrl;
807 dprintf(("UDS: do_sendmsg(%d)\n", minor));
809 memset(&msg_ctrl, '\0', sizeof(struct msg_control));
811 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &msg_ctrl,
812 sizeof(struct msg_control))) != OK)
813 return rc;
815 /* Locate the peer. */
816 peer = -1;
817 if (uds_fd_table[minor].type == SOCK_DGRAM) {
818 if (uds_fd_table[minor].target.sun_path[0] == '\0' ||
819 uds_fd_table[minor].target.sun_family != AF_UNIX)
820 return EDESTADDRREQ;
822 for (i = 0; i < NR_FDS; i++) {
824 * Look for a SOCK_DGRAM socket that is bound on the
825 * target address.
827 if (uds_fd_table[i].type == SOCK_DGRAM &&
828 uds_fd_table[i].addr.sun_family == AF_UNIX &&
829 !strncmp(uds_fd_table[minor].target.sun_path,
830 uds_fd_table[i].addr.sun_path,
831 sizeof(uds_fd_table[i].addr.sun_path))) {
832 peer = i;
833 break;
837 if (peer == -1)
838 return ENOENT;
839 } else {
840 peer = uds_fd_table[minor].peer;
841 if (peer == -1)
842 return ENOTCONN;
845 dprintf(("UDS: sendmsg(%d) -- peer=%d\n", minor, peer));
848 * Note: it's possible that there is already some file descriptors in
849 * ancillary_data if the peer didn't call recvmsg() yet. That's okay.
850 * The receiver will get the current file descriptors plus the new
851 * ones.
853 return send_fds(minor, &msg_ctrl, &uds_fd_table[peer].ancillary_data);
856 static int
857 do_recvmsg(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
859 int rc;
860 struct msg_control msg_ctrl;
861 socklen_t clen_avail = 0;
862 socklen_t clen_needed = 0;
863 socklen_t clen_desired = 0;
865 dprintf(("UDS: do_recvmsg(%d)\n", minor));
866 dprintf(("UDS: minor=%d credentials={pid:%d,uid:%d,gid:%d}\n", minor,
867 uds_fd_table[minor].ancillary_data.cred.pid,
868 uds_fd_table[minor].ancillary_data.cred.uid,
869 uds_fd_table[minor].ancillary_data.cred.gid));
871 memset(&msg_ctrl, '\0', sizeof(struct msg_control));
874 * Get the msg_control from the user. It will include the
875 * amount of space the user has allocated for control data.
877 if ((rc = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &msg_ctrl,
878 sizeof(struct msg_control))) != OK)
879 return rc;
881 clen_avail = MIN(msg_ctrl.msg_controllen, MSG_CONTROL_MAX);
883 if (uds_fd_table[minor].ancillary_data.nfiledes > 0) {
884 clen_needed = CMSG_SPACE(sizeof(int) *
885 uds_fd_table[minor].ancillary_data.nfiledes);
888 /* if there is room we also include credentials */
889 clen_desired = clen_needed + CMSG_SPACE(sizeof(struct uucred));
891 if (clen_needed > clen_avail)
892 return EOVERFLOW;
894 if (uds_fd_table[minor].ancillary_data.nfiledes > 0) {
895 if ((rc = recv_fds(minor, &uds_fd_table[minor].ancillary_data,
896 &msg_ctrl)) != OK)
897 return rc;
900 if (clen_desired <= clen_avail) {
901 rc = recv_cred(minor, &uds_fd_table[minor].ancillary_data,
902 &msg_ctrl);
903 if (rc != OK)
904 return rc;
905 msg_ctrl.msg_controllen = clen_desired;
906 } else
907 msg_ctrl.msg_controllen = clen_needed;
909 /* Send the control data to the user. */
910 return sys_safecopyto(endpt, grant, 0, (vir_bytes) &msg_ctrl,
911 sizeof(struct msg_control));
914 static int
915 do_fionread(devminor_t minor, endpoint_t endpt, cp_grant_id_t grant)
917 int rc;
919 rc = uds_perform_read(minor, NONE, GRANT_INVALID, UDS_BUF, 1);
921 /* What should we do on error? Just set to zero for now. */
922 if (rc < 0)
923 rc = 0;
925 return sys_safecopyto(endpt, grant, 0, (vir_bytes) &rc, sizeof(rc));
929 uds_do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
930 cp_grant_id_t grant)
932 int rc;
934 switch (request) {
935 case NWIOSUDSCONN:
936 /* Connect to a listening socket -- connect(). */
937 rc = do_connect(minor, endpt, grant);
939 break;
941 case NWIOSUDSACCEPT:
942 /* Accept an incoming connection -- accept(). */
943 rc = do_accept(minor, endpt, grant);
945 break;
947 case NWIOSUDSBLOG:
949 * Set the backlog_size and put the socket into the listening
950 * state -- listen().
952 rc = do_listen(minor, endpt, grant);
954 break;
956 case NWIOSUDSTYPE:
957 /* Set the SOCK_ type for this socket -- socket(). */
958 rc = do_socket(minor, endpt, grant);
960 break;
962 case NWIOSUDSADDR:
963 /* Set the address for this socket -- bind(). */
964 rc = do_bind(minor, endpt, grant);
966 break;
968 case NWIOGUDSADDR:
969 /* Get the address for this socket -- getsockname(). */
970 rc = do_getsockname(minor, endpt, grant);
972 break;
974 case NWIOGUDSPADDR:
975 /* Get the address for the peer -- getpeername(). */
976 rc = do_getpeername(minor, endpt, grant);
978 break;
980 case NWIOSUDSSHUT:
982 * Shut down a socket for reading, writing, or both --
983 * shutdown().
985 rc = do_shutdown(minor, endpt, grant);
987 break;
989 case NWIOSUDSPAIR:
990 /* Connect two sockets -- socketpair(). */
991 rc = do_socketpair(minor, endpt, grant);
993 break;
995 case NWIOGUDSSOTYPE:
996 /* Get socket type -- getsockopt(SO_TYPE). */
997 rc = do_getsockopt_sotype(minor, endpt, grant);
999 break;
1001 case NWIOGUDSPEERCRED:
1002 /* Get peer endpoint -- getsockopt(SO_PEERCRED). */
1003 rc = do_getsockopt_peercred(minor, endpt, grant);
1005 break;
1007 case NWIOSUDSTADDR:
1008 /* Set target address -- sendto(). */
1009 rc = do_sendto(minor, endpt, grant);
1011 break;
1013 case NWIOGUDSFADDR:
1014 /* Get from address -- recvfrom(). */
1015 rc = do_recvfrom(minor, endpt, grant);
1017 break;
1019 case NWIOGUDSSNDBUF:
1020 /* Get the send buffer size -- getsockopt(SO_SNDBUF). */
1021 rc = do_getsockopt_sndbuf(minor, endpt, grant);
1023 break;
1025 case NWIOSUDSSNDBUF:
1026 /* Set the send buffer size -- setsockopt(SO_SNDBUF). */
1027 rc = do_setsockopt_sndbuf(minor, endpt, grant);
1029 break;
1031 case NWIOGUDSRCVBUF:
1032 /* Get the send buffer size -- getsockopt(SO_SNDBUF). */
1033 rc = do_getsockopt_rcvbuf(minor, endpt, grant);
1035 break;
1037 case NWIOSUDSRCVBUF:
1038 /* Set the send buffer size -- setsockopt(SO_SNDBUF). */
1039 rc = do_setsockopt_rcvbuf(minor, endpt, grant);
1041 break;
1043 case NWIOSUDSCTRL:
1044 /* Set the control data -- sendmsg(). */
1045 rc = do_sendmsg(minor, endpt, grant);
1047 break;
1049 case NWIOGUDSCTRL:
1050 /* Set the control data -- recvmsg(). */
1051 rc = do_recvmsg(minor, endpt, grant);
1053 break;
1055 case FIONREAD:
1057 * Get the number of bytes immediately available for reading.
1059 rc = do_fionread(minor, endpt, grant);
1061 break;
1063 default:
1065 * The IOCTL command is not valid for /dev/uds -- this happens
1066 * a lot and is normal. A lot of libc functions determine the
1067 * socket type with IOCTLs. Any unrecognized requests simply
1068 * get an ENOTTY response.
1071 rc = ENOTTY;
1074 return rc;