title: How can I use a loopback device (or something similar) with epoll?
body: <p>I am writing a small library module which accesses the UART and uses epoll to react on incoming data. It works fine with the tty device, however, I want to write test cases for this module. When I open a loop device (/dev/loop*) instead of a tty, epoll_ctl fails with EPERM. The Manual states:</p>
  
  <blockquote>
    <p>EPERM: fd does not support epoll.</p>
  </blockquote>
  
  <p>So the question is: Is there any kind of loopback device which could be used with epoll?</p>
  
  <hr>
  
  <p>Here's a gist with my example code: <a href="https://gist.github.com/0815fox/9ce9f19648ce2dc9b23b37dbbb9adab4" rel="nofollow noreferrer">https://gist.github.com/0815fox/9ce9f19648ce2dc9b23b37dbbb9adab4</a></p>
  
  <pre><code>#include &lt;errno.h&gt;
  #include &lt;fcntl.h&gt;
  #include &lt;string.h&gt;
  #include &lt;termios.h&gt;
  #include &lt;unistd.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;sys/epoll.h&gt;
  #include &lt;linux/loop.h&gt;
  #include &lt;sys/ioctl.h&gt;
  #include &lt;pthread.h&gt;
  
  static int getFreeLoopDevice(const char * backingfileName) {
      int loopctlfd, loopfd, backingfile;
      long devnr;
      char loopname[4096];
  
      loopctlfd = open("/dev/loop-control", O_RDWR);
  
      devnr = ioctl(loopctlfd, LOOP_CTL_GET_FREE);
  
      close(loopctlfd);
      return devnr;
  
      sprintf(loopname, "/dev/loop%ld", devnr);
  
      loopfd = open(loopname, O_RDWR);
  
      backingfile = open(backingfileName, O_RDWR);
  
      ioctl(loopfd, LOOP_SET_FD, backingfile);
      return loopfd;
  }
  
  static int set_interface_attribs (int fd, int speed, int parity) {
      struct termios tty;
      memset (&amp;tty, 0, sizeof tty);
      if (tcgetattr (fd, &amp;tty) != 0) {
          fprintf (stderr, "error %d from tcgetattr", errno);
          return -1;
      }
  
      cfsetospeed (&amp;tty, speed);
      cfsetispeed (&amp;tty, speed);
  
      tty.c_cflag = (tty.c_cflag &amp; ~CSIZE) | CS8;     // 8-bit chars
      // disable IGNBRK for mismatched speed tests; otherwise receive break
      // as \000 chars
      tty.c_iflag &amp;= ~IGNBRK;         // disable break processing
      tty.c_lflag = 0;                // no signaling chars, no echo,
                          // no canonical processing
      tty.c_oflag = 0;                // no remapping, no delays
      tty.c_cc[VMIN]  = 0;            // read doesn't block
      tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout
  
      tty.c_iflag &amp;= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
  
      tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                          // enable reading
      tty.c_cflag &amp;= ~(PARENB | PARODD);      // shut off parity
      tty.c_cflag |= parity;
      tty.c_cflag &amp;= ~CSTOPB;
      tty.c_cflag &amp;= ~CRTSCTS;
  
      if (tcsetattr (fd, TCSANOW, &amp;tty) != 0) {
          fprintf (stderr, "error %d from tcsetattr", errno);
          return -1;
      }
      return 0;
  }
  
  void set_blocking (int fd, int should_block) {
      struct termios tty;
      memset (&amp;tty, 0, sizeof tty);
      if (tcgetattr (fd, &amp;tty) != 0)
      {
          fprintf (stderr, "error %d from tggetattr", errno);
          return;
      }
  
      tty.c_cc[VMIN]  = should_block ? 1 : 0;
      tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout
  
      if (tcsetattr (fd, TCSANOW, &amp;tty) != 0)
          fprintf (stderr, "error %d setting term attributes", errno);
  }
  
  static void * uart_thread(void * handle) {
      const int uartHandle = *(int*)handle;
      char buffer[100];
      // the epoll stuff:
      const int ePollFileDescriptor = epoll_create(1);
      struct epoll_event ePollEvent;
      ePollEvent.events = EPOLLIN;
      ePollEvent.data.fd = uartHandle;
      const int epoll_ctl_result = epoll_ctl(ePollFileDescriptor, EPOLL_CTL_ADD, uartHandle, &amp;ePollEvent);
      if (epoll_ctl_result) perror("epoll_ctl\n");
      for (;;) {
          static struct epoll_event events[1] __attribute__((aligned(8)));
          const int eventCount = epoll_wait(ePollFileDescriptor, events, 1, -1);
          if (eventCount &lt; 0) {
              perror("epoll_wait\n");
          } else {
              const int bytesRead = read(uartHandle, buffer, 99);
              if (bytesRead &gt;= 0) buffer[bytesRead] = 0;
              else buffer[0] = 0;
              fprintf(stderr, "Buffer: %s\nBytesread:%d\n", buffer, bytesRead);
          }
      }
  }
  
  int main (void) {
      // working:
      //    char *portname = "/dev/ttyUSB0";
      //    const int uartHandle = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
      // not working:
      const int uartHandle = getFreeLoopDevice("/tmp/my-loop-back");
  
      if (uartHandle &lt; 0) {
          fprintf (stderr, "error %d opening: %s\n", errno, strerror (errno));
          return errno;
      }
  
      set_interface_attribs (uartHandle, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)
      set_blocking (uartHandle, 0);                // set no blocking
  
      // the pthread stuff:
      pthread_t threadHandle;
      const int pthread_create_result = pthread_create( &amp;threadHandle, NULL, uart_thread, (void *) &amp;uartHandle);
  
      while (1) {
          fprintf(stderr, "writing to uart\n");
          write(uartHandle, "hello!\n", 7);
          sleep(10);
      }
  }
  </code></pre>