1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2004-2011 Free Software Foundation Europe e.V.
5 Copyright (C) 2017-2019 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * Process and thread timer routines, built on top of watchdogs.
24 *
25 * Nic Bellamy <nic@bellamy.co.nz>, October 2004.
26 *
27 */
28
29 #include "include/bareos.h"
30 #include "include/jcr.h"
31 #include "lib/edit.h"
32 #include "lib/bsock.h"
33 #include "lib/btimers.h"
34 #include "lib/watchdog.h"
35
36 const int debuglevel = 900;
37
38 /* Forward referenced functions */
39 static void StopBtimer(btimer_t* wid);
40 static btimer_t* btimer_start_common(uint32_t wait);
41
42 /* Forward referenced callback functions */
43 static void CallbackChildTimer(watchdog_t* self);
44 static void CallbackThreadTimer(watchdog_t* self);
45
46 /*
47 * Start a timer on a child process of pid, kill it after wait seconds.
48 *
49 * Returns: btimer_t *(pointer to btimer_t struct) on success
50 * NULL on failure
51 */
start_child_timer(JobControlRecord * jcr,pid_t pid,uint32_t wait)52 btimer_t* start_child_timer(JobControlRecord* jcr, pid_t pid, uint32_t wait)
53 {
54 btimer_t* wid;
55
56 wid = btimer_start_common(wait);
57 if (wid == NULL) { return NULL; }
58 wid->type = TYPE_CHILD;
59 wid->pid = pid;
60 wid->killed = false;
61 wid->jcr = jcr;
62
63 wid->wd->callback = CallbackChildTimer;
64 wid->wd->one_shot = false;
65 wid->wd->interval = wait;
66 RegisterWatchdog(wid->wd);
67
68 Dmsg3(debuglevel, "Start child timer %p, pid %d for %d secs.\n", wid, pid,
69 wait);
70 return wid;
71 }
72
73 /*
74 * Stop child timer
75 */
StopChildTimer(btimer_t * wid)76 void StopChildTimer(btimer_t* wid)
77 {
78 if (wid == NULL) {
79 Dmsg0(debuglevel, "StopChildTimer called with NULL btimer_id\n");
80 return;
81 }
82 Dmsg2(debuglevel, "Stop child timer %p pid %d\n", wid, wid->pid);
83 StopBtimer(wid);
84 }
85
CallbackChildTimer(watchdog_t * self)86 static void CallbackChildTimer(watchdog_t* self)
87 {
88 btimer_t* wid = (btimer_t*)self->data;
89
90 if (!wid->killed) {
91 /* First kill attempt; try killing it softly (kill -SONG) first */
92 wid->killed = true;
93
94 Dmsg2(debuglevel, "watchdog %p term PID %d\n", self, wid->pid);
95
96 /* Kill -TERM the specified PID, and reschedule a -KILL for 5 seconds
97 * later. (Warning: this should let dvd-writepart enough time to term
98 * and kill growisofs, which takes 3 seconds, so the interval must not
99 * be less than 5 seconds)
100 */
101 kill(wid->pid, SIGTERM);
102 self->interval = 5;
103 } else {
104 /* This is the second call - Terminate with prejudice. */
105 Dmsg2(debuglevel, "watchdog %p kill PID %d\n", self, wid->pid);
106
107 kill(wid->pid, SIGKILL);
108
109 /* Setting one_shot to true before we leave ensures we don't get
110 * rescheduled.
111 */
112 self->one_shot = true;
113 }
114 }
115
116 /*
117 * Start a timer on a thread. kill it after wait seconds.
118 *
119 * Returns: btimer_t *(pointer to btimer_t struct) on success
120 * NULL on failure
121 */
start_thread_timer(JobControlRecord * jcr,pthread_t tid,uint32_t wait)122 btimer_t* start_thread_timer(JobControlRecord* jcr,
123 pthread_t tid,
124 uint32_t wait)
125 {
126 char ed1[50];
127 btimer_t* wid;
128
129 wid = btimer_start_common(wait);
130 if (wid == NULL) {
131 Dmsg1(debuglevel, "start_thread_timer return NULL from common. wait=%d.\n",
132 wait);
133 return NULL;
134 }
135
136 wid->type = TYPE_PTHREAD;
137 wid->tid = tid;
138 wid->jcr = jcr;
139 wid->wd->callback = CallbackThreadTimer;
140 wid->wd->one_shot = true;
141 wid->wd->interval = wait;
142 RegisterWatchdog(wid->wd);
143
144 Dmsg3(debuglevel, "Start thread timer %p tid %s for %d secs.\n", wid,
145 edit_pthread(tid, ed1, sizeof(ed1)), wait);
146
147 return wid;
148 }
149
150 /*
151 * Start a timer on a BareosSocket. kill it after wait seconds.
152 *
153 * Returns: btimer_t *(pointer to btimer_t struct) on success
154 * NULL on failure
155 */
StartBsockTimer(BareosSocket * bsock,uint32_t wait)156 btimer_t* StartBsockTimer(BareosSocket* bsock, uint32_t wait)
157 {
158 char ed1[50];
159 btimer_t* wid;
160
161 if (wait <= 0) { /* wait should be > 0 */
162 return NULL;
163 }
164
165 wid = btimer_start_common(wait);
166 if (wid == NULL) { return NULL; }
167
168 wid->type = TYPE_BSOCK;
169 wid->tid = pthread_self();
170 wid->bsock = bsock;
171 wid->jcr = bsock->jcr();
172
173 wid->wd->callback = CallbackThreadTimer;
174 wid->wd->one_shot = true;
175 wid->wd->interval = wait;
176 RegisterWatchdog(wid->wd);
177
178 Dmsg4(debuglevel, "Start bsock timer %p tid=%s for %d secs at %d\n", wid,
179 edit_pthread(wid->tid, ed1, sizeof(ed1)), wait, time(NULL));
180
181 return wid;
182 }
183
184 /*
185 * Stop bsock timer
186 */
StopBsockTimer(btimer_t * wid)187 void StopBsockTimer(btimer_t* wid)
188 {
189 char ed1[50];
190
191 if (wid == NULL) {
192 Dmsg0(900, "StopBsockTimer called with NULL btimer_id\n");
193 return;
194 }
195
196 Dmsg3(debuglevel, "Stop bsock timer %p tid=%s at %d.\n", wid,
197 edit_pthread(wid->tid, ed1, sizeof(ed1)), time(NULL));
198 StopBtimer(wid);
199 }
200
201
202 /*
203 * Stop thread timer
204 */
StopThreadTimer(btimer_t * wid)205 void StopThreadTimer(btimer_t* wid)
206 {
207 char ed1[50];
208
209 if (wid == NULL) {
210 Dmsg0(debuglevel, "StopThreadTimer called with NULL btimer_id\n");
211 return;
212 }
213
214 Dmsg2(debuglevel, "Stop thread timer %p tid=%s.\n", wid,
215 edit_pthread(wid->tid, ed1, sizeof(ed1)));
216 StopBtimer(wid);
217 }
218
CallbackThreadTimer(watchdog_t * self)219 static void CallbackThreadTimer(watchdog_t* self)
220 {
221 char ed1[50];
222 btimer_t* wid = (btimer_t*)self->data;
223
224 Dmsg4(debuglevel, "thread timer %p kill %s tid=%p at %d.\n", self,
225 wid->type == TYPE_BSOCK ? "bsock" : "thread",
226 edit_pthread(wid->tid, ed1, sizeof(ed1)), time(NULL));
227 if (wid->jcr) {
228 Dmsg2(debuglevel, "killed JobId=%u Job=%s\n", wid->jcr->JobId,
229 wid->jcr->Job);
230 }
231
232 if (wid->type == TYPE_BSOCK && wid->bsock) { wid->bsock->SetTimedOut(); }
233 pthread_kill(wid->tid, TIMEOUT_SIGNAL);
234 }
235
btimer_start_common(uint32_t wait)236 static btimer_t* btimer_start_common(uint32_t wait)
237 {
238 btimer_t* wid = (btimer_t*)malloc(sizeof(btimer_t));
239
240 wid->wd = new_watchdog();
241 if (wid->wd == NULL) {
242 free(wid);
243 return NULL;
244 }
245 wid->wd->data = wid;
246 wid->killed = FALSE;
247
248 return wid;
249 }
250
StopBtimer(btimer_t * wid)251 static void StopBtimer(btimer_t* wid)
252 {
253 if (wid == NULL) {
254 Emsg0(M_INFO, 0, _("StopBtimer called with NULL btimer_id\n"));
255 return;
256 }
257 if (wid->wd) {
258 UnregisterWatchdog(wid->wd);
259 free(wid->wd);
260 }
261 free(wid);
262 }
263