]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/job.c
Implement first cut of Copy Job
[bacula/bacula] / bacula / src / stored / job.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 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 John Walker.
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->JobType = JobType;
138    jcr->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
150    /*
151     * Pass back an authorization key for the File daemon
152     */
153    bsnprintf(seed, sizeof(seed), "%p%d", jcr, JobId);
154    make_session_key(auth_key, seed, 1);
155    dir->fsend(OKjob, jcr->VolSessionId, jcr->VolSessionTime, auth_key);
156    Dmsg2(100, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg);
157    jcr->sd_auth_key = bstrdup(auth_key);
158    memset(auth_key, 0, sizeof(auth_key));
159    generate_daemon_event(jcr, "JobStart");
160    return true;
161 }
162
163 bool run_cmd(JCR *jcr)
164 {
165    struct timeval tv;
166    struct timezone tz;
167    struct timespec timeout;
168    int errstat;
169
170    Dsm_check(1);
171    Dmsg1(200, "Run_cmd: %s\n", jcr->dir_bsock->msg);
172    /* The following jobs don't need the FD */
173    switch (jcr->JobType) {
174    case JT_COPY:
175    case JT_MIGRATE:
176    case JT_ARCHIVE:
177       jcr->authenticated = true;
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(050, "%s waiting %d sec for FD to contact SD key=%s\n",
190          jcr->Job, (int)me->client_wait, 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    V(mutex);
205
206    memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
207
208    if (jcr->authenticated && !job_canceled(jcr)) {
209       Dmsg1(100, "Running job %s\n", jcr->Job);
210       run_job(jcr);                   /* Run the job */
211    }
212    return false;
213 }
214
215 /*
216  * After receiving a connection (in dircmd.c) if it is
217  *   from the File daemon, this routine is called.
218  */
219 void handle_filed_connection(BSOCK *fd, char *job_name)
220 {
221    JCR *jcr;
222
223    bmicrosleep(0, 50000);             /* wait 50 millisecs */
224    if (!(jcr=get_jcr_by_full_name(job_name))) {
225       Jmsg1(NULL, M_FATAL, 0, _("FD connect failed: Job name not found: %s\n"), job_name);
226       Dmsg1(3, "**** Job \"%s\" not found", job_name);
227       return;
228    }
229
230    jcr->file_bsock = fd;
231    jcr->file_bsock->set_jcr(jcr);
232
233    Dmsg1(110, "Found Job %s\n", job_name);
234
235    if (jcr->authenticated) {
236       Jmsg2(jcr, M_FATAL, 0, _("Hey!!!! JobId %u Job %s already authenticated.\n"),
237          (uint32_t)jcr->JobId, jcr->Job);
238       free_jcr(jcr);
239       return;
240    }
241
242    /*
243     * Authenticate the File daemon
244     */
245    if (jcr->authenticated || !authenticate_filed(jcr)) {
246       Dmsg1(100, "Authentication failed Job %s\n", jcr->Job);
247       Jmsg(jcr, M_FATAL, 0, _("Unable to authenticate File daemon\n"));
248    } else {
249       jcr->authenticated = true;
250       Dmsg2(110, "OK Authentication jid=%u Job %s\n", (uint32_t)jcr->JobId, jcr->Job);
251    }
252
253    if (!jcr->authenticated) {
254       set_jcr_job_status(jcr, JS_ErrorTerminated);
255    }
256    pthread_cond_signal(&jcr->job_start_wait); /* wake waiting job */
257    free_jcr(jcr);
258    return;
259 }
260
261
262 #ifdef needed
263 /*
264  *   Query Device command from Director
265  *   Sends Storage Daemon's information on the device to the
266  *    caller (presumably the Director).
267  *   This command always returns "true" so that the line is
268  *    not closed on an error.
269  *
270  */
271 bool query_cmd(JCR *jcr)
272 {
273    POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
274    BSOCK *dir = jcr->dir_bsock;
275    DEVRES *device;
276    AUTOCHANGER *changer;
277    bool ok;
278
279    Dmsg1(100, "Query_cmd: %s", dir->msg);
280    ok = sscanf(dir->msg, query_device, dev_name.c_str()) == 1;
281    Dmsg1(100, "<dird: %s\n", dir->msg);
282    if (ok) {
283       unbash_spaces(dev_name);
284       foreach_res(device, R_DEVICE) {
285          /* Find resource, and make sure we were able to open it */
286          if (strcmp(dev_name.c_str(), device->hdr.name) == 0) {
287             if (!device->dev) {
288                device->dev = init_dev(jcr, device);
289             }
290             if (!device->dev) {
291                break;
292             }  
293             ok = dir_update_device(jcr, device->dev);
294             if (ok) {
295                ok = dir->fsend(OK_query);
296             } else {
297                dir->fsend(NO_query);
298             }
299             return ok;
300          }
301       }
302       foreach_res(changer, R_AUTOCHANGER) {
303          /* Find resource, and make sure we were able to open it */
304          if (strcmp(dev_name.c_str(), changer->hdr.name) == 0) {
305             if (!changer->device || changer->device->size() == 0) {
306                continue;              /* no devices */
307             }
308             ok = dir_update_changer(jcr, changer);
309             if (ok) {
310                ok = dir->fsend(OK_query);
311             } else {
312                dir->fsend(NO_query);
313             }
314             return ok;
315          }
316       }
317       /* If we get here, the device/autochanger was not found */
318       unbash_spaces(dir->msg);
319       pm_strcpy(jcr->errmsg, dir->msg);
320       dir->fsend(NO_device, dev_name.c_str());
321       Dmsg1(100, ">dird: %s\n", dir->msg);
322    } else {
323       unbash_spaces(dir->msg);
324       pm_strcpy(jcr->errmsg, dir->msg);
325       dir->fsend(BAD_query, jcr->errmsg);
326       Dmsg1(100, ">dird: %s\n", dir->msg);
327    }
328
329    return true;
330 }
331
332 #endif
333
334
335 /*
336  * Destroy the Job Control Record and associated
337  * resources (sockets).
338  */
339 void stored_free_jcr(JCR *jcr)
340 {
341    Dmsg1(900, "stored_free_jcr JobId=%u\n", jcr->JobId);
342    if (jcr->file_bsock) {
343       jcr->file_bsock->close();
344       jcr->file_bsock = NULL;
345    }
346    if (jcr->job_name) {
347       free_pool_memory(jcr->job_name);
348    }
349    if (jcr->client_name) {
350       free_memory(jcr->client_name);
351       jcr->client_name = NULL;
352    }
353    if (jcr->fileset_name) {
354       free_memory(jcr->fileset_name);
355    }
356    if (jcr->fileset_md5) {
357       free_memory(jcr->fileset_md5);
358    }
359    if (jcr->bsr) {
360       free_bsr(jcr->bsr);
361       jcr->bsr = NULL;
362    }
363    if (jcr->RestoreBootstrap) {
364       unlink(jcr->RestoreBootstrap);
365       free_pool_memory(jcr->RestoreBootstrap);
366       jcr->RestoreBootstrap = NULL;
367    }
368    if (jcr->next_dev || jcr->prev_dev) {
369       Emsg0(M_FATAL, 0, _("In free_jcr(), but still attached to device!!!!\n"));
370    }
371    pthread_cond_destroy(&jcr->job_start_wait);
372    if (jcr->dcrs) {
373       delete jcr->dcrs;
374    }
375    jcr->dcrs = NULL;
376
377    /* Avoid a double free */
378    if (jcr->dcr == jcr->read_dcr) {
379       jcr->read_dcr = NULL;
380    }
381    if (jcr->dcr) {
382       free_dcr(jcr->dcr);
383       jcr->dcr = NULL;
384    }
385    if (jcr->read_dcr) {
386       free_dcr(jcr->read_dcr);
387       jcr->read_dcr = NULL;
388    }
389
390    if (jcr->read_store) {
391       DIRSTORE *store;
392       foreach_alist(store, jcr->read_store) {
393          delete store->device;
394          delete store;
395       }
396       delete jcr->read_store;
397       jcr->read_store = NULL;
398    }
399    if (jcr->write_store) {
400       DIRSTORE *store;
401       foreach_alist(store, jcr->write_store) {
402          delete store->device;
403          delete store;
404       }
405       delete jcr->write_store;
406       jcr->write_store = NULL;
407    }
408    Dsm_check(1);
409
410    if (jcr->JobId != 0)
411       write_state_file(me->working_directory, "bacula-sd", get_first_port_host_order(me->sdaddrs));
412
413    return;
414 }