]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/job.c
Add additional mysql connection debug code
[bacula/bacula] / bacula / src / stored / job.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Job control and execution for Storage Daemon
30  *
31  *   Kern Sibbald, MM
32  *
33  *   Version $Id$
34  *
35  */
36
37 #include "bacula.h"
38 #include "stored.h"
39
40 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
41
42 /* Imported variables */
43 extern uint32_t VolSessionTime;
44
45 /* Imported functions */
46 extern uint32_t newVolSessionId();
47 extern bool do_mac(JCR *jcr);
48
49 /* Requests from the Director daemon */
50 static char jobcmd[] = "JobId=%d job=%127s job_name=%127s client_name=%127s "
51       "type=%d level=%d FileSet=%127s NoAttr=%d SpoolAttr=%d FileSetMD5=%127s "
52       "SpoolData=%d WritePartAfterJob=%d PreferMountedVols=%d SpoolSize=%s\n";
53 static char oldjobcmd[] = "JobId=%d job=%127s job_name=%127s client_name=%127s "
54       "type=%d level=%d FileSet=%127s NoAttr=%d SpoolAttr=%d FileSetMD5=%127s "
55       "SpoolData=%d WritePartAfterJob=%d PreferMountedVols=%d\n";
56
57
58
59 /* Responses sent to Director daemon */
60 static char OKjob[]     = "3000 OK Job SDid=%u SDtime=%u Authorization=%s\n";
61 static char BAD_job[]   = "3915 Bad Job command. stat=%d CMD: %s\n";
62 //static char OK_query[]  = "3001 OK query\n";
63 //static char NO_query[]  = "3918 Query failed\n";
64 //static char BAD_query[] = "3917 Bad query command: %s\n";
65
66 /*
67  * Director requests us to start a job
68  * Basic tasks done here:
69  *  - We pickup the JobId to be run from the Director.
70  *  - We pickup the device, media, and pool from the Director
71  *  - Wait for a connection from the File Daemon (FD)
72  *  - Accept commands from the FD (i.e. run the job)
73  *  - Return when the connection is terminated or
74  *    there is an error.
75  */
76 bool job_cmd(JCR *jcr)
77 {
78    int JobId;
79    char auth_key[100];
80    char spool_size[30];
81    char seed[100];
82    BSOCK *dir = jcr->dir_bsock;
83    POOL_MEM job_name, client_name, job, fileset_name, fileset_md5;
84    int JobType, level, spool_attributes, no_attributes, spool_data;
85    int write_part_after_job, PreferMountedVols;
86    int stat;
87    JCR *ojcr;
88
89    /*
90     * Get JobId and permissions from Director
91     */
92    Dmsg1(100, "<dird: %s", dir->msg);
93    bstrncpy(spool_size, "0", sizeof(spool_size));
94    stat = sscanf(dir->msg, jobcmd, &JobId, job.c_str(), job_name.c_str(),
95               client_name.c_str(),
96               &JobType, &level, fileset_name.c_str(), &no_attributes,
97               &spool_attributes, fileset_md5.c_str(), &spool_data,
98               &write_part_after_job, &PreferMountedVols, spool_size);
99    if (stat != 14) {
100       /* Try old version */
101       stat = sscanf(dir->msg, oldjobcmd, &JobId, job.c_str(), job_name.c_str(),
102               client_name.c_str(),
103               &JobType, &level, fileset_name.c_str(), &no_attributes,
104               &spool_attributes, fileset_md5.c_str(), &spool_data,
105               &write_part_after_job, &PreferMountedVols);
106       if (stat != 13) {
107          pm_strcpy(jcr->errmsg, dir->msg);
108          dir->fsend(BAD_job, stat, jcr->errmsg);
109          Dmsg1(100, ">dird: %s", dir->msg);
110          set_jcr_job_status(jcr, JS_ErrorTerminated);
111          return false;
112       }
113    }
114    /*
115     * Since this job could be rescheduled, we
116     *  check to see if we have it already. If so
117     *  free the old jcr and use the new one.
118     */
119    ojcr = get_jcr_by_full_name(job.c_str());
120    if (ojcr && !ojcr->authenticated) {
121       Dmsg2(100, "Found ojcr=0x%x Job %s\n", (unsigned)(long)ojcr, job.c_str());
122       free_jcr(ojcr);
123    }
124    jcr->JobId = JobId;
125    jcr->VolSessionId = newVolSessionId();
126    jcr->VolSessionTime = VolSessionTime;
127    bstrncpy(jcr->Job, job, sizeof(jcr->Job));
128    unbash_spaces(job_name);
129    jcr->job_name = get_pool_memory(PM_NAME);
130    pm_strcpy(jcr->job_name, job_name);
131    unbash_spaces(client_name);
132    jcr->client_name = get_pool_memory(PM_NAME);
133    pm_strcpy(jcr->client_name, client_name);
134    unbash_spaces(fileset_name);
135    jcr->fileset_name = get_pool_memory(PM_NAME);
136    pm_strcpy(jcr->fileset_name, fileset_name);
137    jcr->set_JobType(JobType);
138    jcr->set_JobLevel(level);
139    jcr->no_attributes = no_attributes;
140    jcr->spool_attributes = spool_attributes;
141    jcr->spool_data = spool_data;
142    jcr->spool_size = str_to_int64(spool_size);
143    jcr->write_part_after_job = write_part_after_job;
144    jcr->fileset_md5 = get_pool_memory(PM_NAME);
145    pm_strcpy(jcr->fileset_md5, fileset_md5);
146    jcr->PreferMountedVols = PreferMountedVols;
147
148    jcr->authenticated = false;
149    jcr->need_fd = true;
150
151    /*
152     * Pass back an authorization key for the File daemon
153     */
154    bsnprintf(seed, sizeof(seed), "%p%d", jcr, JobId);
155    make_session_key(auth_key, seed, 1);
156    dir->fsend(OKjob, jcr->VolSessionId, jcr->VolSessionTime, auth_key);
157    Dmsg2(50, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg);
158    jcr->sd_auth_key = bstrdup(auth_key);
159    memset(auth_key, 0, sizeof(auth_key));
160    generate_daemon_event(jcr, "JobStart");
161    return true;
162 }
163
164 bool run_cmd(JCR *jcr)
165 {
166    struct timeval tv;
167    struct timezone tz;
168    struct timespec timeout;
169    int errstat = 0;
170
171    Dsm_check(1);
172    Dmsg1(200, "Run_cmd: %s\n", jcr->dir_bsock->msg);
173
174    /* If we do not need the FD, we are doing a migrate, copy, or virtual
175     *   backup.
176     */
177    if (!jcr->need_fd) {
178       do_mac(jcr);
179       return false;
180    }
181
182    set_jcr_job_status(jcr, JS_WaitFD);          /* wait for FD to connect */
183    dir_send_job_status(jcr);
184
185    gettimeofday(&tv, &tz);
186    timeout.tv_nsec = tv.tv_usec * 1000;
187    timeout.tv_sec = tv.tv_sec + me->client_wait;
188
189    Dmsg3(50, "%s waiting %d sec for FD to contact SD key=%s\n",
190          jcr->Job, (int)(timeout.tv_sec-time(NULL)), jcr->sd_auth_key);
191
192    /*
193     * Wait for the File daemon to contact us to start the Job,
194     *  when he does, we will be released, unless the 30 minutes
195     *  expires.
196     */
197    P(mutex);
198    while ( !jcr->authenticated && !job_canceled(jcr) ) {
199       errstat = pthread_cond_timedwait(&jcr->job_start_wait, &mutex, &timeout);
200       if (errstat == ETIMEDOUT || errstat == EINVAL || errstat == EPERM) {
201          break;
202       }
203    }
204    Dmsg3(50, "Auth=%d canceled=%d errstat=%d\n", jcr->authenticated,
205       job_canceled(jcr), errstat);
206    V(mutex);
207
208    memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
209
210    if (jcr->authenticated && !job_canceled(jcr)) {
211       Dmsg1(50, "Running job %s\n", jcr->Job);
212       run_job(jcr);                   /* Run the job */
213    }
214    return false;
215 }
216
217 /*
218  * After receiving a connection (in dircmd.c) if it is
219  *   from the File daemon, this routine is called.
220  */
221 void handle_filed_connection(BSOCK *fd, char *job_name)
222 {
223    JCR *jcr;
224
225 /*
226  * With the following bmicrosleep on, running the 
227  * SD under the debugger fails.   
228  */ 
229 // bmicrosleep(0, 50000);             /* wait 50 millisecs */
230    if (!(jcr=get_jcr_by_full_name(job_name))) {
231       Jmsg1(NULL, M_FATAL, 0, _("FD connect failed: Job name not found: %s\n"), job_name);
232       Dmsg1(3, "**** Job \"%s\" not found.\n", job_name);
233       fd->close();
234       return;
235    }
236
237
238    Dmsg1(50, "Found Job %s\n", job_name);
239
240    if (jcr->authenticated) {
241       Jmsg2(jcr, M_FATAL, 0, _("Hey!!!! JobId %u Job %s already authenticated.\n"),
242          (uint32_t)jcr->JobId, jcr->Job);
243       Dmsg2(50, "Hey!!!! JobId %u Job %s already authenticated.\n",
244          (uint32_t)jcr->JobId, jcr->Job);
245       fd->close();
246       free_jcr(jcr);
247       return;
248    }
249
250    jcr->file_bsock = fd;
251    jcr->file_bsock->set_jcr(jcr);
252
253    /*
254     * Authenticate the File daemon
255     */
256    if (jcr->authenticated || !authenticate_filed(jcr)) {
257       Dmsg1(50, "Authentication failed Job %s\n", jcr->Job);
258       Jmsg(jcr, M_FATAL, 0, _("Unable to authenticate File daemon\n"));
259    } else {
260       jcr->authenticated = true;
261       Dmsg2(50, "OK Authentication jid=%u Job %s\n", (uint32_t)jcr->JobId, jcr->Job);
262    }
263
264    if (!jcr->authenticated) {
265       set_jcr_job_status(jcr, JS_ErrorTerminated);
266    }
267    pthread_cond_signal(&jcr->job_start_wait); /* wake waiting job */
268    free_jcr(jcr);
269    return;
270 }
271
272
273 #ifdef needed
274 /*
275  *   Query Device command from Director
276  *   Sends Storage Daemon's information on the device to the
277  *    caller (presumably the Director).
278  *   This command always returns "true" so that the line is
279  *    not closed on an error.
280  *
281  */
282 bool query_cmd(JCR *jcr)
283 {
284    POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
285    BSOCK *dir = jcr->dir_bsock;
286    DEVRES *device;
287    AUTOCHANGER *changer;
288    bool ok;
289
290    Dmsg1(100, "Query_cmd: %s", dir->msg);
291    ok = sscanf(dir->msg, query_device, dev_name.c_str()) == 1;
292    Dmsg1(100, "<dird: %s\n", dir->msg);
293    if (ok) {
294       unbash_spaces(dev_name);
295       foreach_res(device, R_DEVICE) {
296          /* Find resource, and make sure we were able to open it */
297          if (strcmp(dev_name.c_str(), device->hdr.name) == 0) {
298             if (!device->dev) {
299                device->dev = init_dev(jcr, device);
300             }
301             if (!device->dev) {
302                break;
303             }  
304             ok = dir_update_device(jcr, device->dev);
305             if (ok) {
306                ok = dir->fsend(OK_query);
307             } else {
308                dir->fsend(NO_query);
309             }
310             return ok;
311          }
312       }
313       foreach_res(changer, R_AUTOCHANGER) {
314          /* Find resource, and make sure we were able to open it */
315          if (strcmp(dev_name.c_str(), changer->hdr.name) == 0) {
316             if (!changer->device || changer->device->size() == 0) {
317                continue;              /* no devices */
318             }
319             ok = dir_update_changer(jcr, changer);
320             if (ok) {
321                ok = dir->fsend(OK_query);
322             } else {
323                dir->fsend(NO_query);
324             }
325             return ok;
326          }
327       }
328       /* If we get here, the device/autochanger was not found */
329       unbash_spaces(dir->msg);
330       pm_strcpy(jcr->errmsg, dir->msg);
331       dir->fsend(NO_device, dev_name.c_str());
332       Dmsg1(100, ">dird: %s\n", dir->msg);
333    } else {
334       unbash_spaces(dir->msg);
335       pm_strcpy(jcr->errmsg, dir->msg);
336       dir->fsend(BAD_query, jcr->errmsg);
337       Dmsg1(100, ">dird: %s\n", dir->msg);
338    }
339
340    return true;
341 }
342
343 #endif
344
345
346 /*
347  * Destroy the Job Control Record and associated
348  * resources (sockets).
349  */
350 void stored_free_jcr(JCR *jcr)
351 {
352    Dmsg1(900, "stored_free_jcr JobId=%u\n", jcr->JobId);
353    if (jcr->file_bsock) {
354       jcr->file_bsock->close();
355       jcr->file_bsock = NULL;
356    }
357    if (jcr->job_name) {
358       free_pool_memory(jcr->job_name);
359    }
360    if (jcr->client_name) {
361       free_memory(jcr->client_name);
362       jcr->client_name = NULL;
363    }
364    if (jcr->fileset_name) {
365       free_memory(jcr->fileset_name);
366    }
367    if (jcr->fileset_md5) {
368       free_memory(jcr->fileset_md5);
369    }
370    if (jcr->bsr) {
371       free_bsr(jcr->bsr);
372       jcr->bsr = NULL;
373    }
374    /* Free any restore volume list created */
375    free_restore_volume_list(jcr);
376    if (jcr->RestoreBootstrap) {
377       unlink(jcr->RestoreBootstrap);
378       free_pool_memory(jcr->RestoreBootstrap);
379       jcr->RestoreBootstrap = NULL;
380    }
381    if (jcr->next_dev || jcr->prev_dev) {
382       Emsg0(M_FATAL, 0, _("In free_jcr(), but still attached to device!!!!\n"));
383    }
384    pthread_cond_destroy(&jcr->job_start_wait);
385    if (jcr->dcrs) {
386       delete jcr->dcrs;
387    }
388    jcr->dcrs = NULL;
389
390    /* Avoid a double free */
391    if (jcr->dcr == jcr->read_dcr) {
392       jcr->read_dcr = NULL;
393    }
394    if (jcr->dcr) {
395       free_dcr(jcr->dcr);
396       jcr->dcr = NULL;
397    }
398    if (jcr->read_dcr) {
399       free_dcr(jcr->read_dcr);
400       jcr->read_dcr = NULL;
401    }
402
403    if (jcr->read_store) {
404       DIRSTORE *store;
405       foreach_alist(store, jcr->read_store) {
406          delete store->device;
407          delete store;
408       }
409       delete jcr->read_store;
410       jcr->read_store = NULL;
411    }
412    if (jcr->write_store) {
413       DIRSTORE *store;
414       foreach_alist(store, jcr->write_store) {
415          delete store->device;
416          delete store;
417       }
418       delete jcr->write_store;
419       jcr->write_store = NULL;
420    }
421    Dsm_check(1);
422
423    if (jcr->JobId != 0)
424       write_state_file(me->working_directory, "bacula-sd", get_first_port_host_order(me->sdaddrs));
425
426    return;
427 }