1 /*************************************************************************** 2 * Copyright (C) 2015-2020 by Mihai Moldovan <ionic@ionic.de> * 3 * * 4 * This program is free software; you can redistribute it and/or modify * 5 * it under the terms of the GNU General Public License as published by * 6 * the Free Software Foundation; either version 2 of the License, or * 7 * (at your option) any later version. * 8 * * 9 * This program is distributed in the hope that it will be useful, * 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 12 * GNU General Public License for more details. * 13 * * 14 * You should have received a copy of the GNU General Public License * 15 * along with this program; if not, write to the * 16 * Free Software Foundation, Inc., * 17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 18 ***************************************************************************/ 19 20 /* Necessary for Q_OS_UNIX! */ 21 22 #include "unixhelper.h" 23 24 #ifdef Q_OS_UNIX 25 26 #include <csignal> 27 #include <unistd.h> 28 #include <iostream> 29 #include <cstring> 30 #include <cerrno> 31 #include <vector> 32 #include <cstdlib> 33 #include <cstdio> 34 35 /* For documentation please see unixhelper.h. */ 36 37 namespace unixhelper { kill_pgroup(const int signal)38 void kill_pgroup (const int signal) { 39 pid_t pgid_to_kill = getpgrp (); 40 41 if ((SIGHUP == signal) || (-1 == signal)) { 42 /* 43 * In order to not kill ourselves, we need to run this 44 * code in a new process group. 45 */ 46 pid_t tmp_pid = fork (); 47 48 /* Child. */ 49 if (0 == tmp_pid) { 50 /* Create new pgid. */ 51 int err = setpgid (0, 0); 52 53 if (0 != err) { 54 std::perror ("WARNING: unable to change PGID"); 55 std::cerr << "Continuing with normal operation, but process might kill itself before tree vanishes." << std::endl; 56 } 57 58 real_kill_pgroup (pgid_to_kill); 59 } 60 /* Error. */ 61 else if (-1 == tmp_pid) { 62 std::perror ("WARNING: unable to fork off another process to kill original process group"); 63 std::cerr << "Proceeding with normal operation, but process might kill itself before tree vanishes." << std::endl; 64 65 real_kill_pgroup (pgid_to_kill); 66 } 67 /* Parent. */ 68 else { 69 /* 70 * No need to do anything, just exit here in order to not 71 * spawn a bunch of new processes due to subsequent calls 72 * to kill_pgroup () from unix_cleanup (). 73 */ 74 std::exit (EXIT_SUCCESS); 75 } 76 } 77 } 78 real_kill_pgroup(const pid_t pgid)79 void real_kill_pgroup (const pid_t pgid) { 80 /* Try to kill via SIGTERM first. */ 81 if (0 != killpg (pgid, SIGTERM)) { 82 const int saved_errno = errno; 83 std::cerr << "WARNING: unable to send SIGTERM to process group '" << pgid << "': " << std::strerror (saved_errno) << std::endl; 84 } 85 86 /* Grant a grace period of (at least) 10 seconds. */ 87 sleep (10); 88 89 int kill_ret = killpg (pgid, SIGKILL); 90 91 /* 92 * Might be unreachable. 93 * Depending upon which pgroup we just killed, this 94 * code is either unreachable (because killpg () killed 95 * itself already), or being executed. 96 * Let's handle errors and exit, if necessary. 97 */ 98 if (0 != kill_ret) { 99 const int saved_errno = errno; 100 std::cerr << "WARNING: failed to kill process group '" << pgid << "': " << std::strerror (saved_errno) << std::endl; 101 } 102 103 std::exit (EXIT_SUCCESS); 104 } 105 unix_cleanup(const pid_t parent)106 int unix_cleanup (const pid_t parent) { 107 /* 108 * Unblock all signals first. 109 * Signal blocks are inherited, so you never you what is currently set. 110 */ 111 sigset_t empty_set; 112 if (0 != sigemptyset (&empty_set)) { 113 const int saved_errno = errno; 114 std::cerr << "Unable to fetch empty signal set: " << std::strerror (saved_errno) << std::endl; 115 kill_pgroup (-1); 116 117 /* Anything here shall be unreachable. */ 118 } 119 120 if (0 != sigprocmask (SIG_SETMASK, &empty_set, NULL)) { 121 const int saved_errno = errno; 122 std::cerr << "Unable to set empty signal set: " << std::strerror (saved_errno) << std::endl; 123 kill_pgroup (-1); 124 125 /* Anything here shall be unreachable. */ 126 } 127 128 std::vector<int> ignore_signals; 129 ignore_signals.push_back (SIGINT); 130 ignore_signals.push_back (SIGTERM); 131 ignore_signals.push_back (SIGPIPE); 132 ignore_signals.push_back (SIGQUIT); 133 ignore_signals.push_back (SIGUSR1); 134 ignore_signals.push_back (SIGUSR2); 135 136 for (std::vector<int>::iterator it = ignore_signals.begin (); it != ignore_signals.end (); ++it) { 137 struct sigaction sig_action; 138 sig_action.sa_handler = SIG_IGN; 139 sig_action.sa_mask = empty_set; 140 sig_action.sa_flags = SA_RESTART; 141 142 /* Set up signal handler to ignore the current signal. */ 143 if (0 != sigaction (*it, &sig_action, NULL)) { 144 const int saved_errno = errno; 145 std::cerr << "Unable to ignore signal " << strsignal (*it) << ": " << std::strerror (saved_errno) << std::endl; 146 kill_pgroup (-1); 147 148 /* Anything here shall be unreachable. */ 149 } 150 } 151 152 { 153 struct sigaction sig_action; 154 sig_action.sa_handler = kill_pgroup; 155 sig_action.sa_mask = empty_set; 156 sig_action.sa_flags = SA_RESTART; 157 158 if (0 != sigaction (SIGHUP, &sig_action, NULL)) { 159 const int saved_errno = errno; 160 std::cerr << "Unable to set up signal handler for SIGHUP: " << std::strerror (saved_errno) << std::endl; 161 kill_pgroup (-1); 162 163 /* Anything here shall be unreachable. */ 164 } 165 } 166 167 /* Sleep forever... at least two seconds in each run. */ 168 for (;;) { 169 pid_t cur_ppid = getppid (); 170 171 /* cur_ppid should match parent, otherwise the parent died. */ 172 if (cur_ppid != parent) { 173 kill_pgroup (SIGHUP); 174 } 175 176 sleep (2); 177 } 178 179 /* 180 * Anything here shall be unreachable. 181 * But make compilers happy by returning something. 182 */ 183 return (EXIT_SUCCESS); 184 } 185 } 186 187 #endif /* defined (Q_OS_UNIX) */ 188