[PATCH evemu 2/3] evemu-record: add support for --autorestart to cycle buffers
Benjamin Tissoires
benjamin.tissoires at gmail.com
Mon Feb 29 09:39:32 UTC 2016
On Fri, Feb 26, 2016 at 3:53 AM, Peter Hutterer
<peter.hutterer at who-t.net> wrote:
> Uses the existing evemu timeout feature where evemu_record() quits after a
> given timeout. A call of
>
> evemu-record --autorestart 10 /dev/input/event4 scroll.evemu
>
> will use the filename as prefix and create a timestamped output file (e.g.
> scroll.evemu.2016-02-25-09:13). Until the 10s inactivity timeout is hit, all
> events are recorded to that file. After that, the file is closed with a note
> at the bottom and a new file is started.
>
> This enables a user to leave evemu running in the background for a prolonged
> time and recover the most recent recording after triggering a bug without
> having to wade through three days of recordings.
>
> https://bugs.freedesktop.org/show_bug.cgi?id=93752
>
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> ---
> tools/evemu-record.c | 194 +++++++++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 172 insertions(+), 22 deletions(-)
>
> diff --git a/tools/evemu-record.c b/tools/evemu-record.c
> index bedb659..8a78cd1 100644
> --- a/tools/evemu-record.c
> +++ b/tools/evemu-record.c
> @@ -41,6 +41,9 @@
>
> #define _GNU_SOURCE
> #include "evemu.h"
> +#include <assert.h>
> +#include <getopt.h>
> +#include <limits.h>
> #include <stdbool.h>
> #include <stdio.h>
> #include <stdlib.h>
> @@ -55,6 +58,7 @@
> #define INFINITE -1
>
> FILE *output;
> +bool autorestart = false;
>
> static int describe_device(FILE *output, int fd)
> {
> @@ -82,6 +86,120 @@ static void handler (int sig __attribute__((unused)))
> fclose(output);
> output = stdout;
> }
> + autorestart = false;
> +}
> +
> +static inline bool safe_atoi(const char *str, int *val)
> +{
> + char *endptr;
> + long v;
> +
> + v = strtol(str, &endptr, 10);
> + if (str == endptr)
> + return false;
> + if (*str != '\0' && *endptr != '\0')
> + return false;
> +
> + if (v > INT_MAX || v < INT_MIN)
> + return false;
> +
> + *val = v;
> + return true;
> +}
> +
> +static inline void usage()
> +{
> + fprintf(stderr, "Usage: %s [--autorestart=s] <device> [output file]\n",
> + program_invocation_short_name);
> + fprintf(stderr, "Options:\n");
> + fprintf(stderr, " --autorestart=s\n");
> + fprintf(stderr, " Terminate the current recording after <s> seconds\n"
> + " of inactivity and restart a new recording. This option requires\n"
> + " an output file, the file is suffixed with the date and time of \n"
> + " the recording's start.\n"
> + " The timeout must be greater than 0.\n"
> + " This option is only valid for evemu-record.\n");
The man page needs an update to reflect the change.
> +}
> +
> +static inline char* make_filename(const char *prefix)
> +{
> + char *filename;
> + struct tm *tm;
> + time_t t;
> + int rc;
> + char buf[64];
> +
> + t = time(NULL);
> + tm = localtime(&t);
> + rc = strftime(buf, sizeof(buf), "%F-%T", tm);
> + if (rc < 0)
> + return NULL;
> +
> + rc = asprintf(&filename, "%s.%s", prefix, buf);
> + if (rc < 0)
> + return NULL;
> +
> + return filename;
> +}
> +
> +static bool record_device(int fd, unsigned int timeout, const char *prefix)
> +{
> + char *filename = NULL;
> + bool rc = false;
> +
> + assert(!autorestart || prefix != NULL);
> +
> + do {
> + free(filename);
> +
> + if (prefix == NULL) {
> + output = stdout;
> + } else {
> + if (autorestart)
> + filename = make_filename(prefix);
> + else
> + filename = strdup(prefix);
> + if (filename == NULL) {
> + fprintf(stderr, "error: failed to init the filename\n");
> + goto out;
> + }
> + output = fopen(filename, "w");
> + if (!output) {
> + fprintf(stderr, "error: could not open output file (%m)");
> + goto out;
> + }
> + }
> +
> + if (describe_device(output, fd)) {
> + fprintf(stderr, "error: could not describe device\n");
> + goto out;
> + }
> +
> + fprintf(output, "################################\n");
> + fprintf(output, "# Waiting for events #\n");
> + fprintf(output, "################################\n");
> + if (autorestart)
> + fprintf(output, "# Autorestart timeout: %d\n", timeout);
> +
> + if (evemu_record(output, fd, timeout)) {
> + fprintf(stderr, "error: could not record device\n");
> + } else if (autorestart) {
> + fprintf(output, "# Closing after %ds inactivity\n",
> + timeout/1000);
It might be interesting to dump something to the stderr here too (or not).
> + }
> +
> + fflush(output);
> + if (output != stdout) {
> + fclose(output);
> + output = stdout;
> + }
> + } while (autorestart);
> +
> + rc = true;
> +
> +out:
> + free(filename);
> + return rc;
> }
>
> static inline bool test_grab_device(int fd)
> @@ -102,6 +220,10 @@ enum mode {
> EVEMU_DESCRIBE
> };
>
> +enum options {
> + OPT_AUTORESTART,
> +};
> +
> int main(int argc, char *argv[])
> {
> enum mode mode = EVEMU_RECORD;
> @@ -109,6 +231,12 @@ int main(int argc, char *argv[])
> struct sigaction act;
> char *prgm_name = program_invocation_short_name;
> char *device = NULL;
> + int timeout = INFINITE;
> + struct option opts[] = {
> + { "autorestart", required_argument, 0, OPT_AUTORESTART },
> + { 0, 0, 0, 0},
> + };
> + const char *prefix = NULL;
I don't think it's good to initialize prefix here given the rest...
> int rc = 1;
>
> output = stdout;
> @@ -118,10 +246,34 @@ int main(int argc, char *argv[])
> strcmp(prgm_name, "lt-evemu-describe") == 0))
> mode = EVEMU_DESCRIBE;
>
> - device = (argc < 2) ? find_event_devices() : strdup(argv[1]);
> + while (1) {
> + int c;
> + int option_index = 0;
> +
> + c = getopt_long(argc, argv, "", opts, &option_index);
> + if (c == -1)
> + break;
> +
> + switch (c) {
> + case OPT_AUTORESTART:
> + if (!safe_atoi(optarg, &timeout) ||
> + timeout <= 0) {
> + usage();
> + goto out;
> + }
> + timeout *= 1000; /* sec to ms */
> + autorestart = true;
> + break;
> + default:
> + usage();
> + goto out;
> + }
> + }
> +
> + device = (optind >= argc) ? find_event_devices() : strdup(argv[optind++]);
>
> if (device == NULL) {
> - fprintf(stderr, "Usage: %s <device> [output file]\n", argv[0]);
> + usage();
> goto out;
> }
> fd = open(device, O_RDONLY | O_NONBLOCK);
> @@ -142,18 +294,14 @@ int main(int argc, char *argv[])
> goto out;
> }
>
> - if (argc < 3)
> - output = stdout;
> - else {
> - output = fopen(argv[2], "w");
> - if (!output) {
> - fprintf(stderr, "error: could not open output file (%m)");
> + if (optind >= argc) {
> + prefix = NULL;
... or do not set it to NULL here.
> + if (autorestart) {
> + fprintf(stderr, "Option --autoresume requires an output file\n");
> + goto out;
> }
> - }
> -
> - if (describe_device(output, fd)) {
> - fprintf(stderr, "error: could not describe device\n");
> - goto out;
> + } else {
> + prefix = argv[optind++];
> }
prefix contains here argv[optind++]; ...
>
> if (mode == EVEMU_RECORD) {
> @@ -164,17 +312,19 @@ int main(int argc, char *argv[])
> if (!test_grab_device(fd))
> goto out;
>
> - if (describe_device(output, fd)) {
> - fprintf(stderr, "error: could not describe device\n");
> - goto out;
> - }
> + record_device(fd, timeout, prefix);
>
> - fprintf(output, "################################\n");
> - fprintf(output, "# Waiting for events #\n");
> - fprintf(output, "################################\n");
> - if (evemu_record(output, fd, INFINITE))
> - fprintf(stderr, "error: could not record device\n");
> } else if (mode == EVEMU_DESCRIBE) {
> + if (optind >= argc) {
This test is always true in the regular case (no extra parameter)
> + output = stdout;
> + } else {
> + output = fopen(argv[optind++], "w");
... which should be used here. Otherwise, this breaks the existing
evemu-describe behavior.
> + if (!output) {
> + fprintf(stderr, "error: could not open output file (%m)");
> + goto out;
> + }
> + }
> +
> if (describe_device(output, fd)) {
> fprintf(stderr, "error: could not describe device\n");
> goto out;
> --
> 2.5.0
>
Cheers,
Benjamin
More information about the Input-tools
mailing list