mirror of
https://github.com/perk11/runwhenidle.git
synced 2026-01-08 21:27:57 -05:00
149 lines
4.6 KiB
C
149 lines
4.6 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
#include <X11/extensions/scrnsaver.h>
|
|
#include <getopt.h>
|
|
|
|
void pause_command(pid_t pid, int quiet) {
|
|
if (!quiet) {
|
|
printf("User activity is detected, pausing PID %i.\n", pid);
|
|
}
|
|
if (kill(pid, SIGTSTP) == -1) {
|
|
fprintf(stderr, "Pause didn't complete, command possibly exited.\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void resume_command(pid_t pid, int quiet) {
|
|
if (!quiet) {
|
|
printf("Lack of user activity is detected, resuming PID %i.\n", pid);
|
|
}
|
|
if (kill(pid, SIGCONT) == -1) {
|
|
fprintf(stderr, "Resume didn't complete: command has already finished.\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
void print_usage(char* binary_name)
|
|
{
|
|
printf("Usage: %s [--timeout|-t timeout_value_in_seconds] [--verbose|-v] [--quiet|-q] shell_command_to_run\n", binary_name);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
const char *shell_command_to_run = NULL;
|
|
pid_t pid;
|
|
int user_idle_timeout_ms = 300000;
|
|
int verbose = 0;
|
|
int quiet = 0;
|
|
|
|
// Define command line options
|
|
struct option long_options[] = {
|
|
{"timeout", required_argument, NULL, 't'},
|
|
{"verbose", no_argument, NULL, 'v'},
|
|
{"quiet", no_argument, NULL, 'q'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
// Parse command line options
|
|
int option;
|
|
while ((option = getopt_long(argc, argv, "hvqt:", long_options, NULL)) != -1) {
|
|
switch (option) {
|
|
case 't':
|
|
user_idle_timeout_ms = atoi(optarg) * 1000;
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'q':
|
|
quiet = 1;
|
|
break;
|
|
case 'h':
|
|
default:
|
|
print_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
if (optind >= argc) {
|
|
print_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
if (quiet && verbose) {
|
|
printf("Incompatible options --quiet|-q and --verbose|-v used");
|
|
print_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
//Open display and initialize XScreensaverInfo for querying idle time
|
|
Display *dpy = XOpenDisplay(NULL);
|
|
if (!dpy) {
|
|
fprintf(stderr, "Couldn't open an X11 display!\n");
|
|
return 1;
|
|
}
|
|
XScreenSaverInfo *info = XScreenSaverAllocInfo();
|
|
|
|
shell_command_to_run = argv[optind];
|
|
|
|
// Fork a child process to run the command
|
|
pid = fork();
|
|
if (pid < 0) {
|
|
perror("fork");
|
|
exit(1);
|
|
} else if (pid == 0) {
|
|
// Child process
|
|
execl("/bin/sh", "sh", "-c", shell_command_to_run, (char *) NULL);
|
|
perror("execl");
|
|
exit(1);
|
|
}
|
|
|
|
int polling_interval_seconds = 1;
|
|
int sleep_time_seconds = polling_interval_seconds;
|
|
int command_paused = 0;
|
|
// Monitor user activity
|
|
while (1) {
|
|
sleep(sleep_time_seconds);
|
|
XScreenSaverQueryInfo(dpy, DefaultRootWindow(dpy), info);
|
|
|
|
if (info->idle > user_idle_timeout_ms) {
|
|
// User is inactive
|
|
if (command_paused) {
|
|
sleep_time_seconds = polling_interval_seconds; //reset to default value
|
|
if (verbose) {
|
|
fprintf(stderr, "Idle time: %lums\n", info->idle);
|
|
}
|
|
|
|
resume_command(pid, quiet);
|
|
command_paused = 0;
|
|
}
|
|
} else {
|
|
// User is active
|
|
if (!command_paused) {
|
|
if (verbose) {
|
|
fprintf(stderr, "Idle time: %lums\n", info->idle);
|
|
}
|
|
pause_command(pid, quiet);
|
|
command_paused = 1;
|
|
}
|
|
//TODO: this doesn't account for the time it took to pause the command
|
|
sleep_time_seconds = ((user_idle_timeout_ms - info->idle) / 1000) - polling_interval_seconds;
|
|
if (sleep_time_seconds < polling_interval_seconds) {
|
|
sleep_time_seconds = polling_interval_seconds;
|
|
}
|
|
if (verbose) {
|
|
fprintf(stderr, "Polling every second is temporarily disabled due to user activity, next activity check scheduled in %u seconds\n", sleep_time_seconds);
|
|
}
|
|
}
|
|
|
|
// Check if the command has finished
|
|
int status;
|
|
if (waitpid(pid, &status, WNOHANG+WUNTRACED) == pid && WIFEXITED(status)) {
|
|
int exit_code = WEXITSTATUS(status);
|
|
if (verbose) {
|
|
fprintf(stderr, "Command has finished with exit code %u\n", exit_code);
|
|
}
|
|
return exit_code;
|
|
}
|
|
}
|
|
}
|