aboutsummaryrefslogtreecommitdiff
path: root/marsond.c
diff options
context:
space:
mode:
Diffstat (limited to 'marsond.c')
-rw-r--r--marsond.c142
1 files changed, 126 insertions, 16 deletions
diff --git a/marsond.c b/marsond.c
index 885a3e2..f872baf 100644
--- a/marsond.c
+++ b/marsond.c
@@ -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.
*/