]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/job.c
Fix restore spanning volumes
[bacula/bacula] / bacula / src / dird / job.c
1 /*
2  *
3  *   Bacula Director Job processing routines
4  *
5  *     Kern Sibbald, October MM
6  *
7  *    Version $Id$
8  */
9 /*
10    Copyright (C) 2000-2003 Kern Sibbald and John Walker
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2 of
15    the License, or (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public
23    License along with this program; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25    MA 02111-1307, USA.
26
27  */
28
29 #include "bacula.h"
30 #include "dird.h"
31
32 /* Forward referenced subroutines */
33 static void *job_thread(void *arg);
34 static char *edit_run_codes(JCR *jcr, char *omsg, char *imsg);
35 static int acquire_resource_locks(JCR *jcr);
36 #ifdef USE_SEMAPHORE
37 static void backoff_resource_locks(JCR *jcr, int count);
38 static void release_resource_locks(JCR *jcr);
39 #endif
40
41 /* Exported subroutines */
42
43
44 /* Imported subroutines */
45 extern void term_scheduler();
46 extern void term_ua_server();
47 extern int do_backup(JCR *jcr);
48 extern int do_admin(JCR *jcr);
49 extern int do_restore(JCR *jcr);
50 extern int do_verify(JCR *jcr);
51
52 #ifdef USE_SEMAPHORE
53 static semlock_t job_lock;
54 static pthread_mutex_t mutex;
55 static pthread_cond_t  resource_wait;
56 static int waiting = 0;               /* count of waiting threads */
57 #else
58 #ifdef JOB_QUEUE  
59 jobq_t  job_queue;
60 #endif
61 #endif
62
63 void init_job_server(int max_workers)
64 {
65    int stat;
66 #ifdef USE_SEMAPHORE
67    if ((stat = sem_init(&job_lock, max_workers)) != 0) {
68       Emsg1(M_ABORT, 0, _("Could not init job lock: ERR=%s\n"), strerror(stat));
69    }
70    if ((stat = pthread_mutex_init(&mutex, NULL)) != 0) {
71       Emsg1(M_ABORT, 0, _("Could not init resource mutex: ERR=%s\n"), strerror(stat));
72    }
73    if ((stat = pthread_cond_init(&resource_wait, NULL)) != 0) {
74       Emsg1(M_ABORT, 0, _("Could not init resource wait: ERR=%s\n"), strerror(stat));
75    }
76
77 #else
78 #ifdef JOB_QUEUE
79    if ((stat = jobq_init(&job_queue, max_workers, job_thread)) != 0) {
80       Emsg1(M_ABORT, 0, _("Could not init job queue: ERR=%s\n"), strerror(stat));
81    }
82 #endif
83 #endif
84    return;
85 }
86
87 /*
88  * Run a job -- typically called by the scheduler, but may also
89  *              be called by the UA (Console program).
90  *
91  */
92 void run_job(JCR *jcr)
93 {
94    int stat, errstat;
95 #ifdef USE_SEMAPHORE
96    pthread_t tid;
97 #endif
98
99    sm_check(__FILE__, __LINE__, True);
100    init_msg(jcr, jcr->messages);
101    create_unique_job_name(jcr, jcr->job->hdr.name);
102    set_jcr_job_status(jcr, JS_Created);
103    jcr->jr.SchedTime = jcr->sched_time;
104    jcr->jr.StartTime = jcr->start_time;
105    jcr->jr.Type = jcr->JobType;
106    jcr->jr.Level = jcr->JobLevel;
107    jcr->jr.JobStatus = jcr->JobStatus;
108    bstrncpy(jcr->jr.Name, jcr->job->hdr.name, sizeof(jcr->jr.Name));
109    bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
110
111    /* Initialize termination condition variable */
112    if ((errstat = pthread_cond_init(&jcr->term_wait, NULL)) != 0) {
113       Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), strerror(errstat));
114       set_jcr_job_status(jcr, JS_ErrorTerminated);
115       free_jcr(jcr);
116       return;
117    }
118
119    /*
120     * Open database
121     */
122    Dmsg0(50, "Open database\n");
123    jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
124                             jcr->catalog->db_password, jcr->catalog->db_address,
125                             jcr->catalog->db_port, jcr->catalog->db_socket);
126    if (!db_open_database(jcr, jcr->db)) {
127       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
128       set_jcr_job_status(jcr, JS_ErrorTerminated);
129       free_jcr(jcr);
130       return;
131    }
132    Dmsg0(50, "DB opened\n");
133
134    /*
135     * Create Job record  
136     */
137    jcr->jr.JobStatus = jcr->JobStatus;
138    if (!db_create_job_record(jcr, jcr->db, &jcr->jr)) {
139       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
140       set_jcr_job_status(jcr, JS_ErrorTerminated);
141       free_jcr(jcr);
142       return;
143    }
144    jcr->JobId = jcr->jr.JobId;
145    ASSERT(jcr->jr.JobId > 0);
146
147    Dmsg4(50, "Created job record JobId=%d Name=%s Type=%c Level=%c\n", 
148        jcr->JobId, jcr->Job, jcr->jr.Type, jcr->jr.Level);
149    Dmsg0(200, "Add jrc to work queue\n");
150
151 #ifdef USE_SEMAPHORE
152   if ((stat = pthread_create(&tid, NULL, job_thread, (void *)jcr)) != 0) {
153       Emsg1(M_ABORT, 0, _("Unable to create job thread: ERR=%s\n"), strerror(stat));
154    }
155 #else
156 #ifdef JOB_QUEUE
157    /* Queue the job to be run */
158    if ((stat = jobq_add(&job_queue, jcr)) != 0) {
159       Emsg1(M_ABORT, 0, _("Could not add job queue: ERR=%s\n"), strerror(stat));
160    }
161 #endif
162 #endif
163    Dmsg0(100, "Done run_job()\n");
164 }
165
166 /* 
167  * This is the engine called by workq_add() when we were pulled                
168  *  from the work queue.
169  *  At this point, we are running in our own thread 
170  */
171 static void *job_thread(void *arg)
172 {
173    JCR *jcr = (JCR *)arg;
174
175    pthread_detach(pthread_self());
176    sm_check(__FILE__, __LINE__, True);
177
178    for ( ;; ) {
179       if (!acquire_resource_locks(jcr)) {
180          set_jcr_job_status(jcr, JS_Canceled);
181       }
182
183       Dmsg0(200, "=====Start Job=========\n");
184       jcr->start_time = time(NULL);      /* set the real start time */
185       set_jcr_job_status(jcr, JS_Running);
186
187       if (job_canceled(jcr)) {
188          update_job_end_record(jcr);
189       } else if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
190           (utime_t)(jcr->start_time - jcr->sched_time)) {
191          Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
192          set_jcr_job_status(jcr, JS_Canceled);
193          update_job_end_record(jcr);
194       } else {
195
196          /* Run Job */
197          if (jcr->job->RunBeforeJob) {
198             POOLMEM *before = get_pool_memory(PM_FNAME);
199             int status;
200             BPIPE *bpipe;
201             char line[MAXSTRING];
202             
203             before = edit_run_codes(jcr, before, jcr->job->RunBeforeJob);
204             bpipe = open_bpipe(before, 0, "r");
205             free_pool_memory(before);
206             while (fgets(line, sizeof(line), bpipe->rfd)) {
207                Jmsg(jcr, M_INFO, 0, _("RunBefore: %s"), line);
208             }
209             status = close_bpipe(bpipe);
210             if (status != 0) {
211                Jmsg(jcr, M_FATAL, 0, _("RunBeforeJob returned non-zero status=%d\n"),
212                   status);
213                set_jcr_job_status(jcr, JS_FatalError);
214                update_job_end_record(jcr);
215                goto bail_out;
216             }
217          }
218          switch (jcr->JobType) {
219          case JT_BACKUP:
220             do_backup(jcr);
221             if (jcr->JobStatus == JS_Terminated) {
222                do_autoprune(jcr);
223             }
224             break;
225          case JT_VERIFY:
226             do_verify(jcr);
227             if (jcr->JobStatus == JS_Terminated) {
228                do_autoprune(jcr);
229             }
230             break;
231          case JT_RESTORE:
232             do_restore(jcr);
233             if (jcr->JobStatus == JS_Terminated) {
234                do_autoprune(jcr);
235             }
236             break;
237          case JT_ADMIN:
238             do_admin(jcr);
239             if (jcr->JobStatus == JS_Terminated) {
240                do_autoprune(jcr);
241             }
242             break;
243          default:
244             Pmsg1(0, "Unimplemented job type: %d\n", jcr->JobType);
245             break;
246          }
247          if (jcr->job->RunAfterJob) {
248             POOLMEM *after = get_pool_memory(PM_FNAME);
249             int status;
250             BPIPE *bpipe;
251             char line[MAXSTRING];
252             
253             after = edit_run_codes(jcr, after, jcr->job->RunAfterJob);
254             bpipe = open_bpipe(after, 0, "r");
255             free_pool_memory(after);
256             while (fgets(line, sizeof(line), bpipe->rfd)) {
257                Jmsg(jcr, M_INFO, 0, _("RunAfter: %s"), line);
258             }
259             status = close_bpipe(bpipe);
260             if (status != 0) {
261                Jmsg(jcr, M_FATAL, 0, _("RunAfterJob returned non-zero status=%d\n"),
262                   status);
263                set_jcr_job_status(jcr, JS_FatalError);
264                update_job_end_record(jcr);
265             }
266          }
267       }
268 bail_out:
269 #ifndef JOB_QUEUE
270       release_resource_locks(jcr);
271       if (jcr->job->RescheduleOnError && 
272           jcr->JobStatus != JS_Terminated &&
273           jcr->JobStatus != JS_Canceled && 
274           jcr->job->RescheduleTimes > 0 && 
275           jcr->reschedule_count < jcr->job->RescheduleTimes) {
276
277           /*
278            * Reschedule this job by cleaning it up, but
279            *  reuse the same JobId if possible.
280            */
281          jcr->reschedule_count++;
282          jcr->sched_time = time(NULL) + jcr->job->RescheduleInterval;
283          Dmsg2(100, "Rescheduled Job %s to re-run in %d seconds.\n", jcr->Job,
284             (int)jcr->job->RescheduleInterval);
285          jcr->JobStatus = JS_Created; /* force new status */
286          dird_free_jcr(jcr);          /* partial cleanup old stuff */
287          if (jcr->JobBytes == 0) {
288             continue;                    /* reschedule the job */
289          }
290          /* 
291           * Something was actually backed up, so we cannot reuse
292           *   the old JobId or there will be database record
293           *   conflicts.  We now create a new job, copying the
294           *   appropriate fields.
295           */
296          JCR *njcr = new_jcr(sizeof(JCR), dird_free_jcr);
297          set_jcr_defaults(njcr, jcr->job);
298          njcr->reschedule_count = jcr->reschedule_count;
299          njcr->JobLevel = jcr->JobLevel;
300          njcr->JobStatus = jcr->JobStatus;
301          njcr->pool = jcr->pool;
302          njcr->store = jcr->store;
303          njcr->messages = jcr->messages;
304          run_job(njcr);
305       }
306 #endif
307       break;
308    }
309
310 #ifndef JOB_QUEUE
311    if (jcr->db) {
312       Dmsg0(200, "Close DB\n");
313       db_close_database(jcr, jcr->db);
314       jcr->db = NULL;
315    }
316    free_jcr(jcr);
317 #endif
318    Dmsg0(50, "======== End Job ==========\n");
319    sm_check(__FILE__, __LINE__, True);
320    return NULL;
321 }
322
323 /*
324  * Acquire the resources needed. These locks limit the
325  *  number of jobs by each resource. We have limits on
326  *  Jobs, Clients, Storage, and total jobs.
327  */
328 static int acquire_resource_locks(JCR *jcr)
329 {
330 #ifndef JOB_QUEUE
331    time_t now = time(NULL);
332    time_t wtime = jcr->sched_time - now;
333
334    /* Wait until scheduled time arrives */
335    if (wtime > 0 && verbose) {
336       Jmsg(jcr, M_INFO, 0, _("Job %s waiting %d seconds for scheduled start time.\n"), 
337          jcr->Job, wtime);
338       set_jcr_job_status(jcr, JS_WaitStartTime);
339    }
340    /* Check every 30 seconds if canceled */ 
341    while (wtime > 0) {
342       Dmsg2(100, "Waiting on sched time, jobid=%d secs=%d\n", jcr->JobId, wtime);
343       if (wtime > 30) {
344          wtime = 30;
345       }
346       bmicrosleep(wtime, 0);
347       if (job_canceled(jcr)) {
348          return 0;
349       }
350       wtime = jcr->sched_time - time(NULL);
351    }
352 #endif
353
354
355 #ifdef USE_SEMAPHORE
356    int stat;
357
358    /* Initialize semaphores */
359    if (jcr->store->sem.valid != SEMLOCK_VALID) {
360       if ((stat = sem_init(&jcr->store->sem, jcr->store->MaxConcurrentJobs)) != 0) {
361          Emsg1(M_ABORT, 0, _("Could not init Storage semaphore: ERR=%s\n"), strerror(stat));
362       }
363    }
364    if (jcr->client->sem.valid != SEMLOCK_VALID) {
365       if ((stat = sem_init(&jcr->client->sem, jcr->client->MaxConcurrentJobs)) != 0) {
366          Emsg1(M_ABORT, 0, _("Could not init Client semaphore: ERR=%s\n"), strerror(stat));
367       }
368    }
369    if (jcr->job->sem.valid != SEMLOCK_VALID) {
370       if ((stat = sem_init(&jcr->job->sem, jcr->job->MaxConcurrentJobs)) != 0) {
371          Emsg1(M_ABORT, 0, _("Could not init Job semaphore: ERR=%s\n"), strerror(stat));
372       }
373    }
374
375    for ( ;; ) {
376       /* Acquire semaphore */
377       set_jcr_job_status(jcr, JS_WaitJobRes);
378       if ((stat = sem_lock(&jcr->job->sem)) != 0) {
379          Emsg1(M_ABORT, 0, _("Could not acquire Job max jobs lock: ERR=%s\n"), strerror(stat));
380       }
381       set_jcr_job_status(jcr, JS_WaitClientRes);
382       if ((stat = sem_trylock(&jcr->client->sem)) != 0) {
383          if (stat == EBUSY) {
384             backoff_resource_locks(jcr, 1);
385             goto wait;
386          } else {
387             Emsg1(M_ABORT, 0, _("Could not acquire Client max jobs lock: ERR=%s\n"), strerror(stat));
388          }
389       }
390       set_jcr_job_status(jcr, JS_WaitStoreRes);
391       if ((stat = sem_trylock(&jcr->store->sem)) != 0) {
392          if (stat == EBUSY) {
393             backoff_resource_locks(jcr, 2);
394             goto wait;
395          } else {
396             Emsg1(M_ABORT, 0, _("Could not acquire Storage max jobs lock: ERR=%s\n"), strerror(stat));
397          }
398       }
399       set_jcr_job_status(jcr, JS_WaitMaxJobs);
400       if ((stat = sem_trylock(&job_lock)) != 0) {
401          if (stat == EBUSY) {
402             backoff_resource_locks(jcr, 3);
403             goto wait;
404          } else {
405             Emsg1(M_ABORT, 0, _("Could not acquire max jobs lock: ERR=%s\n"), strerror(stat));
406          }
407       }
408       break;
409
410 wait:
411       if (job_canceled(jcr)) {
412          return 0;
413       }
414       P(mutex);
415       /*
416        * Wait for a resource to be released either by backoff or
417        *  by a job terminating.
418        */
419       waiting++;
420       pthread_cond_wait(&resource_wait, &mutex);
421       waiting--;
422       V(mutex);
423       /* Try again */
424    }
425    jcr->acquired_resource_locks = true;
426 #endif
427    return 1;
428 }
429
430 #ifdef USE_SEMAPHORE
431 /*
432  * We could not get all the resource locks because 
433  *  too many jobs are running, so release any locks
434  *  we did acquire, giving others a chance to use them
435  *  while we wait.
436  */
437 static void backoff_resource_locks(JCR *jcr, int count)
438 {
439    P(mutex);
440    switch (count) {
441    case 3:
442       sem_unlock(&jcr->store->sem);
443       /* Fall through wanted */
444    case 2:
445       sem_unlock(&jcr->client->sem);
446       /* Fall through wanted */
447    case 1:
448       sem_unlock(&jcr->job->sem);
449       break;
450    }
451    /*
452     * Since we released a lock, if there are any threads
453     *  waiting, wake them up so that they can try again.
454     */
455    if (waiting > 0) {
456       pthread_cond_broadcast(&resource_wait);
457    }
458    V(mutex);
459 }
460 #endif
461
462 /*
463  * This is called at the end of the job to release
464  *   any resource limits on the number of jobs. If
465  *   there are any other jobs waiting, we wake them
466  *   up so that they can try again.
467  */
468 #ifdef USE_SEMAPHORE
469 static void release_resource_locks(JCR *jcr)
470 {
471    if (!jcr->acquired_resource_locks) {
472       return;                         /* Job canceled, no locks acquired */
473    }
474    P(mutex);
475    sem_unlock(&jcr->store->sem);
476    sem_unlock(&jcr->client->sem);
477    sem_unlock(&jcr->job->sem);
478    sem_unlock(&job_lock);
479    if (waiting > 0) {
480       pthread_cond_broadcast(&resource_wait);
481    }
482    jcr->acquired_resource_locks = false;
483    V(mutex);
484 }
485 #endif
486
487 /*
488  * Get or create a Client record for this Job
489  */
490 int get_or_create_client_record(JCR *jcr)
491 {
492    CLIENT_DBR cr;
493
494    memset(&cr, 0, sizeof(cr));
495    bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
496    cr.AutoPrune = jcr->client->AutoPrune;
497    cr.FileRetention = jcr->client->FileRetention;
498    cr.JobRetention = jcr->client->JobRetention;
499    if (!jcr->client_name) {
500       jcr->client_name = get_pool_memory(PM_NAME);
501    }
502    pm_strcpy(&jcr->client_name, jcr->client->hdr.name);
503    if (!db_create_client_record(jcr, jcr->db, &cr)) {
504       Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"), 
505          db_strerror(jcr->db));
506       return 0;
507    }
508    jcr->jr.ClientId = cr.ClientId;
509    if (cr.Uname[0]) {
510       if (!jcr->client_uname) {
511          jcr->client_uname = get_pool_memory(PM_NAME);
512       }
513       pm_strcpy(&jcr->client_uname, cr.Uname);
514    }
515    Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name, 
516       jcr->jr.ClientId);
517    return 1;
518 }
519
520
521 /*
522  * Write status and such in DB
523  */
524 void update_job_end_record(JCR *jcr)
525 {
526    if (jcr->jr.EndTime == 0) {
527       jcr->jr.EndTime = time(NULL);
528    }
529    jcr->end_time = jcr->jr.EndTime;
530    jcr->jr.JobId = jcr->JobId;
531    jcr->jr.JobStatus = jcr->JobStatus;
532    jcr->jr.JobFiles = jcr->JobFiles;
533    jcr->jr.JobBytes = jcr->JobBytes;
534    jcr->jr.VolSessionId = jcr->VolSessionId;
535    jcr->jr.VolSessionTime = jcr->VolSessionTime;
536    if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
537       Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"), 
538          db_strerror(jcr->db));
539    }
540 }
541
542 /*
543  * Takes base_name and appends (unique) current
544  *   date and time to form unique job name.
545  *
546  *  Returns: unique job name in jcr->Job
547  *    date/time in jcr->start_time
548  */
549 void create_unique_job_name(JCR *jcr, char *base_name)
550 {
551    /* Job start mutex */
552    static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
553    static time_t last_start_time = 0;
554    time_t now;
555    struct tm tm;
556    char dt[MAX_TIME_LENGTH];
557    char name[MAX_NAME_LENGTH];
558    char *p;
559
560    /* Guarantee unique start time -- maximum one per second, and
561     * thus unique Job Name 
562     */
563    P(mutex);                          /* lock creation of jobs */
564    now = time(NULL);
565    while (now == last_start_time) {
566       bmicrosleep(0, 500000);
567       now = time(NULL);
568    }
569    last_start_time = now;
570    V(mutex);                          /* allow creation of jobs */
571    jcr->start_time = now;
572    /* Form Unique JobName */
573    localtime_r(&now, &tm);
574    /* Use only characters that are permitted in Windows filenames */
575    strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm); 
576    bstrncpy(name, base_name, sizeof(name));
577    name[sizeof(name)-22] = 0;          /* truncate if too long */
578    bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s", name, dt); /* add date & time */
579    /* Convert spaces into underscores */
580    for (p=jcr->Job; *p; p++) {
581       if (*p == ' ') {
582          *p = '_';
583       }
584    }
585 }
586
587 /*
588  * Free the Job Control Record if no one is still using it.
589  *  Called from main free_jcr() routine in src/lib/jcr.c so
590  *  that we can do our Director specific cleanup of the jcr.
591  */
592 void dird_free_jcr(JCR *jcr)
593 {
594    Dmsg0(200, "Start dird free_jcr\n");
595
596    if (jcr->sd_auth_key) {
597       free(jcr->sd_auth_key);
598       jcr->sd_auth_key = NULL;
599    }
600    if (jcr->where) {
601       free(jcr->where);
602       jcr->where = NULL;
603    }
604    if (jcr->file_bsock) {
605       Dmsg0(200, "Close File bsock\n");
606       bnet_close(jcr->file_bsock);
607       jcr->file_bsock = NULL;
608    }
609    if (jcr->store_bsock) {
610       Dmsg0(200, "Close Store bsock\n");
611       bnet_close(jcr->store_bsock);
612       jcr->store_bsock = NULL;
613    }
614    if (jcr->fname) {  
615       Dmsg0(200, "Free JCR fname\n");
616       free_pool_memory(jcr->fname);
617       jcr->fname = NULL;
618    }
619    if (jcr->stime) {
620       Dmsg0(200, "Free JCR stime\n");
621       free_pool_memory(jcr->stime);
622       jcr->stime = NULL;
623    }
624    if (jcr->RestoreBootstrap) {
625       free(jcr->RestoreBootstrap);
626       jcr->RestoreBootstrap = NULL;
627    }
628    if (jcr->client_uname) {
629       free_pool_memory(jcr->client_uname);
630       jcr->client_uname = NULL;
631    }
632    Dmsg0(200, "End dird free_jcr\n");
633 }
634
635 /*
636  * Set some defaults in the JCR necessary to
637  * run. These items are pulled from the job
638  * definition as defaults, but can be overridden
639  * later either by the Run record in the Schedule resource,
640  * or by the Console program.
641  */
642 void set_jcr_defaults(JCR *jcr, JOB *job)
643 {
644    jcr->job = job;
645    jcr->JobType = job->JobType;
646    jcr->JobLevel = job->level;
647    jcr->JobPriority = job->Priority;
648    jcr->store = job->storage;
649    jcr->client = job->client;
650    if (!jcr->client_name) {
651       jcr->client_name = get_pool_memory(PM_NAME);
652    }
653    pm_strcpy(&jcr->client_name, jcr->client->hdr.name);
654    jcr->pool = job->pool;
655    jcr->catalog = job->client->catalog;
656    jcr->fileset = job->fileset;
657    jcr->messages = job->messages; 
658    if (jcr->RestoreBootstrap) {
659       free(jcr->RestoreBootstrap);
660    }
661    /* This can be overridden by Console program */
662    if (job->RestoreBootstrap) {
663       jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
664    }
665    /* If no default level given, set one */
666    if (jcr->JobLevel == 0) {
667       switch (jcr->JobType) {
668       case JT_VERIFY:
669          jcr->JobLevel = L_VERIFY_CATALOG;
670          break;
671       case JT_BACKUP:
672          jcr->JobLevel = L_INCREMENTAL;
673          break;
674       case JT_RESTORE:
675       case JT_ADMIN:
676          jcr->JobLevel = L_FULL;
677          break;
678       default:
679          break;
680       }
681    }
682 }
683
684 /*
685  * Edit codes into Run command
686  *  %% = %
687  *  %c = Client's name
688  *  %d = Director's name
689  *  %i = JobId
690  *  %e = Job Exit
691  *  %j = Job
692  *  %l = Job Level
693  *  %n = Job name
694  *  %t = Job type
695  *
696  *  omsg = edited output message
697  *  imsg = input string containing edit codes (%x)
698  *
699  */
700 static char *edit_run_codes(JCR *jcr, char *omsg, char *imsg) 
701 {
702    char *p;
703    const char *str;
704    char add[20];
705
706    *omsg = 0;
707    Dmsg1(200, "edit_run_codes: %s\n", imsg);
708    for (p=imsg; *p; p++) {
709       if (*p == '%') {
710          switch (*++p) {
711          case '%':
712             str = "%";
713             break;
714          case 'c':
715             str = jcr->client_name;
716             if (!str) {
717                str = "";
718             }
719             break;
720          case 'd':
721             str = my_name;
722             break;
723          case 'e':
724             str = job_status_to_str(jcr->JobStatus);
725             break;
726          case 'i':
727             sprintf(add, "%d", jcr->JobId);
728             str = add;
729             break;
730          case 'j':                    /* Job */
731             str = jcr->Job;
732             break;
733          case 'l':
734             str = job_level_to_str(jcr->JobLevel);
735             break;
736          case 'n':
737             str = jcr->job->hdr.name;
738             break;
739          case 't':
740             str = job_type_to_str(jcr->JobType);
741             break;
742          default:
743             add[0] = '%';
744             add[1] = *p;
745             add[2] = 0;
746             str = add;
747             break;
748          }
749       } else {
750          add[0] = *p;
751          add[1] = 0;
752          str = add;
753       }
754       Dmsg1(200, "add_str %s\n", str);
755       pm_strcat(&omsg, (char *)str);
756       Dmsg1(200, "omsg=%s\n", omsg);
757    }
758    return omsg;
759 }