static int mod_detach(void *instance) { rlm_unbound_t *inst = instance; if (inst->log_fd >= 0) { fr_event_fd_delete(inst->el, inst->log_fd, FR_EVENT_FILTER_IO); if (inst->ub) { ub_process(inst->ub); /* This can hang/leave zombies currently * see upstream bug #519 * ...so expect valgrind to complain with -m */ #if 0 ub_ctx_delete(inst->ub); #endif } } if (inst->log_pipe_stream[1]) { fclose(inst->log_pipe_stream[1]); } if (inst->log_pipe_stream[0]) { if (inst->log_pipe_in_use) { fr_event_fd_delete(inst->el, inst->log_pipe[0], FR_EVENT_FILTER_IO); } fclose(inst->log_pipe_stream[0]); } if (inst->log_stream) { fclose(inst->log_stream); } return 0; }
static int mod_close(fr_listen_t *li) { proto_detail_file_t const *inst = talloc_get_type_abort_const(li->app_io_instance, proto_detail_file_t); proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t); if (thread->nr) (void) fr_network_socket_delete(thread->nr, inst->parent->listen); /* * @todo - have our OWN event loop for timers, and a * "copy timer from -> to, which means we only have to * delete our child event loop from the parent on close. */ close(thread->fd); if (thread->vnode_fd >= 0) { if (thread->nr) { (void) fr_network_socket_delete(thread->nr, inst->parent->listen); } else { if (fr_event_fd_delete(thread->el, thread->vnode_fd, FR_EVENT_FILTER_VNODE) < 0) { PERROR("Failed removing DELETE callback on detach"); } } close(thread->vnode_fd); thread->vnode_fd = -1; pthread_mutex_destroy(&thread->worker_mutex); } return 0; }
static void mod_vnode_delete(fr_event_list_t *el, int fd, UNUSED int fflags, void *ctx) { proto_detail_file_thread_t *thread = talloc_get_type_abort(ctx, proto_detail_file_thread_t); proto_detail_file_t const *inst = thread->inst; DEBUG("proto_detail (%s): Deleted %s", thread->name, inst->filename_work); /* * Silently ignore notifications from the directory. We * didn't ask for them, but libkqueue delivers them to * us. */ if (fd == thread->fd) return; if (fd != thread->vnode_fd) { ERROR("Received DELETE for FD %d, when we were expecting one on FD %d - ignoring it", fd, thread->vnode_fd); return; } if (fr_event_fd_delete(el, fd, FR_EVENT_FILTER_VNODE) < 0) { PERROR("Failed removing DELETE callback after deletion"); } close(fd); thread->vnode_fd = -1; /* * Re-initialize the state machine. * * Note that a "delete" may be the result of an atomic * "move", which both deletes the old file, and creates * the new one. */ work_init(thread); }
static void rs_got_packet(UNUSED fr_event_list_t *el, int fd, void *ctx) { static uint64_t count = 0; /* Packets seen */ rs_event_t *event = ctx; pcap_t *handle = event->in->handle; int i; int ret; const uint8_t *data; struct pcap_pkthdr *header; for (i = 0; (event->in->type == PCAP_FILE_IN) || (i < RS_FORCE_YIELD); i++) { ret = pcap_next_ex(handle, &header, &data); if (ret == 0) { /* No more packets available at this time */ return; } if (ret == -2 && (event->in->type == PCAP_FILE_IN)) { INFO("Done reading packets (%s)", event->in->name); fr_event_fd_delete(events, 0, fd); return; } if (ret < 0) { ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle)); return; } count++; rs_packet_process(count, event, header, data); } }
/** Called by libcurl to register a socket that it's interested in receiving IO events for * * * @param[in] easy handle this fd relates to. * @param[in] fd File descriptor curl wants to be notified about. * @param[in] what Which events libcurl wants to be notified of, may be one of: * - CURL_POLL_IN Wait for incoming data. For the socket * to become readable. * - CURL_POLL_OUT Wait for outgoing data. For the socket * to become writable. * - CURL_POLL_INOUT Wait for incoming and outgoing data. * For the socket to become readable or writable. * - CURL_POLL_REMOVE The specified socket/file descriptor is no * longer used by libcurl. * @param[in] ctx The rlm_rest_thread_t specific to this thread. * @param[in] fd_ctx Private data associated with the socket. */ static int _rest_io_event_modify(UNUSED CURL *easy, curl_socket_t fd, int what, void *ctx, UNUSED void *fd_ctx) { rlm_rest_thread_t *thread = talloc_get_type_abort(ctx, rlm_rest_thread_t); switch (what) { case CURL_POLL_IN: if (fr_event_fd_insert(thread, thread->el, fd, _rest_io_service_readable, NULL, _rest_io_service_errored, thread) < 0) { PERROR("multi-handle %p registration failed for read+error events on FD %i", thread->mandle, fd); return -1; } DEBUG4("multi-handle %p registered for read+error events on FD %i", thread->mandle, fd); break; case CURL_POLL_OUT: if (fr_event_fd_insert(thread, thread->el, fd, NULL, _rest_io_service_writable, _rest_io_service_errored, thread) < 0) { PERROR("multi-handle %p registration failed for write+error events on FD %i", thread->mandle, fd); return -1; } DEBUG4("multi-handle %p registered for write+error events on FD %i", thread->mandle, fd); break; case CURL_POLL_INOUT: if (fr_event_fd_insert(thread, thread->el, fd, _rest_io_service_readable, _rest_io_service_writable, _rest_io_service_errored, thread) < 0) { PERROR("multi-handle %p registration failed for read+write+error events on FD %i", thread->mandle, fd); return -1; } DEBUG4("multi-handle %p registered for read+write+error events on FD %i", thread->mandle, fd); break; case CURL_POLL_REMOVE: if (fr_event_fd_delete(thread->el, fd, FR_EVENT_FILTER_IO) < 0) { PERROR("multi-handle %p de-registration failed for FD %i", thread->mandle, fd); return -1; } DEBUG4("multi-handle %p unregistered events for FD %i", thread->mandle, fd); break; default: rad_assert(0); return -1; } return CURLM_OK; }
/** Frees an unlang event, removing it from the request's event loop * * @param[in] ev The event to free. * * @return 0 */ static int _unlang_event_free(unlang_module_event_t *ev) { if (ev->ev) { (void) fr_event_timer_delete(ev->request->el, &(ev->ev)); return 0; } if (ev->fd >= 0) { (void) fr_event_fd_delete(ev->request->el, ev->fd, FR_EVENT_FILTER_IO); } return 0; }
static int _network_socket_free(fr_network_socket_t *s) { fr_network_t *nr = s->nr; fr_channel_data_t *cd; if (!s->dead) { if (fr_event_fd_delete(nr->el, s->fd, s->filter) < 0) { PERROR("Failed deleting socket from event loop in _network_socket_free"); rad_assert("Failed removing socket FD from event loop in _network_socket_free" == NULL); } } rbtree_deletebydata(nr->sockets, s); rbtree_deletebydata(nr->sockets_by_num, s); if (s->listen->app_io->close) { s->listen->app_io->close(s->listen->app_io_instance); } else { close(s->fd); } if (s->pending) { fr_message_done(&s->pending->m); s->pending = NULL; } /* * Clean up any queued entries. */ while ((cd = fr_heap_pop(s->waiting)) != NULL) { fr_message_done(&cd->m); } talloc_free(s->waiting); return 0; }
/* * Mark it as dead, but DON'T free it until all of the replies * have come in. */ static void fr_network_socket_dead(fr_network_t *nr, fr_network_socket_t *s) { if (s->dead) return; s->dead = true; fr_event_fd_delete(nr->el, s->fd, s->filter); /* * If there are no outstanding packets, then we can free * it now. */ if (!s->outstanding) { talloc_free(s); return; } /* * There are still outstanding packets. Leave it in the * socket tree, so that replies from the worker can find * it. When we've received all of the replies, then * fr_network_post_event() will clean up this socket. */ }
/* * The "detail.work" file exists, and is open in the 'fd'. */ static int work_exists(proto_detail_file_thread_t *thread, int fd) { proto_detail_file_t const *inst = thread->inst; bool opened = false; proto_detail_work_thread_t *work; fr_listen_t *li = NULL; struct stat st; fr_event_vnode_func_t funcs = { .delete = mod_vnode_delete }; DEBUG3("proto_detail (%s): Trying to lock %s", thread->name, inst->filename_work); /* * "detail.work" exists, try to lock it. */ if (rad_lockfd_nonblock(fd, 0) < 0) { struct timeval when, now; DEBUG3("proto_detail (%s): Failed locking %s: %s", thread->name, inst->filename_work, fr_syserror(errno)); close(fd); when.tv_usec = thread->lock_interval % USEC; when.tv_sec = thread->lock_interval / USEC; /* * Ensure that we don't do massive busy-polling. */ thread->lock_interval += thread->lock_interval / 2; if (thread->lock_interval > (30 * USEC)) thread->lock_interval = 30 * USEC; DEBUG3("proto_detail (%s): Waiting %d.%06ds for lock on file %s", thread->name, (int) when.tv_sec, (int) when.tv_usec, inst->filename_work); gettimeofday(&now, NULL); fr_timeval_add(&when, &when, &now); if (fr_event_timer_insert(thread, thread->el, &thread->ev, &when, work_retry_timer, thread) < 0) { ERROR("Failed inserting retry timer for %s", inst->filename_work); } return 0; } DEBUG3("proto_detail (%s): Obtained lock and starting to process file %s", thread->name, inst->filename_work); /* * Ignore empty files. */ if (fstat(fd, &st) < 0) { ERROR("Failed opening %s: %s", inst->filename_work, fr_syserror(errno)); unlink(inst->filename_work); close(fd); return 1; } if (!st.st_size) { DEBUG3("proto_detail (%s): %s file is empty, ignoring it.", thread->name, inst->filename_work); unlink(inst->filename_work); close(fd); return 1; } /* * This listener is allocated in a thread-specific * context, so it doesn't need a destructor, */ MEM(li = talloc_zero(NULL, fr_listen_t)); /* * Create a new listener, and insert it into the * scheduler. Shamelessly copied from proto_detail.c * mod_open(), with changes. * * This listener is parented from the worker. So that * when the worker goes away, so does the listener. */ li->app_io = inst->parent->work_io; li->app = inst->parent->self; li->app_instance = inst->parent; li->server_cs = inst->parent->server_cs; /* * The worker may be in a different thread, so avoid * talloc threading issues by using a NULL TALLOC_CTX. */ MEM(li->thread_instance = work = talloc_zero(li, proto_detail_work_thread_t)); li->app_io_instance = inst->parent->work_io_instance; work->inst = li->app_io_instance; work->file_parent = thread; work->ev = NULL; li->fd = work->fd = dup(fd); if (work->fd < 0) { DEBUG("proto_detail (%s): Failed opening %s: %s", thread->name, inst->filename_work, fr_syserror(errno)); close(fd); talloc_free(li); return -1; } /* * Don't do anything until the file has been deleted. * * @todo - ensure that proto_detail_work is done the file... * maybe by creating a new instance? */ if (fr_event_filter_insert(thread, thread->el, fd, FR_EVENT_FILTER_VNODE, &funcs, NULL, thread) < 0) { PERROR("Failed adding work socket to event loop"); close(fd); talloc_free(li); return -1; } /* * Remember this for later. */ thread->vnode_fd = fd; /* * For us, this is the worker listener. * For the worker, this is it's own parent */ thread->listen = li; work->filename_work = talloc_strdup(work, inst->filename_work); /* * Set configurable parameters for message ring buffer. */ li->default_message_size = inst->parent->max_packet_size; li->num_messages = inst->parent->num_messages; pthread_mutex_lock(&thread->worker_mutex); thread->num_workers++; pthread_mutex_unlock(&thread->worker_mutex); /* * Open the detail.work file. */ if (li->app_io->open(li) < 0) { ERROR("Failed opening %s", li->app_io->name); goto error; } opened = true; rad_assert(li->app_io->get_name); li->name = li->app_io->get_name(li); if (!fr_schedule_listen_add(inst->parent->sc, li)) { error: if (fr_event_fd_delete(thread->el, thread->vnode_fd, FR_EVENT_FILTER_VNODE) < 0) { PERROR("Failed removing DELETE callback when opening work file"); } close(thread->vnode_fd); thread->vnode_fd = -1; if (opened) { (void) li->app_io->close(li); thread->listen = NULL; li = NULL; } talloc_free(li); return -1; } /* * Tell the worker to clean itself up. */ work->listen = li; return 0; }