+ lck_rw_lock_shared(unp_list_mtx);
+ head = ((intptr_t)arg1 == SOCK_DGRAM ? &unp_dhead : &unp_shead);
+
+ /*
+ * The process of preparing the PCB list is too time-consuming and
+ * resource-intensive to repeat twice on every request.
+ */
+ if (req->oldptr == USER_ADDR_NULL) {
+ n = unp_count;
+ req->oldidx = 2 * sizeof(xug) + (n + n / 8) *
+ (sizeof(struct xunpcb64));
+ lck_rw_done(unp_list_mtx);
+ return 0;
+ }
+
+ if (req->newptr != USER_ADDR_NULL) {
+ lck_rw_done(unp_list_mtx);
+ return EPERM;
+ }
+
+ /*
+ * OK, now we're committed to doing something.
+ */
+ gencnt = unp_gencnt;
+ n = unp_count;
+
+ bzero(&xug, sizeof(xug));
+ xug.xug_len = sizeof(xug);
+ xug.xug_count = n;
+ xug.xug_gen = gencnt;
+ xug.xug_sogen = so_gencnt;
+ error = SYSCTL_OUT(req, &xug, sizeof(xug));
+ if (error) {
+ lck_rw_done(unp_list_mtx);
+ return error;
+ }
+
+ /*
+ * We are done if there is no pcb
+ */
+ if (n == 0) {
+ lck_rw_done(unp_list_mtx);
+ return 0;
+ }
+
+ MALLOC(unp_list, struct unpcb **, n * sizeof(*unp_list),
+ M_TEMP, M_WAITOK);
+ if (unp_list == 0) {
+ lck_rw_done(unp_list_mtx);
+ return ENOMEM;
+ }
+
+ for (unp = head->lh_first, i = 0; unp && i < n;
+ unp = unp->unp_link.le_next) {
+ if (unp->unp_gencnt <= gencnt) {
+ unp_list[i++] = unp;
+ }
+ }
+ n = i; /* in case we lost some during malloc */
+
+ error = 0;
+ for (i = 0; i < n; i++) {
+ unp = unp_list[i];
+ if (unp->unp_gencnt <= gencnt) {
+ struct xunpcb64 xu;
+ size_t xu_len = sizeof(struct xunpcb64);
+
+ bzero(&xu, xu_len);
+ xu.xu_len = xu_len;
+ xu.xu_unpp = (u_int64_t)VM_KERNEL_ADDRPERM(unp);
+ xu.xunp_link.le_next = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_link.le_next);
+ xu.xunp_link.le_prev = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_link.le_prev);
+ xu.xunp_socket = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_socket);
+ xu.xunp_vnode = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_vnode);
+ xu.xunp_ino = unp->unp_ino;
+ xu.xunp_conn = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_conn);
+ xu.xunp_refs = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_refs.lh_first);
+ xu.xunp_reflink.le_next = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_reflink.le_next);
+ xu.xunp_reflink.le_prev = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_reflink.le_prev);
+ xu.xunp_cc = unp->unp_cc;
+ xu.xunp_mbcnt = unp->unp_mbcnt;
+ xu.xunp_gencnt = unp->unp_gencnt;
+
+ if (unp->unp_socket) {
+ sotoxsocket64(unp->unp_socket, &xu.xu_socket);
+ }
+
+ /*
+ * XXX - need more locking here to protect against
+ * connect/disconnect races for SMP.
+ */
+ if (unp->unp_addr) {
+ bcopy(unp->unp_addr, &xu.xu_au,
+ unp->unp_addr->sun_len);
+ }
+ if (unp->unp_conn && unp->unp_conn->unp_addr) {
+ bcopy(unp->unp_conn->unp_addr,
+ &xu.xu_cau,
+ unp->unp_conn->unp_addr->sun_len);
+ }
+
+ error = SYSCTL_OUT(req, &xu, xu_len);
+ }
+ }
+ if (!error) {
+ /*
+ * Give the user an updated idea of our state.
+ * If the generation differs from what we told
+ * her before, she knows that something happened
+ * while we were processing this request, and it
+ * might be necessary to retry.
+ */
+ bzero(&xug, sizeof(xug));
+ xug.xug_len = sizeof(xug);
+ xug.xug_gen = unp_gencnt;
+ xug.xug_sogen = so_gencnt;
+ xug.xug_count = unp_count;
+ error = SYSCTL_OUT(req, &xug, sizeof(xug));
+ }
+ FREE(unp_list, M_TEMP);
+ lck_rw_done(unp_list_mtx);
+ return error;