]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/job.c
Add new files
[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 void release_resource_locks(JCR *jcr);
36 static int acquire_resource_locks(JCR *jcr);
37 #ifdef USE_SEMAPHORE
38 static void backoff_resource_locks(JCR *jcr, int count);
39 #endif
40
41 /* Exported subroutines */
42 void run_job(JCR *jcr);
43
44
45 /* Imported subroutines */
46 extern void term_scheduler();
47 extern void term_ua_server();
48 extern int do_backup(JCR *jcr);
49 extern int do_admin(JCR *jcr);
50 extern int do_restore(JCR *jcr);
51 extern int do_verify(JCR *jcr);
52
53 #ifdef USE_SEMAPHORE
54 static semlock_t job_lock;
55 static pthread_mutex_t mutex;
56 static pthread_cond_t  resource_wait;
57 static int waiting = 0;               /* count of waiting threads */
58 #else
59 /* Queue of jobs to be run */
60 workq_t job_wq;                   /* our job work queue */
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    if ((stat = workq_init(&job_wq, max_workers, job_thread)) != 0) {
79       Emsg1(M_ABORT, 0, _("Could not init job work queue: ERR=%s\n"), strerror(stat));
80    }
81 #endif
82    return;
83 }
84
85 /*
86  * Run a job -- typically called by the scheduler, but may also
87  *              be called by the UA (Console program).
88  *
89  */
90 void run_job(JCR *jcr)
91 {
92    int stat, errstat;
93 #ifdef USE_SEMAPHORE
94    pthread_t tid;
95 #else
96    workq_ele_t *work_item;
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(30, "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    /* Queue the job to be run */
157    if ((stat = workq_add(&job_wq, (void *)jcr, &work_item, 0)) != 0) {
158       Emsg1(M_ABORT, 0, _("Could not add job to work queue: ERR=%s\n"), strerror(stat));
159    }
160    jcr->work_item = work_item;
161 #endif
162    Dmsg0(200, "Done run_job()\n");
163 }
164
165 /* 
166  * This is the engine called by workq_add() when we were pulled                
167  *  from the work queue.
168  *  At this point, we are running in our own thread 
169  */
170 static void *job_thread(void *arg)
171 {
172    JCR *jcr = (JCR *)arg;
173
174    pthread_detach(pthread_self());
175    sm_check(__FILE__, __LINE__, True);
176
177    for ( ;; ) {
178       if (!acquire_resource_locks(jcr)) {
179          set_jcr_job_status(jcr, JS_Canceled);
180       }
181
182       Dmsg0(200, "=====Start Job=========\n");
183       jcr->start_time = time(NULL);      /* set the real start time */
184       set_jcr_job_status(jcr, JS_Running);
185
186       if (job_canceled(jcr)) {
187          update_job_end_record(jcr);
188       } else if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
189           (utime_t)(jcr->start_time - jcr->sched_time)) {
190          Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
191          set_jcr_job_status(jcr, JS_Canceled);
192          update_job_end_record(jcr);
193       } else {
194
195          /* Run Job */
196          if (jcr->job->RunBeforeJob) {
197             POOLMEM *before = get_pool_memory(PM_FNAME);
198             int status;
199             BPIPE *bpipe;
200             char line[MAXSTRING];
201             
202             before = edit_run_codes(jcr, before, jcr->job->RunBeforeJob);
203             bpipe = open_bpipe(before, 0, "r");
204             while (fgets(line, sizeof(line), bpipe->rfd)) {
205                Jmsg(jcr, M_INFO, 0, _("RunBefore: %s"), line);
206             }
207             status = close_bpipe(bpipe);
208             if (status != 0) {
209                Jmsg(jcr, M_FATAL, 0, _("RunBeforeJob returned non-zero status=%d\n"),
210                   status);
211                set_jcr_job_status(jcr, JS_FatalError);
212                update_job_end_record(jcr);
213                free_pool_memory(before);
214                goto bail_out;
215             }
216             free_pool_memory(before);
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             while (fgets(line, sizeof(line), bpipe->rfd)) {
256                Jmsg(jcr, M_INFO, 0, _("RunAfter: %s"), line);
257             }
258             status = close_bpipe(bpipe);
259             if (status != 0) {
260                Jmsg(jcr, M_FATAL, 0, _("RunAfterJob returned non-zero status=%d\n"),
261                   status);
262                set_jcr_job_status(jcr, JS_FatalError);
263                update_job_end_record(jcr);
264             }
265             free_pool_memory(after);
266          }
267       }
268 bail_out:
269       release_resource_locks(jcr);
270       if (jcr->job->RescheduleOnError && 
271           jcr->JobStatus != JS_Terminated &&
272           jcr->JobStatus != JS_Canceled && 
273           jcr->job->RescheduleTimes > 0 && 
274           jcr->reschedule_count < jcr->job->RescheduleTimes) {
275
276           /*
277            * Reschedule this job by cleaning it up, but
278            *  reuse the same JobId if possible.
279            */
280          jcr->reschedule_count++;
281          jcr->sched_time = time(NULL) + jcr->job->RescheduleInterval;
282          Dmsg2(000, "Reschedule Job %s in %d seconds.\n", jcr->Job,
283             (int)jcr->job->RescheduleInterval);
284          jcr->JobStatus = JS_Created; /* force new status */
285          dird_free_jcr(jcr);          /* partial cleanup old stuff */
286          if (jcr->JobBytes == 0) {
287             continue;                    /* reschedule the job */
288          }
289          /* 
290           * Something was actually backed up, so we cannot reuse
291           *   the old JobId or there will be database record
292           *   conflicts.  We now create a new job, copying the
293           *   appropriate fields.
294           */
295          JCR *njcr = new_jcr(sizeof(JCR), dird_free_jcr);
296          set_jcr_defaults(njcr, jcr->job);
297          njcr->reschedule_count = jcr->reschedule_count;
298          njcr->JobLevel = jcr->JobLevel;
299          njcr->JobStatus = jcr->JobStatus;
300          njcr->pool = jcr->pool;
301          njcr->store = jcr->store;
302          njcr->messages = jcr->messages;
303          run_job(njcr);
304       }
305       break;
306    }
307
308    if (jcr->db) {
309       Dmsg0(200, "Close DB\n");
310       db_close_database(jcr, jcr->db);
311       jcr->db = NULL;
312    }
313    free_jcr(jcr);
314    Dmsg0(50, "======== End Job ==========\n");
315    sm_check(__FILE__, __LINE__, True);
316    return NULL;
317 }
318
319 /*
320  * Acquire the resources needed. These locks limit the
321  *  number of jobs by each resource. We have limits on
322  *  Jobs, Clients, Storage, and total jobs.
323  */
324 static int acquire_resource_locks(JCR *jcr)
325 {
326    time_t now = time(NULL);
327    time_t wtime = jcr->sched_time - now;
328
329    /* Wait until scheduled time arrives */
330    if (wtime > 0 && verbose) {
331       Jmsg(jcr, M_INFO, 0, _("Job %s waiting %d seconds for scheduled start time.\n"), 
332          jcr->Job, wtime);
333       set_jcr_job_status(jcr, JS_WaitStartTime);
334    }
335    /* Check every 30 seconds if canceled */ 
336    while (wtime > 0) {
337       Dmsg2(100, "Waiting on sched time, jobid=%d secs=%d\n", jcr->JobId, wtime);
338       if (wtime > 30) {
339          wtime = 30;
340       }
341       bmicrosleep(wtime, 0);
342       if (job_canceled(jcr)) {
343          return 0;
344       }
345       wtime = jcr->sched_time - time(NULL);
346    }
347
348
349 #ifdef USE_SEMAPHORE
350    int stat;
351
352    /* Initialize semaphores */
353    if (jcr->store->sem.valid != SEMLOCK_VALID) {
354       if ((stat = sem_init(&jcr->store->sem, jcr->store->MaxConcurrentJobs)) != 0) {
355          Emsg1(M_ABORT, 0, _("Could not init Storage semaphore: ERR=%s\n"), strerror(stat));
356       }
357    }
358    if (jcr->client->sem.valid != SEMLOCK_VALID) {
359       if ((stat = sem_init(&jcr->client->sem, jcr->client->MaxConcurrentJobs)) != 0) {
360          Emsg1(M_ABORT, 0, _("Could not init Client semaphore: ERR=%s\n"), strerror(stat));
361       }
362    }
363    if (jcr->job->sem.valid != SEMLOCK_VALID) {
364       if ((stat = sem_init(&jcr->job->sem, jcr->job->MaxConcurrentJobs)) != 0) {
365          Emsg1(M_ABORT, 0, _("Could not init Job semaphore: ERR=%s\n"), strerror(stat));
366       }
367    }
368
369    for ( ;; ) {
370       /* Acquire semaphore */
371       set_jcr_job_status(jcr, JS_WaitJobRes);
372       if ((stat = sem_lock(&jcr->job->sem)) != 0) {
373          Emsg1(M_ABORT, 0, _("Could not acquire Job max jobs lock: ERR=%s\n"), strerror(stat));
374       }
375       set_jcr_job_status(jcr, JS_WaitClientRes);
376       if ((stat = sem_trylock(&jcr->client->sem)) != 0) {
377          if (stat == EBUSY) {
378             backoff_resource_locks(jcr, 1);
379             goto wait;
380          } else {
381             Emsg1(M_ABORT, 0, _("Could not acquire Client max jobs lock: ERR=%s\n"), strerror(stat));
382          }
383       }
384       set_jcr_job_status(jcr, JS_WaitStoreRes);
385       if ((stat = sem_trylock(&jcr->store->sem)) != 0) {
386          if (stat == EBUSY) {
387             backoff_resource_locks(jcr, 2);
388             goto wait;
389          } else {
390             Emsg1(M_ABORT, 0, _("Could not acquire Storage max jobs lock: ERR=%s\n"), strerror(stat));
391          }
392       }
393       set_jcr_job_status(jcr, JS_WaitMaxJobs);
394       if ((stat = sem_trylock(&job_lock)) != 0) {
395          if (stat == EBUSY) {
396             backoff_resource_locks(jcr, 3);
397             goto wait;
398          } else {
399             Emsg1(M_ABORT, 0, _("Could not acquire max jobs lock: ERR=%s\n"), strerror(stat));
400          }
401       }
402       break;
403
404 wait:
405       if (job_canceled(jcr)) {
406          return 0;
407       }
408       P(mutex);
409       /*
410        * Wait for a resource to be released either by backoff or
411        *  by a job terminating.
412        */
413       waiting++;
414       pthread_cond_wait(&resource_wait, &mutex);
415       waiting--;
416       V(mutex);
417       /* Try again */
418    }
419    jcr->acquired_resource_locks = 1;
420 #endif
421    return 1;
422 }
423
424 #ifdef USE_SEMAPHORE
425 /*
426  * We could not get all the resource locks because 
427  *  too many jobs are running, so release any locks
428  *  we did acquire, giving others a chance to use them
429  *  while we wait.
430  */
431 static void backoff_resource_locks(JCR *jcr, int count)
432 {
433    P(mutex);
434    switch (count) {
435    case 3:
436       sem_unlock(&jcr->store->sem);
437       /* Fall through wanted */
438    case 2:
439       sem_unlock(&jcr->client->sem);
440       /* Fall through wanted */
441    case 1:
442       sem_unlock(&jcr->job->sem);
443       break;
444    }
445    /*
446     * Since we released a lock, if there are any threads
447     *  waiting, wake them up so that they can try again.
448     */
449    if (waiting > 0) {
450       pthread_cond_broadcast(&resource_wait);
451    }
452    V(mutex);
453 }
454 #endif
455
456 /*
457  * This is called at the end of the job to release
458  *   any resource limits on the number of jobs. If
459  *   there are any other jobs waiting, we wake them
460  *   up so that they can try again.
461  */
462 static void release_resource_locks(JCR *jcr)
463 {
464    if (!jcr->acquired_resource_locks) {
465       return;                         /* Job canceled, no locks acquired */
466    }
467 #ifdef USE_SEMAPHORE
468    P(mutex);
469    sem_unlock(&jcr->store->sem);
470    sem_unlock(&jcr->client->sem);
471    sem_unlock(&jcr->job->sem);
472    sem_unlock(&job_lock);
473    if (waiting > 0) {
474       pthread_cond_broadcast(&resource_wait);
475    }
476    jcr->acquired_resource_locks = 0;
477    V(mutex);
478 #endif
479 }
480
481 /*
482  * Get or create a Client record for this Job
483  */
484 int get_or_create_client_record(JCR *jcr)
485 {
486    CLIENT_DBR cr;
487
488    memset(&cr, 0, sizeof(cr));
489    bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
490    cr.AutoPrune = jcr->client->AutoPrune;
491    cr.FileRetention = jcr->client->FileRetention;
492    cr.JobRetention = jcr->client->JobRetention;
493    if (!jcr->client_name) {
494       jcr->client_name = get_pool_memory(PM_NAME);
495    }
496    pm_strcpy(&jcr->client_name, jcr->client->hdr.name);
497    if (!db_create_client_record(jcr, jcr->db, &cr)) {
498       Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"), 
499          db_strerror(jcr->db));
500       return 0;
501    }
502    jcr->jr.ClientId = cr.ClientId;
503    if (cr.Uname[0]) {
504       if (!jcr->client_uname) {
505          jcr->client_uname = get_pool_memory(PM_NAME);
506       }
507       pm_strcpy(&jcr->client_uname, cr.Uname);
508    }
509    Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name, 
510       jcr->jr.ClientId);
511    return 1;
512 }
513
514
515 /*
516  * Write status and such in DB
517  */
518 void update_job_end_record(JCR *jcr)
519 {
520    if (jcr->jr.EndTime == 0) {
521       jcr->jr.EndTime = time(NULL);
522    }
523    jcr->end_time = jcr->jr.EndTime;
524    jcr->jr.JobId = jcr->JobId;
525    jcr->jr.JobStatus = jcr->JobStatus;
526    jcr->jr.JobFiles = jcr->JobFiles;
527    jcr->jr.JobBytes = jcr->JobBytes;
528    jcr->jr.VolSessionId = jcr->VolSessionId;
529    jcr->jr.VolSessionTime = jcr->VolSessionTime;
530    if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
531       Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"), 
532          db_strerror(jcr->db));
533    }
534 }
535
536 /*
537  * Takes base_name and appends (unique) current
538  *   date and time to form unique job name.
539  *
540  *  Returns: unique job name in jcr->Job
541  *    date/time in jcr->start_time
542  */
543 void create_unique_job_name(JCR *jcr, char *base_name)
544 {
545    /* Job start mutex */
546    static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
547    static time_t last_start_time = 0;
548    time_t now;
549    struct tm tm;
550    char dt[MAX_TIME_LENGTH];
551    char name[MAX_NAME_LENGTH];
552    char *p;
553
554    /* Guarantee unique start time -- maximum one per second, and
555     * thus unique Job Name 
556     */
557    P(mutex);                          /* lock creation of jobs */
558    now = time(NULL);
559    while (now == last_start_time) {
560       bmicrosleep(0, 500000);
561       now = time(NULL);
562    }
563    last_start_time = now;
564    V(mutex);                          /* allow creation of jobs */
565    jcr->start_time = now;
566    /* Form Unique JobName */
567    localtime_r(&now, &tm);
568    /* Use only characters that are permitted in Windows filenames */
569    strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm); 
570    bstrncpy(name, base_name, sizeof(name));
571    name[sizeof(name)-22] = 0;          /* truncate if too long */
572    bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s", name, dt); /* add date & time */
573    /* Convert spaces into underscores */
574    for (p=jcr->Job; *p; p++) {
575       if (*p == ' ') {
576          *p = '_';
577       }
578    }
579 }
580
581 /*
582  * Free the Job Control Record if no one is still using it.
583  *  Called from main free_jcr() routine in src/lib/jcr.c so
584  *  that we can do our Director specific cleanup of the jcr.
585  */
586 void dird_free_jcr(JCR *jcr)
587 {
588    Dmsg0(200, "Start dird free_jcr\n");
589
590    if (jcr->sd_auth_key) {
591       free(jcr->sd_auth_key);
592       jcr->sd_auth_key = NULL;
593    }
594    if (jcr->where) {
595       free(jcr->where);
596       jcr->where = NULL;
597    }
598    if (jcr->file_bsock) {
599       Dmsg0(200, "Close File bsock\n");
600       bnet_close(jcr->file_bsock);
601       jcr->file_bsock = NULL;
602    }
603    if (jcr->store_bsock) {
604       Dmsg0(200, "Close Store bsock\n");
605       bnet_close(jcr->store_bsock);
606       jcr->store_bsock = NULL;
607    }
608    if (jcr->fname) {  
609       Dmsg0(200, "Free JCR fname\n");
610       free_pool_memory(jcr->fname);
611       jcr->fname = NULL;
612    }
613    if (jcr->stime) {
614       Dmsg0(200, "Free JCR stime\n");
615       free_pool_memory(jcr->stime);
616       jcr->stime = NULL;
617    }
618    if (jcr->RestoreBootstrap) {
619       free(jcr->RestoreBootstrap);
620       jcr->RestoreBootstrap = NULL;
621    }
622    if (jcr->client_uname) {
623       free_pool_memory(jcr->client_uname);
624       jcr->client_uname = NULL;
625    }
626    Dmsg0(200, "End dird free_jcr\n");
627 }
628
629 /*
630  * Set some defaults in the JCR necessary to
631  * run. These items are pulled from the job
632  * definition as defaults, but can be overridden
633  * later either by the Run record in the Schedule resource,
634  * or by the Console program.
635  */
636 void set_jcr_defaults(JCR *jcr, JOB *job)
637 {
638    jcr->job = job;
639    jcr->JobType = job->JobType;
640    jcr->JobLevel = job->level;
641    jcr->store = job->storage;
642    jcr->client = job->client;
643    if (!jcr->client_name) {
644       jcr->client_name = get_pool_memory(PM_NAME);
645    }
646    pm_strcpy(&jcr->client_name, jcr->client->hdr.name);
647    jcr->pool = job->pool;
648    jcr->catalog = job->client->catalog;
649    jcr->fileset = job->fileset;
650    jcr->messages = job->messages; 
651    if (jcr->RestoreBootstrap) {
652       free(jcr->RestoreBootstrap);
653    }
654    /* This can be overridden by Console program */
655    if (job->RestoreBootstrap) {
656       jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
657    }
658    /* If no default level given, set one */
659    if (jcr->JobLevel == 0) {
660       switch (jcr->JobType) {
661       case JT_VERIFY:
662          jcr->JobLevel = L_VERIFY_CATALOG;
663          break;
664       case JT_BACKUP:
665          jcr->JobLevel = L_INCREMENTAL;
666          break;
667       case JT_RESTORE:
668       case JT_ADMIN:
669          jcr->JobLevel = L_FULL;
670          break;
671       default:
672          break;
673       }
674    }
675 }
676
677 /*
678  * Edit codes into Run command
679  *  %% = %
680  *  %c = Client's name
681  *  %d = Director's name
682  *  %i = JobId
683  *  %e = Job Exit
684  *  %j = Job
685  *  %l = Job Level
686  *  %n = Job name
687  *  %t = Job type
688  *
689  *  omsg = edited output message
690  *  imsg = input string containing edit codes (%x)
691  *
692  */
693 static char *edit_run_codes(JCR *jcr, char *omsg, char *imsg) 
694 {
695    char *p;
696    const char *str;
697    char add[20];
698
699    *omsg = 0;
700    Dmsg1(200, "edit_run_codes: %s\n", imsg);
701    for (p=imsg; *p; p++) {
702       if (*p == '%') {
703          switch (*++p) {
704          case '%':
705             str = "%";
706             break;
707          case 'c':
708             str = jcr->client_name;
709             if (!str) {
710                str = "";
711             }
712             break;
713          case 'd':
714             str = my_name;
715             break;
716          case 'e':
717             str = job_status_to_str(jcr->JobStatus);
718             break;
719          case 'i':
720             sprintf(add, "%d", jcr->JobId);
721             str = add;
722             break;
723          case 'j':                    /* Job */
724             str = jcr->Job;
725             break;
726          case 'l':
727             str = job_level_to_str(jcr->JobLevel);
728             break;
729          case 'n':
730             str = jcr->job->hdr.name;
731             break;
732          case 't':
733             str = job_type_to_str(jcr->JobType);
734             break;
735          default:
736             add[0] = '%';
737             add[1] = *p;
738             add[2] = 0;
739             str = add;
740             break;
741          }
742       } else {
743          add[0] = *p;
744          add[1] = 0;
745          str = add;
746       }
747       Dmsg1(200, "add_str %s\n", str);
748       pm_strcat(&omsg, (char *)str);
749       Dmsg1(200, "omsg=%s\n", omsg);
750    }
751    return omsg;
752 }