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