]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/job.c
- Modify wait during use_device to happen only after all devices
[bacula/bacula] / bacula / src / stored / job.c
1 /*
2  *   Job control and execution for Storage Daemon
3  *
4  *   Kern Sibbald, MM
5  *
6  *   Version $Id$
7  *
8  */
9 /*
10    Copyright (C) 2000-2005 Kern Sibbald
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
14    version 2 as ammended with additional clauses defined in the
15    file LICENSE in the main source directory.
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 
20    the file LICENSE for additional details.
21
22  */
23
24 #include "bacula.h"
25 #include "stored.h"
26
27 /* Imported variables */
28 extern uint32_t VolSessionTime;
29
30 /* Imported functions */
31 extern uint32_t newVolSessionId();
32
33 /* Forward referenced functions */
34 static bool use_storage_cmd(JCR *jcr);
35
36 /* Requests from the Director daemon */
37 static char jobcmd[] = "JobId=%d job=%127s job_name=%127s client_name=%127s "
38       "type=%d level=%d FileSet=%127s NoAttr=%d SpoolAttr=%d FileSetMD5=%127s "
39       "SpoolData=%d WritePartAfterJob=%d NewVol=%d\n";
40 static char use_storage[]  = "use storage=%127s media_type=%127s "
41    "pool_name=%127s pool_type=%127s append=%d copy=%d stripe=%d\n";
42 static char use_device[]  = "use device=%127s\n";
43 //static char query_device[] = "query device=%127s";
44
45
46 /* Responses sent to Director daemon */
47 static char OKjob[]     = "3000 OK Job SDid=%u SDtime=%u Authorization=%s\n";
48 static char OK_device[] = "3000 OK use device device=%s\n";
49 static char NO_device[] = "3924 Device \"%s\" not in SD Device resources.\n";
50 //static char NOT_open[]  = "3925 Device \"%s\" could not be opened or does not exist.\n";
51 static char BAD_use[]   = "3913 Bad use command: %s\n";
52 static char BAD_job[]   = "3915 Bad Job command: %s\n";
53 //static char OK_query[]  = "3001 OK query\n";
54 //static char NO_query[]  = "3918 Query failed\n";
55 //static char BAD_query[] = "3917 Bad query command: %s\n";
56
57 /*
58  * Director requests us to start a job
59  * Basic tasks done here:
60  *  - We pickup the JobId to be run from the Director.
61  *  - We pickup the device, media, and pool from the Director
62  *  - Wait for a connection from the File Daemon (FD)
63  *  - Accept commands from the FD (i.e. run the job)
64  *  - Return when the connection is terminated or
65  *    there is an error.
66  */
67 bool job_cmd(JCR *jcr)
68 {
69    int JobId;
70    char auth_key[100];
71    BSOCK *dir = jcr->dir_bsock;
72    POOL_MEM job_name, client_name, job, fileset_name, fileset_md5;
73    int JobType, level, spool_attributes, no_attributes, spool_data;
74    int write_part_after_job, NewVol;
75
76    JCR *ojcr;
77
78    /*
79     * Get JobId and permissions from Director
80     */
81    Dmsg1(100, "<dird: %s\n", dir->msg);
82    if (sscanf(dir->msg, jobcmd, &JobId, job.c_str(), job_name.c_str(),
83               client_name.c_str(),
84               &JobType, &level, fileset_name.c_str(), &no_attributes,
85               &spool_attributes, fileset_md5.c_str(), &spool_data, 
86               &write_part_after_job, &NewVol) != 13) {
87       pm_strcpy(jcr->errmsg, dir->msg);
88       bnet_fsend(dir, BAD_job, jcr->errmsg);
89       Dmsg1(100, ">dird: %s\n", dir->msg);
90       Emsg1(M_FATAL, 0, _("Bad Job Command from Director: %s\n"), jcr->errmsg);
91       set_jcr_job_status(jcr, JS_ErrorTerminated);
92       return false;
93    }
94    /*
95     * Since this job could be rescheduled, we
96     *  check to see if we have it already. If so
97     *  free the old jcr and use the new one.
98     */
99    ojcr = get_jcr_by_full_name(job.c_str());
100    if (ojcr && !ojcr->authenticated) {
101       Dmsg2(100, "Found ojcr=0x%x Job %s\n", (unsigned)(long)ojcr, job.c_str());
102       free_jcr(ojcr);
103    }
104    jcr->JobId = JobId;
105    jcr->VolSessionId = newVolSessionId();
106    jcr->VolSessionTime = VolSessionTime;
107    bstrncpy(jcr->Job, job, sizeof(jcr->Job));
108    unbash_spaces(job_name);
109    jcr->job_name = get_pool_memory(PM_NAME);
110    pm_strcpy(jcr->job_name, job_name);
111    unbash_spaces(client_name);
112    jcr->client_name = get_pool_memory(PM_NAME);
113    pm_strcpy(jcr->client_name, client_name);
114    unbash_spaces(fileset_name);
115    jcr->fileset_name = get_pool_memory(PM_NAME);
116    pm_strcpy(jcr->fileset_name, fileset_name);
117    jcr->JobType = JobType;
118    jcr->JobLevel = level;
119    jcr->no_attributes = no_attributes;
120    jcr->spool_attributes = spool_attributes;
121    jcr->spool_data = spool_data;
122    jcr->write_part_after_job = write_part_after_job;
123    jcr->fileset_md5 = get_pool_memory(PM_NAME);
124    pm_strcpy(jcr->fileset_md5, fileset_md5);
125    jcr->NewVolEachJob = NewVol;
126
127    jcr->authenticated = false;
128
129    /*
130     * Pass back an authorization key for the File daemon
131     */
132    make_session_key(auth_key, NULL, 1);
133    bnet_fsend(dir, OKjob, jcr->VolSessionId, jcr->VolSessionTime, auth_key);
134    Dmsg1(100, ">dird: %s", dir->msg);
135    jcr->sd_auth_key = bstrdup(auth_key);
136    memset(auth_key, 0, sizeof(auth_key));
137    generate_daemon_event(jcr, "JobStart");
138    return true;
139 }
140
141 bool use_cmd(JCR *jcr) 
142 {
143    /*
144     * Wait for the device, media, and pool information
145     */
146    if (!use_storage_cmd(jcr)) {
147       set_jcr_job_status(jcr, JS_ErrorTerminated);
148       memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
149       return false;
150    }
151    return true;
152 }
153
154 bool run_cmd(JCR *jcr)
155 {
156    struct timeval tv;
157    struct timezone tz;
158    struct timespec timeout;
159    int errstat;
160
161    Dmsg1(100, "Run_cmd: %s\n", jcr->dir_bsock->msg);
162    /* The following jobs don't need the FD */
163    switch (jcr->JobType) {
164    case JT_MIGRATION:
165    case JT_COPY:
166    case JT_ARCHIVE:
167       jcr->authenticated = true;
168       run_job(jcr);
169       return false;
170    }
171
172    set_jcr_job_status(jcr, JS_WaitFD);          /* wait for FD to connect */
173    dir_send_job_status(jcr);
174
175    gettimeofday(&tv, &tz);
176    timeout.tv_nsec = tv.tv_usec * 1000;
177    timeout.tv_sec = tv.tv_sec + 30 * 60;        /* wait 30 minutes */
178
179    Dmsg1(100, "%s waiting on FD to contact SD\n", jcr->Job);
180    /*
181     * Wait for the File daemon to contact us to start the Job,
182     *  when he does, we will be released, unless the 30 minutes
183     *  expires.
184     */
185    P(jcr->mutex);
186    for ( ;!job_canceled(jcr); ) {
187       errstat = pthread_cond_timedwait(&jcr->job_start_wait, &jcr->mutex, &timeout);
188       if (errstat == 0 || errstat == ETIMEDOUT) {
189          break;
190       }
191    }
192    V(jcr->mutex);
193
194    memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
195
196    if (jcr->authenticated && !job_canceled(jcr)) {
197       Dmsg1(100, "Running job %s\n", jcr->Job);
198       run_job(jcr);                   /* Run the job */
199    }
200    return false;
201 }
202
203 /*
204  * After receiving a connection (in job.c) if it is
205  *   from the File daemon, this routine is called.
206  */
207 void handle_filed_connection(BSOCK *fd, char *job_name)
208 {
209    JCR *jcr;
210
211    bmicrosleep(0, 50000);             /* wait 50 millisecs */
212    if (!(jcr=get_jcr_by_full_name(job_name))) {
213       Jmsg1(NULL, M_FATAL, 0, _("Job name not found: %s\n"), job_name);
214       Dmsg1(100, "Job name not found: %s\n", job_name);
215       return;
216    }
217
218    jcr->file_bsock = fd;
219    jcr->file_bsock->jcr = jcr;
220
221    Dmsg1(110, "Found Job %s\n", job_name);
222
223    if (jcr->authenticated) {
224       Jmsg2(jcr, M_FATAL, 0, "Hey!!!! JobId %u Job %s already authenticated.\n",
225          jcr->JobId, jcr->Job);
226       free_jcr(jcr);
227       return;
228    }
229
230    /*
231     * Authenticate the File daemon
232     */
233    if (jcr->authenticated || !authenticate_filed(jcr)) {
234       Dmsg1(100, "Authentication failed Job %s\n", jcr->Job);
235       Jmsg(jcr, M_FATAL, 0, _("Unable to authenticate File daemon\n"));
236    } else {
237       jcr->authenticated = true;
238       Dmsg1(110, "OK Authentication Job %s\n", jcr->Job);
239    }
240
241    P(jcr->mutex);
242    if (!jcr->authenticated) {
243       set_jcr_job_status(jcr, JS_ErrorTerminated);
244    }
245    pthread_cond_signal(&jcr->job_start_wait); /* wake waiting job */
246    V(jcr->mutex);
247    free_jcr(jcr);
248    return;
249 }
250
251
252 /*
253  *   Use Device command from Director
254  *   He tells is what Device Name to use, the Media Type,
255  *      the Pool Name, and the Pool Type.
256  *
257  *    Ensure that the device exists and is opened, then store
258  *      the media and pool info in the JCR.
259  */
260 class DIRSTORE {
261 public:
262    alist *device;
263    char name[MAX_NAME_LENGTH];
264    char media_type[MAX_NAME_LENGTH];
265    char pool_name[MAX_NAME_LENGTH];
266    char pool_type[MAX_NAME_LENGTH];
267 };
268
269 static int search_res_for_device(JCR *jcr, DIRSTORE *store, char *device_name, int append);
270
271 static bool use_storage_cmd(JCR *jcr)
272 {
273    POOL_MEM store_name, dev_name, media_type, pool_name, pool_type;
274    BSOCK *dir = jcr->dir_bsock;
275    int append;
276    bool ok;       
277    int Copy, Stripe;
278    alist *dirstore;   
279    DIRSTORE *store;
280    char *device_name;
281    DCR *dcr = NULL;
282    /*
283     * If there are multiple devices, the director sends us
284     *   use_device for each device that it wants to use.
285     */
286    Dmsg1(100, "<dird: %s", dir->msg);
287    dirstore = New(alist(10, not_owned_by_alist));
288    do {
289       ok = sscanf(dir->msg, use_storage, store_name.c_str(), 
290                   media_type.c_str(), pool_name.c_str(), 
291                   pool_type.c_str(), &append, &Copy, &Stripe) == 7;
292       if (!ok) {
293          break;
294       }
295       unbash_spaces(store_name);
296       unbash_spaces(media_type);
297       unbash_spaces(pool_name);
298       unbash_spaces(pool_type);
299       store = new DIRSTORE;
300       dirstore->append(store);
301       memset(store, 0, sizeof(DIRSTORE));
302       store->device = New(alist(10));
303       bstrncpy(store->name, store_name, sizeof(store->name));
304       bstrncpy(store->media_type, media_type, sizeof(store->media_type));
305       bstrncpy(store->pool_name, pool_name, sizeof(store->pool_name));
306       bstrncpy(store->pool_type, pool_type, sizeof(store->pool_type));
307
308       /* Now get all devices */
309       while (bnet_recv(dir) >= 0) {
310          ok = sscanf(dir->msg, use_device, dev_name.c_str()) == 1;
311          if (!ok) {
312             break;
313          }
314          unbash_spaces(dev_name);
315          store->device->append(bstrdup(dev_name.c_str()));
316       }
317    }  while (ok && bnet_recv(dir) >= 0);
318
319 #ifdef DEVELOPER
320    /* This loop is debug code and can be removed */
321    /* ***FIXME**** remove after 1.38 release */
322    foreach_alist(store, dirstore) {
323       Dmsg4(100, "Storage=%s media_type=%s pool=%s pool_type=%s\n", 
324          store->name, store->media_type, store->pool_name, 
325          store->pool_type);
326       foreach_alist(device_name, store->device) {
327          Dmsg1(100, "   Device=%s\n", device_name);
328       }
329    }
330 #endif
331
332    /*                    
333     * At this point, we have a list of all the Director's Storage
334     *  resources indicated for this Job, which include Pool, PoolType,
335     *  storage name, and Media type.     
336     * Then for each of the Storage resources, we have a list of
337     *  device names that were given.
338     *
339     * Wiffle through them and find one that can do the backup.
340     */
341    if (ok) {
342       bool first = true;
343       init_jcr_device_wait_timers(jcr);
344       for ( ;; ) {
345          int need_wait = false;
346          foreach_alist(store, dirstore) {
347             foreach_alist(device_name, store->device) {
348                int stat;
349                stat = search_res_for_device(jcr, store, device_name, append);
350                if (stat == 1) {             /* found available device */
351                   dcr = jcr->dcr;
352                   dcr->Copy = Copy;
353                   dcr->Stripe = Stripe;
354                   ok = true;
355                   goto done;
356                } else if (stat == 0) {      /* device busy */
357                   need_wait = true;
358                }
359             }
360          }
361          /*
362           * If there is some device for which we can wait, then
363           *  wait and try again until the wait time expires
364           */
365          if (!need_wait || !wait_for_device(jcr, jcr->errmsg, first)) {
366             break;
367          }
368          first = false;
369       }
370       if (verbose) {
371          unbash_spaces(dir->msg);
372          pm_strcpy(jcr->errmsg, dir->msg);
373          Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg);
374       }
375       Jmsg(jcr, M_FATAL, 0, _("\n"
376          "     Device \"%s\" with MediaType \"%s\" requested by DIR not found in SD Device resources.\n"),
377            dev_name.c_str(), media_type.c_str());
378       bnet_fsend(dir, NO_device, dev_name.c_str());
379       Dmsg1(100, ">dird: %s\n", dir->msg);
380       ok = false;
381    } else {
382       unbash_spaces(dir->msg);
383       pm_strcpy(jcr->errmsg, dir->msg);
384       if (verbose) {
385          Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg);
386       }
387       Jmsg(jcr, M_FATAL, 0, _("Bad Use Device command: %s\n"), jcr->errmsg);
388       bnet_fsend(dir, BAD_use, jcr->errmsg);
389       Dmsg1(100, ">dird: %s\n", dir->msg);
390       ok = false;
391    }
392
393 done:
394    foreach_alist(store, dirstore) {
395       delete store->device;
396       delete store;
397    }
398    delete dirstore;
399    if (!ok && dcr) {
400       free_dcr(dcr);
401    }
402    return ok;
403 }
404
405 /*
406  *  Returns: 1 -- OK, have DCR
407  *           0 -- must wait
408  *          -1 -- fatal error
409  */
410 static int search_res_for_device(JCR *jcr, DIRSTORE *store, char *device_name, int append)
411 {
412    DEVRES *device;
413    AUTOCHANGER *changer;
414    BSOCK *dir = jcr->dir_bsock;
415    bool ok;
416    DCR *dcr;
417
418    Dmsg1(100, "Search res for %s\n", device_name);
419    foreach_res(device, R_DEVICE) {
420       Dmsg1(100, "Try res=%s\n", device->hdr.name);
421       /* Find resource, and make sure we were able to open it */
422       if (fnmatch(device_name, device->hdr.name, 0) == 0 &&
423           strcmp(device->media_type, store->media_type) == 0) {
424          const int name_len = MAX_NAME_LENGTH;
425          if (!device->dev) {
426             device->dev = init_dev(jcr, NULL, device);
427          }
428          if (!device->dev) {
429             Jmsg(jcr, M_WARNING, 0, _("\n"
430                "     Device \"%s\" requested by DIR could not be opened or does not exist.\n"),
431                  device_name);
432             return 0;
433          }  
434          Dmsg1(100, "Found device %s\n", device->hdr.name);
435          dcr = new_dcr(jcr, device->dev);
436          if (!dcr) {
437             bnet_fsend(dir, _("3926 Could not get dcr for device: %s\n"), device_name);
438             Dmsg1(100, ">dird: %s\n", dir->msg);
439             return -1;
440          }
441          jcr->dcr = dcr;
442          bstrncpy(dcr->pool_name, store->pool_name, name_len);
443          bstrncpy(dcr->pool_type, store->pool_type, name_len);
444          bstrncpy(dcr->media_type, store->media_type, name_len);
445          bstrncpy(dcr->dev_name, device_name, name_len);
446          if (append == SD_APPEND) {
447             ok = reserve_device_for_append(dcr);
448          } else {
449             ok = reserve_device_for_read(dcr);
450          }
451          if (!ok) {
452             free_dcr(jcr->dcr);
453             return 0;
454          }
455          Dmsg1(220, "Got: %s", dir->msg);
456          bash_spaces(device_name);
457          ok = bnet_fsend(dir, OK_device, device_name);
458          Dmsg1(100, ">dird: %s\n", dir->msg);
459          return ok ? 1 : -1;
460       }
461    }
462    foreach_res(changer, R_AUTOCHANGER) {
463       Dmsg1(100, "Try changer res=%s\n", changer->hdr.name);
464       /* Find resource, and make sure we were able to open it */
465       if (fnmatch(device_name, changer->hdr.name, 0) == 0) {
466          const int name_len = MAX_NAME_LENGTH;
467          /* Try each device in this AutoChanger */
468          foreach_alist(device, changer->device) {
469             Dmsg1(100, "Try changer device %s\n", device->hdr.name);
470             if (!device->dev) {
471                device->dev = init_dev(jcr, NULL, device);
472             }
473             if (!device->dev) {
474                Dmsg1(100, "Device %s could not be opened. Skipped\n", device_name);
475                Jmsg(jcr, M_WARNING, 0, _("\n"
476                   "     Device \"%s\" in changer \"%s\" requested by DIR could not be opened or does not exist.\n"),
477                     device->hdr.name, device_name);
478                continue;
479             }
480             if (!device->dev->autoselect) {
481                continue;           /* device is not available */
482             }
483             dcr = new_dcr(jcr, device->dev);
484             if (!dcr) {
485                bnet_fsend(dir, _("3926 Could not get dcr for device: %s\n"), device_name);
486                Dmsg1(100, ">dird: %s\n", dir->msg);
487                return -1;
488             }
489             Dmsg1(100, "Found changer device %s\n", device->hdr.name);
490             bstrncpy(dcr->pool_name, store->pool_name, name_len);
491             bstrncpy(dcr->pool_type, store->pool_type, name_len);
492             bstrncpy(dcr->media_type, store->media_type, name_len);
493             bstrncpy(dcr->dev_name, device_name, name_len);
494             jcr->dcr = dcr;
495             if (append == SD_APPEND) {
496                ok = reserve_device_for_append(dcr);
497             } else {
498                ok = reserve_device_for_read(dcr);
499             }
500             if (!ok) {
501                Jmsg(jcr, M_WARNING, 0, _("Could not reserve device: %s\n"), device_name);
502                free_dcr(jcr->dcr);
503                continue;
504             }
505             POOL_MEM dev_name;
506             Dmsg1(100, "Device %s opened.\n", device_name);
507             pm_strcpy(dev_name, device->hdr.name);
508             bash_spaces(dev_name);
509             ok = bnet_fsend(dir, OK_device, dev_name.c_str());  /* Return real device name */
510             Dmsg1(100, ">dird: %s\n", dir->msg);
511             return ok ? 1 : -1;
512          }
513       }
514    }
515    return 0;                    /* nothing found */
516 }
517
518
519 #ifdef needed
520 /*
521  *   Query Device command from Director
522  *   Sends Storage Daemon's information on the device to the
523  *    caller (presumably the Director).
524  *   This command always returns "true" so that the line is
525  *    not closed on an error.
526  *
527  */
528 bool query_cmd(JCR *jcr)
529 {
530    POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
531    BSOCK *dir = jcr->dir_bsock;
532    DEVRES *device;
533    AUTOCHANGER *changer;
534    bool ok;
535
536    Dmsg1(100, "Query_cmd: %s", dir->msg);
537    ok = sscanf(dir->msg, query_device, dev_name.c_str()) == 1;
538    Dmsg1(100, "<dird: %s\n", dir->msg);
539    if (ok) {
540       unbash_spaces(dev_name);
541 //    LockRes();
542       foreach_res(device, R_DEVICE) {
543          /* Find resource, and make sure we were able to open it */
544          if (fnmatch(dev_name.c_str(), device->hdr.name, 0) == 0) {
545             if (!device->dev) {
546                device->dev = init_dev(jcr, NULL, device);
547             }
548             if (!device->dev) {
549                break;
550             }  
551 //          UnlockRes();
552             ok = dir_update_device(jcr, device->dev);
553             if (ok) {
554                ok = bnet_fsend(dir, OK_query);
555             } else {
556                bnet_fsend(dir, NO_query);
557             }
558             return ok;
559          }
560       }
561       foreach_res(changer, R_AUTOCHANGER) {
562          /* Find resource, and make sure we were able to open it */
563          if (fnmatch(dev_name.c_str(), changer->hdr.name, 0) == 0) {
564 //          UnlockRes();
565             if (!changer->device || changer->device->size() == 0) {
566                continue;              /* no devices */
567             }
568             ok = dir_update_changer(jcr, changer);
569             if (ok) {
570                ok = bnet_fsend(dir, OK_query);
571             } else {
572                bnet_fsend(dir, NO_query);
573             }
574             return ok;
575          }
576       }
577       /* If we get here, the device/autochanger was not found */
578 //    UnlockRes();
579       unbash_spaces(dir->msg);
580       pm_strcpy(jcr->errmsg, dir->msg);
581       bnet_fsend(dir, NO_device, dev_name.c_str());
582       Dmsg1(100, ">dird: %s\n", dir->msg);
583    } else {
584       unbash_spaces(dir->msg);
585       pm_strcpy(jcr->errmsg, dir->msg);
586       bnet_fsend(dir, BAD_query, jcr->errmsg);
587       Dmsg1(100, ">dird: %s\n", dir->msg);
588    }
589
590    return true;
591 }
592
593 #endif
594
595
596 /*
597  * Destroy the Job Control Record and associated
598  * resources (sockets).
599  */
600 void stored_free_jcr(JCR *jcr)
601 {
602    if (jcr->file_bsock) {
603       bnet_close(jcr->file_bsock);
604       jcr->file_bsock = NULL;
605    }
606    if (jcr->job_name) {
607       free_pool_memory(jcr->job_name);
608    }
609    if (jcr->client_name) {
610       free_memory(jcr->client_name);
611       jcr->client_name = NULL;
612    }
613    if (jcr->fileset_name) {
614       free_memory(jcr->fileset_name);
615    }
616    if (jcr->fileset_md5) {
617       free_memory(jcr->fileset_md5);
618    }
619    if (jcr->bsr) {
620       free_bsr(jcr->bsr);
621       jcr->bsr = NULL;
622    }
623    if (jcr->RestoreBootstrap) {
624       unlink(jcr->RestoreBootstrap);
625       free_pool_memory(jcr->RestoreBootstrap);
626       jcr->RestoreBootstrap = NULL;
627    }
628    if (jcr->next_dev || jcr->prev_dev) {
629       Emsg0(M_FATAL, 0, _("In free_jcr(), but still attached to device!!!!\n"));
630    }
631    pthread_cond_destroy(&jcr->job_start_wait);
632    if (jcr->dcrs) {
633       delete jcr->dcrs;
634    }
635    jcr->dcrs = NULL;
636    if (jcr->dcr) {
637       free_dcr(jcr->dcr);
638       jcr->dcr = NULL;
639    }
640    return;
641 }