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.  */  | 
