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