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