Skip to content

Conversation

@JacobKnoth
Copy link

Minimal GNU styled 'timeout',
runs a command with a time limit (monotonic clock) sends a SIGTERM to the child process group at deadline then SIGKILL after a grace period
Exit codes: 124(timeout), 125(internal/usage), 126/127(exec error) Otherwise child status

@github-actions github-actions bot added the 👀 pr-needs-review PR needs review from a maintainer or community member label Sep 13, 2025
@stale
Copy link

stale bot commented Oct 8, 2025

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions!

@stale stale bot added the stale label Oct 8, 2025
Copy link
Member

@LucasChollet LucasChollet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello and welcome to the project!
And first of all, thanks for your contribution, and sorry for the wait.

My comments are mainly on the style rather than the logic itself. We have a lot of custom stuff in the codebase, so it's normal that it takes time to get used to it. I've left some specific comments but in general:

  • Check if there is a Core::System equivalent before using a syscall
  • Prefer the Error type to propagate errors. And I think that you will need Main::set_return_code_for_errors(125).

If you want, you can add your utility to Lagom (so it can run and be tested on the host). Also, we encourage the addition of a man page with new utilities.

Don't hesitate to ask questions on Discord if you need help with any of that.

Comment on lines +52 to +55
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = handle_sigint;
sigaction(SIGINT, &sa, nullptr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try to use less C-ism.

Suggested change
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = handle_sigint;
sigaction(SIGINT, &sa, nullptr);
struct sigaction sa{}; // This will zero initialize the struct.
sa.sa_handler = handle_sigint;
TRY(Core::System::sigaction(SIGINT, &sa, nullptr)); // To get error handling.

perror("negative timeout");
return 125;
}
TRY(Core::System::pledge("stdio proc exec sigaction"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't used pledge with sigaction but I would expect that it is only required for the syscall. And here you're already done with that so you could drop sigaction from the pledge line. Feel free to dismiss if my assumption is false.

Comment on lines +62 to +77
double whole_seconds = static_cast<time_t>(secs);
double fraction = secs - whole_seconds;
timespec requested_timeout {
.tv_sec = static_cast<time_t>(whole_seconds),
.tv_nsec = static_cast<long>(fraction * (double)1000000000),
};
timespec tmp {};
clock_gettime(CLOCK_MONOTONIC, &tmp);
timespec deadline = {
.tv_sec = tmp.tv_sec + requested_timeout.tv_sec,
.tv_nsec = tmp.tv_nsec + requested_timeout.tv_nsec,
};
if (deadline.tv_nsec >= 1'000'000'000) {
deadline.tv_sec++;
deadline.tv_nsec -= 1'000'000'000;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have some time utilities to help you here.
From AK/Time.h, you can invoke MonotonicTime::now() and then add a Duration that you created from secs (that can probably have a less abbreviated and more descriptive name, e.g. seconds, time_limit).

Comment on lines +79 to +91
pid_t child_pid = fork();
if (child_pid < 0) {
perror("fork");
return 125;
}
if (child_pid == 0) {
setpgid(0, 0);
execvp(argv_ptrs[0], argv_ptrs.data());
perror("execvp");
if (errno == ENOENT) {
_exit(127);
}
_exit(126);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, Core::System got your back here. And Core::System::exec will even take a Span<StringView> so you won't have to convert the Vector from ArgsParser.

waitpid(child_pid, &status, 0);
TRY(Core::System::signal(SIGINT, SIG_DFL));
raise(SIGINT);
__builtin_unreachable();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use VERIFY_NOT_REACHED(); for this.

@stale stale bot removed the stale label Oct 14, 2025
@spholz spholz added ⏳ pr-waiting-for-author PR is blocked by feedback / code changes from the author and removed 👀 pr-needs-review PR needs review from a maintainer or community member labels Oct 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⏳ pr-waiting-for-author PR is blocked by feedback / code changes from the author

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants