Add support for verify triggers and verify state saving

This allows you to (for instance) instantly terminate a verify
write workload, and then later that everything was written
correctly up to that very point. This can be useful for testing
powercut scenarios, which is often problematic on storage
devices.

The trigger part is a file based notification scheme, similar
to what is provided for the status dumps. When triggered,
fio will exit immediately and write the verify state safely
to disk. A trigger can be accompanied by a trigger command.
Say you wanted to test powercut safety, the trigger could be
something that immediately cut power to the machine.

The verify state is either saved locally (if run locally),
or saved over the network if run in client/server mode.

Signed-off-by: Jens Axboe <axboe@fb.com>
diff --git a/init.c b/init.c
index 70d5d06..c7fdcdd 100644
--- a/init.c
+++ b/init.c
@@ -64,6 +64,10 @@
 int read_only = 0;
 int status_interval = 0;
 
+char *trigger_file = NULL;
+char *trigger_cmd = NULL;
+long long trigger_timeout = 0;
+
 static int prev_group_jobs;
 
 unsigned long fio_debug = 0;
@@ -242,6 +246,16 @@
 		.val		= 'L',
 	},
 	{
+		.name		= (char *) "trigger",
+		.has_arg	= required_argument,
+		.val		= 'W',
+	},
+	{
+		.name		= (char *) "trigger-timeout",
+		.has_arg	= required_argument,
+		.val		= 'B',
+	},
+	{
 		.name		= NULL,
 	},
 };
@@ -1665,6 +1679,8 @@
 #ifdef CONFIG_ZLIB
 	printf("  --inflate-log=log\tInflate and output compressed log\n");
 #endif
+	printf("  --trigger=file:cmd\tExecute trigger cmd when file exists\n");
+	printf("  --trigger-timeout=t\tExecute trigger af this time\n");
 	printf("\nFio was written by Jens Axboe <jens.axboe@oracle.com>");
 	printf("\n                   Jens Axboe <jaxboe@fusionio.com>");
 	printf("\n                   Jens Axboe <axboe@fb.com>\n");
@@ -2184,6 +2200,39 @@
 			status_interval = val / 1000;
 			break;
 			}
+		case 'W': {
+			char *split, *cmd;
+			size_t sz;
+
+			split = strchr(optarg, ':');
+			if (!split) {
+				log_err("fio: trigger is file:command\n");
+				do_exit++;
+				exit_val = 1;
+			}
+
+			sz = split - optarg;
+			trigger_file = calloc(1, sz + 1);
+			strncpy(trigger_file, optarg, sz);
+
+			split++;
+			cmd = trigger_cmd = strdup(split);
+			strip_blank_front(&trigger_cmd);
+			strip_blank_end(trigger_cmd);
+			if (strlen(trigger_cmd) == 0) {
+				free(cmd);
+				trigger_cmd = NULL;
+			}
+			break;
+			}
+		case 'B':
+			if (check_str_time(optarg, &trigger_timeout, 1)) {
+				log_err("fio: failed parsing time %s\n", optarg);
+				do_exit++;
+				exit_val = 1;
+			}
+			trigger_timeout /= 1000000;
+			break;
 		case '?':
 			log_err("%s: unrecognized option '%s'\n", argv[0],
 							argv[optind - 1]);