/* * Copyright (c) 2017-2024 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include "skywalk_test_driver.h" #include "skywalk_test_common.h" #include "skywalk_test_utils.h" /* Copied from skt_oneslot */ static int skt_shutdown_common(int argc, char *argv[], int method) { int error; channel_t channel; uuid_t channel_uuid; ring_id_t ringid; channel_ring_t ring; channel_slot_t slot; uint32_t avail; slot_prop_t prop; int channelfd, sock_fd; fd_set rfdset, wfdset, efdset; struct pollfd fds, sock_fds; int kq; struct kevent kev, sock_kev; uint64_t ts; error = uuid_parse(argv[3], channel_uuid); SKTC_ASSERT_ERR(!error); channel = sktu_channel_create_extended(channel_uuid, 0, CHANNEL_DIR_TX_RX, CHANNEL_RING_ID_ANY, NULL, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1); assert(channel); channelfd = os_channel_get_fd(channel); assert(channelfd != -1); sock_fd = socket(PF_INET, SOCK_STREAM, 0); assert(sock_fd != -1); error = pid_shutdown_sockets(getpid(), SHUTDOWN_SOCKET_LEVEL_DISCONNECT_ALL); SKTC_ASSERT_ERR(!error); switch (method) { case 0: FD_ZERO(&rfdset); FD_ZERO(&wfdset); FD_ZERO(&efdset); FD_SET(channelfd, &rfdset); FD_SET(channelfd, &wfdset); FD_SET(channelfd, &efdset); error = select(channelfd + 1, &rfdset, &wfdset, &efdset, NULL); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); assert(!FD_ISSET(channelfd, &rfdset)); assert(!FD_ISSET(channelfd, &wfdset)); assert(FD_ISSET(channelfd, &efdset)); break; case 1: sock_fds.fd = sock_fd; fds.events = sock_fds.events = POLLWRNORM; fds.revents = sock_fds.revents = 0; /* socket */ error = poll(&sock_fds, 1, -1); T_LOG("poll sock POLLWRNORM: error(%d) events(0x%x)" " revents(0x%x)\n", error, sock_fds.events, sock_fds.revents); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); assert(sock_fds.fd == sock_fd); assert(sock_fds.events == POLLWRNORM); assert((sock_fds.revents & POLLHUP) != 0); /* channel */ fds.fd = channelfd; error = poll(&fds, 1, -1); T_LOG("poll chan POLLWRNORM: error(%d) events(0x%x)" " revents(0x%x)\n", error, fds.events, fds.revents); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); /* ensure that the return values match with socket */ assert(fds.fd == channelfd); assert(fds.events == sock_fds.events); assert(fds.revents == sock_fds.revents); break; case 2: /* socket */ kq = kqueue(); assert(kq != -1); /* event registration */ EV_SET(&sock_kev, sock_fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, NULL); error = kevent(kq, &sock_kev, 1, &sock_kev, 1, NULL); T_LOG("kqueue sock EVFILT_WRITE: error(%d) " "flags(0x%x) fflags(0x%x) data(0x%lx)\n", error, sock_kev.flags, sock_kev.fflags, sock_kev.data); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); assert(sock_kev.filter == EVFILT_WRITE); assert(sock_kev.ident == sock_fd); assert(sock_kev.udata == NULL); assert(sock_kev.flags & EV_ADD); assert(sock_kev.flags & EV_ENABLE); assert(sock_kev.flags & EV_EOF); /* event processing */ error = kevent(kq, NULL, 0, &sock_kev, 1, NULL); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); assert(sock_kev.filter == EVFILT_WRITE); assert(sock_kev.ident == sock_fd); assert(sock_kev.udata == NULL); assert(sock_kev.flags & EV_ADD); assert(sock_kev.flags & EV_ENABLE); assert(sock_kev.flags & EV_EOF); close(kq); /* channel */ kq = kqueue(); assert(kq != -1); EV_SET(&kev, channelfd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, NULL); /* * event registration on a defunct channel should * return EV_EOF */ error = kevent(kq, &kev, 1, &kev, 1, NULL); T_LOG("kqueue chan EVFILT_WRITE: error(%d) " "flags(0x%x) fflags(0x%x) data(0x%lx)\n", error, kev.flags, kev.fflags, kev.data); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); /* ensure that the return values match with socket */ assert(kev.filter == sock_kev.filter); assert(kev.ident == channelfd); assert(kev.udata == sock_kev.udata); assert(kev.flags == sock_kev.flags); assert(kev.data == 0); /* * event processing on a defunct channel should * return EV_EOF */ error = kevent(kq, NULL, 0, &kev, 1, NULL); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); /* ensure that the return values match with socket */ assert(kev.filter == sock_kev.filter); assert(kev.ident == channelfd); assert(kev.udata == sock_kev.udata); assert(kev.flags == sock_kev.flags); assert(kev.data == 0); close(kq); /* check EVFILT_NW_CHANNEL filter */ kq = kqueue(); assert(kq != -1); EV_SET(&kev, channelfd, EVFILT_NW_CHANNEL, EV_ADD | EV_ENABLE, 0, 0, NULL); error = kevent(kq, &kev, 1, &kev, 1, NULL); T_LOG("kqueue EVFILT_NW_CHANNEL: error(%d) " "flags(0x%x) fflags(0x%x) data(0x%lx)\n", error, kev.flags, kev.fflags, kev.data); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); assert(kev.filter == EVFILT_NW_CHANNEL); assert(kev.ident == channelfd); assert(kev.udata == NULL); assert(kev.flags & EV_ADD); assert(kev.flags & EV_ENABLE); assert(kev.flags & EV_EOF); assert(kev.data == 0); error = kevent(kq, NULL, 0, &kev, 1, NULL); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); assert(kev.filter == EVFILT_NW_CHANNEL); assert(kev.udata == NULL); assert(kev.flags & EV_ADD); assert(kev.flags & EV_ENABLE); assert(kev.flags & EV_EOF); assert(kev.data == 0); break; default: abort(); } error = os_channel_is_defunct(channel); assert(error != 0); ringid = os_channel_ring_id(channel, CHANNEL_FIRST_TX_RING); ring = os_channel_tx_ring(channel, ringid); assert(ring); avail = os_channel_available_slot_count(ring); assert(!avail); slot = os_channel_get_next_slot(ring, NULL, &prop); assert(!slot); error = os_channel_advance_slot(ring, slot); SKTC_ASSERT_ERR(error == ENXIO); error = os_channel_sync(channel, CHANNEL_SYNC_TX); SKTC_ASSERT_ERR(error == -1); SKTC_ASSERT_ERR(errno == ENXIO); error = os_channel_pending(ring); assert(error == 0); ts = os_channel_ring_sync_time(ring); assert(ts == 0); ts = os_channel_ring_notify_time(ring); assert(ts == 0); switch (method) { case 0: FD_ZERO(&rfdset); FD_ZERO(&wfdset); FD_ZERO(&efdset); FD_SET(channelfd, &rfdset); FD_SET(channelfd, &wfdset); FD_SET(channelfd, &efdset); error = select(channelfd + 1, &rfdset, &wfdset, &efdset, NULL); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); assert(!FD_ISSET(channelfd, &rfdset)); assert(!FD_ISSET(channelfd, &wfdset)); assert(FD_ISSET(channelfd, &efdset)); break; case 1: sock_fds.fd = sock_fd; fds.events = sock_fds.events = POLLRDNORM; fds.revents = sock_fds.revents = 0; /* socket */ error = poll(&sock_fds, 1, -1); T_LOG("poll sock POLLRDNORM: error(%d) events(0x%x)" " revents(0x%x)\n", error, sock_fds.events, sock_fds.revents); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); assert(sock_fds.fd == sock_fd); assert(sock_fds.events == POLLRDNORM); assert((sock_fds.revents & POLLHUP) != 0); /* channel */ fds.fd = channelfd; error = poll(&fds, 1, -1); T_LOG("poll chan POLLRDNORM: error(%d) events(0x%x)" " revents(0x%x)\n", error, fds.events, fds.revents); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); /* ensure that the return values match with socket */ assert(fds.fd == channelfd); assert(fds.events == sock_fds.events); assert(fds.revents == sock_fds.revents); break; case 2: /* socket */ kq = kqueue(); assert(kq != -1); /* event registration */ EV_SET(&sock_kev, sock_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); error = kevent(kq, &sock_kev, 1, &sock_kev, 1, NULL); T_LOG("kqueue sock EVFILT_READ: error(%d) " "flags(0x%x) fflags(0x%x) data(0x%lx)\n", error, sock_kev.flags, sock_kev.fflags, sock_kev.data); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); assert(sock_kev.filter == EVFILT_READ); assert(sock_kev.ident == sock_fd); assert(sock_kev.udata == NULL); assert(sock_kev.flags & EV_ADD); assert(sock_kev.flags & EV_ENABLE); assert(sock_kev.flags & EV_EOF); /* event processing */ error = kevent(kq, NULL, 0, &sock_kev, 1, NULL); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); assert(sock_kev.filter == EVFILT_READ); assert(sock_kev.ident == sock_fd); assert(sock_kev.udata == NULL); assert(sock_kev.flags & EV_ADD); assert(sock_kev.flags & EV_ENABLE); assert(sock_kev.flags & EV_EOF); close(kq); /* channel */ kq = kqueue(); assert(kq != -1); EV_SET(&kev, channelfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); /* * event registration on a defunct channel should * return EV_EOF */ error = kevent(kq, &kev, 1, &kev, 1, NULL); T_LOG("kqueue chan EVFILT_READ: error(%d) " "flags(0x%x) fflags(0x%x) data(0x%lx)\n", error, kev.flags, kev.fflags, kev.data); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); /* ensure that the return values match with socket */ assert(kev.filter == sock_kev.filter); assert(kev.ident == channelfd); assert(kev.udata == sock_kev.udata); assert(kev.flags == sock_kev.flags); assert(kev.data == 0); /* * event processing on a defunct channel should * return EV_EOF */ error = kevent(kq, NULL, 0, &kev, 1, NULL); SKTC_ASSERT_ERR(error != -1); SKTC_ASSERT_ERR(error == 1); /* ensure that the return values match with socket */ assert(kev.filter == sock_kev.filter); assert(kev.ident == channelfd); assert(kev.udata == sock_kev.udata); assert(kev.flags == sock_kev.flags); assert(kev.data == 0); close(kq); break; default: abort(); } ringid = os_channel_ring_id(channel, CHANNEL_FIRST_RX_RING); ring = os_channel_rx_ring(channel, ringid); assert(ring); avail = os_channel_available_slot_count(ring); assert(!avail); slot = os_channel_get_next_slot(ring, NULL, &prop); assert(!slot); //T_LOG("Got message \"%s\" len %d\n", (char *)prop.sp_buf_ptr, prop.sp_len); // XXX test this? //prop.sp_len = 0; //os_channel_set_slot_properties(ring, slot, &prop); /* slot is NULL here */ error = os_channel_advance_slot(ring, slot); SKTC_ASSERT_ERR(error == ENXIO); error = os_channel_sync(channel, CHANNEL_SYNC_RX); SKTC_ASSERT_ERR(error == -1); SKTC_ASSERT_ERR(errno == ENXIO); os_channel_destroy(channel); return 0; } static int skt_shutdown_select_main(int argc, char *argv[]) { return skt_shutdown_common(argc, argv, 0); } static int skt_shutdown_poll_main(int argc, char *argv[]) { return skt_shutdown_common(argc, argv, 1); } static int skt_shutdown_kqueue_main(int argc, char *argv[]) { return skt_shutdown_common(argc, argv, 2); } struct skywalk_test skt_shutdownus = { "shutdownus", "shuts down channel on upipe and calls select", SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_USER_PIPE, skt_shutdown_select_main, SKTC_GENERIC_UPIPE_ARGV, sktc_generic_upipe_nexus_init, sktc_cleanup_nexus, }; struct skywalk_test skt_shutdownks = { "shutdownks", "shuts down channel on kpipe and calls select", SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE | SK_FEATURE_NEXUS_KERNEL_PIPE_LOOPBACK, skt_shutdown_select_main, SKTC_GENERIC_KPIPE_ARGV, sktc_generic_kpipe_init, sktc_generic_kpipe_fini, }; struct skywalk_test skt_shutdownup = { "shutdownup", "shuts down channel on upipe and calls poll", SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_USER_PIPE, skt_shutdown_poll_main, SKTC_GENERIC_UPIPE_ARGV, sktc_generic_upipe_nexus_init, sktc_cleanup_nexus, }; struct skywalk_test skt_shutdownkp = { "shutdownkp", "shuts down channel on kpipe and calls poll", SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE | SK_FEATURE_NEXUS_KERNEL_PIPE_LOOPBACK, skt_shutdown_poll_main, SKTC_GENERIC_KPIPE_ARGV, sktc_generic_kpipe_init, sktc_generic_kpipe_fini, }; struct skywalk_test skt_shutdownuk = { "shutdownuk", "shuts down channel on upipe and calls kqueue", SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_USER_PIPE, skt_shutdown_kqueue_main, SKTC_GENERIC_UPIPE_ARGV, sktc_generic_upipe_nexus_init, sktc_cleanup_nexus, }; struct skywalk_test skt_shutdownkk = { "shutdownkk", "shuts down channel on kpipe and calls kqueue", SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE | SK_FEATURE_NEXUS_KERNEL_PIPE_LOOPBACK, skt_shutdown_kqueue_main, SKTC_GENERIC_KPIPE_ARGV, sktc_generic_kpipe_init, sktc_generic_kpipe_fini, };