+ if (so->so_proto->pr_getlock != NULL)
+ mutex_held = (*so->so_proto->pr_getlock)(so, 0);
+ else
+ mutex_held = so->so_proto->pr_domain->dom_mtx;
+ error = msleep((caddr_t)&so->so_timeo, mutex_held,
+ PSOCK | PCATCH, __func__, 0);
+ if (so->so_state & SS_DRAINING) {
+ error = ECONNABORTED;
+ }
+ if (error != 0)
+ break;
+ }
+ if (error == 0) {
+ error = so->so_error;
+ so->so_error = 0;
+ }
+out:
+ socket_unlock(so, 1);
+ return (error);
+}
+
+int
+peeloff(struct proc *p, struct peeloff_args *uap, int *retval)
+{
+ /*
+ * Due to similiarity with a POSIX interface, define as
+ * an unofficial cancellation point.
+ */
+ __pthread_testcancel(1);
+ return (peeloff_nocancel(p, uap, retval));
+}
+
+static int
+peeloff_nocancel(struct proc *p, struct peeloff_args *uap, int *retval)
+{
+ struct fileproc *fp;
+ struct socket *mp_so, *so = NULL;
+ int newfd, fd = uap->s;
+ short fflag; /* type must match fp->f_flag */
+ int error;
+
+ *retval = -1;
+
+ error = fp_getfsock(p, fd, &fp, &mp_so);
+ if (error != 0) {
+ if (error == EOPNOTSUPP)
+ error = ENOTSOCK;
+ goto out_nofile;
+ }
+ if (mp_so == NULL) {
+ error = EBADF;
+ goto out;
+ }
+
+ socket_lock(mp_so, 1);
+ error = sopeelofflocked(mp_so, uap->aid, &so);
+ if (error != 0) {
+ socket_unlock(mp_so, 1);
+ goto out;
+ }
+ VERIFY(so != NULL);
+ socket_unlock(mp_so, 0); /* keep ref on mp_so for us */
+
+ fflag = fp->f_flag;
+ error = falloc(p, &fp, &newfd, vfs_context_current());
+ if (error != 0) {
+ /* drop this socket (probably ran out of file descriptors) */
+ soclose(so);
+ sodereference(mp_so); /* our mp_so ref */
+ goto out;
+ }
+
+ fp->f_flag = fflag;
+ fp->f_ops = &socketops;
+ fp->f_data = (caddr_t)so;
+
+ /*
+ * If the socket has been marked as inactive by sosetdefunct(),
+ * disallow further operations on it.
+ */
+ if (so->so_flags & SOF_DEFUNCT) {
+ sodefunct(current_proc(), so,
+ SHUTDOWN_SOCKET_LEVEL_DISCONNECT_INTERNAL);
+ }
+
+ proc_fdlock(p);
+ procfdtbl_releasefd(p, newfd, NULL);
+ fp_drop(p, newfd, fp, 1);
+ proc_fdunlock(p);
+
+ sodereference(mp_so); /* our mp_so ref */
+ *retval = newfd;
+
+out:
+ file_drop(fd);
+
+out_nofile:
+ return (error);
+}
+
+int
+disconnectx(struct proc *p, struct disconnectx_args *uap, int *retval)
+{
+ /*
+ * Due to similiarity with a POSIX interface, define as
+ * an unofficial cancellation point.
+ */
+ __pthread_testcancel(1);
+ return (disconnectx_nocancel(p, uap, retval));
+}
+
+static int
+disconnectx_nocancel(struct proc *p, struct disconnectx_args *uap, int *retval)
+{
+#pragma unused(p, retval)
+ struct socket *so;
+ int fd = uap->s;
+ int error;
+
+ error = file_socket(fd, &so);
+ if (error != 0)
+ return (error);
+ if (so == NULL) {
+ error = EBADF;
+ goto out;
+ }
+
+ error = sodisconnectx(so, uap->aid, uap->cid);
+out:
+ file_drop(fd);
+ return (error);
+}
+
+/*
+ * Returns: 0 Success
+ * socreate:EAFNOSUPPORT
+ * socreate:EPROTOTYPE
+ * socreate:EPROTONOSUPPORT
+ * socreate:ENOBUFS
+ * socreate:ENOMEM
+ * socreate:EISCONN
+ * socreate:??? [other protocol families, IPSEC]
+ * falloc:ENFILE
+ * falloc:EMFILE
+ * falloc:ENOMEM
+ * copyout:EFAULT
+ * soconnect2:EINVAL
+ * soconnect2:EPROTOTYPE
+ * soconnect2:??? [other protocol families[
+ */
+int
+socketpair(struct proc *p, struct socketpair_args *uap,
+ __unused int32_t *retval)
+{
+ struct fileproc *fp1, *fp2;
+ struct socket *so1, *so2;
+ int fd, error, sv[2];
+
+ AUDIT_ARG(socket, uap->domain, uap->type, uap->protocol);
+ error = socreate(uap->domain, &so1, uap->type, uap->protocol);
+ if (error)
+ return (error);
+ error = socreate(uap->domain, &so2, uap->type, uap->protocol);
+ if (error)
+ goto free1;
+
+ error = falloc(p, &fp1, &fd, vfs_context_current());
+ if (error) {
+ goto free2;
+ }
+ fp1->f_flag = FREAD|FWRITE;
+ fp1->f_ops = &socketops;
+ fp1->f_data = (caddr_t)so1;
+ sv[0] = fd;
+
+ error = falloc(p, &fp2, &fd, vfs_context_current());
+ if (error) {
+ goto free3;
+ }
+ fp2->f_flag = FREAD|FWRITE;
+ fp2->f_ops = &socketops;
+ fp2->f_data = (caddr_t)so2;
+ sv[1] = fd;
+
+ error = soconnect2(so1, so2);
+ if (error) {
+ goto free4;
+ }
+ if (uap->type == SOCK_DGRAM) {
+ /*
+ * Datagram socket connection is asymmetric.
+ */
+ error = soconnect2(so2, so1);
+ if (error) {
+ goto free4;
+ }
+ }
+
+ if ((error = copyout(sv, uap->rsv, 2 * sizeof (int))) != 0)
+ goto free4;
+
+ proc_fdlock(p);
+ procfdtbl_releasefd(p, sv[0], NULL);
+ procfdtbl_releasefd(p, sv[1], NULL);
+ fp_drop(p, sv[0], fp1, 1);
+ fp_drop(p, sv[1], fp2, 1);
+ proc_fdunlock(p);
+
+ return (0);
+free4: