Fix segfault with net io engine and no file/hostname given
[fio.git] / engines / net.c
index d6821a4092e7898aa0fea956a6069931fde3a8b2..cf6025f1aa5ca6ecce389654be999ca135369476 100644 (file)
 
 struct netio_data {
        int listenfd;
-       int send_to_net;
        int use_splice;
-       int type;
        int pipes[2];
-       char host[64];
        struct sockaddr_in addr;
        struct sockaddr_un addr_un;
 };
 
+struct netio_options {
+       struct thread_data *td;
+       unsigned int port;
+       unsigned int proto;
+       unsigned int listen;
+};
+
 struct udp_close_msg {
        uint32_t magic;
        uint32_t cmd;
@@ -45,6 +49,55 @@ enum {
        FIO_TYPE_UNIX   = 3,
 };
 
+static int str_hostname_cb(void *data, const char *input);
+static struct fio_option options[] = {
+       {
+               .name   = "hostname",
+               .type   = FIO_OPT_STR_STORE,
+               .cb     = str_hostname_cb,
+               .help   = "Hostname for net IO engine",
+       },
+       {
+               .name   = "port",
+               .type   = FIO_OPT_INT,
+               .off1   = offsetof(struct netio_options, port),
+               .minval = 1,
+               .maxval = 65535,
+               .help   = "Port to use for TCP or UDP net connections",
+       },
+       {
+               .name   = "protocol",
+               .alias  = "proto",
+               .type   = FIO_OPT_STR,
+               .off1   = offsetof(struct netio_options, proto),
+               .help   = "Network protocol to use",
+               .def    = "tcp",
+               .posval = {
+                         { .ival = "tcp",
+                           .oval = FIO_TYPE_TCP,
+                           .help = "Transmission Control Protocol",
+                         },
+                         { .ival = "udp",
+                           .oval = FIO_TYPE_UDP,
+                           .help = "Unreliable Datagram Protocol",
+                         },
+                         { .ival = "unix",
+                           .oval = FIO_TYPE_UNIX,
+                           .help = "UNIX domain socket",
+                         },
+               },
+       },
+       {
+               .name   = "listen",
+               .type   = FIO_OPT_STR_SET,
+               .off1   = offsetof(struct netio_options, listen),
+               .help   = "Listen for incoming TCP connections",
+       },
+       {
+               .name   = NULL,
+       },
+};
+
 /*
  * Return -1 for error and 'nr events' for a positive number
  * of events
@@ -78,13 +131,16 @@ static int poll_wait(struct thread_data *td, int fd, short events)
 
 static int fio_netio_prep(struct thread_data *td, struct io_u *io_u)
 {
-       struct netio_data *nd = td->io_ops->data;
+       struct netio_options *o = td->eo;
 
        /*
         * Make sure we don't see spurious reads to a receiver, and vice versa
         */
-       if ((nd->send_to_net && io_u->ddir == DDIR_READ) ||
-           (!nd->send_to_net && io_u->ddir == DDIR_WRITE)) {
+       if (o->proto == FIO_TYPE_TCP)
+               return 0;
+
+       if ((o->listen && io_u->ddir == DDIR_WRITE) ||
+           (!o->listen && io_u->ddir == DDIR_READ)) {
                td_verror(td, EINVAL, "bad direction");
                return 1;
        }
@@ -230,10 +286,11 @@ static int fio_netio_splice_out(struct thread_data *td, struct io_u *io_u)
 static int fio_netio_send(struct thread_data *td, struct io_u *io_u)
 {
        struct netio_data *nd = td->io_ops->data;
+       struct netio_options *o = td->eo;
        int ret, flags = OS_MSG_DONTWAIT;
 
        do {
-               if (nd->type == FIO_TYPE_UDP) {
+               if (o->proto == FIO_TYPE_UDP) {
                        struct sockaddr *to = (struct sockaddr *) &nd->addr;
 
                        ret = sendto(io_u->file->fd, io_u->xfer_buf,
@@ -283,10 +340,11 @@ static int is_udp_close(struct io_u *io_u, int len)
 static int fio_netio_recv(struct thread_data *td, struct io_u *io_u)
 {
        struct netio_data *nd = td->io_ops->data;
+       struct netio_options *o = td->eo;
        int ret, flags = OS_MSG_DONTWAIT;
 
        do {
-               if (nd->type == FIO_TYPE_UDP) {
+               if (o->proto == FIO_TYPE_UDP) {
                        fio_socklen_t len = sizeof(nd->addr);
                        struct sockaddr *from = (struct sockaddr *) &nd->addr;
 
@@ -316,19 +374,20 @@ static int fio_netio_recv(struct thread_data *td, struct io_u *io_u)
 static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
 {
        struct netio_data *nd = td->io_ops->data;
+       struct netio_options *o = td->eo;
        int ret;
 
        fio_ro_check(td, io_u);
 
        if (io_u->ddir == DDIR_WRITE) {
-               if (!nd->use_splice || nd->type == FIO_TYPE_UDP ||
-                   nd->type == FIO_TYPE_UNIX) 
+               if (!nd->use_splice || o->proto == FIO_TYPE_UDP ||
+                   o->proto == FIO_TYPE_UNIX)
                        ret = fio_netio_send(td, io_u);
                else
                        ret = fio_netio_splice_out(td, io_u);
        } else if (io_u->ddir == DDIR_READ) {
-               if (!nd->use_splice || nd->type == FIO_TYPE_UDP ||
-                   nd->type == FIO_TYPE_UDP)
+               if (!nd->use_splice || o->proto == FIO_TYPE_UDP ||
+                   o->proto == FIO_TYPE_UNIX)
                        ret = fio_netio_recv(td, io_u);
                else
                        ret = fio_netio_splice_in(td, io_u);
@@ -359,19 +418,20 @@ static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
 static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
 {
        struct netio_data *nd = td->io_ops->data;
+       struct netio_options *o = td->eo;
        int type, domain;
 
-       if (nd->type == FIO_TYPE_TCP) {
+       if (o->proto == FIO_TYPE_TCP) {
                domain = AF_INET;
                type = SOCK_STREAM;
-       } else if (nd->type == FIO_TYPE_UDP) {
+       } else if (o->proto == FIO_TYPE_UDP) {
                domain = AF_INET;
                type = SOCK_DGRAM;
-       } else if (nd->type == FIO_TYPE_UNIX) {
+       } else if (o->proto == FIO_TYPE_UNIX) {
                domain = AF_UNIX;
                type = SOCK_STREAM;
        } else {
-               log_err("fio: bad network type %d\n", nd->type);
+               log_err("fio: bad network type %d\n", o->proto);
                f->fd = -1;
                return 1;
        }
@@ -382,9 +442,9 @@ static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
                return 1;
        }
 
-       if (nd->type == FIO_TYPE_UDP)
+       if (o->proto == FIO_TYPE_UDP)
                return 0;
-       else if (nd->type == FIO_TYPE_TCP) {
+       else if (o->proto == FIO_TYPE_TCP) {
                fio_socklen_t len = sizeof(nd->addr);
 
                if (connect(f->fd, (struct sockaddr *) &nd->addr, len) < 0) {
@@ -411,9 +471,10 @@ static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
 static int fio_netio_accept(struct thread_data *td, struct fio_file *f)
 {
        struct netio_data *nd = td->io_ops->data;
+       struct netio_options *o = td->eo;
        fio_socklen_t socklen = sizeof(nd->addr);
 
-       if (nd->type == FIO_TYPE_UDP) {
+       if (o->proto == FIO_TYPE_UDP) {
                f->fd = nd->listenfd;
                return 0;
        }
@@ -435,8 +496,9 @@ static int fio_netio_accept(struct thread_data *td, struct fio_file *f)
 static int fio_netio_open_file(struct thread_data *td, struct fio_file *f)
 {
        int ret;
+       struct netio_options *o = td->eo;
 
-       if (td_read(td))
+       if (o->listen)
                ret = fio_netio_accept(td, f);
        else
                ret = fio_netio_connect(td, f);
@@ -464,13 +526,13 @@ static void fio_netio_udp_close(struct thread_data *td, struct fio_file *f)
 
 static int fio_netio_close_file(struct thread_data *td, struct fio_file *f)
 {
-       struct netio_data *nd = td->io_ops->data;
+       struct netio_options *o = td->eo;
 
        /*
         * If this is an UDP connection, notify the receiver that we are
         * closing down the link
         */
-       if (nd->type == FIO_TYPE_UDP)
+       if (o->proto == FIO_TYPE_UDP)
                fio_netio_udp_close(td, f);
 
        return generic_close_file(td, f);
@@ -510,15 +572,14 @@ static int fio_netio_setup_connect_unix(struct thread_data *td,
        return 0;
 }
 
-static int fio_netio_setup_connect(struct thread_data *td, const char *host,
-                                  unsigned short port)
+static int fio_netio_setup_connect(struct thread_data *td)
 {
-       struct netio_data *nd = td->io_ops->data;
+       struct netio_options *o = td->eo;
 
-       if (nd->type == FIO_TYPE_UDP || nd->type == FIO_TYPE_TCP)
-               return fio_netio_setup_connect_inet(td, host, port);
+       if (o->proto == FIO_TYPE_UDP || o->proto == FIO_TYPE_TCP)
+               return fio_netio_setup_connect_inet(td, td->o.filename,o->port);
        else
-               return fio_netio_setup_connect_unix(td, host);
+               return fio_netio_setup_connect_unix(td, td->o.filename);
 }
 
 static int fio_netio_setup_listen_unix(struct thread_data *td, const char *path)
@@ -557,9 +618,10 @@ static int fio_netio_setup_listen_unix(struct thread_data *td, const char *path)
 static int fio_netio_setup_listen_inet(struct thread_data *td, short port)
 {
        struct netio_data *nd = td->io_ops->data;
+       struct netio_options *o = td->eo;
        int fd, opt, type;
 
-       if (nd->type == FIO_TYPE_TCP)
+       if (o->proto == FIO_TYPE_TCP)
                type = SOCK_STREAM;
        else
                type = SOCK_DGRAM;
@@ -595,20 +657,20 @@ static int fio_netio_setup_listen_inet(struct thread_data *td, short port)
        return 0;
 }
 
-static int fio_netio_setup_listen(struct thread_data *td, const char *path,
-                                 short port)
+static int fio_netio_setup_listen(struct thread_data *td)
 {
        struct netio_data *nd = td->io_ops->data;
+       struct netio_options *o = td->eo;
        int ret;
 
-       if (nd->type == FIO_TYPE_UDP || nd->type == FIO_TYPE_TCP)
-               ret = fio_netio_setup_listen_inet(td, port);
+       if (o->proto == FIO_TYPE_UDP || o->proto == FIO_TYPE_TCP)
+               ret = fio_netio_setup_listen_inet(td, o->port);
        else
-               ret = fio_netio_setup_listen_unix(td, path);
+               ret = fio_netio_setup_listen_unix(td, td->o.filename);
 
        if (ret)
                return ret;
-       if (nd->type == FIO_TYPE_UDP)
+       if (o->proto == FIO_TYPE_UDP)
                return 0;
 
        if (listen(nd->listenfd, 10) < 0) {
@@ -622,72 +684,50 @@ static int fio_netio_setup_listen(struct thread_data *td, const char *path,
 
 static int fio_netio_init(struct thread_data *td)
 {
-       struct netio_data *nd = td->io_ops->data;
-       unsigned int port;
-       char host[64], buf[128];
-       char *sep, *portp, *modep;
+       struct netio_options *o = td->eo;
        int ret;
 
-       if (td_rw(td)) {
-               log_err("fio: network connections must be read OR write\n");
-               return 1;
-       }
        if (td_random(td)) {
                log_err("fio: network IO can't be random\n");
                return 1;
        }
 
-       strcpy(buf, td->o.filename);
-
-       sep = strchr(buf, ',');
-       if (!sep)
-               goto bad_host;
+       if (o->proto == FIO_TYPE_UNIX && o->port) {
+               log_err("fio: network IO port not valid with unix socket\n");
+               return 1;
+       } else if (o->proto != FIO_TYPE_UNIX && !o->port) {
+               log_err("fio: network IO requires port for tcp or udp\n");
+               return 1;
+       }
 
-       *sep = '\0';
-       sep++;
-       strcpy(host, buf);
-       if (!strlen(host))
-               goto bad_host;
+       if (o->proto != FIO_TYPE_TCP) {
+               if (o->listen) {
+                       log_err("fio: listen only valid for TCP proto IO\n");
+                       return 1;
+               }
+               if (td_rw(td)) {
+                       log_err("fio: datagram network connections must be"
+                                  " read OR write\n");
+                       return 1;
+               }
+               if (o->proto == FIO_TYPE_UNIX && !td->o.filename) {
+                       log_err("fio: UNIX sockets need host/filename\n");
+                       return 1;
+               }
+               o->listen = td_read(td);
+       }
 
-       modep = NULL;
-       portp = sep;
-       sep = strchr(portp, ',');
-       if (sep) {
-               *sep = '\0';
-               modep = sep + 1;
+       if (o->proto != FIO_TYPE_UNIX && o->listen && td->o.filename) {
+               log_err("fio: hostname not valid for inbound network IO\n");
+               return 1;
        }
 
-       if (!strncmp("tcp", modep, strlen(modep)) ||
-           !strncmp("TCP", modep, strlen(modep)))
-               nd->type = FIO_TYPE_TCP;
-       else if (!strncmp("udp", modep, strlen(modep)) ||
-                !strncmp("UDP", modep, strlen(modep)))
-               nd->type = FIO_TYPE_UDP;
-       else if (!strncmp("unix", modep, strlen(modep)) ||
-                !strncmp("UNIX", modep, strlen(modep)))
-               nd->type = FIO_TYPE_UNIX;
+       if (o->listen)
+               ret = fio_netio_setup_listen(td);
        else
-               goto bad_host;
-
-       if (nd->type != FIO_TYPE_UNIX) {
-               port = strtol(portp, NULL, 10);
-               if (!port || port > 65535)
-                       goto bad_host;
-       } else
-               port = 0;
-
-       if (td_read(td)) {
-               nd->send_to_net = 0;
-               ret = fio_netio_setup_listen(td, host, port);
-       } else {
-               nd->send_to_net = 1;
-               ret = fio_netio_setup_connect(td, host, port);
-       }
+               ret = fio_netio_setup_connect(td);
 
        return ret;
-bad_host:
-       log_err("fio: bad network host/port/protocol: %s\n", td->o.filename);
-       return 1;
 }
 
 static void fio_netio_cleanup(struct thread_data *td)
@@ -710,6 +750,11 @@ static int fio_netio_setup(struct thread_data *td)
 {
        struct netio_data *nd;
 
+       if (!td->files_index) {
+               add_file(td, td->o.filename ?: "net");
+               td->o.nr_files = td->o.nr_files ?: 1;
+       }
+
        if (!td->io_ops->data) {
                nd = malloc(sizeof(*nd));;
 
@@ -742,34 +787,48 @@ static int fio_netio_setup_splice(struct thread_data *td)
 }
 
 static struct ioengine_ops ioengine_splice = {
-       .name           = "netsplice",
-       .version        = FIO_IOOPS_VERSION,
-       .prep           = fio_netio_prep,
-       .queue          = fio_netio_queue,
-       .setup          = fio_netio_setup_splice,
-       .init           = fio_netio_init,
-       .cleanup        = fio_netio_cleanup,
-       .open_file      = fio_netio_open_file,
-       .close_file     = generic_close_file,
-       .flags          = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
-                         FIO_SIGTERM | FIO_PIPEIO,
+       .name                   = "netsplice",
+       .version                = FIO_IOOPS_VERSION,
+       .prep                   = fio_netio_prep,
+       .queue                  = fio_netio_queue,
+       .setup                  = fio_netio_setup_splice,
+       .init                   = fio_netio_init,
+       .cleanup                = fio_netio_cleanup,
+       .open_file              = fio_netio_open_file,
+       .close_file             = generic_close_file,
+       .options                = options,
+       .option_struct_size     = sizeof(struct netio_options),
+       .flags                  = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
+                                 FIO_SIGTERM | FIO_PIPEIO,
 };
 #endif
 
 static struct ioengine_ops ioengine_rw = {
-       .name           = "net",
-       .version        = FIO_IOOPS_VERSION,
-       .prep           = fio_netio_prep,
-       .queue          = fio_netio_queue,
-       .setup          = fio_netio_setup,
-       .init           = fio_netio_init,
-       .cleanup        = fio_netio_cleanup,
-       .open_file      = fio_netio_open_file,
-       .close_file     = fio_netio_close_file,
-       .flags          = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
-                         FIO_SIGTERM | FIO_PIPEIO,
+       .name                   = "net",
+       .version                = FIO_IOOPS_VERSION,
+       .prep                   = fio_netio_prep,
+       .queue                  = fio_netio_queue,
+       .setup                  = fio_netio_setup,
+       .init                   = fio_netio_init,
+       .cleanup                = fio_netio_cleanup,
+       .open_file              = fio_netio_open_file,
+       .close_file             = fio_netio_close_file,
+       .options                = options,
+       .option_struct_size     = sizeof(struct netio_options),
+       .flags                  = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
+                                 FIO_SIGTERM | FIO_PIPEIO,
 };
 
+static int str_hostname_cb(void *data, const char *input)
+{
+       struct netio_options *o = data;
+
+       if (o->td->o.filename)
+               free(o->td->o.filename);
+       o->td->o.filename = strdup(input);
+       return 0;
+}
+
 static void fio_init fio_netio_register(void)
 {
        register_ioengine(&ioengine_rw);