]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/btimers.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / lib / btimers.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * Process and thread timer routines, built on top of watchdogs.
21  *
22  *    Nic Bellamy <nic@bellamy.co.nz>, October 2004.
23  *
24 */
25
26 #include "bacula.h"
27 #include "jcr.h"
28
29 const int dbglvl = 900;
30
31 /* Forward referenced functions */
32 static void stop_btimer(btimer_t *wid);
33 static btimer_t *btimer_start_common(uint32_t wait);
34
35 /* Forward referenced callback functions */
36 static void callback_child_timer(watchdog_t *self);
37 static void callback_thread_timer(watchdog_t *self);
38 #ifdef xxx
39 static void destructor_thread_timer(watchdog_t *self);
40 static void destructor_child_timer(watchdog_t *self);
41 #endif
42
43 /*
44  * Start a timer on a child process of pid, kill it after wait seconds.
45  *
46  *  Returns: btimer_t *(pointer to btimer_t struct) on success
47  *           NULL on failure
48  */
49 btimer_t *start_child_timer(JCR *jcr, pid_t pid, uint32_t wait)
50 {
51    btimer_t *wid;
52
53    wid = btimer_start_common(wait);
54    if (wid == NULL) {
55       return NULL;
56    }
57    wid->type = TYPE_CHILD;
58    wid->pid = pid;
59    wid->killed = false;
60    wid->jcr = jcr;
61
62    wid->wd->callback = callback_child_timer;
63    wid->wd->one_shot = false;
64    wid->wd->interval = wait;
65    register_watchdog(wid->wd);
66
67    Dmsg3(dbglvl, "Start child timer %p, pid %d for %d secs.\n", wid, pid, wait);
68    return wid;
69 }
70
71 /*
72  * Stop child timer
73  */
74 void stop_child_timer(btimer_t *wid)
75 {
76    if (wid == NULL) {
77       return;
78    }
79    Dmsg2(dbglvl, "Stop child timer %p pid %d\n", wid, wid->pid);
80    stop_btimer(wid);
81 }
82
83 #ifdef xxx
84 static void destructor_child_timer(watchdog_t *self)
85 {
86    btimer_t *wid = (btimer_t *)self->data;
87    free(wid->wd);
88    free(wid);
89 }
90 #endif
91
92 static void callback_child_timer(watchdog_t *self)
93 {
94    btimer_t *wid = (btimer_t *)self->data;
95
96    if (!wid->killed) {
97       /* First kill attempt; try killing it softly (kill -SONG) first */
98       wid->killed = true;
99
100       Dmsg2(dbglvl, "watchdog %p term PID %d\n", self, wid->pid);
101
102       /* Kill -TERM the specified PID, and reschedule a -KILL for 5 seconds
103        * later. (Warning: this should let dvd-writepart enough time to term
104        * and kill growisofs, which takes 3 seconds, so the interval must not
105        * be less than 5 seconds)
106        */
107       kill(wid->pid, SIGTERM);
108       self->interval = 10;
109    } else {
110       /* This is the second call - terminate with prejudice. */
111       Dmsg2(dbglvl, "watchdog %p kill PID %d\n", self, wid->pid);
112
113       kill(wid->pid, SIGKILL);
114
115       /* Setting one_shot to true before we leave ensures we don't get
116        * rescheduled.
117        */
118       self->one_shot = true;
119    }
120 }
121
122 /*
123  * Start a timer on a thread. kill it after wait seconds.
124  *
125  *  Returns: btimer_t *(pointer to btimer_t struct) on success
126  *           NULL on failure
127  */
128 btimer_t *start_thread_timer(JCR *jcr, pthread_t tid, uint32_t wait)
129 {
130    btimer_t *wid;
131    wid = btimer_start_common(wait);
132    if (wid == NULL) {
133       Dmsg1(dbglvl, "start_thread_timer return NULL from common. wait=%d.\n", wait);
134       return NULL;
135    }
136    wid->type = TYPE_PTHREAD;
137    wid->tid = tid;
138    wid->jcr = jcr;
139
140    wid->wd->callback = callback_thread_timer;
141    wid->wd->one_shot = true;
142    wid->wd->interval = wait;
143    register_watchdog(wid->wd);
144
145    Dmsg3(dbglvl, "Start thread timer %p tid %p for %d secs.\n", wid, tid, wait);
146
147    return wid;
148 }
149
150 /*
151  * Start a timer on a BSOCK. kill it after wait seconds.
152  *
153  *  Returns: btimer_t *(pointer to btimer_t struct) on success
154  *           NULL on failure
155  */
156 btimer_t *start_bsock_timer(BSOCK *bsock, uint32_t wait)
157 {
158    btimer_t *wid;
159    if (wait <= 0) {                 /* wait should be > 0 */
160       return NULL;
161    }
162    wid = btimer_start_common(wait);
163    if (wid == NULL) {
164       return NULL;
165    }
166    wid->type = TYPE_BSOCK;
167    wid->tid = pthread_self();
168    wid->bsock = bsock;
169    wid->jcr = bsock->jcr();
170
171    wid->wd->callback = callback_thread_timer;
172    wid->wd->one_shot = true;
173    wid->wd->interval = wait;
174    register_watchdog(wid->wd);
175
176    Dmsg4(dbglvl, "Start bsock timer %p tid=%p for %d secs at %d\n", wid,
177          wid->tid, wait, time(NULL));
178
179    return wid;
180 }
181
182 /*
183  * Stop bsock timer
184  */
185 void stop_bsock_timer(btimer_t *wid)
186 {
187    if (wid == NULL) {
188       return;
189    }
190    Dmsg3(dbglvl, "Stop bsock timer %p tid=%p at %d.\n", wid, wid->tid, time(NULL));
191    stop_btimer(wid);
192 }
193
194
195 /*
196  * Stop thread timer
197  */
198 void stop_thread_timer(btimer_t *wid)
199 {
200    if (wid == NULL) {
201       return;
202    }
203    Dmsg2(dbglvl, "Stop thread timer %p tid=%p.\n", wid, wid->tid);
204    stop_btimer(wid);
205 }
206
207 #ifdef xxx
208 static void destructor_thread_timer(watchdog_t *self)
209 {
210    btimer_t *wid = (btimer_t *)self->data;
211    free(wid->wd);
212    free(wid);
213 }
214 #endif
215
216 static void callback_thread_timer(watchdog_t *self)
217 {
218    btimer_t *wid = (btimer_t *)self->data;
219
220    Dmsg4(dbglvl, "thread timer %p kill %s tid=%p at %d.\n", self,
221       wid->type == TYPE_BSOCK ? "bsock" : "thread", wid->tid, time(NULL));
222    if (wid->jcr) {
223       Dmsg2(dbglvl, "killed jid=%u Job=%s\n", wid->jcr->JobId, wid->jcr->Job);
224    }
225
226    if (wid->type == TYPE_BSOCK && wid->bsock) {
227       wid->bsock->set_timed_out();
228    }
229    pthread_kill(wid->tid, TIMEOUT_SIGNAL);
230 }
231
232 static btimer_t *btimer_start_common(uint32_t wait)
233 {
234    btimer_t *wid = (btimer_t *)malloc(sizeof(btimer_t));
235
236    wid->wd = new_watchdog();
237    if (wid->wd == NULL) {
238       free(wid);
239       return NULL;
240    }
241    wid->wd->data = wid;
242    wid->killed = false;
243
244    return wid;
245 }
246
247 /*
248  * Stop btimer
249  */
250 static void stop_btimer(btimer_t *wid)
251 {
252    if (wid == NULL) {
253       Emsg0(M_ABORT, 0, _("stop_btimer called with NULL btimer_id\n"));
254    }
255    unregister_watchdog(wid->wd);
256    free(wid->wd);
257    free(wid);
258 }