Automated code formatting

This commit is contained in:
Konstantin Pereiaslov
2024-01-01 22:13:00 -06:00
parent e833f1645f
commit f964e64751
9 changed files with 96 additions and 68 deletions

View File

@@ -1,14 +1,16 @@
# runwhenidle
runwhenidle is a Linux utility that can be used to run a computationally or IO-intensive program when user is not
in front of the computer, pausing it once the user is back, resuming once the user left, often without requiring adaptation from the program being run.
in front of the computer, pausing it once the user is back, resuming once the user left, often without requiring
adaptation from the program being run.
runwhenidle runs a command given to it, pauses it if the user is active by sending SIGTSTP (or optionally SIGSTOP) to the command,
runwhenidle runs a command given to it, pauses it if the user is active by sending SIGTSTP (or optionally SIGSTOP) to
the command,
when the user activity stops, runwhenidle resumes the command by sending it SIGCONT signal.
It then checks once per second if user activity has resumed, and once it is, pauses the command again.
runwhenidle uses XScreenSaverQueryInfo() to check when last user activity happened therefore a running X server is required.
runwhenidle uses XScreenSaverQueryInfo() to check when last user activity happened therefore a running X server is
required.
Wayland is not currently supported.
If runwhenidle receives an interruption signal (SIGINT or SIGTERM), it will pass that signal to the command it is
@@ -17,7 +19,8 @@ to handle the signal.
## Installation
**Ubuntu and Debian**: Download the deb file attached to the [latest release](https://github.com/perk11/runwhenidle/releases/latest).
**Ubuntu and Debian**: Download the deb file attached to
the [latest release](https://github.com/perk11/runwhenidle/releases/latest).
**Arch**: Available in AUR: https://aur.archlinux.org/packages/runwhenidle
@@ -25,9 +28,10 @@ Other Distros: You will need to compile runwhenidle yourself.
## Compiling
Make sure you have `gcc`, `make`, `git` and `libxss-dev` installed. Run `make release`. This should produce a binary file `runwhenidle` in the project directory.
Make sure you have `gcc`, `make`, `git` and `libxss-dev` installed. Run `make release`. This should produce a binary
file `runwhenidle` in the project directory.
If you want to install it system-wide, run `sudo make install` or simply `sudo cp ./runwhenidle /usr/bin`.
If you want to install it system-wide, run `sudo make install` or simply `sudo cp ./runwhenidle /usr/bin`.
## Usage
@@ -46,7 +50,7 @@ If you want to install it system-wide, run `sudo make install` or simply `sudo c
| `--version\| -V` | Print the program version information. | |
### Example 1:
runwhenidle -t 100 -v cp /filea /fileb
Run the `cp` command and pause it while user is active. When user is inactive for 100 seconds, resume the command.
@@ -56,15 +60,21 @@ Output debug information to stderr.
runwhenidle --timeout=300 -q cat /dev/zero
Run the `cat /dev/zero` command and pause it while user is active. `-q` option makes sure runwhenidle doesn't output anything other than the output of `cat /dev/zero`.
Run the `cat /dev/zero` command and pause it while user is active. `-q` option makes sure runwhenidle doesn't output
anything other than the output of `cat /dev/zero`.
### Known issues
1. Wayland support. runwhenidle currently doesn't work without XScreenSaver, but Wayland support should be possible and is planned (at least for the DEs supporting ext-idle-notify, which now both Gnome and KDE support).
2. The signal isn't sent to child processes, so if your process starts new processes, they will not receive the signal and will continue running. Unofrtunately there is no universal solution for this, but I plan to look at what needs to be done to improve this behavior.
3. When running from cron, runwhenidle doesn't work. This is now resolved, but you need to use --pause-method=SIGSTOP parameter. For some reason, cron is launching things in a way that ignores SIGTSTP signal.
1. Wayland support. runwhenidle currently doesn't work without XScreenSaver, but Wayland support should be possible and
is planned (at least for the DEs supporting ext-idle-notify, which now both Gnome and KDE support).
2. The signal isn't sent to child processes, so if your process starts new processes, they will not receive the signal
and will continue running. Unofrtunately there is no universal solution for this, but I plan to look at what needs to
be done to improve this behavior.
3. When running from cron, runwhenidle doesn't work. This is now resolved, but you need to use --pause-method=SIGSTOP
parameter. For some reason, cron is launching things in a way that ignores SIGTSTP signal.
### Building Ubuntu/Debian package
Make sure you have docker installed and run:
make debian-package

View File

@@ -13,7 +13,7 @@
const long TIMEOUT_MAX_SUPPORTED_VALUE = 100000000; //~3 years
const long TIMEOUT_MIN_SUPPORTED_VALUE = 1;
const long START_MONITOR_AFTER_MAX_SUPPORTED_VALUE = TIMEOUT_MAX_SUPPORTED_VALUE*1000;
const long START_MONITOR_AFTER_MAX_SUPPORTED_VALUE = TIMEOUT_MAX_SUPPORTED_VALUE * 1000;
const long START_MONITOR_AFTER_MIN_SUPPORTED_VALUE = 0;
void print_usage(char *binary_name) {
@@ -24,9 +24,11 @@ void print_usage(char *binary_name) {
printf(" --pause-method|-m <method> Specify method for pausing the command when user is not idle. Available parameters: SIGTSTP (can be ignored by the program), SIGSTOP (can not be ignored). (default: SIGTSTP).\n");
printf(" --verbose|-v Enable verbose output for monitoring.\n");
printf(" --debug Enable debugging output.\n");
printf(" --quiet|-q Suppress all output from %s except errors and only display output from the command that is running\n", binary_name);
printf(" --quiet|-q Suppress all output from %s except errors and only display output from the command that is running\n",
binary_name);
printf(" --version|-V Print the program version information.\n");
}
void print_version() {
printf("runwhenidle %s\n", VERSION);
}
@@ -62,22 +64,23 @@ char *read_remaining_arguments_as_char(int argc,
remaining_arguments_string[current_length_of_all_arguments++] = ' '; // Add space separator
}
assert(current_length_of_all_arguments == memory_to_be_allocated_for_remaining_arguments_string);
remaining_arguments_string[current_length_of_all_arguments - 1] = '\0'; // Replace the last space separator with a null terminator
remaining_arguments_string[current_length_of_all_arguments -
1] = '\0'; // Replace the last space separator with a null terminator
return remaining_arguments_string;
}
char *parse_command_line_arguments(int argc, char *argv[]) {
struct option long_options[] = {
{"timeout", required_argument, NULL, 't'},
{"timeout", required_argument, NULL, 't'},
{"start-monitor-after", required_argument, NULL, 'a'},
{"pause-method", required_argument, NULL, 'm'},
{"verbose", no_argument, NULL, 'v'},
{"debug", no_argument, NULL, 'd'},
{"quiet", no_argument, NULL, 'q'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
{"pause-method", required_argument, NULL, 'm'},
{"verbose", no_argument, NULL, 'v'},
{"debug", no_argument, NULL, 'd'},
{"quiet", no_argument, NULL, 'q'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
// Parse command line options
@@ -100,8 +103,8 @@ char *parse_command_line_arguments(int argc, char *argv[]) {
start_monitor_after_ms = strtol(optarg, NULL, 10);
if (start_monitor_after_ms < START_MONITOR_AFTER_MIN_SUPPORTED_VALUE || errno != 0) {
fprintf_error( "Invalid start-monitor-after time value: \"%s\" Range supported: %ld-%ld.\n", optarg,
START_MONITOR_AFTER_MIN_SUPPORTED_VALUE, START_MONITOR_AFTER_MAX_SUPPORTED_VALUE
fprintf_error("Invalid start-monitor-after time value: \"%s\" Range supported: %ld-%ld.\n", optarg,
START_MONITOR_AFTER_MIN_SUPPORTED_VALUE, START_MONITOR_AFTER_MAX_SUPPORTED_VALUE
);
print_usage(argv[0]);
exit(1);
@@ -153,15 +156,16 @@ char *parse_command_line_arguments(int argc, char *argv[]) {
exit(1);
}
}
if (debug) fprintf(stderr,
"verbose: %i, debug: %i, quiet: %i, pause_method: %i, user_idle_timeout_ms: %lu, start_monitoring_after_ms: %ld\n",
verbose,
debug,
quiet,
pause_method,
user_idle_timeout_ms,
start_monitor_after_ms
);
if (debug)
fprintf(stderr,
"verbose: %i, debug: %i, quiet: %i, pause_method: %i, user_idle_timeout_ms: %lu, start_monitoring_after_ms: %ld\n",
verbose,
debug,
quiet,
pause_method,
user_idle_timeout_ms,
start_monitor_after_ms
);
if (optind >= argc) {
print_usage(argv[0]);
exit(1);

View File

@@ -11,4 +11,5 @@ extern long unsigned user_idle_timeout_ms;
* @return A character pointer to the remaining command line arguments as a single string.
*/
char *parse_command_line_arguments(int argc, char *argv[]);
#endif //RUNWHENIDLE_ARGUMENTS_PARSING_H

39
main.c
View File

@@ -26,10 +26,10 @@ long unsigned user_idle_timeout_ms = 300000;
long long polling_interval_ms = 1000;
const long long POLLING_INTERVAL_BEFORE_STARTING_MONITORING_MS = 100;
const char *pause_method_string[] = {
//order must match order in pause_method enum
[PAUSE_METHOD_SIGTSTP] = "SIGTSTP",
[PAUSE_METHOD_SIGSTOP] = "SIGSTOP",
NULL // Sentinel value to indicate the end of the array
//order must match order in pause_method enum
[PAUSE_METHOD_SIGTSTP] = "SIGTSTP",
[PAUSE_METHOD_SIGSTOP] = "SIGSTOP",
NULL // Sentinel value to indicate the end of the array
};
int xscreensaver_is_available;
Display *x_display;
@@ -41,8 +41,7 @@ volatile sig_atomic_t command_paused = 0;
pid_t pid;
long unsigned query_user_idle_time()
{
long unsigned query_user_idle_time() {
if (xscreensaver_is_available) {
XScreenSaverQueryInfo(x_display, DefaultRootWindow(x_display), xscreensaver_info);
return xscreensaver_info->idle;
@@ -63,6 +62,7 @@ int handle_interruption() {
//Wait for the child process to complete
return wait_for_pid_to_exit_synchronously(pid);
}
void sigint_handler(int signum) {
if (!quiet) {
printf("Received SIGINT, sending SIGINT to the command and waiting for it to finish.\n");
@@ -70,6 +70,7 @@ void sigint_handler(int signum) {
send_signal_to_pid(pid, signum, "SIGINT");
interruption_received = 1;
}
void sigterm_handler(int signum) {
if (!quiet) {
printf("Received SIGTERM, sending SIGTERM to the command and waiting for it to finish.\n");
@@ -83,13 +84,16 @@ long long pause_or_resume_command_depending_on_user_activity(
long long sleep_time_ms,
unsigned long user_idle_time_ms) {
if (user_idle_time_ms >= user_idle_timeout_ms) {
if (debug) fprintf(stderr,"Idle time: %lums, idle timeout: %lums, user is inactive\n", user_idle_time_ms, user_idle_timeout_ms);
if (debug)
fprintf(stderr, "Idle time: %lums, idle timeout: %lums, user is inactive\n", user_idle_time_ms,
user_idle_timeout_ms);
if (command_paused) {
sleep_time_ms = polling_interval_ms; //reset to default value
if (verbose) {
fprintf(stderr, "Idle time: %lums, idle timeout: %lums, resuming command\n", user_idle_time_ms, user_idle_timeout_ms);
fprintf(stderr, "Idle time: %lums, idle timeout: %lums, resuming command\n", user_idle_time_ms,
user_idle_timeout_ms);
}
if (!quiet){
if (!quiet) {
printf("Lack of user activity detected. ");
//intentionally no new line here, resume_command will print the rest of the message.
}
@@ -106,24 +110,30 @@ long long pause_or_resume_command_depending_on_user_activity(
fprintf(stderr, "Idle time: %lums.\n", user_idle_time_ms);
}
pause_command_recursively(pid);
if (debug) fprintf(stderr,"Command paused\n");
if (debug) fprintf(stderr, "Command paused\n");
command_paused = 1;
command_was_paused_this_iteration = 1;
}
sleep_time_ms = user_idle_timeout_ms - user_idle_time_ms;
if (debug) fprintf(stderr,"Target sleep time: %llums\n", sleep_time_ms);
if (debug) fprintf(stderr, "Target sleep time: %llums\n", sleep_time_ms);
if (command_was_paused_this_iteration) {
if (debug) fprintf(stderr, "Command was paused this iteration\n");
struct timespec time_before_sleep;
clock_gettime(CLOCK_MONOTONIC, &time_before_sleep);
long long pausing_time_ms = get_elapsed_time_ms(time_when_starting_to_pause, time_before_sleep);
if (debug) fprintf(stderr, "Target sleep time before taking into account time it took to pause: %lldms, time it took to pause: %lldms\n", sleep_time_ms, pausing_time_ms);
if (debug)
fprintf(stderr,
"Target sleep time before taking into account time it took to pause: %lldms, time it took to pause: %lldms\n",
sleep_time_ms, pausing_time_ms);
sleep_time_ms = sleep_time_ms - pausing_time_ms;
}
if (sleep_time_ms < polling_interval_ms) {
if (debug) fprintf(stderr, "Target sleep time %lldms is less than polling interval %lldms, resetting it to polling interval\n", sleep_time_ms, polling_interval_ms);
if (debug)
fprintf(stderr,
"Target sleep time %lldms is less than polling interval %lldms, resetting it to polling interval\n",
sleep_time_ms, polling_interval_ms);
sleep_time_ms = polling_interval_ms;
}
if (verbose) {
@@ -156,7 +166,8 @@ int main(int argc, char *argv[]) {
}
if (!xscreensaver_is_available) {
fprintf_error("No available method for detecting user idle time on the system, user will be considered idle to allow the command to finish.\n");
fprintf_error(
"No available method for detecting user idle time on the system, user will be considered idle to allow the command to finish.\n");
}
pid = run_shell_command(shell_command_to_run);

View File

@@ -45,14 +45,15 @@ void handle_kill_error(char *signal_name, pid_t pid, int kill_errno) {
typedef struct ProcessNode {
int process_id;
struct ProcessNode* next_node;
struct ProcessNode *next_node;
} ProcessNode;
typedef struct ProcessInfo {
int process_id;
int parent_process_id;
} ProcessInfo;
ProcessNode* get_child_processes_linked_list(int initial_parent_process_id) {
ProcessNode *get_child_processes_linked_list(int initial_parent_process_id) {
DIR *proc_directory = opendir("/proc/");
if (proc_directory == NULL) {
fprintf_error("Could not open /proc directory");
@@ -62,7 +63,7 @@ ProcessNode* get_child_processes_linked_list(int initial_parent_process_id) {
// Stage 1: Read all process and parent IDs into an array
const int NUMBER_OF_PROCESSES_INITIALLY_ALLOCATED = 4096;
int processes_allocated = NUMBER_OF_PROCESSES_INITIALLY_ALLOCATED;
ProcessInfo* all_processes = malloc(processes_allocated * sizeof (ProcessInfo));
ProcessInfo *all_processes = malloc(processes_allocated * sizeof(ProcessInfo));
int total_processes = 0;
struct dirent *directory_entry;
@@ -72,7 +73,7 @@ ProcessNode* get_child_processes_linked_list(int initial_parent_process_id) {
while ((directory_entry = readdir(proc_directory)) != NULL) {
if (total_processes == processes_allocated) {
processes_allocated *= 2;
ProcessInfo* new_all_processes = realloc(all_processes, processes_allocated * sizeof(ProcessInfo));
ProcessInfo *new_all_processes = realloc(all_processes, processes_allocated * sizeof(ProcessInfo));
if (!new_all_processes) {
perror("Failed to allocate memory while reading processes list");
exit(1);
@@ -146,14 +147,14 @@ ProcessNode* get_child_processes_linked_list(int initial_parent_process_id) {
void send_signal_to_pid(pid_t pid, int signal, char *signal_name) {
if (debug) {
printf("Sending %s to %i\n",signal_name, pid);
printf("Sending %s to %i\n", signal_name, pid);
}
int kill_result = kill(pid, signal);
if (kill_result == -1) {
handle_kill_error(signal_name, pid, errno);
exit(1);
} else {
if (debug) fprintf(stderr, "kill function sending %s returned %i\n",signal_name, kill_result);
if (debug) fprintf(stderr, "kill function sending %s returned %i\n", signal_name, kill_result);
}
}
@@ -174,12 +175,11 @@ void pause_command(pid_t pid) {
}
}
void pause_command_recursively(pid_t pid)
{
void pause_command_recursively(pid_t pid) {
pause_command(pid);
ProcessNode* child_process_ids = get_child_processes_linked_list(pid);
ProcessNode* current_node = child_process_ids;
ProcessNode* previous_node;
ProcessNode *child_process_ids = get_child_processes_linked_list(pid);
ProcessNode *current_node = child_process_ids;
ProcessNode *previous_node;
while (current_node) {
pause_command(current_node->process_id);
@@ -196,12 +196,11 @@ void resume_command(pid_t pid) {
send_signal_to_pid(pid, SIGCONT, "SIGCONT");
}
void resume_command_recursively(pid_t pid)
{
void resume_command_recursively(pid_t pid) {
resume_command(pid);
ProcessNode* child_process_ids = get_child_processes_linked_list(pid);
ProcessNode* current_node = child_process_ids;
ProcessNode* previous_node;
ProcessNode *child_process_ids = get_child_processes_linked_list(pid);
ProcessNode *current_node = child_process_ids;
ProcessNode *previous_node;
while (current_node) {
resume_command(current_node->process_id);

View File

@@ -1,5 +1,6 @@
#ifndef RUNWHENIDLE_PROCESS_HANDLING_H
#define RUNWHENIDLE_PROCESS_HANDLING_H
/**
* Sends a signal to a specified process and handles any errors that occur during the process.
*
@@ -8,6 +9,7 @@
* @param signal_name The name of the signal being sent.
*/
void send_signal_to_pid(pid_t pid, int signal, char *signal_name);
/**
* Pauses a specified process using pause method specified in pause_method variable
*
@@ -62,4 +64,5 @@ int wait_for_pid_to_exit_synchronously(int pid);
* @param pid The process ID (PID) of the target process to check.
*/
void exit_if_pid_has_finished(pid_t pid);
#endif //RUNWHENIDLE_PROCESS_HANDLING_H

View File

@@ -2,12 +2,10 @@
#include <errno.h>
#include "sleep_utils.h"
int sleep_for_milliseconds(long milliseconds)
{
int sleep_for_milliseconds(long milliseconds) {
struct timespec ts;
if (milliseconds < 0)
{
if (milliseconds < 0) {
errno = EINVAL;
return -1;
}

View File

@@ -1,4 +1,5 @@
#include "time_utils.h"
long long get_elapsed_time_ms(struct timespec start, struct timespec end) {
long long start_ms = start.tv_sec * 1000LL + start.tv_nsec / 1000000LL;
long long end_ms = end.tv_sec * 1000LL + end.tv_nsec / 1000000LL;

View File

@@ -1,5 +1,6 @@
#ifndef RUNWHENIDLE_TIME_UTILS_H
#define RUNWHENIDLE_TIME_UTILS_H
#include <time.h>
/**