Remember to close sockets on error
[fio.git] / server.c
index 4c12f65c8f1981aaad46ff26dd6944525689d638..c1a2824c12ad5675eda66a457de22811d85095ad 100644 (file)
--- a/server.c
+++ b/server.c
@@ -8,21 +8,28 @@
 #include <sys/poll.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <syslog.h>
+#include <signal.h>
 
 #include "fio.h"
 #include "server.h"
 #include "crc/crc16.h"
+#include "ieee754.h"
 
 int fio_net_port = 8765;
 
 int exit_backend = 0;
 
 static int server_fd = -1;
+static char *fio_server_arg;
+static char *bind_sock;
+static struct sockaddr_in saddr_in;
 
 int fio_send_data(int sk, const void *p, unsigned int len)
 {
@@ -93,7 +100,7 @@ static int verify_convert_cmd(struct fio_net_cmd *cmd)
        cmd->pdu_len    = le32_to_cpu(cmd->pdu_len);
 
        switch (cmd->version) {
-       case FIO_SERVER_VER1:
+       case FIO_SERVER_VER2:
                break;
        default:
                log_err("fio: bad server cmd version %d\n", cmd->version);
@@ -111,7 +118,7 @@ static int verify_convert_cmd(struct fio_net_cmd *cmd)
 /*
  * Read (and defragment, if necessary) incoming commands
  */
-struct fio_net_cmd *fio_net_recv_cmd(int sk, int block)
+struct fio_net_cmd *fio_net_recv_cmd(int sk)
 {
        struct fio_net_cmd cmd, *cmdret = NULL;
        size_t cmd_size = 0, pdu_offset = 0;
@@ -120,37 +127,6 @@ struct fio_net_cmd *fio_net_recv_cmd(int sk, int block)
        void *pdu = NULL;
 
        do {
-               struct pollfd pfd;
-
-               pfd.fd = sk;
-               pfd.events = POLLIN;
-               ret = 0;
-               do {
-                       int timeo = block ? 100 : 10;
-
-                       ret = poll(&pfd, 1, timeo);
-                       if (ret < 0) {
-                               if (errno == EINTR)
-                                       break;
-                               log_err("fio: poll: %s\n", strerror(errno));
-                               break;
-                       } else if (!ret) {
-                               if (!block)
-                                       return NULL;
-                               continue;
-                       }
-
-                       if (pfd.revents & POLLIN)
-                               break;
-                       if (pfd.revents & (POLLERR|POLLHUP)) {
-                               ret = 1;
-                               break;
-                       }
-               } while (ret >= 0 && block);
-
-               if (ret < 0)
-                       break;
-
                ret = fio_recv_data(sk, &cmd, sizeof(cmd));
                if (ret)
                        break;
@@ -258,12 +234,9 @@ int fio_net_send_cmd(int fd, uint16_t opcode, const void *buf, off_t size)
 
 int fio_net_send_simple_cmd(int sk, uint16_t opcode, uint64_t serial)
 {
-       struct fio_net_cmd cmd = {
-               .version        = __cpu_to_le16(FIO_SERVER_VER1),
-               .opcode         = cpu_to_le16(opcode),
-               .serial         = cpu_to_le64(serial),
-       };
+       struct fio_net_cmd cmd;
 
+       fio_init_net_cmd(&cmd, opcode, NULL, 0);
        fio_net_cmd_crc(&cmd);
 
        return fio_send_data(sk, &cmd, sizeof(cmd));
@@ -280,8 +253,10 @@ static int handle_job_cmd(struct fio_net_cmd *cmd)
        char *buf = (char *) cmd->payload;
        int ret;
 
-       if (parse_jobs_ini(buf, 1, 0))
+       if (parse_jobs_ini(buf, 1, 0)) {
+               fio_server_send_quit_cmd();
                return -1;
+       }
 
        fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_START, 0);
 
@@ -306,8 +281,10 @@ static int handle_jobline_cmd(struct fio_net_cmd *cmd)
                dprint(FD_NET, "server: %d: %s\n", i, argv[i]);
        }
 
-       if (parse_cmd_line(pdu->argc, argv))
+       if (parse_cmd_line(pdu->argc, argv)) {
+               fio_server_send_quit_cmd();
                return -1;
+       }
 
        fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_START, 0);
 
@@ -323,6 +300,9 @@ static int handle_probe_cmd(struct fio_net_cmd *cmd)
 
        memset(&probe, 0, sizeof(probe));
        gethostname((char *) probe.hostname, sizeof(probe.hostname));
+#ifdef FIO_BIG_ENDIAN
+       probe.bigendian = 1;
+#endif
        probe.fio_major = FIO_MAJOR;
        probe.fio_minor = FIO_MINOR;
        probe.fio_patch = FIO_PATCH;
@@ -367,7 +347,37 @@ static int handle_connection(int sk, int block)
 
        /* read forever */
        while (!exit_backend) {
-               cmd = fio_net_recv_cmd(sk, block);
+               struct pollfd pfd = {
+                       .fd     = sk,
+                       .events = POLLIN,
+               };
+
+               ret = 0;
+               do {
+                       ret = poll(&pfd, 1, 100);
+                       if (ret < 0) {
+                               if (errno == EINTR)
+                                       break;
+                               log_err("fio: poll: %s\n", strerror(errno));
+                               break;
+                       } else if (!ret) {
+                               if (!block)
+                                       return 0;
+                               continue;
+                       }
+
+                       if (pfd.revents & POLLIN)
+                               break;
+                       if (pfd.revents & (POLLERR|POLLHUP)) {
+                               ret = 1;
+                               break;
+                       }
+               } while (!exit_backend);
+
+               if (ret < 0)
+                       break;
+
+               cmd = fio_net_recv_cmd(sk);
                if (!cmd) {
                        ret = -1;
                        break;
@@ -395,8 +405,8 @@ void fio_server_idle_loop(void)
 
 static int accept_loop(int listen_sk)
 {
-       struct sockaddr addr;
-       unsigned int len = sizeof(addr);
+       struct sockaddr_in addr;
+       fio_socklen_t len = sizeof(addr);
        struct pollfd pfd;
        int ret, sk, flags, exitval = 0;
 
@@ -425,13 +435,13 @@ again:
        if (exit_backend)
                goto out;
 
-       sk = accept(listen_sk, &addr, &len);
+       sk = accept(listen_sk, (struct sockaddr *) &addr, &len);
        if (sk < 0) {
                log_err("fio: accept: %s\n", strerror(errno));
                return -1;
        }
 
-       dprint(FD_NET, "server got a connection\n");
+       dprint(FD_NET, "server: connect from %s\n", inet_ntoa(addr.sin_addr));
 
        server_fd = sk;
 
@@ -452,7 +462,7 @@ int fio_server_text_output(const char *buf, unsigned int len)
        if (server_fd != -1)
                return fio_net_send_cmd(server_fd, FIO_NET_CMD_TEXT, buf, len);
 
-       return 0;
+       return fwrite(buf, len, 1, f_err);
 }
 
 static void convert_io_stat(struct io_stat *dst, struct io_stat *src)
@@ -460,9 +470,12 @@ static void convert_io_stat(struct io_stat *dst, struct io_stat *src)
        dst->max_val    = cpu_to_le64(src->max_val);
        dst->min_val    = cpu_to_le64(src->min_val);
        dst->samples    = cpu_to_le64(src->samples);
-       /* FIXME */
-       dst->mean       = __cpu_to_le64(src->mean);
-       dst->S          = __cpu_to_le64(src->S);
+
+       /*
+        * Encode to IEEE 754 for network transfer
+        */
+       dst->mean.u.i   = __cpu_to_le64(fio_double_to_uint64(src->mean.u.f));
+       dst->S.u.i      = __cpu_to_le64(fio_double_to_uint64(src->S.u.f));
 }
 
 static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src)
@@ -517,7 +530,12 @@ void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs)
        p.ts.minf               = cpu_to_le64(ts->minf);
        p.ts.majf               = cpu_to_le64(ts->majf);
        p.ts.clat_percentiles   = cpu_to_le64(ts->clat_percentiles);
-       p.ts.percentile_list    = NULL;
+
+       for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) {
+               fio_fp64_t *fp = &p.ts.percentile_list[i];
+
+               fp->u.i = __cpu_to_le64(fio_double_to_uint64(fp->u.f));
+       }
 
        for (i = 0; i < FIO_IO_U_MAP_NR; i++) {
                p.ts.io_u_map[i]        = cpu_to_le32(ts->io_u_map[i]);
@@ -623,14 +641,9 @@ int fio_server_log(const char *format, ...)
        return fio_server_text_output(buffer, len);
 }
 
-static int fio_server(void)
+static int fio_init_server_ip(void)
 {
-       struct sockaddr_in saddr_in;
-       struct sockaddr addr;
-       unsigned int len;
-       int sk, opt, ret;
-
-       dprint(FD_NET, "starting server\n");
+       int sk, opt;
 
        sk = socket(AF_INET, SOCK_STREAM, 0);
        if (sk < 0) {
@@ -641,37 +654,143 @@ static int fio_server(void)
        opt = 1;
        if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
                log_err("fio: setsockopt: %s\n", strerror(errno));
+               close(sk);
                return -1;
        }
 #ifdef SO_REUSEPORT
-       if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
+       if (setsockopt(sk, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
                log_err("fio: setsockopt: %s\n", strerror(errno));
+               close(sk);
                return -1;
        }
 #endif
 
        saddr_in.sin_family = AF_INET;
-       saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
        saddr_in.sin_port = htons(fio_net_port);
 
        if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0) {
                log_err("fio: bind: %s\n", strerror(errno));
+               close(sk);
                return -1;
        }
 
+       return sk;
+}
+
+static int fio_init_server_sock(void)
+{
+       struct sockaddr_un addr;
+       fio_socklen_t len;
+       mode_t mode;
+       int sk;
+
+       sk = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (sk < 0) {
+               log_err("fio: socket: %s\n", strerror(errno));
+               return -1;
+       }
+
+       mode = umask(000);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strcpy(addr.sun_path, bind_sock);
+       unlink(bind_sock);
+
+       len = sizeof(addr.sun_family) + strlen(bind_sock) + 1;
+
+       if (bind(sk, (struct sockaddr *) &addr, len) < 0) {
+               log_err("fio: bind: %s\n", strerror(errno));
+               close(sk);
+               return -1;
+       }
+
+       umask(mode);
+       return sk;
+}
+
+static int fio_init_server_connection(void)
+{
+       int sk;
+
+       dprint(FD_NET, "starting server\n");
+
+       if (!bind_sock)
+               sk = fio_init_server_ip();
+       else
+               sk = fio_init_server_sock();
+
+       if (sk < 0)
+               return sk;
+
        if (listen(sk, 1) < 0) {
                log_err("fio: listen: %s\n", strerror(errno));
                return -1;
        }
 
-       len = sizeof(addr);
-       if (getsockname(sk, &addr, &len) < 0) {
-               log_err("fio: getsockname: %s\n", strerror(errno));
-               return -1;
+       return sk;
+}
+
+/*
+ * Server arg should be one of:
+ *
+ * sock:/path/to/socket
+ *   ip:1.2.3.4
+ *      1.2.3.4
+ *
+ * Where sock uses unix domain sockets, and ip binds the server to
+ * a specific interface. If no arguments are given to the server, it
+ * uses IP and binds to 0.0.0.0.
+ *
+ */
+static int fio_handle_server_arg(void)
+{
+       saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
+
+       if (!fio_server_arg)
+               return 0;
+       if (!strncmp(fio_server_arg, "sock:", 5)) {
+               bind_sock = fio_server_arg += 5;
+               return 0;
+       } else {
+               char *host = fio_server_arg;
+
+               if (!strncmp(host, "ip:", 3))
+                       host += 3;
+
+               if (inet_aton(host, &saddr_in.sin_addr) != 1) {
+                       struct hostent *hent;
+
+                       hent = gethostbyname(host);
+                       if (hent)
+                               memcpy(&saddr_in.sin_addr, hent->h_addr, 4);
+               }
+               return 0;
        }
+}
+
+static int fio_server(void)
+{
+       int sk, ret;
+
+       dprint(FD_NET, "starting server\n");
+
+       if (fio_handle_server_arg())
+               return -1;
+
+       sk = fio_init_server_connection();
+       if (sk < 0)
+               return -1;
 
        ret = accept_loop(sk);
+
        close(sk);
+
+       if (fio_server_arg) {
+               free(fio_server_arg);
+               fio_server_arg = NULL;
+       }
+
        return ret;
 }
 
@@ -722,3 +841,8 @@ int fio_start_server(int daemonize)
        log_syslog = 1;
        return fio_server();
 }
+
+void fio_server_add_arg(const char *arg)
+{
+       fio_server_arg = strdup(arg);
+}