diff options
author | B. Watson <urchlay@slackware.uk> | 2025-05-06 07:18:21 -0400 |
---|---|---|
committer | B. Watson <urchlay@slackware.uk> | 2025-05-06 07:18:21 -0400 |
commit | df054a1821264dfd339afac61656c6a2261fafbf (patch) | |
tree | c60b6b356e480d055fdd067c794d6cbd66d63ab2 /marsond.c | |
parent | 67ebbac0be7ad917e794ca5ea0496d0a1ead83b8 (diff) | |
download | marsond-df054a1821264dfd339afac61656c6a2261fafbf.tar.gz |
much work (run as a daemon, drop priviliges, set priority, etc).
Diffstat (limited to 'marsond.c')
-rw-r--r-- | marsond.c | 142 |
1 files changed, 126 insertions, 16 deletions
@@ -7,6 +7,12 @@ #include <fcntl.h> #include <errno.h> #include <stdarg.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> + +#include <sys/resource.h> +#include <sys/mman.h> #include <linux/input.h> #include <linux/uinput.h> @@ -17,9 +23,13 @@ #define DEFAULT_KBD "/dev/input/by-id/usb-Marson_Marson_Keyboard_and_Mouse_Link_Ver:ps2120L-event-kbd" +#define DEFAULT_USER "nobody" +#define DEFAULT_GROUP "nobody" + const char *self; int debugging = 0; /* -v */ +int foreground = 0; /* -v */ const char *keyboard_dev = DEFAULT_KBD; /* -k */ int delay_ms = DEFAULT_DELAY_MS; /* -d */ @@ -78,12 +88,13 @@ void parse_args(int argc, char **argv) { } } - while( (opt = getopt(argc, argv, "d:hk:vV")) != -1) { + while( (opt = getopt(argc, argv, "d:hfk:vV")) != -1) { switch(opt) { - case 'v': debugging++; break; case 'd': delay_ms = atoi(optarg); break; - case 'k': keyboard_dev = optarg; break; + case 'f': foreground++; break; case 'h': print_help(); exit(0); break; + case 'k': keyboard_dev = optarg; break; + case 'v': debugging++; break; case 'V': version(); exit(0); break; case '?': die("invalid option (try --help)"); break; default: print_help(); exit(1); break; @@ -93,6 +104,64 @@ void parse_args(int argc, char **argv) { if(delay_ms < 1) die("invalid -d argument"); } +/* since every keystroke passes through this daemon, we *don't* + want it to be swapped out. it would suck if you couldn't type + because you were compiling something huge... note that we have to + do this after all the forking, see mlockall(2) for details. */ +void lock_memory(void) { + if(mlockall(MCL_CURRENT) >= 0) { + debug("mlockall() succeeded"); + } else { + warn("can't lock memory: %s", strerror(errno)); + } +} + +/* TODO: look into using *actual* realtime scheduling, as + described in sched(7). */ +void set_realtime(void) { + if(setpriority(PRIO_PROCESS, 0, -20) >= 0) { + debug("setpriority() succeeded"); + } else { + warn("can't setpriority(): %s", strerror(errno)); + } +} + +void drop_privs(void) { + const char *user, *group; + struct passwd *pw; + struct group *gr; + gid_t gid; + + user = getenv("MARSON_USER"); + + if(!user) user = DEFAULT_USER; + + if( !(pw = getpwnam(user)) ) { + die("no such user '%s'", user); + } + + if( (group = getenv("MARSON_GROUP")) ) { + if( !(gr = getgrnam(user)) ) { + die("no such group '%s'", group); + } + gid = gr->gr_gid; + } else { + gid = pw->pw_gid; + } + + debug("about to drop privileges to user/group %d/%d", pw->pw_uid, gid); + + if(setgid(gid) < 0) { + die("can't change group ID to %d: %s", gid, strerror(errno)); + } + + if(setuid(pw->pw_uid) < 0) { + die("can't change user ID to %d: %s", pw->pw_uid, strerror(errno)); + } + + debug("now running as user/group %d/%d", getuid(), getgid()); +} + void daemonize(void) { int pid; @@ -117,10 +186,28 @@ void daemonize(void) { } /* we are the grandchild here */ + + /* nothing left to say, close stdin/out/err and cd to / so at + shutdown time, umount won't trip over our cwd. */ chdir("/"); close(0); close(1); close(2); + + /* nowhere for debug messages to go, so turn it off */ + debugging = 0; + + lock_memory(); + set_realtime(); + drop_privs(); +} + +void set_evbit(int outfd, int event) { + if(ioctl(outfd, UI_SET_EVBIT, event) >= 0) { + debug("UI_SET_EVBIT %d OK"); + } else { + die("UI_SET_EVBIT %d failed: %s", event, strerror(errno)); + } } int main(int argc, char **argv) { @@ -151,12 +238,12 @@ int main(int argc, char **argv) { die("failed to grab %s: %s", keyboard_dev, strerror(errno)); } - if(ioctl(outfd, UI_SET_EVBIT, EV_KEY) >= 0) { - debug("UI_SET_EVBIT OK"); - } else { - die("UI_SET_EVBIT failed: %s", strerror(errno)); - } + set_evbit(outfd, EV_KEY); /* get and send keystroke events... */ + set_evbit(outfd, EV_SYN); /* ...sync events... */ + set_evbit(outfd, EV_MSC); /* ...and miscellaneous. */ + /* the other event types will never be sent by a keyboard. */ + /* we want all possible keystrokes */ for(i = 0; i < KEY_MAX; i++) { if(ioctl(outfd, UI_SET_KEYBIT, i) >= 0) { /* we don't wanna be *that* verbose */ @@ -185,28 +272,47 @@ int main(int argc, char **argv) { die("UI_DEV_CREATE failed: %s", strerror(errno)); } - if(!debugging) daemonize(); - /* note: don't call die() unless debugging is true, since daemonize() - closes our stderr (there's nowhere for the error message to print). */ + /* this must be done while still running as root... */ + if(foreground) { + lock_memory(); + set_realtime(); + drop_privs(); + } else { + daemonize(); + } + /* note: don't call debug() or die() unless foreground is true, since + daemonize() closes our stderr (there's nowhere for the messages + to print). */ while(1) { if(read(infd, &ev, sizeof(ev)) < 0) { - if(debugging) + if(foreground) { die("read from %s failed: %s", keyboard_dev, strerror(errno)); + } else { + exit(1); + } } - debug("event: type %d, code %d, value %d\n", - ev.type, ev.code, ev.value); + if(foreground) { + debug("got event: type %d, code %d, value %d\n", + ev.type, ev.code, ev.value); + } + /* value == 0 means key release (a 1 would be a press) */ if(ev.type == EV_KEY && ev.code == KEY_ENTER && ev.value == 0) { - debug("got Enter key release, delaying %d ms", delay_ms); + if(foreground) { + debug("!!! got Enter key release, delaying %d ms", delay_ms); + } usleep(delay_ms * 1000); } if(write(outfd, &ev, sizeof(ev)) < 0) { - if(debugging) + if(foreground) { die("write to /dev/uinput failed: %s", strerror(errno)); + } else { + exit(1); + } } } @@ -231,4 +337,8 @@ event: type 0, code 0, value 0 event: type 4, code 4, value 458792 event: type 1, code 28, value 0 event: type 0, code 0, value 0 + +Onlt the type 1 messages concern us, but we have to pass +them all through. code 28 is KEY_ENTER, value is 1 for press, +0 for release. */ |