Avoid overwriting allocated pid_file
[fio.git] / init.c
diff --git a/init.c b/init.c
index fa1df8e7e760eac9c7a37f7d8afaa3ff9614c6cd..fcc135e65eaf1f2f721f310aa8d6355c1331967d 100644 (file)
--- a/init.c
+++ b/init.c
 #include "profile.h"
 #include "server.h"
 #include "idletime.h"
+#include "filelock.h"
 
 #include "lib/getopt.h"
 #include "lib/strcasestr.h"
 
+#include "crc/test.h"
+
 const char fio_version_string[] = FIO_VERSION;
 
 #define FIO_RANDSEED           (0xb1899bedUL)
@@ -35,20 +38,21 @@ const char fio_version_string[] = FIO_VERSION;
 static char **ini_file;
 static int max_jobs = FIO_MAX_JOBS;
 static int dump_cmdline;
-static int def_timeout;
+static long long def_timeout;
 static int parse_only;
 
 static struct thread_data def_thread;
 struct thread_data *threads = NULL;
+static char **job_sections;
+static int nr_job_sections;
 
 int exitall_on_terminate = 0;
 int output_format = FIO_OUTPUT_NORMAL;
+int append_terse_output = 0;
 int eta_print = FIO_ETA_AUTO;
 int eta_new_line = 0;
 FILE *f_out = NULL;
 FILE *f_err = NULL;
-char **job_sections = NULL;
-int nr_job_sections = 0;
 char *exec_profile = NULL;
 int warnings_fatal = 0;
 int terse_version = 3;
@@ -108,6 +112,11 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = optional_argument,
                .val            = 'F' | FIO_CLIENT_FLAG,
        },
+       {
+               .name           = (char *) "append-terse",
+               .has_arg        = optional_argument,
+               .val            = 'f',
+       },
        {
                .name           = (char *) "version",
                .has_arg        = no_argument,
@@ -241,7 +250,7 @@ void free_threads_shm(void)
        }
 }
 
-void free_shm(void)
+static void free_shm(void)
 {
        if (threads) {
                file_hash_exit();
@@ -251,6 +260,7 @@ void free_shm(void)
        }
 
        options_free(fio_options, &def_thread);
+       fio_filelock_exit();
        scleanup();
 }
 
@@ -308,6 +318,14 @@ static int setup_thread_area(void)
        return 0;
 }
 
+static void set_cmd_options(struct thread_data *td)
+{
+       struct thread_options *o = &td->o;
+
+       if (!o->timeout)
+               o->timeout = def_timeout;
+}
+
 /*
  * Return a free job structure.
  */
@@ -316,8 +334,10 @@ static struct thread_data *get_new_job(int global, struct thread_data *parent,
 {
        struct thread_data *td;
 
-       if (global)
+       if (global) {
+               set_cmd_options(&def_thread);
                return &def_thread;
+       }
        if (setup_thread_area()) {
                log_err("error: failed to setup shm segment\n");
                return NULL;
@@ -344,9 +364,10 @@ static struct thread_data *get_new_job(int global, struct thread_data *parent,
 
        td->thread_number = thread_number;
 
-       if (!parent || !parent->o.group_reporting)
+       if (!parent->o.group_reporting)
                stat_number++;
 
+       set_cmd_options(td);
        return td;
 }
 
@@ -412,6 +433,26 @@ static int fixed_block_size(struct thread_options *o)
                o->min_bs[DDIR_READ] == o->min_bs[DDIR_TRIM];
 }
 
+
+static unsigned long long get_rand_start_delay(struct thread_data *td)
+{
+       unsigned long long delayrange;
+       unsigned long r;
+
+       delayrange = td->o.start_delay_high - td->o.start_delay;
+
+       if (td->o.use_os_rand) {
+               r = os_random_long(&td->delay_state);
+               delayrange = (unsigned long long) ((double) delayrange * (r / (OS_RAND_MAX + 1.0)));
+       } else {
+               r = __rand(&td->__delay_state);
+               delayrange = (unsigned long long) ((double) delayrange * (r / (FRAND_MAX + 1.0)));
+       }
+
+       delayrange += td->o.start_delay;
+       return delayrange;
+}
+
 /*
  * Lazy way of fixing up options that depend on each other. We could also
  * define option callback handlers, but this is easier.
@@ -496,6 +537,9 @@ static int fixup_options(struct thread_data *td)
        if (!o->file_size_high)
                o->file_size_high = o->file_size_low;
 
+       if (o->start_delay_high)
+               o->start_delay = get_rand_start_delay(td);
+
        if (o->norandommap && o->verify != VERIFY_NONE
            && !fixed_block_size(o))  {
                log_err("fio: norandommap given for variable block sizes, "
@@ -647,6 +691,11 @@ static int fixup_options(struct thread_data *td)
        if (td->o.rand_seed)
                td->o.rand_repeatable = 0;
 
+       if ((td->io_ops->flags & FIO_NOEXTEND) && td->o.file_append) {
+               log_err("fio: can't append/extent with IO engine %s\n", td->io_ops->name);
+               ret = 1;
+       }
+
        return ret;
 }
 
@@ -711,6 +760,7 @@ static void td_fill_rand_seeds_os(struct thread_data *td)
 
        os_random_seed(td->rand_seeds[FIO_RAND_FILE_SIZE_OFF], &td->file_size_state);
        os_random_seed(td->rand_seeds[FIO_RAND_TRIM_OFF], &td->trim_state);
+       os_random_seed(td->rand_seeds[FIO_RAND_START_DELAY], &td->delay_state);
 
        if (!td_random(td))
                return;
@@ -736,6 +786,7 @@ static void td_fill_rand_seeds_internal(struct thread_data *td)
 
        init_rand_seed(&td->__file_size_state, td->rand_seeds[FIO_RAND_FILE_SIZE_OFF]);
        init_rand_seed(&td->__trim_state, td->rand_seeds[FIO_RAND_TRIM_OFF]);
+       init_rand_seed(&td->__delay_state, td->rand_seeds[FIO_RAND_START_DELAY]);
 
        if (!td_random(td))
                return;
@@ -751,6 +802,12 @@ static void td_fill_rand_seeds_internal(struct thread_data *td)
 
 void td_fill_rand_seeds(struct thread_data *td)
 {
+       if (td->o.allrand_repeatable) {
+               for (int i = 0; i < FIO_RAND_NR_OFFS; i++)
+                       td->rand_seeds[i] = FIO_RANDSEED * td->thread_number
+                               + i;
+       }
+
        if (td->o.use_os_rand)
                td_fill_rand_seeds_os(td);
        else
@@ -885,6 +942,7 @@ static char *make_filename(char *buf, struct thread_options *o,
 {
        struct fpre_keyword *f;
        char copy[PATH_MAX];
+       size_t dst_left = PATH_MAX - 1;
 
        if (!o->filename_format || !strlen(o->filename_format)) {
                sprintf(buf, "%s.%d.%d", jobname, jobnum, filenum);
@@ -912,25 +970,47 @@ static char *make_filename(char *buf, struct thread_options *o,
                        if (pre_len) {
                                strncpy(dst, buf, pre_len);
                                dst += pre_len;
+                               dst_left -= pre_len;
                        }
 
                        switch (f->key) {
-                       case FPRE_JOBNAME:
-                               dst += sprintf(dst, "%s", jobname);
+                       case FPRE_JOBNAME: {
+                               int ret;
+
+                               ret = snprintf(dst, dst_left, "%s", jobname);
+                               if (ret < 0)
+                                       break;
+                               dst += ret;
+                               dst_left -= ret;
                                break;
-                       case FPRE_JOBNUM:
-                               dst += sprintf(dst, "%d", jobnum);
+                               }
+                       case FPRE_JOBNUM: {
+                               int ret;
+
+                               ret = snprintf(dst, dst_left, "%d", jobnum);
+                               if (ret < 0)
+                                       break;
+                               dst += ret;
+                               dst_left -= ret;
                                break;
-                       case FPRE_FILENUM:
-                               dst += sprintf(dst, "%d", filenum);
+                               }
+                       case FPRE_FILENUM: {
+                               int ret;
+
+                               ret = snprintf(dst, dst_left, "%d", filenum);
+                               if (ret < 0)
+                                       break;
+                               dst += ret;
+                               dst_left -= ret;
                                break;
+                               }
                        default:
                                assert(0);
                                break;
                        }
 
                        if (post_start)
-                               strcpy(dst, buf + post_start);
+                               strncpy(dst, buf + post_start, dst_left);
 
                        strcpy(buf, copy);
                } while (1);
@@ -989,10 +1069,10 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                file_alloced = 1;
 
                if (o->nr_files == 1 && exists_and_not_file(jobname))
-                       add_file(td, jobname);
+                       add_file(td, jobname, job_add_num, 0);
                else {
                        for (i = 0; i < o->nr_files; i++)
-                               add_file(td, make_filename(fname, o, jobname, td->thread_number, i));
+                               add_file(td, make_filename(fname, o, jobname, job_add_num, i), job_add_num, 0);
                }
        }
 
@@ -1045,12 +1125,12 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        if (setup_rate(td))
                goto err;
 
-       if (o->lat_log_file) {
+       if (o->lat_log_file || write_lat_log) {
                setup_log(&td->lat_log, o->log_avg_msec, IO_LOG_TYPE_LAT);
                setup_log(&td->slat_log, o->log_avg_msec, IO_LOG_TYPE_SLAT);
                setup_log(&td->clat_log, o->log_avg_msec, IO_LOG_TYPE_CLAT);
        }
-       if (o->bw_log_file)
+       if (o->bw_log_file || write_bw_log)
                setup_log(&td->bw_log, o->log_avg_msec, IO_LOG_TYPE_BW);
        if (o->iops_log_file)
                setup_log(&td->iops_log, o->log_avg_msec, IO_LOG_TYPE_IOPS);
@@ -1118,15 +1198,25 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                td_new->o.new_group = 0;
 
                if (file_alloced) {
-                       td_new->o.filename = NULL;
+                       if (td_new->files) {
+                               struct fio_file *f;
+                               for_each_file(td_new, f, i) {
+                                       if (f->file_name)
+                                               sfree(f->file_name);
+                                       sfree(f);
+                               }
+                               free(td_new->files);
+                               td_new->files = NULL;
+                       }
                        td_new->files_index = 0;
                        td_new->files_size = 0;
-                       td_new->files = NULL;
+                       if (td_new->o.filename) {
+                               free(td_new->o.filename);
+                               td_new->o.filename = NULL;
+                       }
                }
 
-               job_add_num = numjobs - 1;
-
-               if (add_job(td_new, jobname, job_add_num, 1, client_type))
+               if (add_job(td_new, jobname, numjobs, 1, client_type))
                        goto err;
        }
 
@@ -1368,9 +1458,6 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
                i++;
        }
 
-       for (i = 0; i < num_opts; i++)
-               free(opts[i]);
-
        free(string);
        free(name);
        free(opts);
@@ -1384,8 +1471,8 @@ static int fill_def_thread(void)
        memset(&def_thread, 0, sizeof(def_thread));
 
        fio_getaffinity(getpid(), &def_thread.o.cpumask);
-       def_thread.o.timeout = def_timeout;
        def_thread.o.error_dump = 1;
+
        /*
         * fill default options
         */
@@ -1606,13 +1693,11 @@ static int client_flag_set(char c)
        return 0;
 }
 
-void parse_cmd_client(void *client, char *opt)
+static void parse_cmd_client(void *client, char *opt)
 {
        fio_client_add_cmd_option(client, opt);
 }
 
-extern int fio_crctest(const char *);
-
 int parse_cmd_line(int argc, char *argv[], int client_type)
 {
        struct thread_data *td = NULL;
@@ -1641,7 +1726,11 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        smalloc_pool_size = atoi(optarg);
                        break;
                case 't':
-                       def_timeout = atoi(optarg);
+                       if (check_str_time(optarg, &def_timeout, 1)) {
+                               log_err("fio: failed parsing time %s\n", optarg);
+                               do_exit++;
+                               exit_val = 1;
+                       }
                        break;
                case 'l':
                        write_lat_log = 1;
@@ -1650,6 +1739,9 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        write_bw_log = 1;
                        break;
                case 'o':
+                       if (f_out)
+                               fclose(f_out);
+
                        f_out = fopen(optarg, "w+");
                        if (!f_out) {
                                perror("fopen output");
@@ -1676,6 +1768,9 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        else
                                output_format = FIO_OUTPUT_NORMAL;
                        break;
+               case 'f':
+                       append_terse_output = 1;
+                       break;
                case 'h':
                        if (!cur_client) {
                                usage(argv[0]);
@@ -1724,7 +1819,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                case 'E': {
                        long long t = 0;
 
-                       if (str_to_decimal(optarg, &t, 0, NULL)) {
+                       if (str_to_decimal(optarg, &t, 0, NULL, 1)) {
                                log_err("fio: failed parsing eta time %s\n", optarg);
                                exit_val = 1;
                                do_exit++;
@@ -1756,6 +1851,8 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        break;
                        }
                case 'p':
+                       if (exec_profile)
+                               free(exec_profile);
                        exec_profile = strdup(optarg);
                        break;
                case FIO_GETOPT_JOB: {
@@ -1765,7 +1862,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        if (!strncmp(opt, "name", 4) && td) {
                                ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type);
                                if (ret)
-                                       return 0;
+                                       goto out_free;
                                td = NULL;
                        }
                        if (!td) {
@@ -1780,7 +1877,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
 
                                td = get_new_job(global, &def_thread, 1);
                                if (!td || ioengine_load(td))
-                                       return 0;
+                                       goto out_free;
                                fio_options_set_ioengine_opts(l_opts, td);
                        }
 
@@ -1802,7 +1899,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        if (!ret && !strcmp(opt, "ioengine")) {
                                free_ioengine(td);
                                if (ioengine_load(td))
-                                       return 0;
+                                       goto out_free;
                                fio_options_set_ioengine_opts(l_opts, td);
                        }
                        break;
@@ -1810,6 +1907,10 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                case FIO_GETOPT_IOENGINE: {
                        const char *opt = l_opts[lidx].name;
                        char *val = optarg;
+
+                       if (!td)
+                               break;
+
                        ret = fio_cmd_ioengine_option_parse(td, opt, val);
                        break;
                }
@@ -1837,6 +1938,8 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        backend = 1;
                        break;
                case 'D':
+                       if (optarg)
+                               free(optarg);
                        pid_file = strdup(optarg);
                        break;
                case 'I':
@@ -1884,7 +1987,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                case 'L': {
                        long long val;
 
-                       if (check_str_time(optarg, &val)) {
+                       if (check_str_time(optarg, &val, 0)) {
                                log_err("fio: failed parsing time %s\n", optarg);
                                do_exit++;
                                exit_val = 1;
@@ -1908,14 +2011,13 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
        if (do_exit && !(is_backend || nr_clients))
                exit(exit_val);
 
-       if (nr_clients && fio_clients_connect()) {
-               do_exit++;
-               exit_val = 1;
-               return -1;
-       }
+       if (nr_clients && fio_clients_connect())
+               exit(1);
 
        if (is_backend && backend)
                return fio_start_server(pid_file);
+       else if (pid_file)
+               free(pid_file);
 
        if (td) {
                if (!ret)
@@ -1929,6 +2031,10 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                optind++;
        }
 
+out_free:
+       if (pid_file)
+               free(pid_file);
+
        return ini_idx;
 }
 
@@ -1964,7 +2070,7 @@ int parse_options(int argc, char *argv[])
 
        if (job_files > 0) {
                for (i = 0; i < job_files; i++) {
-                       if (fill_def_thread())
+                       if (i && fill_def_thread())
                                return 1;
                        if (nr_clients) {
                                if (fio_clients_send_ini(ini_file[i]))
@@ -1985,6 +2091,7 @@ int parse_options(int argc, char *argv[])
 
        free(ini_file);
        fio_options_free(&def_thread);
+       filesetup_mem_free();
 
        if (!thread_number) {
                if (parse_dryrun())