16#define QDEBUG(...) Ase::debug ("AtQuit", __VA_ARGS__)
20static void atquit_run_terminate_handlers ();
30 while ((entry =
readdir (dir)) !=
nullptr) {
31 char fullpath[
PATH_MAX + 1] = { 0, };
34 if (
stat (fullpath, &sb) == -1 || !S_ISREG (sb.st_mode))
36 FILE *file =
fopen (fullpath,
"r");
38 const int BUFFER_SIZE = 8192;
39 char buffer[BUFFER_SIZE + 1] = { 0, };
40 fread (buffer, BUFFER_SIZE, 1, file);
42 const char *tag =
"@@TEMPFILE_PID=";
43 char *tagp =
strstr (buffer, tag);
47 char procpath[
PATH_MAX + 1] = { 0, };
49 if (
access (procpath, F_OK) != 0) {
50 if (
unlink (fullpath) == 0)
52 diag (
"AtQuit: %s: remove \"%s\": %s", __func__, fullpath,
strerror (
errno));
69 atquit_add (atquit_handler);
73 atquit_run_terminate_handlers();
74 atquit_del (atquit_handler);
76 delete atquit_handler;
94 while (tentries.
size()) {
100 diag (
"AtQuit: %s: remove \"%s\": %s", __func__, tentry,
strerror (
errno));
113 g_pending_removals.add (filename);
120 g_pending_removals.del (filename);
127 atquit_run_terminate_handlers();
147 for (
pid_t pid : current) {
151 diag (
"AtQuit: found orphan pid=%d, sending signal=%d", pid, sig);
163 while ((result =
waitpid (-1, &status, WNOHANG)) > 0) {
174 for (
pid_t pid : pids_) {
175 diag (
"AtQuit: %s: pid=%d signal=%d", __func__, pid, sig);
191 return pids_.
empty();
199 snprintf (path,
sizeof (path),
"/proc/self/task/%d/children",
getpid());
200 FILE *f =
fopen (path,
"r");
201 if (!f)
return children;
203 while (
fscanf (f,
"%d", &pid) == 1 && pid > 0)
204 children.push_back (pid);
212static KillPids g_kill_pids;
218 g_kill_pids.add (pid);
225 g_kill_pids.del (pid);
233 for (
const auto &arg : argv)
234 argvptr.push_back (arg.c_str());
235 argvptr.push_back (
nullptr);
236 const char **child_argv = &argvptr[0];
240 return {
errno,
"fork" };
247 int max_fd =
sysconf (_SC_OPEN_MAX);
250 for (
int i = 3; i < max_fd; i++)
252 bool keep = (i == stdio_fd);
253 for (
int kfd : keep_fds)
263 const char *
const home =
getenv (
"HOME");
264 if (
false && home &&
chdir (home) < 0) {
265 ereason = {
errno,
"chdir" };
270 if (
dup2 (stdio_fd, STDOUT_FILENO) < 0) {
271 ereason = {
errno,
"dup2 stdout" };
274 if (
dup2 (stdio_fd, STDERR_FILENO) < 0) {
275 ereason = {
errno,
"dup2 stderr" };
281 ereason = {
errno,
"unsetenv" };
286 if (
sigprocmask (SIG_SETMASK, &empty_mask,
nullptr) < 0) {
287 ereason = {
errno,
"sigprocmask" };
291 if (pdeathsig > 0 && prctl (PR_SET_PDEATHSIG, SIGKILL) < 0) {
292 ereason = {
errno,
"prctl(PR_SET_PDEATHSIG)" };
295 execvp (child_argv[0],
const_cast<char *
const *
> (child_argv));
297 ereason = {
errno,
"execvp" };
299 fprintf (
stderr,
"%s[pid=%d]: fork to exec %s: %s: %s\n", program_invocation_short_name, pid,
300 child_argv[0], ereason.what.
c_str(),
strerror (ereason.error));
309 middlename +=
"XXXXXX";
324 atquit_run_terminate_handlers();
335 atquit_handlers.atquit_funcs.
push_back (func);
348AtquitHandlers::call_hooks()
351 while (atquit_funcs.
size())
365atquit_make_subreaper ()
369 if (prctl (PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) < 0)
370 warning (
"Ase: prctl(PR_SET_CHILD_SUBREAPER) failed: %s\n", strerror (errno));
374atquit_run_terminate_handlers ()
376 atquit_triggered_ =
true;
383 atquit_handlers.call_hooks();
386 for (
int step = 0; step < 10; step++) {
390 if (g_kill_pids.
empty())
399 if (!g_kill_pids.
empty()) {
410atquit_terminate (
int exitcode)
412 atquit_run_terminate_handlers();
419 return atquit_triggered_;
size_t erase_first(C &container, const std::function< bool(typename C::value_type const &value)> &pred)
Erase first element for which pred() is true in vector or list.
The Anklang C++ API namespace.
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
void atquit_add_removal(const std::string &filename)
Remove filename (or directory) when the program terminates.
void atquit_add_kill_pid(int pid)
Kill pid when the program terminates.
void atquit_del_kill_pid(int pid)
Undo a previous atquit_add_kill_pid() call.
void atquit_del_removal(const std::string &filename)
Undo a previous atquit_add_removal() call.
ErrorReason spawn_process(const std::vector< std::string > &argv, pid_t *child_pid, int pdeathsig, int stdio_fd, const std::vector< int > &keep_fds)
Spawn a child process after cleaning up the environment.
void cleanup_orphaned_tempfiles(const std::string &directory)
Delete all files that contain @TEMPFILE_PID=d@ without a running pid_t d.
std::string create_tempfile_dir(const std::string &basename)
Create temporary directory under /tmp, scheduled for removal atquit.
Cleanup list of child processes still running at exit.
void kill_all(int sig)
Send signal sig to all tracked PIDs.
void clear()
Clear the PID list after both kill phases are complete.
void reap()
Reap zombies and remove dead PIDs from the list.
bool empty()
Return true if no PIDs are tracked.
void collect_children(int sig)
Find previously orphaned child processes, optionally send signal.
static std::vector< pid_t > read_children()
Read orphaned grandchildren reparented to this process as subreaper.
Cleanup list of temporary files/dirs to be removed at exit.
T temp_directory_path(T... args)