]> sourceware.org Git - newlib-cygwin.git/commitdiff
Cygwin: AF_UNIX: implement sendmsg
authorKen Brown <kbrown@cornell.edu>
Thu, 13 May 2021 01:53:49 +0000 (21:53 -0400)
committerKen Brown <kbrown@cornell.edu>
Fri, 4 Jun 2021 16:36:46 +0000 (12:36 -0400)
First cut.  Not yet tested.

winsup/cygwin/fhandler.h
winsup/cygwin/fhandler_socket_unix.cc

index 45d622debc84c163b110a98ace854a09fa829080..109e73d99ab31b2c1c21e628ea96702c0f413ead 100644 (file)
@@ -1171,6 +1171,7 @@ class fhandler_socket_unix : public fhandler_socket
   ssize_t __stdcall readv (const struct iovec *const iov, int iovcnt,
                           ssize_t tot = -1);
 
+  bool create_cmsg_data (af_unix_pkt_hdr_t *packet, const struct msghdr *msg);
   ssize_t sendmsg (const struct msghdr *msg, int flags);
   ssize_t sendto (const void *ptr, size_t len, int flags,
                  const struct sockaddr *to, int tolen);
index 2178c38ea0671ea3cf08e2b2fe555323b75253f6..825bf1c850eff632e718f1dc68b31accd99c633a 100644 (file)
@@ -1869,11 +1869,172 @@ fhandler_socket_unix::readv (const struct iovec *const iov, int iovcnt,
   return recvmsg (&msg, 0);
 }
 
+bool
+fhandler_socket_unix::create_cmsg_data (af_unix_pkt_hdr_t *packet,
+                                       const struct msghdr *msg)
+{
+  return true;
+}
+
+
 ssize_t
 fhandler_socket_unix::sendmsg (const struct msghdr *msg, int flags)
 {
-  set_errno (EAFNOSUPPORT);
-  return -1;
+  tmp_pathbuf tp;
+  char mqueue_name[CYGWIN_MQUEUE_SOCKET_NAME_LEN + 1];
+  ssize_t ret = -1;
+  int send_ret = -1;
+  HANDLE fh = NULL;
+  mqd_t mqd_dgram = (mqd_t) -1;
+  mqd_t mqd_out;
+  af_unix_pkt_hdr_t *packet;
+  int err;
+
+  __try
+    {
+      /* Valid flags: MSG_DONTWAIT, MSG_NOSIGNAL */
+      if (flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL))
+       {
+         set_errno (EOPNOTSUPP);
+         __leave;
+       }
+      /* FIXME: Check this.  It's from Stevens, UNIX Network
+        Programming, discussion of select.  He doesn't say whether we
+        also clear so_error.*/
+      err = so_error ();
+      if (err)
+       {
+         set_errno (err);
+         __leave;
+       }
+      if (get_socket_type () == SOCK_STREAM)
+       {
+         if (msg->msg_namelen)
+           {
+             set_errno (connect_state () == connected ? EISCONN : EOPNOTSUPP);
+             __leave;
+           }
+         if (connect_state () != connected)
+           {
+             set_errno (ENOTCONN);
+             __leave;
+           }
+       }
+      else
+       {
+         sun_name_t sun;
+         int peer_type;
+
+         if (msg->msg_namelen)
+           sun.set ((const struct sockaddr_un *) msg->msg_name,
+                    msg->msg_namelen);
+         else
+           sun = *peer_sun_path ();
+         fh = open_socket (&sun, peer_type, mqueue_name);
+         if (!fh)
+           __leave;
+         NtClose (fh);
+         if (peer_type != SOCK_DGRAM)
+           {
+             set_errno (EPROTOTYPE);
+             __leave;
+           }
+         mqd_dgram = open_mqueue (mqueue_name,
+                            is_nonblocking () || flags & MSG_DONTWAIT);
+         if (mqd_dgram == (mqd_t) -1)
+           __leave;
+       }
+      packet = (af_unix_pkt_hdr_t *) tp.w_get ();
+      /* For STREAM sockets, always send shutdown info in case a
+        shutdown call failed. */
+      shut_state shut_info =
+       get_socket_type () == SOCK_STREAM
+       ? (shut_state) saw_shutdown ()
+       : _SHUT_NONE;
+      if (get_socket_type () == SOCK_DGRAM && binding_state () == bound)
+       {
+         packet->init (false, shut_info, sun_path ()->un_len, 0, 0);
+         memcpy (AF_UNIX_PKT_NAME (packet), &sun_path ()->un,
+                 sun_path ()->un_len);
+       }
+      else
+       packet->init (false, shut_info, 0, 0, 0);
+      if (!create_cmsg_data (packet, msg))
+       __leave;
+      for (int i = 0; i < msg->msg_iovlen; ++i)
+       if (!AF_UNIX_PKT_DATA_APPEND (packet, msg->msg_iov[i].iov_base,
+                                     msg->msg_iov[i].iov_len))
+         {
+           if (packet->data_len == 0)
+             {
+               set_errno (EMSGSIZE);
+               __leave;
+             }
+           else
+             break;
+         }
+      /* A packet can have 0 length only on a datagram socket. */
+      if (packet->data_len == 0 && get_socket_type () == SOCK_STREAM)
+       {
+         ret = 0;
+         __leave;
+       }
+      io_lock ();
+      /* Handle MSG_DONTWAIT in blocking mode.  Already done in DGRAM case. */
+      if (get_socket_type () == SOCK_STREAM && !is_nonblocking ()
+         && (flags & MSG_DONTWAIT))
+       set_mqueue_non_blocking (get_mqd_out (), true);
+
+      mqd_out = mqd_dgram != (mqd_t) -1 ? mqd_dgram : get_mqd_out ();
+      bool nonblocking = is_nonblocking () || (flags & MSG_DONTWAIT);
+      grab_admin_pkt ();
+      bool shutdown = saw_shutdown_write ();
+      if (!shutdown && nonblocking)
+       send_ret = mq_send (mqd_out, (const char *) packet,
+                           packet->pckt_len, 0);
+      else
+       {
+         /* FIXME: Is this reasonable? */
+#define AF_UNIX_SEND_TIMEOUT 10 * NSPERSEC/MSPERSEC      /* 10 ms */
+         struct timespec timeout;
+         clock_gettime (CLOCK_REALTIME, &timeout);
+         while (!shutdown)
+           {
+             /* We don't want to block forever if the peer has shut
+                down.  FIXME: This might only be appropriate for
+                STREAM sockets. */
+             timeout.tv_nsec += AF_UNIX_SEND_TIMEOUT;
+             if (timeout.tv_nsec >= NSPERSEC)
+               {
+                 timeout.tv_nsec -= NSPERSEC;
+                 ++timeout.tv_sec;
+               }
+             send_ret = mq_timedsend (mqd_out, (const char *) packet,
+                                      packet->pckt_len, 0, &timeout);
+             if (send_ret >= 0 || get_errno () != ETIMEDOUT)
+               break;
+             grab_admin_pkt ();
+             shutdown = saw_shutdown_write ();
+           }
+       }
+      if (get_socket_type () == SOCK_STREAM && !is_nonblocking ()
+         && (flags & MSG_DONTWAIT))
+       set_mqueue_non_blocking (get_mqd_out (), false);
+      io_unlock ();
+      if (send_ret == 0)
+       ret = packet->data_len;
+      else if (shutdown && get_socket_type () == SOCK_STREAM)
+       {
+         set_errno (EPIPE);
+         if (!(flags & MSG_NOSIGNAL))
+           raise (SIGPIPE);
+       }
+    }
+  __except (EFAULT)
+  __endtry
+  if (mqd_dgram != (mqd_t) -1)
+    mq_close (mqd_dgram);
+  return ret;
 }
 
 ssize_t
This page took 0.03826 seconds and 5 git commands to generate.