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