Example #1
0
/** 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();
}
Example #2
0
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);
}
Example #3
0
/** 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;
}