123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- /*
- * Copyright 2017 Markus Hennecke <markus-hennecke@markus-hennecke.de>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #define _GNU_SOURCE
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <dirent.h>
- #include <fcntl.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- #include <err.h>
- #include <errno.h>
- #include <ctype.h>
- #include <signal.h>
- #include <pwd.h>
- #include <poll.h>
- #include <linux/input.h>
- #define PIDFILE "/run/mpcinputd.pid"
- volatile sig_atomic_t quit = 0;
- void *
- read_file(char *_file)
- {
- struct stat st;
- void *result;
- int fd;
- ssize_t n, s, done = 0;
- if (lstat(_file, &st) != 0) {
- warn("stat");
- return NULL;
- }
-
- if (! (st.st_mode & S_IFREG)) {
- warnx("%s is no regular file", _file);
- return NULL;
- }
- s = st.st_size;
- if ((fd = open(_file, O_RDONLY)) == -1)
- err(1, "open");
- if ((result = calloc(1, s)) == NULL)
- err(1, "malloc");
- while (done < s) {
- n = read(fd, (char *)result + done, s - done);
- if (n < 0)
- err(1, "read");
- if (n == 0) /* sysfs won't give us the actual length... */
- break;
- done += n;
- }
- close(fd);
- return result;
- }
- void
- chomp(char *_s)
- {
- size_t idx = strlen(_s);
- while (idx && !isprint(_s[idx])) {
- _s[idx] = '\0';
- --idx;
- }
- }
- char *
- find_input_device(char *_name)
- {
- DIR *d;
- char *result = NULL;
- struct dirent *ent;
- int dev_num = -1;
- if ((d = opendir("/sys/class/input")) == NULL) {
- warn("opendir");
- return NULL;
- }
- while ((ent = readdir(d)) != NULL && dev_num == -1) {
- char *dev_name;
- if (strncmp("input", ent->d_name, 5 /* strlen("input") */)) {
- continue;
- }
- if (asprintf(&dev_name, "/sys/class/input/%s/name",
- ent->d_name) < 0) {
- warn("asprintf");
- continue;
- }
- char *name = read_file(dev_name);
- if (name) {
- chomp(name);
- if (strcmp(name, _name) == 0) {
- char *fst_digit = ent->d_name;
- char *end_digit = NULL;
- while (*fst_digit && !isdigit(*fst_digit))
- ++fst_digit;
- dev_num = strtol(fst_digit, &end_digit, 10);
- if (*end_digit != '\0') {
- warnx("Unable to parse device number");
- dev_num = -1;
- }
- }
- }
- free(name);
- free(dev_name);
- }
- if (dev_num >= 0) {
- if (asprintf(&result, "/dev/input/event%d", dev_num) == -1)
- err(1, "asprintf");
- }
- closedir(d);
- return result;
- }
- void
- sig_handler(int _sig)
- {
- switch (_sig) {
- case SIGINT:
- case SIGTERM:
- quit = 1;
- break;
- default:
- break;
- }
- }
- int
- mpc_command(char *_cmd)
- {
- int rc;
- char *cmd;
- if (asprintf(&cmd, "mpc %s >/dev/null 2>&1", _cmd) == -1) {
- warn("asprintf");
- return -1;
- }
- rc = system(cmd);
- if (rc != 0) {
- warnx("cmd '%s' returned with exit code %d", cmd, rc);
- }
- free(cmd);
- return rc;
- }
- void
- drop_privileges(char *_user)
- {
- struct passwd *ent;
-
- while ((ent = getpwent()) != NULL) {
- if (strcmp(ent->pw_name, _user) == 0) {
- if (setgid(ent->pw_gid) == -1)
- err(1, "setgid");
- if (setuid(ent->pw_uid) == -1)
- err(1, "setuid");
- break;
- }
- }
- endpwent();
- }
- void
- write_pid_file(char *_pidfile)
- {
- int fd;
- struct stat st;
- if (stat(_pidfile, &st) == -1) {
- if (errno != ENOENT)
- err(1, "stat");
- } else {
- errx(1, "pid file already exists");
- }
- if ((fd = creat(_pidfile, 0755)) == -1)
- err(1, "creat");
- FILE *f = fdopen(fd, "a");
- pid_t pid = getpid();
- fprintf(f, "%d\n", pid);
- fclose(f);
- }
- void
- handle_input(char *_device)
- {
- int flags;
- struct pollfd poll_fd;
- int fd = open(_device, O_RDONLY);
- if (fd == -1)
- err(1, "open");
- flags = fcntl(fd, F_GETFL, 0);
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
- err(1, "fcntl");
- if (daemon(0, 0) != 0)
- err(1, "daemon");
- write_pid_file(PIDFILE);
- drop_privileges("nobody");
- //signal(SIGINT, sig_handler);
- signal(SIGTERM, sig_handler);
- poll_fd.fd = fd;
- poll_fd.events = POLLIN;
- while (!quit) {
- struct input_event ev;
- ssize_t n;
- int res;
- res = poll(&poll_fd, 1, -1);
- if (res < 0)
- err(1, "poll");
- if (res == 0)
- continue;
-
- n = read(fd, &ev, sizeof(ev));
- if (n < 0)
- err(1, "read");
- if (n == 0)
- continue;
- if (n != sizeof(ev))
- errx(1, "read: invalid size");
- if (ev.type != EV_MSC || ev.code != EV_MSC)
- continue;
- switch (ev.value) {
- case 0x826f15:
- printf("Play button pressed\n");
- mpc_command("toggle");
- break;
- case 0x826f14:
- printf("Record button pressed\n");
- break;
- case 0x826f09:
- printf("Stop button pressed\n");
- mpc_command("stop");
- break;
- case 0x826f04:
- printf("Previous button pressed\n");
- mpc_command("prev");
- break;
- case 0x826f0a:
- printf("Pause button pressed\n");
- mpc_command("toggle");
- break;
- case 0x826f06:
- printf("Next button pressed\n");
- mpc_command("next");
- break;
- case 0x826f0e:
- printf("mute button pressed\n");
- mpc_command("pause");
- break;
- default:
- printf("Key: %08x\n", ev.value);
- break;
- }
- }
- close(fd);
- }
- void
- usage(void)
- {
- extern char *__progname;
- fprintf(stderr, "usage: %s input_name\n", __progname);
- exit(1);
- }
- int
- main(int argc, char **argv)
- {
- if (argc != 2)
- usage();
- if (geteuid())
- errx(1, "need root privileges");
-
- char *device_name = argv[1];
- char *dev = find_input_device(device_name);
- if (dev == NULL)
- errx(1, "Unable to find input device for %s", device_name);
- handle_input(dev);
- return 0;
- }
|