Make commands that fail, fail faster and move first idle check before first sleep, but after an initial 300ms sleep

This commit is contained in:
Konstantin Pereiaslov
2023-05-19 22:56:22 -05:00
parent 83c82b352e
commit 482fb66e54
5 changed files with 68 additions and 14 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
runwhenidle
package-build/
*.o

View File

@@ -4,6 +4,8 @@ CC=gcc
ifeq ($(PREFIX),)
PREFIX := /usr
endif
SOURCES = sleep_utils.c main.c
OBJECTS = $(SOURCES:.c=.o)
all: executable
@@ -13,15 +15,18 @@ release: executable
debug: CCFLAGS += -DDEBUG -ggdb
debug: executable
executable:
$(CC) main.c -o $(TARGET_EXEC) $(CCFLAGS) $(LDFLAGS) $(LDLIBS)
%.o: %.c
$(CC) $(CCFLAGS) -c $< -o $@ $(LDFLAGS) $(LDLIBS)
executable: $(OBJECTS)
$(CC) $(CCFLAGS) $(OBJECTS) -o $(TARGET_EXEC) $(LDFLAGS) $(LDLIBS)
install: release
install -d $(DESTDIR)$(PREFIX)/bin/
install -m 755 $(TARGET_EXEC) $(DESTDIR)$(PREFIX)/bin/
clean:
rm -f runwhenidle
rm -f $(OBJECTS) $(TARGET_EXEC)
debian-package:
docker build --build-arg HOST_UID=`id -u` --tag runwhenidle-ubuntu2204-build distro-packages/ubuntu22.04

35
main.c
View File

@@ -7,6 +7,8 @@
#include <X11/extensions/scrnsaver.h>
#include <getopt.h>
#include "sleep_utils.h"
int verbose;
int quiet;
@@ -68,6 +70,17 @@ pid_t run_shell_command(const char *shell_command_to_run, pid_t pid) {
return pid;
}
void exit_if_pid_has_finished(pid_t pid) {
int status;
if (waitpid(pid, &status, WNOHANG + WUNTRACED) == pid && WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
if (verbose) {
fprintf(stderr, "PID %i has finished with exit code %u\n", pid, exit_code);
}
exit(exit_code);
}
}
int main(int argc, char *argv[]) {
pid_t pid;
int user_idle_timeout_ms = 300000;
@@ -120,14 +133,23 @@ int main(int argc, char *argv[]) {
pid = run_shell_command(argv[optind], pid);
// Let command run for 300ms to give it a chance to error-out or provide initial output.
// 300ms is chosen to avoid giving user a noticeable delay while giving most quick commands a chance to finish.
sleep_for_milliseconds(300);
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);
// Checking this after querying the screensaver timer so that the command is still running while
// we're querying the screensaver and has a chance to do some work and finish,
// but before potentially pausing the command to avoid trying to pause it if it completed.
exit_if_pid_has_finished(pid);
if (info->idle > user_idle_timeout_ms) {
// User is inactive
if (command_paused) {
@@ -159,15 +181,6 @@ int main(int argc, char *argv[]) {
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;
}
sleep(sleep_time_seconds);
}
}

20
sleep_utils.c Normal file
View File

@@ -0,0 +1,20 @@
#include <time.h>
#include <errno.h>
int sleep_for_milliseconds(long milliseconds)
{
struct timespec ts;
if (milliseconds < 0)
{
errno = EINVAL;
return -1;
}
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = (milliseconds % 1000) * 1000000;
//We don't care about sleeping for exact amount in case sleep is interrupted by a signal,
//which is why NULL is used for the second argument
return nanosleep(&ts, NULL);
}

15
sleep_utils.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef SLEEP_UTILS_H
#define SLEEP_UTILS_H
#include <time.h>
#include <errno.h>
/**
* Sleeps for the specified number of milliseconds unless interrupted by a signal.
*
* @param milliseconds The number of milliseconds to sleep.
* @return 0 on success, -1 on failure or being interrupted by a signal (sets errno to EINVAL if milliseconds is negative).
*/
int sleep_for_milliseconds(long milliseconds);
#endif /* SLEEP_UTILS_H */