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