4 * Doesn't transfer any data, merely run 3rd party tools
8 #include "../optgroup.h"
16 unsigned int std_redirect;
20 static struct fio_option options[] = {
24 .type = FIO_OPT_STR_STORE,
25 .off1 = offsetof(struct exec_options, program),
26 .help = "Program to execute",
27 .category = FIO_OPT_C_ENGINE,
28 .group = FIO_OPT_G_INVALID,
33 .type = FIO_OPT_STR_STORE,
34 .off1 = offsetof(struct exec_options, arguments),
35 .help = "Arguments to pass",
36 .category = FIO_OPT_C_ENGINE,
37 .group = FIO_OPT_G_INVALID,
41 .lname = "Grace time",
45 .off1 = offsetof(struct exec_options, grace_time),
46 .help = "Grace time before sending a SIGKILL",
47 .category = FIO_OPT_C_ENGINE,
48 .group = FIO_OPT_G_INVALID,
51 .name = "std_redirect",
52 .lname = "Std redirect",
55 .off1 = offsetof(struct exec_options, std_redirect),
56 .help = "Redirect stdout & stderr to files",
57 .category = FIO_OPT_C_ENGINE,
58 .group = FIO_OPT_G_INVALID,
65 char *str_replace(char *orig, const char *rep, const char *with)
68 Replace a substring by another.
70 Returns the new string if occurences were found
71 Returns orig if no occurence is found
73 char *result, *insert, *tmp;
74 int len_rep, len_with, len_front, count;
76 // sanity checks and initialization
80 len_rep = strlen(rep);
86 len_with = strlen(with);
89 for (count = 0; (tmp = strstr(insert, rep)); ++count) {
90 insert = tmp + len_rep;
93 tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);
99 insert = strstr(orig, rep);
100 len_front = insert - orig;
101 tmp = strncpy(tmp, orig, len_front) + len_front;
102 tmp = strcpy(tmp, with) + len_with;
103 orig += len_front + len_rep;
109 char *expand_variables(struct thread_options *o, char *arguments)
112 char *expanded_runtime, *expanded_name;
113 snprintf(str, sizeof(str), "%lld", o->timeout / 1000000);
115 /* %r is replaced by the runtime in seconds */
116 expanded_runtime = str_replace(arguments, "%r", str);
118 /* %n is replaced by the name of the running job */
119 expanded_name = str_replace(expanded_runtime, "%n", o->name);
121 return expanded_name;
124 static int exec_background(struct thread_options *o, struct exec_options *eo)
126 char *outfilename = NULL, *errfilename = NULL;
127 int outfd = 0, errfd = 0;
129 char *expanded_arguments = NULL;
130 /* For the arguments splitting */
131 char **arguments_array = NULL;
133 char *exec_cmd = NULL;
134 size_t arguments_nb_items = 0, q;
136 if (asprintf(&outfilename, "%s.stdout", o->name) < 0)
139 if (asprintf(&errfilename, "%s.stderr", o->name) < 0) {
144 /* If we have variables in the arguments, let's expand them */
145 expanded_arguments = expand_variables(o, eo->arguments);
147 if (eo->std_redirect) {
148 log_info("%s : Saving output of %s %s : stdout=%s stderr=%s\n",
149 o->name, eo->program, expanded_arguments, outfilename,
152 /* Creating the stderr & stdout output files */
153 outfd = open(outfilename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
155 log_err("fio: cannot open output file %s : %s\n",
156 outfilename, strerror(errno));
162 errfd = open(errfilename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
164 log_err("fio: cannot open output file %s : %s\n",
165 errfilename, strerror(errno));
171 log_info("%s : Running %s %s\n",
172 o->name, eo->program, expanded_arguments);
177 /* We are on the control thread (parent side of the fork */
180 if (eo->std_redirect) {
181 /* The output file is for the client side of the fork */
190 /* If the fork failed */
192 log_err("fio: forking failed %s \n", strerror(errno));
193 if (eo->std_redirect) {
202 /* We are in the worker (child side of the fork) */
204 if (eo->std_redirect) {
205 dup2(outfd, 1); // replace stdout by the output file we create
206 dup2(errfd, 2); // replace stderr by the output file we create
213 /* Let's split the command line into a null terminated array to be passed to the exec'd program
214 But don't asprintf expanded_arguments if NULL as it would be converted
215 to a '(null)' argument, while we want no arguments at all. */
216 if (expanded_arguments != NULL) {
217 if (asprintf(&exec_cmd, "%s %s", eo->program, expanded_arguments) < 0)
220 if (asprintf(&exec_cmd, "%s", eo->program) < 0)
224 /* Let's build an argv array to based on the program name and arguments */
229 if (!(q = strcspn(p, " ")))
234 realloc(arguments_array,
235 (arguments_nb_items +
236 1) * sizeof(char *));
237 arguments_array[arguments_nb_items] =
239 strncpy(arguments_array[arguments_nb_items], p,
241 arguments_array[arguments_nb_items][q] = 0;
242 arguments_nb_items++;
247 /* Adding a null-terminated item to close the list */
249 realloc(arguments_array,
250 (arguments_nb_items + 1) * sizeof(char *));
251 arguments_array[arguments_nb_items] = NULL;
253 /* Replace the fio program from the child fork by the target program */
254 execvp(arguments_array[0], arguments_array);
256 // We never reach this place
260 static enum fio_q_status
261 fio_exec_queue(struct thread_data *td, struct io_u fio_unused * io_u)
263 struct thread_options *o = &td->o;
264 struct exec_options *eo = td->eo;
266 /* Let's execute the program the first time we get queued */
268 exec_background(o, eo);
270 /* The program is running in background, let's check on a regular basis
271 if the time is over and if we need to stop the tool */
272 usleep(o->thinktime);
273 if (utime_since_now(&td->start) > o->timeout) {
274 /* Let's stop the child */
275 kill(eo->pid, SIGTERM);
276 /* Let's give grace_time (1 sec by default) to the 3rd party tool to stop */
277 sleep(eo->grace_time);
281 return FIO_Q_COMPLETED;
284 static int fio_exec_init(struct thread_data *td)
286 struct thread_options *o = &td->o;
287 struct exec_options *eo = td->eo;
288 int td_previous_state;
294 "no program is defined, it is mandatory to define one",
299 log_info("%s : program=%s, arguments=%s\n",
300 td->o.name, eo->program, eo->arguments);
302 /* Saving the current thread state */
303 td_previous_state = td->runstate;
305 /* Reporting that we are preparing the engine
306 * This is useful as the qsort() calibration takes time
307 * This prevents the job from starting before init is completed
309 td_set_runstate(td, TD_SETTING_UP);
312 * set thinktime_sleep and thinktime_spin appropriately
314 o->thinktime_blocks = 1;
315 o->thinktime_blocks_type = THINKTIME_BLOCKS_TYPE_COMPLETE;
316 o->thinktime_spin = 0;
317 o->thinktime = 50000; /* 50ms pause when waiting for the program to complete */
319 o->nr_files = o->open_files = 1;
321 /* Let's restore the previous state. */
322 td_set_runstate(td, td_previous_state);
326 static void fio_exec_cleanup(struct thread_data *td)
328 struct exec_options *eo = td->eo;
329 /* Send a sigkill to ensure the job is well terminated */
331 kill(eo->pid, SIGKILL);
335 fio_exec_open(struct thread_data fio_unused * td,
336 struct fio_file fio_unused * f)
341 static struct ioengine_ops ioengine = {
343 .version = FIO_IOOPS_VERSION,
344 .queue = fio_exec_queue,
345 .init = fio_exec_init,
346 .cleanup = fio_exec_cleanup,
347 .open_file = fio_exec_open,
348 .flags = FIO_SYNCIO | FIO_DISKLESSIO | FIO_NOIO,
350 .option_struct_size = sizeof(struct exec_options),
353 static void fio_init fio_exec_register(void)
355 register_ioengine(&ioengine);
358 static void fio_exit fio_exec_unregister(void)
360 unregister_ioengine(&ioengine);