/** Terminate sources. * * Close listening Sockets and shutdown() others * * XXX: This function is not very efficient (going through the linked list of * channels and repeatedly calling functions which do the same), but it's only * used for cleanup, so it should be fine. * * \see eventloop_stop */ static void terminate_fds(void) { Channel *ch = self.channels, *next; while (ch != NULL) { next = ch->next; o_log(O_LOG_DEBUG4, "EventLoop: Terminating channel %s\n", ch->name); if (!ch->is_active || socket_is_disconnected(ch->socket) || socket_is_listening(ch->socket)) { o_log(O_LOG_DEBUG3, "EventLoop: Releasing listening channel %s\n", ch->name); eventloop_socket_release((SockEvtSource*)ch); } else if (self.force_stop) { o_log(O_LOG_DEBUG3, "EventLoop: Closing down %s\n", ch->name); eventloop_socket_release((SockEvtSource*)ch); socket_close(ch->socket); } else { o_log(O_LOG_DEBUG3, "EventLoop: Shutting down %s\n", ch->name); socket_shutdown(ch->socket); ch->is_shutting_down = 1; } ch = next; } update_fds(); }
static void rescan(devevent_t *de) { int i; char path[32]; char name[80]; for(i = 0; i < DE_MAXDEVS; i++) { de_dev_t *dd = &de->de_devs[i]; snprintf(path, sizeof(path), "/dev/input/event%d", i); if(dd->dd_present) { if(!access(path, O_RDWR)) continue; TRACE(TRACE_INFO, "DE", "Device %s disconnected", path); close(dd->dd_fd); dd->dd_present = 0; } else { dd->dd_fd = open(path, O_RDWR); if(dd->dd_fd == -1) continue; name[sizeof(name) - 1] = 0; if(ioctl(dd->dd_fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) strcpy(name, "???"); TRACE(TRACE_INFO, "DE", "Found %s on %s", name, path); dd->dd_present = 1; } } update_fds(de); }
/** Run the global EventLoop until eventloop_stop() or eventloop_terminate() is called. * * The loop is based around the poll(3) system call. It monitor event sources * such as Channel or Timers, registered in the respective fields of the global * EventLoop object self. It first consider all timers to find whether some * have expired and to set the timeout for the poll(3) call. It then calls * poll(3) on the file descriptors (STDIN or sockets) related to active * Channels, and runs the relevant callbacks for those with pending events. It * finally executes the callback functions of the expired timers. The loop * will not return until eventloop_stop() or eventloop_terminate() is called. * In the former case, it will try to wait until all active sockets are close, * while not in the latter. * * \return the (non-zero) value passed to eventloop_stop() or eventloop_terminate() * * \see eventloop_init, eventloop_stop, eventloop_terminate * \eventloop_on_stdin, eventloop_on_monitor_in_channel, eventloop_on_read_in_channel, eventloop_on_out_channel * \see o_el_timer_callback, o_el_read_socket_callback, o_el_monitor_socket_callback, o_el_state_socket_callback, o_el_timer_callback * \see poll(3) */ int eventloop_run() { int i; self.stopping = 0; self.force_stop = 0; self.start = self.now = self.last_reaped = time(NULL); while (!self.stopping || (self.size>0 && !self.force_stop)) { // Check for active timers int timeout = -1; TimerInt* t = self.timers; while (t != NULL) { if (t->is_active) { int delta = 1000 * (t->due_time - self.now); if (delta < 0) delta = 0; // overdue if (delta < timeout || timeout < 0) timeout = delta; } t = t->next; } if (timeout != -1) o_log(O_LOG_DEBUG3, "EventLoop: Timeout = %d\n", timeout); if (self.fds_dirty) if (update_fds()<1 && timeout < 0) /* No FD nor timeout */ continue; o_log(O_LOG_DEBUG4, "EventLoop: About to poll() on %d FDs with a timeout of %ds\n", self.size, timeout); /* for(i=0; i < self.size; i++) { o_log(O_LOG_DEBUG4, "EventLoop: FD %d->%s\n", self.fds[i].fd, self.fds_channels[i]->name); } */ int count = poll(self.fds, self.size, timeout); self.now = time(NULL); if (count < 1) { o_log(O_LOG_DEBUG4, "EventLoop: Timeout\n"); } else { // Check sockets i = 0; o_log(O_LOG_DEBUG4, "EventLoop: Got events\n"); for (; i < self.size; i++) { Channel* ch = self.fds_channels[i]; if (self.fds[i].revents & POLLERR) { char buf[32]; SocketStatus status; int len; if ((len = recv(self.fds[i].fd, buf, 32, 0)) <= 0) { switch (errno) { case ECONNREFUSED: status = SOCKET_CONN_REFUSED; break; default: status = SOCKET_UNKNOWN; if (!ch->status_cbk) { o_log(O_LOG_ERROR, "EventLoop: While reading from socket '%s': (%d) %s\n", ch->name, errno, strerror(errno)); } } eventloop_socket_activate((SockEvtSource*)ch, 0); do_status_callback (ch, status, errno); } else { o_log(O_LOG_ERROR, "EventLoop: Expected error on socket '%s' but read '%s'\n", ch->name, buf); } } else if (self.fds[i].revents & POLLHUP) { eventloop_socket_activate((SockEvtSource*)ch, 0); /* Client closed the connection, but there might still be bytes for us to read from our end of the connection. */ int len; int fd = self.fds[i].fd; char buf[MAX_READ_BUFFER_SIZE]; do { if (fd == 0) { len = read(fd, buf, MAX_READ_BUFFER_SIZE); } else { len = recv(fd, buf, 512, 0); } if (len > 0) { o_log(O_LOG_DEBUG3, "EventLoop: Received last %i bytes\n", len); do_read_callback (ch, buf, len); } } while (len > 0); do_status_callback (ch, SOCKET_CONN_CLOSED, 0); } else if (self.fds[i].revents & POLLIN) { char buf[MAX_READ_BUFFER_SIZE]; if (ch->read_cbk) { int len; int fd = self.fds[i].fd; if (fd == 0) { // stdin len = read(fd, buf, MAX_READ_BUFFER_SIZE); } else { // socket len = recv(fd, buf, 512, 0); } ch->last_activity = self.now; if (len > 0) { o_log(O_LOG_DEBUG3, "EventLoop: Received %i bytes\n", len); do_read_callback (ch, buf, len); } else if (len == 0 && ch->socket != NULL) { // skip stdin // closed down eventloop_socket_activate((SockEvtSource*)ch, 0); do_status_callback (ch, SOCKET_CONN_CLOSED, 0); } else if (len < 0) { if (errno == ENOTSOCK) { o_log(O_LOG_ERROR, "EventLoop: Monitored socket '%s' is now invalid; " "removing from monitored set\n", ch->name); eventloop_socket_remove ((SockEvtSource*)ch); } else { o_log(O_LOG_ERROR, "EventLoop: Unrecognized read error not handled (errno=%d)\n", errno); } } } else { do_monitor_callback (ch); } } else if (ch->is_shutting_down) { /* The socket was shutting down, and nothing new has appeared; * We flushed the buffers, mark it as removable */ eventloop_socket_release((SockEvtSource*)ch); } if (self.fds[i].revents & POLLOUT) { do_status_callback(ch, SOCKET_WRITEABLE, 0); if (0 != ch->last_activity) { /* If we track the activity of this socket */ ch->last_activity = self.now; } } if (self.fds[i].revents & POLLNVAL) { o_log(O_LOG_WARN, "EventLoop: socket '%s' invalid, deactivating...\n", ch->name); eventloop_socket_activate((SockEvtSource*)ch, 0); do_status_callback(ch, SOCKET_DROPPED, 0); } /* We reap idle channels as we go through the list. XXX: There might * be a corner case where all FDs are already used, and some of them * idle, however a new a new connection (on one listening socket early * in the list) would be dropped before cleanup triggered by its * arrival freed the resources it needs (from the idle sockets further * towards the end of the list. See #959.*/ if (ch->last_activity != 0 && self.socket_timeout > 0 && self.now - ch->last_activity > self.socket_timeout) { o_log(O_LOG_DEBUG2, "EventLoop: Socket '%s' idle for %ds, reaping...\n", ch->name, self.now - ch->last_activity); do_status_callback(ch, SOCKET_IDLE, 0); } } for (i = 0; i < self.size; i++) { Channel* ch = self.fds_channels[i]; if (ch->is_removable) eventloop_socket_remove ((SockEvtSource*)ch); } } if (timeout >= 0) { // check timers TimerInt* t = self.timers; while (t != NULL) { if (t->is_active) { if (t->due_time <= self.now) { // fires o_log(O_LOG_DEBUG2, "EventLoop: Timer '%s' fired\n", t->name); if (t->callback) t->callback((TimerEvtSource*)t, t->handle); if (t->is_periodic) { while ((t->due_time += t->period) < self.now) { // should really only happen during debugging o_log(O_LOG_WARN, "EventLoop: Skipped timer period for '%s'\n", t->name); } } else { t->is_active = 0; } } } t = t->next; } } } return self.stopping; }