static void kqueue_set_nsecs(struct kqueue_timer *our_timer, uint64_t nsecs) { struct timespec nowait = { 0, 1 }; #ifdef HAVE_KEVENT64 struct kevent64_s kev; EV_SET64(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_NSECONDS, nsecs, 0, 0, 0); kevent64(our_timer->handle, &kev, 1, NULL, 0, 0, &nowait); #else struct kevent kev; EV_SET(&kev, our_timer->handle, EVFILT_TIMER, EV_ADD | EV_ENABLE, #ifdef NOTE_NSECONDS nsecs <= 0xFFffFFff ? NOTE_NSECONDS : #endif #ifdef NOTE_USECONDS NOTE_USECONDS #else /* Milliseconds, if no constants are defined */ 0 #endif , #ifdef NOTE_NSECONDS nsecs <= 0xFFffFFff ? nsecs : #endif #ifdef NOTE_USECONDS nsecs / 1000 #else /* Milliseconds, if nothing else is defined */ nsecs / 1000000 #endif , NULL); kevent(our_timer->handle, &kev, 1, NULL, 0, &nowait); #endif }
void poll_events(reg q, function f) { struct kevent64_s ke[MAX_EVENTS]; int n = kevent64(q,NULL,0,ke,MAX_EVENTS,0,NULL); if (n <= 0) return; for (int i = 0; i < n; ++i) f(ke[i].ident,ke[i].filter == EVFILT_PROC ? PROC : NONE); }
reg add_event(reg q, reg fd, enum event_types t, enum event_flags f) { struct kevent64_s ke; EV_SET64(&ke,fd,(t == READ || t == RESP ? EVFILT_READ : t == WRITE || t == REQ ? EVFILT_WRITE : t == PROC ? EVFILT_PROC : t == NODE ? EVFILT_VNODE : 0), EV_ADD | (f == ONESHOT ? EV_ONESHOT : 0), (f == EXIT ? NOTE_EXIT : 0), 0, 0, 0, 0); return kevent64(q,&ke,1,NULL,0,0, &ts); }
sleep(1); T_LOG("waited %d seconds for sleep...", i+1); } return -1; } T_DECL(kevent_continuous_time_periodic_tick, "kevent(EVFILT_TIMER with NOTE_MACH_CONTINUOUS_TIME)", T_META_LTEPHASE(LTE_POSTINIT)){ mach_timebase_info(&tb_info); int kq; T_ASSERT_POSIX_SUCCESS((kq = kqueue()), NULL); struct kevent64_s change = {0}; EV_SET64(&change, 1, EVFILT_TIMER, EV_ADD, NOTE_SECONDS | NOTE_MACH_CONTINUOUS_TIME, 4, 0, 0, 0); T_LOG("EV_SET(&change, 1, EVFILT_TIMER, EV_ADD, NOTE_SECONDS | NOTE_MACH_CONTINUOUS_TIME, 4, 0, 0, 0);"); T_ASSERT_POSIX_ZERO(kevent64(kq, &change, 1, NULL, 0, 0, NULL), NULL); uint64_t abs_then = mach_absolute_time(); uint64_t cnt_then = mach_continuous_time();; trigger_sleep(1); int sleep_secs = wait_for_sleep(); struct kevent64_s event = {0}; T_WITH_ERRNO; T_ASSERT_EQ(kevent64(kq, NULL, 0, &event, 1, 0, NULL), 1, "kevent() should have returned one event"); T_LOG("event = {.ident = %llx, .filter = %d, .flags = %d, .fflags = %d, .data = %lld, .udata = %lld}", event.ident, event.filter, event.flags, event.fflags, event.data, event.udata); T_ASSERT_EQ(event.flags & EV_ERROR, 0, "event should not have EV_ERROR set: %s", event.flags & EV_ERROR ? strerror((int)event.data) : "no error"); uint64_t abs_now = mach_absolute_time(); uint64_t cnt_now = mach_continuous_time();; uint64_t ct_ms_progressed = tick_to_ms(cnt_now - cnt_then);
int main() { kern_return_t kr; mach_port_t bport, port, pset; struct msg_recv message; struct msg_send reply; struct kevent64_s kev; int kq, r; task_get_special_port(mach_task_self(), TASK_BOOTSTRAP_PORT, &bport); syslog(LOG_ERR, "bootstrap port: %d", bport); kr = bootstrap_check_in(bootstrap_port, "mach.service-test", &port); if (kr != KERN_SUCCESS) { syslog(LOG_ERR, "bootstrap_check_in: kr=%d", kr); exit(1); } syslog(LOG_ERR, "service port: %d", port); kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &pset); if (kr != KERN_SUCCESS) { syslog(LOG_ERR, "mach_port_allocate: kr=%d", kr); exit(1); } kr = mach_port_move_member(mach_task_self(), port, pset); if (kr != KERN_SUCCESS) { syslog(LOG_ERR, "mach_port_move_member: kr=%d", kr); exit(1); } kq = kqueue(); syslog(LOG_ERR, "kqueue fd: %d", kq); memset(&kev, 0, sizeof(struct kevent64_s)); EV_SET64(&kev, pset, EVFILT_MACHPORT, EV_ADD | EV_ENABLE, 0, 0, 0, 0, 0); if (kevent64(kq, &kev, 1, NULL, 0, 0, NULL) < 0) { syslog(LOG_ERR, "kevent64: %s (%d)", strerror(errno), errno); return 0; } for (;;) { message.hdr.msgh_local_port = port; message.hdr.msgh_size = sizeof(struct msg_recv); r = kevent64(kq, NULL, 0, &kev, 1, 0, NULL); if (r < 0) { syslog(LOG_ERR, "kevent64 failed: %s (%d)", strerror(errno), errno); continue; } syslog(LOG_ERR, "kevent64: events=%d", r); kr = mach_msg_receive((mach_msg_header_t *)&message); if (kr != KERN_SUCCESS) syslog(LOG_ERR, "mach_msg_receive failure: kr=%d", kr); else syslog(LOG_ERR, "received message on port %d: body=%s", message.hdr.msgh_remote_port, message.body); memset(&reply, 0, sizeof(struct msg_send)); sprintf(&reply.body[0], "hello buddy"); reply.hdr.msgh_local_port = MACH_PORT_NULL; reply.hdr.msgh_remote_port = message.hdr.msgh_remote_port; reply.hdr.msgh_size = sizeof(struct msg_send); reply.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); kr = mach_msg_send((mach_msg_header_t *)&reply); if (kr != KERN_SUCCESS) syslog(LOG_ERR, "mach_msg_send failure: kr=%d", kr); } }
void * server(void *serverarg) { int kq; struct kevent64_s kev[1]; int err; struct port_args args; int idx; kern_return_t ret; int totalmsg = num_msgs * num_clients; args.server_num = (int) (long) serverarg; setup_server_ports(&args); thread_setup(args.server_num + 1); kq = kqueue(); if (kq == -1) { perror("kqueue"); exit(1); } EV_SET64(&kev[0], args.pset, EVFILT_MACHPORT, (EV_ADD | EV_CLEAR | EV_DISPATCH), #if DIRECT_MSG_RCV MACH_RCV_MSG|MACH_RCV_LARGE, 0, 0, (mach_vm_address_t)args.req_msg, args.req_size); #else 0, 0, 0, 0, 0); #endif err = kevent64(kq, kev, 1, NULL, 0, 0, NULL); if (err == -1) { perror("kevent"); exit(1); } for (idx = 0; idx < totalmsg; idx++) { if (verbose) printf("server awaiting message %d\n", idx); retry: EV_SET64(&kev[0], args.pset, EVFILT_MACHPORT, EV_ENABLE, #if DIRECT_MSG_RCV MACH_RCV_MSG|MACH_RCV_LARGE, 0, 0, (mach_vm_address_t)args.req_msg, args.req_size); #else 0, 0, 0, 0, 0); #endif err = kevent64(kq, kev, 1, kev, 1, 0, NULL); if (err == -1) { perror("kevent64"); exit(1); } if (err == 0) { // printf("kevent64: returned zero\n"); goto retry; } #if DIRECT_MSG_RCV ret = kev[0].fflags; if (MACH_MSG_SUCCESS != ret) { if (verbose) printf("kevent64() mach_msg_return=%d", ret); mach_error("kevent64 (msg receive): ", ret); exit(1); } #else if (kev[0].data != args.port) printf("kevent64(MACH_PORT_NULL) port name (0x%x) != expected (0x%x)\n", kev[0].data, args.port); args.req_msg->msgh_bits = 0; args.req_msg->msgh_size = args.req_size; args.req_msg->msgh_local_port = args.port; ret = mach_msg(args.req_msg, MACH_RCV_MSG|MACH_RCV_INTERRUPT|MACH_RCV_LARGE, 0, args.req_size, args.pset, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (MACH_RCV_INTERRUPTED == ret) break; if (MACH_MSG_SUCCESS != ret) { if (verbose) printf("mach_msg() ret=%d", ret); mach_error("mach_msg (receive): ", ret); exit(1); } #endif if (verbose) printf("server received message %d\n", idx); if (args.req_msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) { ret = vm_deallocate(mach_task_self(), (vm_address_t)((ipc_complex_message *)args.req_msg)->descriptor.address, ((ipc_complex_message *)args.req_msg)->descriptor.size); } if (1 == args.req_msg->msgh_id) { if (verbose) printf("server sending reply %d\n", idx); args.reply_msg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND); args.reply_msg->msgh_size = args.reply_size; args.reply_msg->msgh_remote_port = args.req_msg->msgh_remote_port; args.reply_msg->msgh_local_port = args.req_msg->msgh_local_port; args.reply_msg->msgh_id = 2; ret = mach_msg(args.reply_msg, MACH_SEND_MSG, args.reply_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (MACH_MSG_SUCCESS != ret) { mach_error("mach_msg (send): ", ret); exit(1); } } } }
/* * debugger will register kevents, attach+kill child, wait for quorum on events, * then exit. */ void do_debugger(pid_t child, debugger_exit_t debugger_exit_time) { int kq; int ret; struct kevent64_s kev; int deathcount = 0; int stat_loc; setprogname("DEBUGGER"); logline("debugger pid %d has child pid %d. waiting for process exit...", getpid(), child); sleep(1); fprintf(stderr, "\n"); ret = ptrace(PT_ATTACH, child, 0, 0); if (ret == -1) err(1, "ptrace(PT_ATTACH)"); ret = waitpid(child, &stat_loc, WUNTRACED); if (ret == -1) err(1, "waitpid(child, WUNTRACED)"); logline("child process stopped: %s", print_exit(child, stat_loc)); if (debugger_exit_time == eDebuggerExitWithoutDetach) { logline("exiting because of eDebuggerExitWithoutDetach"); exit(0); } else if (debugger_exit_time == eDebuggerExitAfterDetach) { ret = ptrace(PT_DETACH, child, 0, 0); if (ret == -1) err(1, "ptrace(PT_DETACH)"); ret = kill(child, SIGKILL); if (ret == -1) err(1, "kill(SIGKILL)"); logline("exiting because of eDebuggerExitAfterDetach"); exit(0); } kq = kqueue(); if (kq < 0) err(1, "kqueue"); EV_SET64(&kev, child, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT|NOTE_EXITSTATUS|NOTE_EXIT_DETAIL|NOTE_FORK|NOTE_EXEC|NOTE_SIGNAL, 0, child, 0, 0); ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); if (ret == -1) err(1, "kevent64 EVFILT_PROC"); EV_SET64(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, child, 0, 0); ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); if (ret == -1) err(1, "kevent64 EVFILT_SIGNAL"); sleep(1); fprintf(stderr, "\n"); ret = ptrace(PT_KILL, child, 0, 0); if (ret == -1) err(1, "ptrace(PT_KILL)"); while(1) { ret = kevent64(kq, NULL, 0, &kev, 1, 0, NULL); if (ret == -1) { if (errno == EINTR) continue; err(1, "kevent64"); } else if (ret == 0) { continue; } logline("kevent64 returned ident %llu filter %s fflags %s data %s", kev.ident, str_kev_filter(kev.filter), str_kev_fflags(kev.filter, kev.fflags), str_kev_data(kev.filter, kev.fflags, kev.data, kev.udata)); if (kev.filter == EVFILT_SIGNAL) { /* must be SIGCHLD */ deathcount++; } else if (kev.filter == EVFILT_PROC) { if ((kev.fflags & (NOTE_EXIT|NOTE_EXITSTATUS)) == (NOTE_EXIT|NOTE_EXITSTATUS)) { deathcount++; } } if (deathcount >= 2) { break; } } if (debugger_exit_time == eDebuggerExitAfterKillWithoutWaitpid) { logline("exiting because of eDebuggerExitAfterKillWithoutWaitpid"); exit(0); } sleep(1); fprintf(stderr, "\n"); ret = waitpid(child, &stat_loc, 0); if (ret == -1) err(1, "waitpid(%d) by debugger failed", child); logline("child process: %s", print_exit(child, stat_loc)); /* Received both SIGCHLD and NOTE_EXIT */ exit(0); }
/* * debugger will register kevents, wait for quorum on events, then exit */ void do_parent(pid_t child, pid_t debugger, parent_exit_t parent_exit_time, debugger_exit_t debugger_exit_time) { int kq; int ret; struct kevent64_s kev; int deathcount = 0; int childsignalcount = 0; int stat_loc; setprogname("PARENT"); logline("parent pid %d has child pid %d and debugger pid %d. waiting for processes to exit...", getpid(), child, debugger); kq = kqueue(); if (kq < 0) err(1, "kqueue"); EV_SET64(&kev, child, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT|NOTE_EXITSTATUS|NOTE_EXIT_DETAIL|NOTE_FORK|NOTE_EXEC|NOTE_SIGNAL, 0, child, 0, 0); ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); if (ret == -1) err(1, "kevent64 EVFILT_PROC"); EV_SET64(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD|EV_ENABLE, 0, 0, child, 0, 0); ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); if (ret == -1) err(1, "kevent64 EVFILT_SIGNAL"); EV_SET64(&kev, 7, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, NOTE_SECONDS, 7, 0, 0, 0); ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); if (ret == -1) err(1, "kevent64 EVFILT_TIMER"); while(1) { ret = kevent64(kq, NULL, 0, &kev, 1, 0, NULL); if (ret == -1) { if (errno == EINTR) continue; err(1, "kevent64"); } else if (ret == 0) { break; } logline("kevent64 returned ident %llu filter %s fflags %s data %s", kev.ident, str_kev_filter(kev.filter), str_kev_fflags(kev.filter, kev.fflags), str_kev_data(kev.filter, kev.fflags, kev.data, kev.udata)); if (kev.filter == EVFILT_SIGNAL) { /* must be SIGCHLD */ deathcount++; } else if (kev.filter == EVFILT_PROC) { if (child == kev.udata) { if ((kev.fflags & (NOTE_EXIT|NOTE_EXITSTATUS)) == (NOTE_EXIT|NOTE_EXITSTATUS)) { deathcount++; } else if (kev.fflags & NOTE_SIGNAL) { childsignalcount++; if ((parent_exit_time == eParentExitAfterDebuggerAttach) && (childsignalcount >= 2)) { /* second signal is attach */ logline("exiting because of eParentExitAfterDebuggerAttach"); exit(0); } } else if (kev.fflags & NOTE_FORK) { if (parent_exit_time == eParentExitBeforeDebuggerAttach) { logline("exiting because of eParentExitBeforeDebuggerAttach"); exit(0); } } } } else if (kev.filter == EVFILT_TIMER) { errx(1, "timed out waiting for NOTE_EXIT"); } if (deathcount >= (parent_exit_time == eParentExitAfterWaitpidAndSIGCHLD ? 2 : 1)) { break; } } if (parent_exit_time == eParentExitBeforeWaitpid) { logline("exiting because of eParentExitBeforeWaitpid"); exit(0); } ret = waitpid(child, &stat_loc, 0); if (ret == -1) err(1, "waitpid(%d) by parent failed", child); logline("child process: %s", print_exit(child, stat_loc)); if (!WIFSIGNALED(stat_loc) || (SIGKILL != WTERMSIG(stat_loc))) errx(1, "child did not exit as expected"); ret = waitpid(debugger, &stat_loc, 0); if (ret == -1) err(1, "waitpid(%d) by parent failed", debugger); logline("debugger process: %s", print_exit(debugger, stat_loc)); if (!WIFEXITED(stat_loc) || (0 != WEXITSTATUS(stat_loc))) errx(1, "debugger did not exit as expected"); /* Received both SIGCHLD and NOTE_EXIT, as needed */ logline("exiting beacuse of eParentExitAfterWaitpid/eParentExitAfterWaitpidAndSIGCHLD"); exit(0); }
void do_grandparent(pid_t parent, pid_t child, pid_t debugger, debugger_exit_t debugger_exit_time) { pid_t result; int stat_loc; int exit_code = 0; int kq; int ret; struct kevent64_s kev; int neededdeathcount = (debugger != -1) ? 3 : 2; setprogname("GRANDPARENT"); logline("grandparent pid %d has parent pid %d and child pid %d. waiting for parent process exit...", getpid(), parent, child); /* make sure we can at least observe real child's exit */ kq = kqueue(); if (kq < 0) err(1, "kqueue"); EV_SET64(&kev, child, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, child, 0, 0); ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); if (ret == -1) err(1, "kevent64 EVFILT_PROC"); EV_SET64(&kev, parent, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, parent, 0, 0); ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); if (ret == -1) err(1, "kevent64 EVFILT_PROC"); if (debugger != -1) { EV_SET64(&kev, debugger, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, debugger, 0, 0); ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); if (ret == -1) err(1, "kevent64 EVFILT_PROC"); } EV_SET64(&kev, 5, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, NOTE_SECONDS, 5, 0, 0, 0); ret = kevent64(kq, &kev, 1, NULL, 0, 0, NULL); if (ret == -1) err(1, "kevent64 EVFILT_TIMER"); while(1) { ret = kevent64(kq, NULL, 0, &kev, 1, 0, NULL); if (ret == -1) { if (errno == EINTR) continue; err(1, "kevent64"); } else if (ret == 0) { break; } logline("kevent64 returned ident %llu filter %s fflags %s data %s", kev.ident, str_kev_filter(kev.filter), str_kev_fflags(kev.filter, kev.fflags), str_kev_data(kev.filter, kev.fflags, kev.data, kev.udata)); if (kev.filter == EVFILT_PROC) { if (child == kev.udata) { neededdeathcount--; } else if (parent == kev.udata) { neededdeathcount--; } else if ((debugger != -1) && (debugger == kev.udata)) { neededdeathcount--; } } else if (kev.filter == EVFILT_TIMER) { logline("timed out waiting for NOTE_EXIT"); exit_code = 1; break; } if (neededdeathcount == 0) { break; } } result = waitpid(parent, &stat_loc, 0); if (result == -1) err(1, "waitpid(%d) by grandparent failed", parent); logline("parent process: %s", print_exit(parent, stat_loc)); if (!WIFEXITED(stat_loc) || (0 != WEXITSTATUS(stat_loc))) { exit_code = 1; } if (iszombie(parent)) { logline("parent %d is now a zombie", parent); exit_code = 1; } if (iszombie(child)) { logline("child %d is now a zombie", child); exit_code = 1; } if ((debugger != -1) && iszombie(debugger)) { logline("debugger %d is now a zombie", debugger); exit_code = 1; } exit(exit_code); }