Put in a substitute pcap_inject() function, since earlier versions of
[people/xl0/gpxe.git] / src / util / hijack.c
index 37b7b81..f504e4f 100644 (file)
 
 #define SNAPLEN 1600
 
+/*
+ * FIXME: is there a way to detect the version of the libpcap library?
+ * Version 0.9 has pcap_inject; version 0.8 doesn't, but both report
+ * their version number as 2.4.
+ */
+#define HAVE_PCAP_INJECT 0
+
 struct hijack {
        pcap_t *pcap;
        int fd;
@@ -40,6 +47,37 @@ struct hijack_options {
 
 static int daemonised = 0;
 
+static int signalled = 0;
+
+static void flag_signalled ( int signal __attribute__ (( unused )) ) {
+       signalled = 1;
+}
+
+#if ! HAVE_PCAP_INJECT
+/**
+ * Substitute for pcap_inject(), if this version of libpcap doesn't
+ * have it.  Will almost certainly only work under Linux.
+ *
+ */
+static int pcap_inject ( pcap_t *pcap, const void *data, size_t len ) {
+       int fd;
+       char *errbuf = pcap_geterr ( pcap );
+
+       fd = pcap_get_selectable_fd ( pcap );
+       if ( fd < 0 ) {
+               snprintf ( errbuf, PCAP_ERRBUF_SIZE,
+                          "could not get file descriptor" );
+               return -1;
+       }
+       if ( write ( fd, data, len ) != len ) {
+               snprintf ( errbuf, PCAP_ERRBUF_SIZE,
+                          "could not write data: %s", strerror ( errno ) );
+               return -1;
+       }
+       return len;
+}
+#endif /* ! HAVE_PCAP_INJECT */
+
 /**
  * Log error message
  *
@@ -363,13 +401,21 @@ static int listen_for_hijackers ( struct hijack_listener *listener,
 
        logmsg ( LOG_INFO, "Listening on %s\n", listener->sun.sun_path );
 
-       while ( 1 ) {
-               /* Accept new connection */
+       while ( ! signalled ) {
+               /* Accept new connection, interruptibly */
+               siginterrupt ( SIGINT, 1 );
+               siginterrupt ( SIGHUP, 1 );
                fd = accept ( listener->fd, NULL, 0 );
+               siginterrupt ( SIGINT, 0 );
+               siginterrupt ( SIGHUP, 0 );
                if ( fd < 0 ) {
-                       logmsg ( LOG_ERR, "accept failed: %s\n",
-                                strerror ( errno ) );
-                       goto err;
+                       if ( errno == EINTR ) {
+                               continue;
+                       } else {
+                               logmsg ( LOG_ERR, "accept failed: %s\n",
+                                        strerror ( errno ) );
+                               goto err;
+                       }
                }
 
                /* Fork child process */
@@ -389,6 +435,8 @@ static int listen_for_hijackers ( struct hijack_listener *listener,
                close ( fd );
        }
 
+       logmsg ( LOG_INFO, "Stopped listening on %s\n",
+                listener->sun.sun_path );
        return 0;
 
  err:
@@ -527,7 +575,7 @@ static int daemonise ( const char *interface ) {
 int main ( int argc, char **argv ) {
        struct hijack_options options;
        struct hijack_listener listener;
-       struct sigaction sigchld;
+       struct sigaction sa;
 
        /* Parse command-line options */
        if ( parse_options ( argc, argv, &options ) < 0 )
@@ -547,11 +595,25 @@ int main ( int argc, char **argv ) {
        }
 
        /* Avoid creating zombies */
-       memset ( &sigchld, 0, sizeof ( sigchld ) );
-       sigchld.sa_handler = SIG_IGN;
-       sigchld.sa_flags = SA_NOCLDWAIT;
-       if ( sigaction ( SIGCHLD, &sigchld, NULL ) < 0 ) {
-               logmsg ( LOG_ERR, "Could not set signal handler: %s",
+       memset ( &sa, 0, sizeof ( sa ) );
+       sa.sa_handler = SIG_IGN;
+       sa.sa_flags = SA_RESTART | SA_NOCLDWAIT;
+       if ( sigaction ( SIGCHLD, &sa, NULL ) < 0 ) {
+               logmsg ( LOG_ERR, "Could not set SIGCHLD handler: %s",
+                        strerror ( errno ) );
+               exit ( 1 );
+       }
+
+       /* Set 'signalled' flag on SIGINT or SIGHUP */
+       sa.sa_handler = flag_signalled;
+       sa.sa_flags = SA_RESTART | SA_RESETHAND;
+       if ( sigaction ( SIGINT, &sa, NULL ) < 0 ) {
+               logmsg ( LOG_ERR, "Could not set SIGINT handler: %s",
+                        strerror ( errno ) );
+               exit ( 1 );
+       }
+       if ( sigaction ( SIGHUP, &sa, NULL ) < 0 ) {
+               logmsg ( LOG_ERR, "Could not set SIGHUP handler: %s",
                         strerror ( errno ) );
                exit ( 1 );
        }