Whamcloud - gitweb
Land b_release_1_4_4 onto HEAD (20050810_0253)
[fs/lustre-release.git] / lnet / utils / acceptor.c
index f6367d4..a270ad2 100644 (file)
 #include <sys/ioctl.h>
 #include <unistd.h>
 #include <syslog.h>
+#include <stdarg.h>
+#include <signal.h>
 #include <errno.h>
+#ifdef HAVE_LIBWRAP
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <tcpd.h>
+#endif
 
+#include <libcfs/portals_utils.h>
 #include <portals/api-support.h>
-#include <portals/list.h>
 #include <portals/lib-types.h>
 #include <portals/socknal.h>
 
 /* should get this from autoconf somehow */
 #ifndef PIDFILE_DIR
 #define PIDFILE_DIR "/var/run"
-#endif 
+#endif
 
-#define PROGNAME "acceptor"
+char progname[] = "acceptor";
+char name_port[40];             /* for signal handler */
 
-void create_pidfile(char *name, int port)
+#ifdef HAVE_LIBWRAP
+/* needed because libwrap declares these as externs */
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif
+
+void usage(char *progname)
+{
+        fprintf(stderr, "usage: %s [-N nal_id] [-p] [-l] port\n\n"
+                " -l\tKeep stdin/stdout open\n"
+                " -p\tAllow connections from non-privileged ports\n", progname);
+        exit (1);
+}
+
+void errlog(int level, const char *fmt, ...)
+{
+        va_list arg;
+        FILE *out;
+
+        switch (level) {
+        case LOG_DEBUG:
+        case LOG_INFO:
+        case LOG_NOTICE:
+                out = stdout;
+                break;
+        default:
+                out = stderr;
+                break;
+        }
+        va_start(arg, fmt);
+        fprintf(out, "%s: ", name_port);
+        vfprintf(out, fmt, arg);
+        va_end(arg);
+        va_start(arg, fmt);
+        vsyslog(level, fmt, arg);
+        va_end(arg);
+}
+
+char *pidfile_name(char *name_port)
 {
-        char pidfile[1024];
-        FILE *fp;
-
-        snprintf(pidfile, sizeof(pidfile), "%s/%s-%d.pid", 
-                 PIDFILE_DIR, name, port);
-        
-        if ((fp = fopen(pidfile, "w"))) {
-                fprintf(fp, "%d\n", getpid());
-                fclose(fp);
+        static char pidfile[1024];
+
+        snprintf(pidfile, sizeof(pidfile), "%s/%s.pid", PIDFILE_DIR, name_port);
+
+        return pidfile;
+}
+
+int pidfile_create(char *name_port)
+{
+        char *pidfile = pidfile_name(name_port);
+        int fd, rc;
+
+        if ((fd = open(pidfile, O_CREAT | O_WRONLY)) >= 0) {
+                char pid[16];
+                int size = snprintf(pid, sizeof(pid), "%u\n", getpid());
+                if (write(fd, pid, size) != size) {
+                        /* hard error or short write */
+                        rc = errno ? : EIO;
+                } else {
+                        rc = 0;
+                }
+                close(fd);
         } else {
-                syslog(LOG_ERR, "%s: %s\n", pidfile, 
-                       strerror(errno));
+                rc = errno;
         }
+
+        if (rc)
+                errlog(LOG_ERR, " error creating %s: %s\n",
+                       pidfile, strerror(rc));
+
+        return rc;
 }
 
-int pidfile_exists(char *name, int port)
+int pidfile_cleanup(char *name_port)
 {
-        char pidfile[1024];
-
-        snprintf(pidfile, sizeof(pidfile), "%s/%s-%d.pid", 
-                 PIDFILE_DIR, name, port);
-        
-        if (!access(pidfile, F_OK)) {
-                fprintf(stderr, "%s: exists, acceptor already running.\n", 
-                        pidfile);
-                return (1);
-        } 
-        return (0);
+        char *pidfile = pidfile_name(name_port);
+        int rc;
+
+        rc = unlink(pidfile);
+        if (rc && errno != -ENOENT)
+                fprintf(stderr, "%s: error removing %s: %s\n",
+                        progname, pidfile, strerror(errno));
+        errlog(LOG_NOTICE, "exiting\n");
+
+        return errno;
 }
 
-int
-parse_size (int *sizep, char *str)
+int pidfile_exists(char *name_port)
 {
-        int             size;
-        char            mod[32];
+        char *pidfile = pidfile_name(name_port);
+        FILE *fpid;
+        int pid, rc;
 
-        switch (sscanf (str, "%d%1[gGmMkK]", &size, mod))
-        {
-        default:
-                return (-1);
-
-        case 1:
-                *sizep = size;
-                return (0);
-
-        case 2:
-                switch (*mod)
-                {
-                case 'g':
-                case 'G':
-                        *sizep = size << 30;
-                        return (0);
-
-                case 'm':
-                case 'M':
-                        *sizep = size << 20;
-                        return (0);
-
-                case 'k':
-                case 'K':
-                        *sizep = size << 10;
-                        return (0);
+        fpid = fopen(pidfile, "r+");
+        if (fpid == NULL) {
+                if (errno == ENOENT)
+                        return 0;
 
-                default:
-                        *sizep = size;
-                        return (0);
-                }
+                fprintf(stderr, "%s: error opening %s: %s.\n",
+                        progname, pidfile, strerror(errno));
+                return (1);
         }
+
+        rc = fscanf(fpid, "%i", &pid);
+        fclose(fpid);
+        if (rc != 1) {
+                fprintf(stderr,"%s: %s didn't contain a valid pid, removing.\n",
+                        progname, pidfile);
+                goto stale;
+        }
+
+        if (kill(pid, 0) == 0) {
+                fprintf(stderr, "%s: %s exists, acceptor pid %d running.\n",
+                        progname, pidfile, pid);
+                return (1);
+        }
+
+        fprintf(stderr, "%s: stale %s exists, pid %d doesn't, removing.\n",
+                progname, pidfile, pid);
+stale:
+        pidfile_cleanup(name_port);
+        return (0);
+}
+
+void handler(int sig)
+{
+        exit(sig);
 }
 
-void
-show_connection (int fd, __u32 net_ip)
+void atexit_handler(void)
 {
-        struct hostent *h = gethostbyaddr ((char *)&net_ip, sizeof net_ip, AF_INET);
-        __u32 host_ip = ntohl (net_ip);
-        int  rxmem = 0;
-        int  txmem = 0;
-        int  nonagle = 0;
+        pidfile_cleanup(name_port);
+}
+
+void show_connection(int fd, __u32 net_ip)
+{
+        static long last_time;
+        static __u32 host_ip;
+        long now = time(0);
+        struct hostent *h;
         int  len;
         char host[1024];
-        
-        len = sizeof (txmem);
-        if (getsockopt (fd, SOL_SOCKET, SO_SNDBUF, &txmem, &len) != 0)
-                perror ("Cannot get write buffer size");
-        
-        len = sizeof (rxmem);
-        if (getsockopt (fd, SOL_SOCKET, SO_RCVBUF, &rxmem, &len) != 0)
-                perror ("Cannot get read buffer size");
-        
-        len = sizeof (nonagle);
-        if (getsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &nonagle, &len) != 0)
-                perror ("Cannot get nagle");
+
+        /* Don't show repeats for same host, it adds no value */
+        if (host_ip == ntohl(net_ip) && (now - last_time) < 5)
+                return;
+
+        h = gethostbyaddr((char *)&net_ip, sizeof(net_ip), AF_INET);
+        last_time = now;
+        host_ip = ntohl(net_ip);
 
         if (h == NULL)
-                snprintf (host, sizeof(host), "%d.%d.%d.%d", (host_ip >> 24) & 0xff,
-                                    (host_ip >> 16) & 0xff, (host_ip >> 8) & 0xff, host_ip & 0xff);
+                snprintf(host, sizeof(host), "%d.%d.%d.%d",
+                         (host_ip >> 24) & 0xff, (host_ip >> 16) & 0xff,
+                         (host_ip >> 8)  & 0xff, host_ip & 0xff);
         else
-                snprintf (host, sizeof(host), "%s", h->h_name);
-                
-        syslog (LOG_INFO, "Accepted host: %s snd: %d rcv %d nagle: %s\n", 
-                host, txmem, rxmem, nonagle ? "disabled" : "enabled");
-}
+                snprintf(host, sizeof(host), "%s", h->h_name);
 
-void
-usage (char *myname)
-{
-        fprintf (stderr, "Usage: %s [-r recv_mem] [-s send_mem] [-n] [-N nal_id] port\n", myname);
-        exit (1);
+        syslog(LOG_INFO, "accepted host: %s\n", host);
 }
 
 int main(int argc, char **argv)
@@ -143,56 +200,39 @@ int main(int argc, char **argv)
         int o, fd, rc, port, pfd;
         struct sockaddr_in srvaddr;
         int c;
-        int rxmem = 0;
-        int txmem = 0;
         int noclose = 0;
-        int nonagle = 1;
         int nal = SOCKNAL;
-        int bind_irq = 0;
-        
-        while ((c = getopt (argc, argv, "N:r:s:nli")) != -1)
-                switch (c)
-                {
-                case 'r':
-                        if (parse_size (&rxmem, optarg) != 0 || rxmem < 0)
-                                usage (argv[0]);
-                        break;
-                        
-                case 's':
-                        if (parse_size (&txmem, optarg) != 0 || txmem < 0)
-                                usage (argv[0]);
-                        break;
+        int rport;
+        int require_privports = 1;
 
-                case 'n':
-                        nonagle = 0;
+        while ((c = getopt (argc, argv, "N:lp")) != -1) {
+                switch (c) {
+                case 'N':
+                        if (sscanf(optarg, "%d", &nal) != 1 ||
+                            nal < 0 || nal > NAL_MAX_NR)
+                                usage(argv[0]);
                         break;
-
                 case 'l':
                         noclose = 1;
                         break;
-
-                case 'i':
-                        bind_irq = 1;
-                        break;
-                        
-                case 'N':
-                        if (parse_size(&nal, optarg) != 0 || 
-                            nal < 0 || nal > NAL_MAX_NR)
-                                usage(argv[0]);
+                case 'p':
+                        require_privports = 0;
                         break;
-                        
                 default:
                         usage (argv[0]);
                         break;
                 }
+        }
 
         if (optind >= argc)
                 usage (argv[0]);
 
         port = atol(argv[optind++]);
 
-        if (pidfile_exists(PROGNAME, port))
-                exit(1);
+        snprintf(name_port, sizeof(name_port) - 1, "%s-%d", progname, port);
+        if (pidfile_exists(name_port))
+                return(EEXIST);
+        openlog(name_port, LOG_PID, LOG_DAEMON);
 
         memset(&srvaddr, 0, sizeof(srvaddr));
         srvaddr.sin_family = AF_INET;
@@ -201,74 +241,56 @@ int main(int argc, char **argv)
 
         fd = socket(PF_INET, SOCK_STREAM, 0);
         if (fd < 0) {
-                perror("opening socket");
-                exit(1);
+                rc = errno;
+                errlog(LOG_ERR, "error opening socket: %s\n", strerror(errno));
+                return(rc);
         }
 
         o = 1;
         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &o, sizeof(o))) {
-                perror("Cannot set REUSEADDR socket opt");
-                exit(1);
-        }
-
-        if (nonagle)
-        {
-                o = 1;
-                rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &o, sizeof (o));
-                if (rc != 0) 
-                { 
-                        perror ("Cannot disable nagle");
-                        exit (1);
-                }
+                rc = errno;
+                errlog(LOG_ERR, "cannot set REUSEADDR socket opt: %s\n",
+                       strerror(errno));
+                return(rc);
         }
 
-        if (txmem != 0)
-        {
-                rc = setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &txmem, sizeof (txmem));
-                if (rc != 0)
-                {
-                        perror ("Cannot set write buffer size");
-                        exit (1);
-                }
-        }
-        
-        if (rxmem != 0)
-        {
-                rc = setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &rxmem, sizeof (rxmem));
-                if (rc != 0)
-                {
-                        perror ("Cannot set read buffer size");
-                        exit (1);
-               }
-        }
-                
         rc = bind(fd, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
-        if ( rc == -1 ) {
-                perror("bind: ");
-                exit(1);
+        if (rc == -1) {
+                rc = errno;
+                errlog(LOG_ERR, "error binding to socket: %s\n",
+                       strerror(errno));
+                return(rc);
         }
 
         if (listen(fd, 127)) {
+                rc = errno;
                 perror("listen: ");
-                exit(1);
+                return(rc);
         }
-        fprintf(stderr, "listening on port %d\n", port);
+        printf("listening on port %d\n", port);
 
         pfd = open("/dev/portals", O_RDWR);
-        if ( pfd < 0 ) {
-                perror("opening portals device");
-                exit(1);
+        if (pfd < 0) {
+                rc = errno;
+                errlog(LOG_ERR, "opening portals device: %s\n",strerror(errno));
+                return(rc);
         }
 
-        rc = daemon(1, noclose);
+        rc = daemon(0, noclose);
         if (rc < 0) {
-                perror("daemon(): ");
-                exit(1);
+                rc = errno;
+                errlog(LOG_ERR, "error daemonizing: %s\n", strerror(errno));
+                return(rc);
         }
 
-        openlog(PROGNAME, LOG_PID, LOG_DAEMON);
-        syslog(LOG_INFO, "started, listening on port %d\n", port);
-        create_pidfile(PROGNAME, port);
+        signal(SIGHUP, SIG_IGN);
+        signal(SIGINT, handler);
+        signal(SIGQUIT, handler);
+        signal(SIGTERM, handler);
+
+        errlog(LOG_NOTICE, "started, listening on port %d\n", port);
+        if (pidfile_create(name_port) == 0)
+                atexit(atexit_handler);
 
         while (1) {
                 struct sockaddr_in clntaddr;
@@ -276,11 +298,40 @@ int main(int argc, char **argv)
                 int cfd;
                 struct portal_ioctl_data data;
                 struct portals_cfg pcfg;
-                
+#ifdef HAVE_LIBWRAP
+                struct request_info request;
+#endif
+                char addrstr[INET_ADDRSTRLEN];
+
                 cfd = accept(fd, (struct sockaddr *)&clntaddr, &len);
-                if ( cfd < 0 ) {
-                        perror("accept");
-                        exit(0);
+                if (cfd < 0) {
+                        errlog(LOG_ERR, "error accepting connection: %s\n",
+                               strerror(errno));
+                        break;
+                        //continue;
+                }
+
+                inet_ntop(AF_INET, &clntaddr.sin_addr, addrstr,INET_ADDRSTRLEN);
+#ifdef HAVE_LIBWRAP
+                /* libwrap access control */
+                request_init(&request, RQ_DAEMON, "lustre", RQ_FILE, cfd, 0);
+                sock_host(&request);
+                if (!hosts_access(&request)) {
+                        errlog(LOG_WARNING, "unauthorized access from %s:%hd\n",
+                               addrstr, ntohs(clntaddr.sin_port));
+                        close (cfd);
+                        continue;
+                }
+#endif
+
+                if (require_privports &&
+                    ntohs(clntaddr.sin_port) >= IPPORT_RESERVED) {
+                        errlog(LOG_ERR,
+                               "closing non-privileged connection from %s:%d\n",
+                               addrstr, ntohs(clntaddr.sin_port));
+                        rc = close(cfd);
+                        if (rc)
+                                perror ("close un-privileged client failed");
                         continue;
                 }
 
@@ -289,24 +340,24 @@ int main(int argc, char **argv)
                 PCFG_INIT(pcfg, NAL_CMD_REGISTER_PEER_FD);
                 pcfg.pcfg_nal = nal;
                 pcfg.pcfg_fd = cfd;
-                pcfg.pcfg_flags = bind_irq;
                 pcfg.pcfg_misc = SOCKNAL_CONN_NONE; /* == incoming connection */
-                
+
                 PORTAL_IOC_INIT(data);
                 data.ioc_pbuf1 = (char*)&pcfg;
                 data.ioc_plen1 = sizeof(pcfg);
-                
+
                 if (ioctl(pfd, IOC_PORTAL_NAL_CMD, &data) < 0) {
-                        perror("ioctl failed");
+                        errlog(LOG_ERR, "portals ioctl failed for %s: %s\n",
+                               addrstr, strerror(errno));
                 } else {
-                        printf("client registered\n");
+                        errlog(LOG_DEBUG, "client %s registered\n", addrstr);
                 }
                 rc = close(cfd);
                 if (rc)
-                        perror ("close failed");
+                        perror("close failed");
         }
 
         closelog();
-        exit(0);
 
+        return (0);
 }