]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
- Remove xpg4 lib from FreeBSD build as it is no longer needed and
[bacula/bacula] / bacula / src / dird / migrate.c
1 /*
2  *
3  *   Bacula Director -- migrate.c -- responsible for doing
4  *     migration jobs.
5  *
6  *     Kern Sibbald, September MMIV
7  *
8  *  Basic tasks done here:
9  *     Open DB and create records for this job.
10  *     Open Message Channel with Storage daemon to tell him a job will be starting.
11  *     Open connection with Storage daemon and pass him commands
12  *       to do the backup.
13  *     When the Storage daemon finishes the job, update the DB.
14  *
15  *   Version $Id$
16  */
17 /*
18    Copyright (C) 2004-2006 Kern Sibbald
19
20    This program is free software; you can redistribute it and/or
21    modify it under the terms of the GNU General Public License
22    version 2 as amended with additional clauses defined in the
23    file LICENSE in the main source directory.
24
25    This program is distributed in the hope that it will be useful,
26    but WITHOUT ANY WARRANTY; without even the implied warranty of
27    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
28    the file LICENSE for additional details.
29
30  */
31
32 #include "bacula.h"
33 #include "dird.h"
34 #include "ua.h"
35 #ifndef HAVE_REGEX_H
36 #include "lib/bregex.h"
37 #else
38 #include <regex.h>
39 #endif
40
41 static char OKbootstrap[] = "3000 OK bootstrap\n";
42 static bool get_job_to_migrate(JCR *jcr);
43 struct idpkt;
44 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
45                  const char *query2, const char *type);
46 static void start_migration_job(JCR *jcr);
47
48 /* 
49  * Called here before the job is run to do the job
50  *   specific setup.
51  */
52 bool do_migration_init(JCR *jcr)
53 {
54    POOL_DBR pr;
55
56    /* If we find a job to migrate it is previous_jr.JobId */
57    if (!get_job_to_migrate(jcr)) {
58       return false;
59    }
60
61    if (jcr->previous_jr.JobId == 0) {
62       return true;                    /* no work */
63    }
64
65    if (!get_or_create_fileset_record(jcr)) {
66       return false;
67    }
68
69    /*
70     * Get the Pool record -- first apply any level defined pools
71     */
72    switch (jcr->previous_jr.JobLevel) {
73    case L_FULL:
74       if (jcr->full_pool) {
75          jcr->pool = jcr->full_pool;
76       }
77       break;
78    case L_INCREMENTAL:
79       if (jcr->inc_pool) {
80          jcr->pool = jcr->inc_pool;
81       }
82       break;
83    case L_DIFFERENTIAL:
84       if (jcr->dif_pool) {
85          jcr->pool = jcr->dif_pool;
86       }
87       break;
88    }
89    memset(&pr, 0, sizeof(pr));
90    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
91
92    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
93       /* Try to create the pool */
94       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
95          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
96             db_strerror(jcr->db));
97          return false;
98       } else {
99          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
100       }
101    }
102    jcr->jr.PoolId = pr.PoolId;
103
104    /* If pool storage specified, use it instead of job storage */
105    copy_storage(jcr, jcr->pool->storage);
106
107    if (!jcr->storage) {
108       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
109       return false;
110    }
111
112    if (!create_restore_bootstrap_file(jcr)) {
113       return false;
114    }
115    return true;
116 }
117
118 /*
119  * Do a Migration of a previous job
120  *
121  *  Returns:  false on failure
122  *            true  on success
123  */
124 bool do_migration(JCR *jcr)
125 {
126    POOL_DBR pr;
127    POOL *pool;
128    char ed1[100];
129    BSOCK *sd;
130    JOB *job, *prev_job;
131    JCR *prev_jcr;
132
133    if (jcr->previous_jr.JobId == 0) {
134       set_jcr_job_status(jcr, JS_Terminated);
135       migration_cleanup(jcr, jcr->JobStatus);
136       return true;                    /* no work */
137    }
138
139    Dmsg4(000, "Previous:: Name=%s JobId=%d Type=%c Level=%c\n",
140       jcr->previous_jr.Name, jcr->previous_jr.JobId, 
141       jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
142
143    Dmsg4(000, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
144       jcr->jr.Name, jcr->jr.JobId, 
145       jcr->jr.JobType, jcr->jr.JobLevel);
146
147    LockRes();
148    job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
149    prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
150    UnlockRes();
151    if (!job || !prev_job) {
152       return false;
153    }
154
155    /* 
156     *  prev_jcr is the new Job that corresponds to the original
157     *  job. It "runs" at the same time as the current 
158     *  migration job and becomes a new backup job that replaces
159     *  the original backup job.  Most operations on the current
160     *  migration jcr are also done on the prev_jcr.
161     */
162    prev_jcr = jcr->previous_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
163    memcpy(&prev_jcr->previous_jr, &jcr->previous_jr, sizeof(prev_jcr->previous_jr));
164
165    /* Turn the prev_jcr into a "real" job */
166    set_jcr_defaults(prev_jcr, prev_job);
167    if (!setup_job(prev_jcr)) {
168       return false;
169    }
170    /* Set output PoolId and FileSetId. */
171    prev_jcr->jr.PoolId = jcr->jr.PoolId;
172    prev_jcr->jr.FileSetId = jcr->jr.FileSetId;
173
174    /*
175     * Get the PoolId used with the original job. Then
176     *  find the pool name from the database record.
177     */
178    memset(&pr, 0, sizeof(pr));
179    pr.PoolId = prev_jcr->previous_jr.PoolId;
180    if (!db_get_pool_record(jcr, jcr->db, &pr)) {
181       Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
182             edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
183          return false;
184    }
185    /* Get the pool resource corresponding to the original job */
186    pool = (POOL *)GetResWithName(R_POOL, pr.Name);
187    if (!pool) {
188       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
189       return false;
190    }
191
192    /* Check Migration time and High/Low water marks */
193    /* ***FIXME*** */
194
195    /* If pool storage specified, use it for restore */
196    copy_storage(prev_jcr, pool->storage);
197
198    /* If the original backup pool has a NextPool, make sure a 
199     *  record exists in the database.
200     */
201    if (pool->NextPool) {
202       memset(&pr, 0, sizeof(pr));
203       bstrncpy(pr.Name, pool->NextPool->hdr.name, sizeof(pr.Name));
204
205       while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
206          /* Try to create the pool */
207          if (create_pool(jcr, jcr->db, pool->NextPool, POOL_OP_CREATE) < 0) {
208             Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. %s"), pr.Name,
209                db_strerror(jcr->db));
210             return false;
211          } else {
212             Jmsg(jcr, M_INFO, 0, _("Pool \"%s\" created in database.\n"), pr.Name);
213          }
214       }
215       /*
216        * put the "NextPool" resource pointer in our jcr so that we
217        * can pull the Storage reference from it.
218        */
219       prev_jcr->pool = jcr->pool = pool->NextPool;
220       prev_jcr->jr.PoolId = jcr->jr.PoolId = pr.PoolId;
221    }
222
223    /* If pool storage specified, use it instead of job storage for backup */
224    copy_storage(jcr, jcr->pool->storage);
225
226    /* Print Job Start message */
227    Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
228         edit_uint64(jcr->JobId, ed1), jcr->Job);
229
230    set_jcr_job_status(jcr, JS_Running);
231    set_jcr_job_status(prev_jcr, JS_Running);
232    Dmsg2(000, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
233    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
234       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
235       return false;
236    }
237
238    if (!db_update_job_start_record(prev_jcr, prev_jcr->db, &prev_jcr->jr)) {
239       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(prev_jcr->db));
240       return false;
241    }
242
243
244    /*
245     * Open a message channel connection with the Storage
246     * daemon. This is to let him know that our client
247     * will be contacting him for a backup  session.
248     *
249     */
250    Dmsg0(110, "Open connection with storage daemon\n");
251    set_jcr_job_status(jcr, JS_WaitSD);
252    set_jcr_job_status(prev_jcr, JS_WaitSD);
253    /*
254     * Start conversation with Storage daemon
255     */
256    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
257       return false;
258    }
259    sd = jcr->store_bsock;
260    /*
261     * Now start a job with the Storage daemon
262     */
263    Dmsg2(000, "Read store=%s, write store=%s\n", 
264       ((STORE *)prev_jcr->storage->first())->hdr.name,
265       ((STORE *)jcr->storage->first())->hdr.name);
266    if (!start_storage_daemon_job(jcr, prev_jcr->storage, jcr->storage)) {
267       return false;
268    }
269    Dmsg0(150, "Storage daemon connection OK\n");
270
271    if (!send_bootstrap_file(jcr, sd) ||
272        !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
273       return false;
274    }
275
276    if (!bnet_fsend(sd, "run")) {
277       return false;
278    }
279
280    /*
281     * Now start a Storage daemon message thread
282     */
283    if (!start_storage_daemon_message_thread(jcr)) {
284       return false;
285    }
286
287
288    set_jcr_job_status(jcr, JS_Running);
289    set_jcr_job_status(prev_jcr, JS_Running);
290
291    /* Pickup Job termination data */
292    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
293    wait_for_storage_daemon_termination(jcr);
294
295    set_jcr_job_status(jcr, jcr->SDJobStatus);
296    if (jcr->JobStatus == JS_Terminated) {
297       migration_cleanup(jcr, jcr->JobStatus);
298       return true;
299    }
300    return false;
301 }
302
303 struct idpkt {
304    POOLMEM *list;
305    uint32_t count;
306 };
307
308 /*
309  * Callback handler make list of DB Ids
310  */
311 static int dbid_handler(void *ctx, int num_fields, char **row)
312 {
313    idpkt *ids = (idpkt *)ctx;
314
315    Dmsg3(000, "count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
316    if (ids->count == 0) {
317       ids->list[0] = 0;
318    } else {
319       pm_strcat(ids->list, ",");
320    }
321    pm_strcat(ids->list, row[0]);
322    ids->count++;
323    return 0;
324 }
325
326
327 struct uitem {
328    dlink link;   
329    char *item;
330 };
331
332 static int item_compare(void *item1, void *item2)
333 {
334    uitem *i1 = (uitem *)item1;
335    uitem *i2 = (uitem *)item2;
336    return strcmp(i1->item, i2->item);
337 }
338
339 static int unique_name_handler(void *ctx, int num_fields, char **row)
340 {
341    dlist *list = (dlist *)ctx;
342
343    uitem *new_item = (uitem *)malloc(sizeof(uitem));
344    uitem *item;
345    
346    memset(new_item, 0, sizeof(uitem));
347    new_item->item = bstrdup(row[0]);
348    Dmsg1(000, "Item=%s\n", row[0]);
349    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
350    if (item != new_item) {            /* already in list */
351       free(new_item->item);
352       free((char *)new_item);
353       return 0;
354    }
355    return 0;
356 }
357
358 /* Get Job names in Pool */
359 const char *sql_job =
360    "SELECT DISTINCT Job.Name from Job,Pool"
361    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
362
363 /* Get JobIds from regex'ed Job names */
364 const char *sql_jobids_from_job =
365    "SELECT DISTINCT Job.JobId FROM Job,Pool"
366    " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
367    " ORDER by Job.StartTime";
368
369 /* Get Client names in Pool */
370 const char *sql_client =
371    "SELECT DISTINCT Client.Name from Client,Pool,Job"
372    " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
373    " Job.PoolId=Pool.PoolId";
374
375 /* Get JobIds from regex'ed Client names */
376 const char *sql_jobids_from_client =
377    "SELECT DISTINCT Job.JobId FROM Job,Pool"
378    " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
379    " AND Job.ClientId=Client.ClientId "
380    " ORDER by Job.StartTime";
381
382 /* Get Volume names in Pool */
383 const char *sql_vol = 
384    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
385    " VolStatus in ('Full','Used','Error') AND"
386    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
387
388 /* Get JobIds from regex'ed Volume names */
389 const char *sql_jobids_from_vol =
390    "SELECT DISTINCT Job.JobId FROM Media,JobMedia,Job"
391    " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
392    " AND JobMedia.JobId=Job.JobId" 
393    " ORDER by Job.StartTime";
394    
395
396
397
398
399 const char *sql_smallest_vol = 
400    "SELECT MediaId FROM Media,Pool WHERE"
401    " VolStatus in ('Full','Used','Error') AND"
402    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
403    " ORDER BY VolBytes ASC LIMIT 1";
404
405 const char *sql_oldest_vol = 
406    "SELECT MediaId FROM Media,Pool WHERE"
407    " VolStatus in ('Full','Used','Error') AND"
408    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
409    " ORDER BY LastWritten ASC LIMIT 1";
410
411 const char *sql_jobids_from_mediaid =
412    "SELECT DISTINCT Job.JobId FROM JobMedia,Job"
413    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
414    " ORDER by Job.StartTime";
415
416 const char *sql_pool_bytes =
417    "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
418    " VolStatus in ('Full','Used','Error','Append') AND"
419    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
420
421 const char *sql_vol_bytes =
422    "SELECT MediaId FROM Media,Pool WHERE"
423    " VolStatus in ('Full','Used','Error') AND"
424    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
425    " VolBytes<%s ORDER BY LastWritten ASC LIMIT 1";
426
427
428 const char *sql_ujobid =
429    "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
430    " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
431    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
432
433
434
435 /*
436  * Returns: false on error
437  *          true  if OK and jcr->previous_jr filled in
438  */
439 static bool get_job_to_migrate(JCR *jcr)
440 {
441    char ed1[30];
442    POOL_MEM query(PM_MESSAGE);
443    JobId_t JobId;
444    int stat;
445    char *p;
446    idpkt ids;
447
448    ids.list = get_pool_memory(PM_MESSAGE);
449    Dmsg1(000, "list=%p\n", ids.list);
450    ids.list[0] = 0;
451    ids.count = 0;
452
453    if (jcr->MigrateJobId != 0) {
454       Dmsg1(000, "previous jobid=%u\n", jcr->MigrateJobId);
455       edit_uint64(jcr->MigrateJobId, ids.list);
456       ids.count = 1;
457    } else {
458       switch (jcr->job->selection_type) {
459       case MT_JOB:
460          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
461             goto bail_out;
462          } 
463          break;
464       case MT_CLIENT:
465          if (!regex_find_jobids(jcr, &ids, sql_client, 
466               sql_jobids_from_client, "Client")) {
467             goto bail_out;
468          } 
469          break;
470       case MT_VOLUME:
471          if (!regex_find_jobids(jcr, &ids, sql_vol, 
472              sql_jobids_from_vol, "Volume")) {
473             goto bail_out;
474          } 
475          break;
476       case MT_SQLQUERY:
477          if (!jcr->job->selection_pattern) {
478             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
479             goto bail_out;
480          }
481          Dmsg1(000, "SQL=%s\n", jcr->job->selection_pattern);
482          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
483               dbid_handler, (void *)&ids)) {
484             Jmsg(jcr, M_FATAL, 0,
485                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
486             goto bail_out;
487          }
488          break;
489
490
491 /***** Below not implemented yet *********/
492       case MT_SMALLEST_VOL:
493          Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
494 //       Mmsg(query2, sql_jobids_from_mediaid, JobIds);
495 //       Dmsg1(000, "Smallest Vol Jobids=%s\n", JobIds);
496          break;
497       case MT_OLDEST_VOL:
498          Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
499 //       Mmsg(query2, sql_jobids_from_mediaid, JobIds);
500 //       Dmsg1(000, "Oldest Vol Jobids=%s\n", JobIds);
501          break;
502       case MT_POOL_OCCUPANCY:
503          Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
504 //       Dmsg1(000, "Pool Occupancy Jobids=%s\n", JobIds);
505          break;
506       case MT_POOL_TIME:
507          Dmsg0(000, "Pool time not implemented\n");
508          break;
509       default:
510          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
511          goto bail_out;
512       }
513    }
514
515    p = ids.list;
516    JobId = 0;
517    stat = get_next_jobid_from_list(&p, &JobId);
518    Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
519    jcr->MigrateJobId = JobId;
520    start_migration_job(jcr);
521    if (stat < 0) {
522       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
523       goto bail_out;
524    } else if (stat == 0) {
525       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
526       goto ok_out;
527    }
528    
529    jcr->previous_jr.JobId = JobId;
530    Dmsg1(000, "Previous jobid=%d\n", jcr->previous_jr.JobId);
531
532    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
533       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
534            edit_int64(jcr->previous_jr.JobId, ed1),
535            db_strerror(jcr->db));
536       goto bail_out;
537    }
538    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
539       jcr->previous_jr.JobId, jcr->previous_jr.Job);
540
541 ok_out:
542    free_pool_memory(ids.list);
543    return true;
544
545 bail_out:
546    free_pool_memory(ids.list);
547    return false;
548 }
549
550 static void start_migration_job(JCR *jcr)
551 {
552    UAContext *ua = new_ua_context(jcr);
553    char ed1[50];
554    ua->batch = true;
555    Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, 
556         edit_uint64(jcr->MigrateJobId, ed1));
557    Dmsg1(000, "=============== Migration cmd=%s\n", ua->cmd);
558    parse_ua_args(ua);                 /* parse command */
559 // int stat = run_cmd(ua, ua->cmd);
560    int stat = (int)jcr->MigrateJobId;
561    if (stat == 0) {
562       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
563    } else {
564       Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
565    }
566    free_ua_context(ua);
567 }
568
569
570 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
571                  const char *query2, const char *type) {
572    dlist *item_chain;
573    uitem *item = NULL;
574    uitem *last_item = NULL;
575    regex_t preg;
576    char prbuf[500];
577    int rc;
578    bool ok = false;
579    POOL_MEM query(PM_MESSAGE);
580
581    item_chain = New(dlist(item, &item->link));
582    if (!jcr->job->selection_pattern) {
583       Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
584          type);
585       goto bail_out;
586    }
587    Dmsg1(000, "regex=%s\n", jcr->job->selection_pattern);
588    /* Compile regex expression */
589    rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
590    if (rc != 0) {
591       regerror(rc, &preg, prbuf, sizeof(prbuf));
592       Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
593            jcr->job->selection_pattern, prbuf);
594       goto bail_out;
595    }
596    /* Basic query for names */
597    Mmsg(query, query1, jcr->pool->hdr.name);
598    Dmsg1(000, "query1=%s\n", query.c_str());
599    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
600         (void *)item_chain)) {
601       Jmsg(jcr, M_FATAL, 0,
602            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
603       goto bail_out;
604    }
605    /* Now apply the regex to the names and remove any item not matched */
606    foreach_dlist(item, item_chain) {
607       const int nmatch = 30;
608       regmatch_t pmatch[nmatch];
609       if (last_item) {
610          Dmsg1(000, "Remove item %s\n", last_item->item);
611          free(last_item->item);
612          item_chain->remove(last_item);
613       }
614       Dmsg1(000, "Jobitem=%s\n", item->item);
615       rc = regexec(&preg, item->item, nmatch, pmatch,  0);
616       if (rc == 0) {
617          last_item = NULL;   /* keep this one */
618       } else {   
619          last_item = item;
620       }
621    }
622    if (last_item) {
623       free(last_item->item);
624       Dmsg1(000, "Remove item %s\n", last_item->item);
625       item_chain->remove(last_item);
626    }
627    regfree(&preg);
628    /* 
629     * At this point, we have a list of items in item_chain
630     *  that have been matched by the regex, so now we need
631     *  to look up their jobids.
632     */
633    ids->count = 0;
634    foreach_dlist(item, item_chain) {
635       Dmsg1(000, "Got Job: %s\n", item->item);
636       Mmsg(query, query2, item->item, jcr->pool->hdr.name);
637       Dmsg1(000, "query2=%s\n", query.c_str());
638       if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
639          Jmsg(jcr, M_FATAL, 0,
640               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
641          goto bail_out;
642       }
643    }
644    if (ids->count == 0) {
645       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
646    }
647    ok = true;
648 bail_out:
649    Dmsg2(000, "Count=%d Jobids=%s\n", ids->count, ids->list);
650    delete item_chain;
651    Dmsg0(000, "After delete item_chain\n");
652    return ok;
653 }
654
655
656 /*
657  * Release resources allocated during backup.
658  */
659 void migration_cleanup(JCR *jcr, int TermCode)
660 {
661    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
662    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
663    char ec6[50], ec7[50], ec8[50];
664    char term_code[100], sd_term_msg[100];
665    const char *term_msg;
666    int msg_type;
667    MEDIA_DBR mr;
668    double kbps;
669    utime_t RunTime;
670    JCR *prev_jcr = jcr->previous_jcr;
671    POOL_MEM query(PM_MESSAGE);
672
673    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
674    dequeue_messages(jcr);             /* display any queued messages */
675    memset(&mr, 0, sizeof(mr));
676    set_jcr_job_status(jcr, TermCode);
677    update_job_end_record(jcr);        /* update database */
678
679    /* Check if we actually did something */
680    if (prev_jcr) {
681       prev_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
682       prev_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
683       prev_jcr->VolSessionId = jcr->VolSessionId;
684       prev_jcr->VolSessionTime = jcr->VolSessionTime;
685
686       set_jcr_job_status(prev_jcr, TermCode);
687
688       update_job_end_record(prev_jcr);
689
690       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
691                   "JobTDate=%s WHERE JobId=%s", 
692          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
693          edit_uint64(jcr->previous_jr.JobTDate, ec1),
694          edit_uint64(prev_jcr->jr.JobId, ec2));
695       db_sql_query(prev_jcr->db, query.c_str(), NULL, NULL);
696
697       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
698          Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
699             db_strerror(jcr->db));
700          set_jcr_job_status(jcr, JS_ErrorTerminated);
701       }
702
703       bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
704       if (!db_get_media_record(jcr, jcr->db, &mr)) {
705          Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
706             mr.VolumeName, db_strerror(jcr->db));
707          set_jcr_job_status(jcr, JS_ErrorTerminated);
708       }
709
710       update_bootstrap_file(prev_jcr);
711
712       if (!db_get_job_volume_names(prev_jcr, prev_jcr->db, prev_jcr->jr.JobId, &prev_jcr->VolumeName)) {
713          /*
714           * Note, if the job has erred, most likely it did not write any
715           *  tape, so suppress this "error" message since in that case
716           *  it is normal.  Or look at it the other way, only for a
717           *  normal exit should we complain about this error.
718           */
719          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
720             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(prev_jcr->db));
721          }
722          prev_jcr->VolumeName[0] = 0;         /* none */
723       }
724   }
725
726    msg_type = M_INFO;                 /* by default INFO message */
727    switch (jcr->JobStatus) {
728       case JS_Terminated:
729          if (jcr->Errors || jcr->SDErrors) {
730             term_msg = _("%s OK -- with warnings");
731          } else {
732             term_msg = _("%s OK");
733          }
734          break;
735       case JS_FatalError:
736       case JS_ErrorTerminated:
737          term_msg = _("*** %s Error ***");
738          msg_type = M_ERROR;          /* Generate error message */
739          if (jcr->store_bsock) {
740             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
741             if (jcr->SD_msg_chan) {
742                pthread_cancel(jcr->SD_msg_chan);
743             }
744          }
745          break;
746       case JS_Canceled:
747          term_msg = _("%s Canceled");
748          if (jcr->store_bsock) {
749             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
750             if (jcr->SD_msg_chan) {
751                pthread_cancel(jcr->SD_msg_chan);
752             }
753          }
754          break;
755       default:
756          term_msg = _("Inappropriate %s term code");
757          break;
758    }
759    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
760    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
761    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
762    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
763    if (RunTime <= 0) {
764       kbps = 0;
765    } else {
766       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
767    }
768
769
770    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
771
772    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
773 "  Prev Backup JobId:      %s\n"
774 "  New Backup JobId:       %s\n"
775 "  Migration JobId:        %s\n"
776 "  Migration Job:          %s\n"
777 "  Backup Level:           %s%s\n"
778 "  Client:                 %s\n"
779 "  FileSet:                \"%s\" %s\n"
780 "  Pool:                   \"%s\"\n"
781 "  Start time:             %s\n"
782 "  End time:               %s\n"
783 "  Elapsed time:           %s\n"
784 "  Priority:               %d\n"
785 "  SD Files Written:       %s\n"
786 "  SD Bytes Written:       %s (%sB)\n"
787 "  Rate:                   %.1f KB/s\n"
788 "  Volume name(s):         %s\n"
789 "  Volume Session Id:      %d\n"
790 "  Volume Session Time:    %d\n"
791 "  Last Volume Bytes:      %s (%sB)\n"
792 "  SD Errors:              %d\n"
793 "  SD termination status:  %s\n"
794 "  Termination:            %s\n\n"),
795    VERSION,
796    LSMDATE,
797         edt, 
798         prev_jcr ? edit_uint64(jcr->previous_jr.JobId, ec6) : "0", 
799         prev_jcr ? edit_uint64(prev_jcr->jr.JobId, ec7) : "0",
800         edit_uint64(jcr->jr.JobId, ec8),
801         jcr->jr.Job,
802         level_to_str(jcr->JobLevel), jcr->since,
803         jcr->client->hdr.name,
804         jcr->fileset->hdr.name, jcr->FSCreateTime,
805         jcr->pool->hdr.name,
806         sdt,
807         edt,
808         edit_utime(RunTime, elapsed, sizeof(elapsed)),
809         jcr->JobPriority,
810         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
811         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
812         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
813         (float)kbps,
814         prev_jcr ? prev_jcr->VolumeName : "",
815         jcr->VolSessionId,
816         jcr->VolSessionTime,
817         edit_uint64_with_commas(mr.VolBytes, ec4),
818         edit_uint64_with_suffix(mr.VolBytes, ec5),
819         jcr->SDErrors,
820         sd_term_msg,
821         term_code);
822
823    Dmsg1(100, "migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
824    if (jcr->previous_jcr) {
825       free_jcr(jcr->previous_jcr);
826       jcr->previous_jcr = NULL;
827    }
828    Dmsg0(100, "Leave migrate_cleanup()\n");
829 }