/*****************************************************************************
*
* evemu - Kernel device emulation
*
* Copyright (C) 2010-2012 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*
* Copyright (C) 2010 Henrik Rydberg
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
****************************************************************************/
#define _GNU_SOURCE
#include "evemu.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "find_event_devices.h"
#define INFINITE -1
static FILE *output;
static bool autorestart = false;
static int describe_device(FILE *output, int fd)
{
struct evemu_device *dev;
int ret = -ENOMEM;
dev = evemu_new(NULL);
if (!dev)
goto out;
ret = evemu_extract(dev, fd);
if (ret)
goto out;
evemu_write(dev, output);
fflush(output);
out:
evemu_delete(dev);
return ret;
}
static void handler (int sig __attribute__((unused)))
{
fflush(output);
if (output != stdout) {
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(const char *program_name)
{
fprintf(stderr, "Usage: %s [--autorestart=s] [output file]\n",
program_name);
fprintf(stderr, "Options:\n");
fprintf(stderr, " --autorestart=s\n");
fprintf(stderr, " Terminate the current recording after 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");
}
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;
long ftell_start = 0 , ftell_end = 1;
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);
ftell_start = ftell(output);
}
if (evemu_record(output, fd, timeout)) {
fprintf(stderr, "error: could not record device\n");
} else if (autorestart) {
ftell_end = ftell(output);
fprintf(output, "# Closing after %ds inactivity\n",
timeout/1000);
}
fflush(output);
if (output != stdout) {
fclose(output);
output = stdout;
if (autorestart && ftell_start == ftell_end)
unlink(filename);
}
} while (autorestart);
rc = true;
out:
free(filename);
return rc;
}
static inline bool test_grab_device(int fd)
{
if (ioctl(fd, EVIOCGRAB, (void*)1) < 0) {
fprintf(stderr, "error: this device is grabbed and I cannot record events\n");
fprintf(stderr, "see the evemu-record man page for more information\n");
return false;
} else {
ioctl(fd, EVIOCGRAB, (void*)0);
}
return true;
}
enum mode {
EVEMU_RECORD,
EVEMU_DESCRIBE
};
enum options {
OPT_AUTORESTART,
};
int main(int argc, char *argv[])
{
enum mode mode = EVEMU_RECORD;
int fd = -1;
struct sigaction act;
char *prgm_name = basename(argv[0]);
char *device = NULL;
int timeout = INFINITE;
struct option opts[] = {
{ "autorestart", required_argument, 0, OPT_AUTORESTART },
{ 0, 0, 0, 0},
};
const char *prefix = NULL;
int rc = 1;
output = stdout;
if (prgm_name && (strcmp(prgm_name, "evemu-describe") == 0 ||
/* when run directly from the sources (not installed) */
strcmp(prgm_name, "lt-evemu-describe") == 0))
mode = EVEMU_DESCRIBE;
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(prgm_name);
goto out;
}
timeout *= 1000; /* sec to ms */
autorestart = true;
break;
default:
usage(prgm_name);
goto out;
}
}
device = (optind >= argc) ? find_event_devices() : strdup(argv[optind++]);
if (device == NULL) {
usage(prgm_name);
goto out;
}
fd = open(device, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
fprintf(stderr, "error: could not open device (%m)\n");
goto out;
}
memset (&act, '\0', sizeof(act));
act.sa_handler = &handler;
if (sigaction(SIGTERM, &act, NULL) < 0) {
fprintf (stderr, "Could not attach TERM signal handler (%m)\n");
goto out;
}
if (sigaction(SIGINT, &act, NULL) < 0) {
fprintf (stderr, "Could not attach INT signal handler (%m)\n");
goto out;
}
if (optind >= argc) {
if (autorestart) {
fprintf(stderr, "Option --autorestart requires an output file\n");
goto out;
}
} else {
prefix = argv[optind++];
}
if (mode == EVEMU_RECORD) {
#ifdef EVIOCSCLOCKID
int clockid = CLOCK_MONOTONIC;
ioctl(fd, EVIOCSCLOCKID, &clockid);
#endif
if (!test_grab_device(fd))
goto out;
record_device(fd, timeout, prefix);
} else if (mode == EVEMU_DESCRIBE) {
if (prefix) {
output = fopen(prefix, "w");
if (!output) {
fprintf(stderr, "error: could not open output file (%m)\n");
goto out;
}
}
if (describe_device(output, fd)) {
fprintf(stderr, "error: could not describe device\n");
goto out;
}
}
rc = 0;
out:
free(device);
close(fd);
if (output && output != stdout) {
fclose(output);
output = stdout;
}
return rc;
}