]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/fd_cmds.c
Strip pathname portion off all message routines that print filename:line.
[bacula/bacula] / bacula / src / dird / fd_cmds.c
1 /*
2  *
3  *   Bacula Director -- fd_cmds.c -- send commands to File daemon
4  *
5  *     Kern Sibbald, October MM
6  *
7  *    This routine is run as a separate thread.  There may be more
8  *    work to be done to make it totally reentrant!!!!
9  *
10  *  Utility functions for sending info to File Daemon.
11  *   These functions are used by both backup and verify.
12  *
13  *   Version $Id$
14  */
15 /*
16    Copyright (C) 2000-2006 Kern Sibbald
17
18    This program is free software; you can redistribute it and/or
19    modify it under the terms of the GNU General Public License
20    version 2 as amended with additional clauses defined in the
21    file LICENSE in the main source directory.
22
23    This program is distributed in the hope that it will be useful,
24    but WITHOUT ANY WARRANTY; without even the implied warranty of
25    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
26    the file LICENSE for additional details.
27
28  */
29
30 #include "bacula.h"
31 #include "dird.h"
32 #include "findlib/find.h"
33
34 /* Commands sent to File daemon */
35 static char filesetcmd[]  = "fileset%s\n"; /* set full fileset */
36 static char jobcmd[]      = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
37 /* Note, mtime_only is not used here -- implemented as file option */
38 static char levelcmd[]    = "level = %s%s mtime_only=%d\n";
39 static char runscript[]   = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
40 static char runbeforenow[]= "RunBeforeNow\n";
41
42 /* Responses received from File daemon */
43 static char OKinc[]          = "2000 OK include\n";
44 static char OKjob[]          = "2000 OK Job";
45 static char OKlevel[]        = "2000 OK level\n";
46 static char OKRunScript[]    = "2000 OK RunScript\n";
47 static char OKRunBeforeNow[] = "2000 OK RunBeforeNow\n";
48
49 /* Forward referenced functions */
50
51 /* External functions */
52 extern DIRRES *director;
53 extern int FDConnectTimeout;
54
55 #define INC_LIST 0
56 #define EXC_LIST 1
57
58 /*
59  * Open connection with File daemon.
60  * Try connecting every retry_interval (default 10 sec), and
61  *   give up after max_retry_time (default 30 mins).
62  */
63
64 int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time,
65                            int verbose)
66 {
67    BSOCK   *fd;
68    char ed1[30];
69
70    if (!jcr->file_bsock) {
71       fd = bnet_connect(jcr, retry_interval, max_retry_time,
72            _("File daemon"), jcr->client->address,
73            NULL, jcr->client->FDport, verbose);
74       if (fd == NULL) {
75          set_jcr_job_status(jcr, JS_ErrorTerminated);
76          return 0;
77       }
78       Dmsg0(10, "Opened connection with File daemon\n");
79    } else {
80       fd = jcr->file_bsock;           /* use existing connection */
81    }
82    fd->res = (RES *)jcr->client;      /* save resource in BSOCK */
83    jcr->file_bsock = fd;
84    set_jcr_job_status(jcr, JS_Running);
85
86    if (!authenticate_file_daemon(jcr)) {
87       set_jcr_job_status(jcr, JS_ErrorTerminated);
88       return 0;
89    }
90
91    /*
92     * Now send JobId and authorization key
93     */
94    bnet_fsend(fd, jobcmd, edit_int64(jcr->JobId, ed1), jcr->Job, jcr->VolSessionId,
95       jcr->VolSessionTime, jcr->sd_auth_key);
96    if (strcmp(jcr->sd_auth_key, "dummy") != 0) {
97       memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
98    }
99    Dmsg1(100, ">filed: %s", fd->msg);
100    if (bget_dirmsg(fd) > 0) {
101        Dmsg1(110, "<filed: %s", fd->msg);
102        if (strncmp(fd->msg, OKjob, strlen(OKjob)) != 0) {
103           Jmsg(jcr, M_FATAL, 0, _("File daemon \"%s\" rejected Job command: %s\n"),
104              jcr->client->hdr.name, fd->msg);
105           set_jcr_job_status(jcr, JS_ErrorTerminated);
106           return 0;
107        } else if (jcr->db) {
108           CLIENT_DBR cr;
109           memset(&cr, 0, sizeof(cr));
110           bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
111           cr.AutoPrune = jcr->client->AutoPrune;
112           cr.FileRetention = jcr->client->FileRetention;
113           cr.JobRetention = jcr->client->JobRetention;
114           bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname));
115           if (!db_update_client_record(jcr, jcr->db, &cr)) {
116              Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"),
117                 db_strerror(jcr->db));
118           }
119        }
120    } else {
121       Jmsg(jcr, M_FATAL, 0, _("FD gave bad response to JobId command: %s\n"),
122          bnet_strerror(fd));
123       set_jcr_job_status(jcr, JS_ErrorTerminated);
124       return 0;
125    }
126    return 1;
127 }
128
129 /*
130  * This subroutine edits the last job start time into a
131  *   "since=date/time" buffer that is returned in the
132  *   variable since.  This is used for display purposes in
133  *   the job report.  The time in jcr->stime is later
134  *   passed to tell the File daemon what to do.
135  */
136 void get_level_since_time(JCR *jcr, char *since, int since_len)
137 {
138    int JobLevel;
139
140    since[0] = 0;
141    if (jcr->cloned) {
142       if (jcr->stime && jcr->stime[0]) {
143          bstrncpy(since, _(", since="), since_len);
144          bstrncat(since, jcr->stime, since_len);
145       }
146       return;
147    }
148    if (!jcr->stime) {
149       jcr->stime = get_pool_memory(PM_MESSAGE);
150    } 
151    jcr->stime[0] = 0;
152    /* Lookup the last FULL backup job to get the time/date for a
153     * differential or incremental save.
154     */
155    switch (jcr->JobLevel) {
156    case L_DIFFERENTIAL:
157    case L_INCREMENTAL:
158       /* Look up start time of last job */
159       jcr->jr.JobId = 0;     /* flag for db_find_job_start time */
160       if (!db_find_job_start_time(jcr, jcr->db, &jcr->jr, &jcr->stime)) {
161          /* No job found, so upgrade this one to Full */
162          Jmsg(jcr, M_INFO, 0, "%s", db_strerror(jcr->db));
163          Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full backup found in catalog. Doing FULL backup.\n"));
164          bsnprintf(since, since_len, _(" (upgraded from %s)"),
165             level_to_str(jcr->JobLevel));
166          jcr->JobLevel = jcr->jr.JobLevel = L_FULL;
167       } else {
168          if (jcr->job->rerun_failed_levels) {
169             if (db_find_failed_job_since(jcr, jcr->db, &jcr->jr, jcr->stime, JobLevel)) {
170                Jmsg(jcr, M_INFO, 0, _("Prior failed job found in catalog. Upgrading to %s.\n"),
171                   level_to_str(JobLevel));
172                bsnprintf(since, since_len, _(" (upgraded from %s)"),
173                   level_to_str(jcr->JobLevel));
174                jcr->JobLevel = jcr->jr.JobLevel = JobLevel;
175                jcr->jr.JobId = jcr->JobId;
176                break;
177             }
178          }
179          bstrncpy(since, _(", since="), since_len);
180          bstrncat(since, jcr->stime, since_len);
181       }
182       jcr->jr.JobId = jcr->JobId;
183       break;
184    }
185    Dmsg2(100, "Level=%c last start time=%s\n", jcr->JobLevel, jcr->stime);
186 }
187
188 static void send_since_time(JCR *jcr)
189 {
190    BSOCK   *fd = jcr->file_bsock;
191    utime_t stime;
192    char ed1[50];
193
194    stime = str_to_utime(jcr->stime);
195    bnet_fsend(fd, levelcmd, NT_("since_utime "), edit_uint64(stime, ed1), 0);
196    while (bget_dirmsg(fd) >= 0) {  /* allow him to poll us to sync clocks */
197       Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
198    }
199 }
200
201
202 /*
203  * Send level command to FD.
204  * Used for backup jobs and estimate command.
205  */
206 bool send_level_command(JCR *jcr)
207 {
208    BSOCK   *fd = jcr->file_bsock;
209    /*
210     * Send Level command to File daemon
211     */
212    switch (jcr->JobLevel) {
213    case L_BASE:
214       bnet_fsend(fd, levelcmd, "base", " ", 0);
215       break;
216    /* L_NONE is the console, sending something off to the FD */
217    case L_NONE:
218    case L_FULL:
219       bnet_fsend(fd, levelcmd, "full", " ", 0);
220       break;
221    case L_DIFFERENTIAL:
222       bnet_fsend(fd, levelcmd, "differential", " ", 0);
223       send_since_time(jcr);
224       break;
225    case L_INCREMENTAL:
226       bnet_fsend(fd, levelcmd, "incremental", " ", 0);
227       send_since_time(jcr);
228       break;
229    case L_SINCE:
230    default:
231       Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"),
232          jcr->JobLevel, jcr->JobLevel);
233       return 0;
234    }
235    Dmsg1(120, ">filed: %s", fd->msg);
236    if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) {
237       return 0;
238    }
239    return 1;
240 }
241
242 /*
243  * Send either an Included or an Excluded list to FD
244  */
245 static bool send_fileset(JCR *jcr)
246 {
247    FILESET *fileset = jcr->fileset;
248    BSOCK   *fd = jcr->file_bsock;
249    int num;
250    bool include = true;
251
252    for ( ;; ) {
253       if (include) {
254          num = fileset->num_includes;
255       } else {
256          num = fileset->num_excludes;
257       }
258       for (int i=0; i<num; i++) {
259          BPIPE *bpipe;
260          FILE *ffd;
261          char buf[2000];
262          char *p;
263          int optlen, stat;
264          INCEXE *ie;
265          int j, k;
266
267          if (include) {
268             ie = fileset->include_items[i];
269             bnet_fsend(fd, "I\n");
270          } else {
271             ie = fileset->exclude_items[i];
272             bnet_fsend(fd, "E\n");
273          }
274          for (j=0; j<ie->num_opts; j++) {
275             FOPTS *fo = ie->opts_list[j];
276             bnet_fsend(fd, "O %s\n", fo->opts);
277
278             bool enhanced_wild = false;
279             for (k=0; fo->opts[k]!='\0'; k++) {
280                if (fo->opts[k]=='W') {
281                   enhanced_wild = true;
282                   break;
283                }
284             }
285
286             for (k=0; k<fo->regex.size(); k++) {
287                bnet_fsend(fd, "R %s\n", fo->regex.get(k));
288             }
289             for (k=0; k<fo->regexdir.size(); k++) {
290                bnet_fsend(fd, "RD %s\n", fo->regexdir.get(k));
291             }
292             for (k=0; k<fo->regexfile.size(); k++) {
293                bnet_fsend(fd, "RF %s\n", fo->regexfile.get(k));
294             }
295             for (k=0; k<fo->wild.size(); k++) {
296                bnet_fsend(fd, "W %s\n", fo->wild.get(k));
297             }
298             for (k=0; k<fo->wilddir.size(); k++) {
299                bnet_fsend(fd, "WD %s\n", fo->wilddir.get(k));
300             }
301             for (k=0; k<fo->wildfile.size(); k++) {
302                bnet_fsend(fd, "WF %s\n", fo->wildfile.get(k));
303             }
304             for (k=0; k<fo->wildbase.size(); k++) {
305                bnet_fsend(fd, "W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
306             }
307             for (k=0; k<fo->base.size(); k++) {
308                bnet_fsend(fd, "B %s\n", fo->base.get(k));
309             }
310             for (k=0; k<fo->fstype.size(); k++) {
311                bnet_fsend(fd, "X %s\n", fo->fstype.get(k));
312             }
313             for (k=0; k<fo->drivetype.size(); k++) {
314                bnet_fsend(fd, "XD %s\n", fo->drivetype.get(k));
315             }
316             if (fo->reader) {
317                bnet_fsend(fd, "D %s\n", fo->reader);
318             }
319             if (fo->writer) {
320                bnet_fsend(fd, "T %s\n", fo->writer);
321             }
322             bnet_fsend(fd, "N\n");
323          }
324
325          for (j=0; j<ie->name_list.size(); j++) {
326             p = (char *)ie->name_list.get(j);
327             switch (*p) {
328             case '|':
329                p++;                      /* skip over the | */
330                fd->msg = edit_job_codes(jcr, fd->msg, p, "");
331                bpipe = open_bpipe(fd->msg, 0, "r");
332                if (!bpipe) {
333                   berrno be;
334                   Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
335                      p, be.strerror());
336                   goto bail_out;
337                }
338                bstrncpy(buf, "F ", sizeof(buf));
339                Dmsg1(500, "Opts=%s\n", buf);
340                optlen = strlen(buf);
341                while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) {
342                   fd->msglen = Mmsg(fd->msg, "%s", buf);
343                   Dmsg2(500, "Inc/exc len=%d: %s", fd->msglen, fd->msg);
344                   if (!bnet_send(fd)) {
345                      Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
346                      goto bail_out;
347                   }
348                }
349                if ((stat=close_bpipe(bpipe)) != 0) {
350                   berrno be;
351                   Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
352                      p, be.strerror(stat));
353                   goto bail_out;
354                }
355                break;
356             case '<':
357                p++;                      /* skip over < */
358                if ((ffd = fopen(p, "rb")) == NULL) {
359                   berrno be;
360                   Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
361                      p, be.strerror());
362                   goto bail_out;
363                }
364                bstrncpy(buf, "F ", sizeof(buf));
365                Dmsg1(500, "Opts=%s\n", buf);
366                optlen = strlen(buf);
367                while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
368                   fd->msglen = Mmsg(fd->msg, "%s", buf);
369                   if (!bnet_send(fd)) {
370                      Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
371                      goto bail_out;
372                   }
373                }
374                fclose(ffd);
375                break;
376             case '\\':
377                p++;                      /* skip over \ */
378                /* Note, fall through wanted */
379             default:
380                pm_strcpy(fd->msg, "F ");
381                fd->msglen = pm_strcat(fd->msg, p);
382                Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
383                if (!bnet_send(fd)) {
384                   Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
385                   goto bail_out;
386                }
387                break;
388             }
389          }
390          bnet_fsend(fd, "N\n");
391       }
392       if (!include) {                 /* If we just did excludes */
393          break;                       /*   all done */
394       }
395       include = false;                /* Now do excludes */
396    }
397
398    bnet_sig(fd, BNET_EOD);            /* end of data */
399    if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
400       goto bail_out;
401    }
402    return true;
403
404 bail_out:
405    set_jcr_job_status(jcr, JS_ErrorTerminated);
406    return false;
407
408 }
409
410
411 /*
412  * Send include list to File daemon
413  */
414 bool send_include_list(JCR *jcr)
415 {
416    BSOCK *fd = jcr->file_bsock;
417    if (jcr->fileset->new_include) {
418       bnet_fsend(fd, filesetcmd, jcr->fileset->enable_vss ? " vss=1" : "");
419       return send_fileset(jcr);
420    }
421    return true;
422 }
423
424
425 /*
426  * Send exclude list to File daemon
427  *   Under the new scheme, the Exclude list
428  *   is part of the FileSet sent with the
429  *   "include_list" above.
430  */
431 bool send_exclude_list(JCR *jcr)
432 {
433    return true;
434 }
435
436
437 /*
438  * Send bootstrap file if any to the socket given (FD or SD).
439  *  This is used for restore, verify VolumeToCatalog, and
440  *  for migration.
441  */
442 bool send_bootstrap_file(JCR *jcr, BSOCK *sock)
443 {
444    FILE *bs;
445    char buf[1000];
446    const char *bootstrap = "bootstrap\n";
447
448    Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
449    if (!jcr->RestoreBootstrap) {
450       return true;
451    }
452    bs = fopen(jcr->RestoreBootstrap, "rb");
453    if (!bs) {
454       berrno be;
455       Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
456          jcr->RestoreBootstrap, be.strerror());
457       set_jcr_job_status(jcr, JS_ErrorTerminated);
458       return false;
459    }
460    bnet_fsend(sock, bootstrap);
461    while (fgets(buf, sizeof(buf), bs)) {
462       bnet_fsend(sock, "%s", buf);
463    }
464    bnet_sig(sock, BNET_EOD);
465    fclose(bs);
466    if (jcr->unlink_bsr) {
467       unlink(jcr->RestoreBootstrap);
468       jcr->unlink_bsr = false;
469    }                         
470    return true;
471 }
472
473 /*
474  * Send RunScripts to File daemon
475  */
476 int send_runscripts_commands(JCR *jcr)
477 {
478    POOLMEM *msg = get_pool_memory(PM_FNAME);
479    BSOCK *fd = jcr->file_bsock;
480    RUNSCRIPT *cmd;
481    bool launch_before_cmd = false;
482    POOLMEM *ehost = get_pool_memory(PM_FNAME);
483
484    Dmsg0(120, "bdird: sending runscripts to fd\n");
485    
486    foreach_alist(cmd, jcr->job->RunScripts) {
487       
488       if (cmd->can_run_at_level(jcr->JobLevel) && cmd->target) {
489
490          ehost = edit_job_codes(jcr, ehost, cmd->target, "");
491          Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost);
492
493          if (strcmp(ehost, jcr->client->hdr.name) == 0) {
494             pm_strcpy(msg, cmd->command);
495             bash_spaces(msg);
496             bnet_fsend(fd, runscript, cmd->on_success, 
497                                       cmd->on_failure,
498                                       cmd->abort_on_error,
499                                       cmd->when,
500                                       msg);
501
502             Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command);
503
504             if (!response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR)) {
505                set_jcr_job_status(jcr, JS_ErrorTerminated);
506                free_pool_memory(msg);
507                free_pool_memory(ehost);
508                return 0;
509             }
510             launch_before_cmd=true;
511          }
512          /*
513            else {
514            send command to an other client
515            }
516          */
517       }        
518    }
519    
520    /* TODO : we have to play with other client */
521    if (launch_before_cmd) {
522       bnet_fsend(fd, runbeforenow);
523       if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
524         set_jcr_job_status(jcr, JS_ErrorTerminated);
525         free_pool_memory(msg);
526         free_pool_memory(ehost);
527         return 0;
528       }
529    }
530    free_pool_memory(msg);
531    free_pool_memory(ehost);
532    return 1;
533 }
534
535
536 /*
537  * Read the attributes from the File daemon for
538  * a Verify job and store them in the catalog.
539  */
540 int get_attributes_and_put_in_catalog(JCR *jcr)
541 {
542    BSOCK   *fd;
543    int n = 0;
544    ATTR_DBR ar;
545
546    fd = jcr->file_bsock;
547    jcr->jr.FirstIndex = 1;
548    memset(&ar, 0, sizeof(ar));
549    jcr->FileIndex = 0;
550
551    Dmsg0(120, "bdird: waiting to receive file attributes\n");
552    /* Pickup file attributes and digest */
553    while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
554
555    /*****FIXME****** improve error handling to stop only on
556     * really fatal problems, or the number of errors is too
557     * large.
558     */
559       long file_index;
560       int stream, len;
561       char *attr, *p, *fn;
562       char Opts_Digest[MAXSTRING];      /* either Verify opts or MD5/SHA1 digest */
563       char digest[CRYPTO_DIGEST_MAX_SIZE];
564
565       jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
566       if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Opts_Digest)) != 3) {
567          Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
568 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
569          set_jcr_job_status(jcr, JS_ErrorTerminated);
570          return 0;
571       }
572       p = fd->msg;
573       skip_nonspaces(&p);             /* skip FileIndex */
574       skip_spaces(&p);
575       skip_nonspaces(&p);             /* skip Stream */
576       skip_spaces(&p);
577       skip_nonspaces(&p);             /* skip Opts_SHA1 */
578       p++;                            /* skip space */
579       fn = jcr->fname;
580       while (*p != 0) {
581          *fn++ = *p++;                /* copy filename */
582       }
583       *fn = *p++;                     /* term filename and point to attribs */
584       attr = p;
585
586       if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
587          jcr->JobFiles++;
588          jcr->FileIndex = file_index;
589          ar.attr = attr;
590          ar.fname = jcr->fname;
591          ar.FileIndex = file_index;
592          ar.Stream = stream;
593          ar.link = NULL;
594          ar.JobId = jcr->JobId;
595          ar.ClientId = jcr->ClientId;
596          ar.PathId = 0;
597          ar.FilenameId = 0;
598          ar.Digest = NULL;
599          ar.DigestType = CRYPTO_DIGEST_NONE;
600
601          Dmsg2(111, "dird<filed: stream=%d %s\n", stream, jcr->fname);
602          Dmsg1(120, "dird<filed: attr=%s\n", attr);
603
604          if (!db_create_file_attributes_record(jcr, jcr->db, &ar)) {
605             Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
606             set_jcr_job_status(jcr, JS_Error);
607             continue;
608          }
609          jcr->FileId = ar.FileId;
610       } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
611          if (jcr->FileIndex != (uint32_t)file_index) {
612             Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
613                stream_to_ascii(stream), file_index, jcr->FileIndex);
614             set_jcr_job_status(jcr, JS_Error);
615             continue;
616          }
617          db_escape_string(digest, Opts_Digest, strlen(Opts_Digest));
618          Dmsg2(120, "DigestLen=%d Digest=%s\n", strlen(digest), digest);
619          if (!db_add_digest_to_file_record(jcr, jcr->db, jcr->FileId, digest,
620                    crypto_digest_stream_type(stream))) {
621             Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
622             set_jcr_job_status(jcr, JS_Error);
623          }
624       }
625       jcr->jr.JobFiles = jcr->JobFiles = file_index;
626       jcr->jr.LastIndex = file_index;
627    }
628    if (is_bnet_error(fd)) {
629       Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
630                         bnet_strerror(fd));
631       set_jcr_job_status(jcr, JS_ErrorTerminated);
632       return 0;
633    }
634
635    set_jcr_job_status(jcr, JS_Terminated);
636    return 1;
637 }