]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
- Integrate the majority of the Mingw cross-tools submission
[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    /*
277     * Now start a Storage daemon message thread
278     */
279    if (!start_storage_daemon_message_thread(jcr)) {
280       return false;
281    }
282
283    if (!bnet_fsend(sd, "run")) {
284       return false;
285    }
286
287    set_jcr_job_status(jcr, JS_Running);
288    set_jcr_job_status(prev_jcr, JS_Running);
289
290    /* Pickup Job termination data */
291    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
292    wait_for_storage_daemon_termination(jcr);
293
294    set_jcr_job_status(jcr, jcr->SDJobStatus);
295    if (jcr->JobStatus == JS_Terminated) {
296       migration_cleanup(jcr, jcr->JobStatus);
297       return true;
298    }
299    return false;
300 }
301
302 struct idpkt {
303    POOLMEM *list;
304    uint32_t count;
305 };
306
307 /*
308  * Callback handler make list of DB Ids
309  */
310 static int dbid_handler(void *ctx, int num_fields, char **row)
311 {
312    idpkt *ids = (idpkt *)ctx;
313
314    Dmsg3(000, "count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
315    if (ids->count == 0) {
316       ids->list[0] = 0;
317    } else {
318       pm_strcat(ids->list, ",");
319    }
320    pm_strcat(ids->list, row[0]);
321    ids->count++;
322    return 0;
323 }
324
325
326 struct uitem {
327    dlink link;   
328    char *item;
329 };
330
331 static int item_compare(void *item1, void *item2)
332 {
333    uitem *i1 = (uitem *)item1;
334    uitem *i2 = (uitem *)item2;
335    return strcmp(i1->item, i2->item);
336 }
337
338 static int unique_name_handler(void *ctx, int num_fields, char **row)
339 {
340    dlist *list = (dlist *)ctx;
341
342    uitem *new_item = (uitem *)malloc(sizeof(uitem));
343    uitem *item;
344    
345    memset(new_item, 0, sizeof(uitem));
346    new_item->item = bstrdup(row[0]);
347    Dmsg1(000, "Item=%s\n", row[0]);
348    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
349    if (item != new_item) {            /* already in list */
350       free(new_item->item);
351       free((char *)new_item);
352       return 0;
353    }
354    return 0;
355 }
356
357 /* Get Job names in Pool */
358 const char *sql_job =
359    "SELECT DISTINCT Job.Name from Job,Pool"
360    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
361
362 /* Get JobIds from regex'ed Job names */
363 const char *sql_jobids_from_job =
364    "SELECT DISTINCT Job.JobId FROM Job,Pool"
365    " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
366    " ORDER by Job.StartTime";
367
368 /* Get Client names in Pool */
369 const char *sql_client =
370    "SELECT DISTINCT Client.Name from Client,Pool,Job"
371    " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
372    " Job.PoolId=Pool.PoolId";
373
374 /* Get JobIds from regex'ed Client names */
375 const char *sql_jobids_from_client =
376    "SELECT DISTINCT Job.JobId FROM Job,Pool"
377    " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
378    " AND Job.ClientId=Client.ClientId "
379    " ORDER by Job.StartTime";
380
381 /* Get Volume names in Pool */
382 const char *sql_vol = 
383    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
384    " VolStatus in ('Full','Used','Error') AND"
385    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
386
387 /* Get JobIds from regex'ed Volume names */
388 const char *sql_jobids_from_vol =
389    "SELECT DISTINCT Job.JobId FROM Media,JobMedia,Job"
390    " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
391    " AND JobMedia.JobId=Job.JobId" 
392    " ORDER by Job.StartTime";
393    
394
395
396
397
398 const char *sql_smallest_vol = 
399    "SELECT MediaId FROM Media,Pool WHERE"
400    " VolStatus in ('Full','Used','Error') AND"
401    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
402    " ORDER BY VolBytes ASC LIMIT 1";
403
404 const char *sql_oldest_vol = 
405    "SELECT MediaId FROM Media,Pool WHERE"
406    " VolStatus in ('Full','Used','Error') AND"
407    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
408    " ORDER BY LastWritten ASC LIMIT 1";
409
410 const char *sql_jobids_from_mediaid =
411    "SELECT DISTINCT Job.JobId FROM JobMedia,Job"
412    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
413    " ORDER by Job.StartTime";
414
415 const char *sql_pool_bytes =
416    "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
417    " VolStatus in ('Full','Used','Error','Append') AND"
418    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
419
420 const char *sql_vol_bytes =
421    "SELECT MediaId FROM Media,Pool WHERE"
422    " VolStatus in ('Full','Used','Error') AND"
423    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
424    " VolBytes<%s ORDER BY LastWritten ASC LIMIT 1";
425
426
427 const char *sql_ujobid =
428    "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
429    " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
430    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
431
432
433
434 /*
435  * Returns: false on error
436  *          true  if OK and jcr->previous_jr filled in
437  */
438 static bool get_job_to_migrate(JCR *jcr)
439 {
440    char ed1[30];
441    POOL_MEM query(PM_MESSAGE);
442    JobId_t JobId;
443    int stat;
444    char *p;
445    idpkt ids;
446
447    ids.list = get_pool_memory(PM_MESSAGE);
448    Dmsg1(000, "list=%p\n", ids.list);
449    ids.list[0] = 0;
450    ids.count = 0;
451
452    if (jcr->MigrateJobId != 0) {
453       Dmsg1(000, "previous jobid=%u\n", jcr->MigrateJobId);
454       edit_uint64(jcr->MigrateJobId, ids.list);
455       ids.count = 1;
456    } else {
457       switch (jcr->job->selection_type) {
458       case MT_JOB:
459          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
460             goto bail_out;
461          } 
462          break;
463       case MT_CLIENT:
464          if (!regex_find_jobids(jcr, &ids, sql_client, 
465               sql_jobids_from_client, "Client")) {
466             goto bail_out;
467          } 
468          break;
469       case MT_VOLUME:
470          if (!regex_find_jobids(jcr, &ids, sql_vol, 
471              sql_jobids_from_vol, "Volume")) {
472             goto bail_out;
473          } 
474          break;
475       case MT_SQLQUERY:
476          if (!jcr->job->selection_pattern) {
477             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
478             goto bail_out;
479          }
480          Dmsg1(000, "SQL=%s\n", jcr->job->selection_pattern);
481          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
482               dbid_handler, (void *)&ids)) {
483             Jmsg(jcr, M_FATAL, 0,
484                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
485             goto bail_out;
486          }
487          break;
488
489
490 /***** Below not implemented yet *********/
491       case MT_SMALLEST_VOL:
492          Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
493 //       Mmsg(query2, sql_jobids_from_mediaid, JobIds);
494 //       Dmsg1(000, "Smallest Vol Jobids=%s\n", JobIds);
495          break;
496       case MT_OLDEST_VOL:
497          Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
498 //       Mmsg(query2, sql_jobids_from_mediaid, JobIds);
499 //       Dmsg1(000, "Oldest Vol Jobids=%s\n", JobIds);
500          break;
501       case MT_POOL_OCCUPANCY:
502          Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
503 //       Dmsg1(000, "Pool Occupancy Jobids=%s\n", JobIds);
504          break;
505       case MT_POOL_TIME:
506          Dmsg0(000, "Pool time not implemented\n");
507          break;
508       default:
509          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
510          goto bail_out;
511       }
512    }
513
514    p = ids.list;
515    JobId = 0;
516    stat = get_next_jobid_from_list(&p, &JobId);
517    Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
518    jcr->MigrateJobId = JobId;
519    start_migration_job(jcr);
520    if (stat < 0) {
521       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
522       goto bail_out;
523    } else if (stat == 0) {
524       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
525       goto ok_out;
526    }
527    
528    jcr->previous_jr.JobId = JobId;
529    Dmsg1(000, "Previous jobid=%d\n", jcr->previous_jr.JobId);
530
531    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
532       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
533            edit_int64(jcr->previous_jr.JobId, ed1),
534            db_strerror(jcr->db));
535       goto bail_out;
536    }
537    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
538       jcr->previous_jr.JobId, jcr->previous_jr.Job);
539
540 ok_out:
541    free_pool_memory(ids.list);
542    return true;
543
544 bail_out:
545    free_pool_memory(ids.list);
546    return false;
547 }
548
549 static void start_migration_job(JCR *jcr)
550 {
551    UAContext *ua = new_ua_context(jcr);
552    char ed1[50];
553    ua->batch = true;
554    Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, 
555         edit_uint64(jcr->MigrateJobId, ed1));
556    Dmsg1(000, "=============== Migration cmd=%s\n", ua->cmd);
557    parse_ua_args(ua);                 /* parse command */
558 // int stat = run_cmd(ua, ua->cmd);
559    int stat = (int)jcr->MigrateJobId;
560    if (stat == 0) {
561       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
562    } else {
563       Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
564    }
565    free_ua_context(ua);
566 }
567
568
569 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
570                  const char *query2, const char *type) {
571    dlist *item_chain;
572    uitem *item = NULL;
573    uitem *last_item = NULL;
574    regex_t preg;
575    char prbuf[500];
576    int rc;
577    bool ok = false;
578    POOL_MEM query(PM_MESSAGE);
579
580    item_chain = New(dlist(item, &item->link));
581    if (!jcr->job->selection_pattern) {
582       Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
583          type);
584       goto bail_out;
585    }
586    Dmsg1(000, "regex=%s\n", jcr->job->selection_pattern);
587    /* Compile regex expression */
588    rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
589    if (rc != 0) {
590       regerror(rc, &preg, prbuf, sizeof(prbuf));
591       Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
592            jcr->job->selection_pattern, prbuf);
593       goto bail_out;
594    }
595    /* Basic query for names */
596    Mmsg(query, query1, jcr->pool->hdr.name);
597    Dmsg1(000, "query1=%s\n", query.c_str());
598    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
599         (void *)item_chain)) {
600       Jmsg(jcr, M_FATAL, 0,
601            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
602       goto bail_out;
603    }
604    /* Now apply the regex to the names and remove any item not matched */
605    foreach_dlist(item, item_chain) {
606       const int nmatch = 30;
607       regmatch_t pmatch[nmatch];
608       if (last_item) {
609          Dmsg1(000, "Remove item %s\n", last_item->item);
610          free(last_item->item);
611          item_chain->remove(last_item);
612       }
613       Dmsg1(000, "Jobitem=%s\n", item->item);
614       rc = regexec(&preg, item->item, nmatch, pmatch,  0);
615       if (rc == 0) {
616          last_item = NULL;   /* keep this one */
617       } else {   
618          last_item = item;
619       }
620    }
621    if (last_item) {
622       free(last_item->item);
623       Dmsg1(000, "Remove item %s\n", last_item->item);
624       item_chain->remove(last_item);
625    }
626    regfree(&preg);
627    /* 
628     * At this point, we have a list of items in item_chain
629     *  that have been matched by the regex, so now we need
630     *  to look up their jobids.
631     */
632    ids->count = 0;
633    foreach_dlist(item, item_chain) {
634       Dmsg1(000, "Got Job: %s\n", item->item);
635       Mmsg(query, query2, item->item, jcr->pool->hdr.name);
636       Dmsg1(000, "query2=%s\n", query.c_str());
637       if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
638          Jmsg(jcr, M_FATAL, 0,
639               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
640          goto bail_out;
641       }
642    }
643    if (ids->count == 0) {
644       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
645    }
646    ok = true;
647 bail_out:
648    Dmsg2(000, "Count=%d Jobids=%s\n", ids->count, ids->list);
649    delete item_chain;
650    Dmsg0(000, "After delete item_chain\n");
651    return ok;
652 }
653
654
655 /*
656  * Release resources allocated during backup.
657  */
658 void migration_cleanup(JCR *jcr, int TermCode)
659 {
660    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
661    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
662    char ec6[50], ec7[50], ec8[50];
663    char term_code[100], sd_term_msg[100];
664    const char *term_msg;
665    int msg_type;
666    MEDIA_DBR mr;
667    double kbps;
668    utime_t RunTime;
669    JCR *prev_jcr = jcr->previous_jcr;
670    POOL_MEM query(PM_MESSAGE);
671
672    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
673    dequeue_messages(jcr);             /* display any queued messages */
674    memset(&mr, 0, sizeof(mr));
675    set_jcr_job_status(jcr, TermCode);
676    update_job_end_record(jcr);        /* update database */
677
678    /* Check if we actually did something */
679    if (prev_jcr) {
680       prev_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
681       prev_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
682       prev_jcr->VolSessionId = jcr->VolSessionId;
683       prev_jcr->VolSessionTime = jcr->VolSessionTime;
684
685       set_jcr_job_status(prev_jcr, TermCode);
686
687       update_job_end_record(prev_jcr);
688
689       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
690                   "JobTDate=%s WHERE JobId=%s", 
691          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
692          edit_uint64(jcr->previous_jr.JobTDate, ec1),
693          edit_uint64(prev_jcr->jr.JobId, ec2));
694       db_sql_query(prev_jcr->db, query.c_str(), NULL, NULL);
695
696       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
697          Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
698             db_strerror(jcr->db));
699          set_jcr_job_status(jcr, JS_ErrorTerminated);
700       }
701
702       bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
703       if (!db_get_media_record(jcr, jcr->db, &mr)) {
704          Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
705             mr.VolumeName, db_strerror(jcr->db));
706          set_jcr_job_status(jcr, JS_ErrorTerminated);
707       }
708
709       update_bootstrap_file(prev_jcr);
710
711       if (!db_get_job_volume_names(prev_jcr, prev_jcr->db, prev_jcr->jr.JobId, &prev_jcr->VolumeName)) {
712          /*
713           * Note, if the job has erred, most likely it did not write any
714           *  tape, so suppress this "error" message since in that case
715           *  it is normal.  Or look at it the other way, only for a
716           *  normal exit should we complain about this error.
717           */
718          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
719             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(prev_jcr->db));
720          }
721          prev_jcr->VolumeName[0] = 0;         /* none */
722       }
723   }
724
725    msg_type = M_INFO;                 /* by default INFO message */
726    switch (jcr->JobStatus) {
727       case JS_Terminated:
728          if (jcr->Errors || jcr->SDErrors) {
729             term_msg = _("%s OK -- with warnings");
730          } else {
731             term_msg = _("%s OK");
732          }
733          break;
734       case JS_FatalError:
735       case JS_ErrorTerminated:
736          term_msg = _("*** %s Error ***");
737          msg_type = M_ERROR;          /* Generate error message */
738          if (jcr->store_bsock) {
739             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
740             if (jcr->SD_msg_chan) {
741                pthread_cancel(jcr->SD_msg_chan);
742             }
743          }
744          break;
745       case JS_Canceled:
746          term_msg = _("%s Canceled");
747          if (jcr->store_bsock) {
748             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
749             if (jcr->SD_msg_chan) {
750                pthread_cancel(jcr->SD_msg_chan);
751             }
752          }
753          break;
754       default:
755          term_msg = _("Inappropriate %s term code");
756          break;
757    }
758    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
759    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
760    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
761    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
762    if (RunTime <= 0) {
763       kbps = 0;
764    } else {
765       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
766    }
767
768
769    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
770
771    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
772 "  Prev Backup JobId:      %s\n"
773 "  New Backup JobId:       %s\n"
774 "  Migration JobId:        %s\n"
775 "  Migration Job:          %s\n"
776 "  Backup Level:           %s%s\n"
777 "  Client:                 %s\n"
778 "  FileSet:                \"%s\" %s\n"
779 "  Pool:                   \"%s\"\n"
780 "  Start time:             %s\n"
781 "  End time:               %s\n"
782 "  Elapsed time:           %s\n"
783 "  Priority:               %d\n"
784 "  SD Files Written:       %s\n"
785 "  SD Bytes Written:       %s (%sB)\n"
786 "  Rate:                   %.1f KB/s\n"
787 "  Volume name(s):         %s\n"
788 "  Volume Session Id:      %d\n"
789 "  Volume Session Time:    %d\n"
790 "  Last Volume Bytes:      %s (%sB)\n"
791 "  SD Errors:              %d\n"
792 "  SD termination status:  %s\n"
793 "  Termination:            %s\n\n"),
794    VERSION,
795    LSMDATE,
796         edt, 
797         prev_jcr ? edit_uint64(jcr->previous_jr.JobId, ec6) : "0", 
798         prev_jcr ? edit_uint64(prev_jcr->jr.JobId, ec7) : "0",
799         edit_uint64(jcr->jr.JobId, ec8),
800         jcr->jr.Job,
801         level_to_str(jcr->JobLevel), jcr->since,
802         jcr->client->hdr.name,
803         jcr->fileset->hdr.name, jcr->FSCreateTime,
804         jcr->pool->hdr.name,
805         sdt,
806         edt,
807         edit_utime(RunTime, elapsed, sizeof(elapsed)),
808         jcr->JobPriority,
809         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
810         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
811         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
812         (float)kbps,
813         prev_jcr ? prev_jcr->VolumeName : "",
814         jcr->VolSessionId,
815         jcr->VolSessionTime,
816         edit_uint64_with_commas(mr.VolBytes, ec4),
817         edit_uint64_with_suffix(mr.VolBytes, ec5),
818         jcr->SDErrors,
819         sd_term_msg,
820         term_code);
821
822    Dmsg1(100, "migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
823    if (jcr->previous_jcr) {
824       free_jcr(jcr->previous_jcr);
825       jcr->previous_jcr = NULL;
826    }
827    Dmsg0(100, "Leave migrate_cleanup()\n");
828 }