]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
Implement new Finclude/Fexclude -- drop FileOptions
[bacula/bacula] / bacula / src / dird / dird_conf.c
1 /*
2  *   Main configuration file parser for Bacula Directors,
3  *    some parts may be split into separate files such as
4  *    the schedule configuration (sch_config.c).
5  *
6  *   Note, the configuration file parser consists of three parts
7  *
8  *   1. The generic lexical scanner in lib/lex.c and lib/lex.h
9  *
10  *   2. The generic config  scanner in lib/parse_config.c and 
11  *      lib/parse_config.h.
12  *      These files contain the parser code, some utility
13  *      routines, and the common store routines (name, int,
14  *      string).
15  *
16  *   3. The daemon specific file, which contains the Resource
17  *      definitions as well as any specific store routines
18  *      for the resource records.
19  *
20  *     Kern Sibbald, January MM
21  *
22  *     Version $Id$
23  */
24 /*
25    Copyright (C) 2000-2003 Kern Sibbald and John Walker
26
27    This program is free software; you can redistribute it and/or
28    modify it under the terms of the GNU General Public License as
29    published by the Free Software Foundation; either version 2 of
30    the License, or (at your option) any later version.
31
32    This program is distributed in the hope that it will be useful,
33    but WITHOUT ANY WARRANTY; without even the implied warranty of
34    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35    General Public License for more details.
36
37    You should have received a copy of the GNU General Public
38    License along with this program; if not, write to the Free
39    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
40    MA 02111-1307, USA.
41
42  */
43
44 #include "bacula.h"
45 #include "dird.h"
46
47 /* Define the first and last resource ID record
48  * types. Note, these should be unique for each
49  * daemon though not a requirement.
50  */
51 int r_first = R_FIRST;
52 int r_last  = R_LAST;
53 pthread_mutex_t res_mutex =  PTHREAD_MUTEX_INITIALIZER;
54
55 /* Imported subroutines */
56 extern void store_run(LEX *lc, struct res_items *item, int index, int pass);
57 extern void store_finc(LEX *lc, struct res_items *item, int index, int pass);
58 extern void store_inc(LEX *lc, struct res_items *item, int index, int pass);
59
60
61 /* Forward referenced subroutines */
62
63 static void store_backup(LEX *lc, struct res_items *item, int index, int pass);
64 static void store_restore(LEX *lc, struct res_items *item, int index, int pass);
65 static void store_jobtype(LEX *lc, struct res_items *item, int index, int pass);
66 static void store_level(LEX *lc, struct res_items *item, int index, int pass);
67 static void store_replace(LEX *lc, struct res_items *item, int index, int pass);
68
69
70 /* We build the current resource here as we are
71  * scanning the resource configuration definition,
72  * then move it to allocated memory when the resource
73  * scan is complete.
74  */
75 URES res_all;
76 int  res_all_size = sizeof(res_all);
77
78
79 /* Definition of records permitted within each
80  * resource with the routine to process the record 
81  * information.  NOTE! quoted names must be in lower case.
82  */ 
83 /* 
84  *    Director Resource
85  *
86  *   name          handler     value                 code flags    default_value
87  */
88 static struct res_items dir_items[] = {
89    {"name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
90    {"description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
91    {"messages",    store_res,      ITEM(res_dir.messages), R_MSGS, 0, 0},
92    {"dirport",     store_pint,     ITEM(res_dir.DIRport),  0, ITEM_DEFAULT, 9101},
93    {"diraddress",  store_str,      ITEM(res_dir.DIRaddr),  0, 0, 0},
94    {"queryfile",   store_dir,      ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
95    {"workingdirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
96    {"piddirectory", store_dir,     ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0},
97    {"subsysdirectory", store_dir,  ITEM(res_dir.subsys_directory), 0, ITEM_REQUIRED, 0},
98    {"maximumconcurrentjobs", store_pint, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
99    {"password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
100    {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
101    {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
102    {NULL, NULL, NULL, 0, 0, 0}
103 };
104
105 /* 
106  *    Client or File daemon resource
107  *
108  *   name          handler     value                 code flags    default_value
109  */
110
111 static struct res_items cli_items[] = {
112    {"name",     store_name,       ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
113    {"description", store_str,     ITEM(res_client.hdr.desc), 0, 0, 0},
114    {"address",  store_str,        ITEM(res_client.address),  0, ITEM_REQUIRED, 0},
115    {"fdport",   store_pint,       ITEM(res_client.FDport),   0, ITEM_DEFAULT, 9102},
116    {"password", store_password,   ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
117    {"catalog",  store_res,        ITEM(res_client.catalog),  R_CATALOG, 0, 0},
118    {"fileretention", store_time,  ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
119    {"jobretention",  store_time,  ITEM(res_client.JobRetention),  0, ITEM_DEFAULT, 60*60*24*180},
120    {"autoprune", store_yesno,     ITEM(res_client.AutoPrune), 1, ITEM_DEFAULT, 1},
121    {NULL, NULL, NULL, 0, 0, 0} 
122 };
123
124 /* Storage daemon resource
125  *
126  *   name          handler     value                 code flags    default_value
127  */
128 static struct res_items store_items[] = {
129    {"name",      store_name,     ITEM(res_store.hdr.name),   0, ITEM_REQUIRED, 0},
130    {"description", store_str,    ITEM(res_store.hdr.desc),   0, 0, 0},
131    {"sdport",    store_pint,     ITEM(res_store.SDport),     0, ITEM_DEFAULT, 9103},
132    {"sddport",   store_pint,     ITEM(res_store.SDDport),    0, 0, 0}, /* deprecated */
133    {"address",   store_str,      ITEM(res_store.address),    0, ITEM_REQUIRED, 0},
134    {"password",  store_password, ITEM(res_store.password),   0, ITEM_REQUIRED, 0},
135    {"device",    store_strname,  ITEM(res_store.dev_name),   0, ITEM_REQUIRED, 0},
136    {"mediatype", store_strname,  ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
137    {"autochanger", store_yesno,  ITEM(res_store.autochanger), 1, ITEM_DEFAULT, 0},
138    {NULL, NULL, NULL, 0, 0, 0} 
139 };
140
141 /* 
142  *    Catalog Resource Directives
143  *
144  *   name          handler     value                 code flags    default_value
145  */
146 static struct res_items cat_items[] = {
147    {"name",     store_name,     ITEM(res_cat.hdr.name),    0, ITEM_REQUIRED, 0},
148    {"description", store_str,   ITEM(res_cat.hdr.desc),    0, 0, 0},
149    {"address",  store_str,      ITEM(res_cat.db_address),  0, 0, 0},
150    {"dbaddress", store_str,     ITEM(res_cat.db_address),  0, 0, 0},
151    {"dbport",   store_pint,     ITEM(res_cat.db_port),      0, 0, 0},
152    /* keep this password as store_str for the moment */
153    {"password", store_str,      ITEM(res_cat.db_password), 0, 0, 0},
154    {"user",     store_str,      ITEM(res_cat.db_user),     0, 0, 0},
155    {"dbname",   store_str,      ITEM(res_cat.db_name),     0, ITEM_REQUIRED, 0},
156    {"dbsocket", store_str,      ITEM(res_cat.db_socket),   0, 0, 0}, 
157    {NULL, NULL, NULL, 0, 0, 0} 
158 };
159
160 /* 
161  *    Job Resource Directives
162  *
163  *   name          handler     value                 code flags    default_value
164  */
165 static struct res_items job_items[] = {
166    {"name",     store_name,    ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
167    {"description", store_str,  ITEM(res_job.hdr.desc), 0, 0, 0},
168    {"backup",   store_backup,  ITEM(res_job),          JT_BACKUP, 0, 0},
169    {"verify",   store_backup,  ITEM(res_job),          JT_VERIFY, 0, 0},
170    {"restore",  store_restore, ITEM(res_job),          JT_RESTORE, 0, 0},
171    {"schedule", store_res,     ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
172    {"type",     store_jobtype, ITEM(res_job),          0, 0, 0},
173    {"level",    store_level,   ITEM(res_job),          0, 0, 0},
174    {"messages", store_res,     ITEM(res_job.messages), R_MSGS, 0, 0},
175    {"storage",  store_res,     ITEM(res_job.storage),  R_STORAGE, 0, 0},
176    {"pool",     store_res,     ITEM(res_job.pool),     R_POOL, 0, 0},
177    {"client",   store_res,     ITEM(res_job.client),   R_CLIENT, 0, 0},
178    {"fileset",  store_res,     ITEM(res_job.fileset),  R_FILESET, 0, 0},
179    {"where",    store_dir,     ITEM(res_job.RestoreWhere), 0, 0, 0},
180    {"replace",  store_replace, ITEM(res_job.replace), REPLACE_ALWAYS, ITEM_DEFAULT, 0},
181    {"bootstrap",store_dir,     ITEM(res_job.RestoreBootstrap), 0, 0, 0},
182    {"maxruntime", store_time,  ITEM(res_job.MaxRunTime), 0, 0, 0},
183    {"maxstartdelay", store_time,ITEM(res_job.MaxStartDelay), 0, 0, 0},
184    {"prefixlinks", store_yesno, ITEM(res_job.PrefixLinks), 1, ITEM_DEFAULT, 0},
185    {"prunejobs",   store_yesno, ITEM(res_job.PruneJobs), 1, ITEM_DEFAULT, 0},
186    {"prunefiles",  store_yesno, ITEM(res_job.PruneFiles), 1, ITEM_DEFAULT, 0},
187    {"prunevolumes", store_yesno, ITEM(res_job.PruneVolumes), 1, ITEM_DEFAULT, 0},
188    {"runbeforejob", store_str,  ITEM(res_job.RunBeforeJob), 0, 0, 0},
189    {"runafterjob",  store_str,  ITEM(res_job.RunAfterJob),  0, 0, 0},
190    {"spoolattributes", store_yesno, ITEM(res_job.SpoolAttributes), 1, ITEM_DEFAULT, 0},
191    {"writebootstrap", store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
192    {NULL, NULL, NULL, 0, 0, 0} 
193 };
194
195 /* FileSet resource
196  *
197  *   name          handler     value                 code flags    default_value
198  */
199 static struct res_items fs_items[] = {
200    {"name",        store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
201    {"description", store_str,  ITEM(res_fs.hdr.desc), 0, 0, 0},
202    {"include",     store_inc,  NULL,                  0, 0, 0},
203    {"finclude",    store_finc, NULL,                  0, ITEM_NO_EQUALS, 0},
204    {"exclude",     store_inc,  NULL,                  1, 0, 0},
205    {"fexclude",    store_finc, NULL,                  1, ITEM_NO_EQUALS, 0},
206    {NULL,          NULL,       NULL,                  0, 0, 0} 
207 };
208
209 /* Schedule -- see run_conf.c */
210 /* Schedule
211  *
212  *   name          handler     value                 code flags    default_value
213  */
214 static struct res_items sch_items[] = {
215    {"name",     store_name,  ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
216    {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
217    {"run",      store_run,   ITEM(res_sch.run),      0, 0, 0},
218    {NULL, NULL, NULL, 0, 0, 0} 
219 };
220
221 /* Group resource -- not implemented
222  *
223  *   name          handler     value                 code flags    default_value
224  */
225 static struct res_items group_items[] = {
226    {"name",        store_name, ITEM(res_group.hdr.name), 0, ITEM_REQUIRED, 0},
227    {"description", store_str,  ITEM(res_group.hdr.desc), 0, 0, 0},
228    {NULL, NULL, NULL, 0, 0, 0} 
229 };
230
231 /* Pool resource
232  *
233  *   name             handler     value                        code flags default_value
234  */
235 static struct res_items pool_items[] = {
236    {"name",            store_name,    ITEM(res_pool.hdr.name),      0, ITEM_REQUIRED, 0},
237    {"description",     store_str,     ITEM(res_pool.hdr.desc),      0, 0,     0},
238    {"pooltype",        store_strname, ITEM(res_pool.pool_type),     0, ITEM_REQUIRED, 0},
239    {"labelformat",     store_strname, ITEM(res_pool.label_format),  0, 0,     0},
240    {"usecatalog",      store_yesno, ITEM(res_pool.use_catalog),     1, ITEM_DEFAULT,  1},
241    {"usevolumeonce",   store_yesno, ITEM(res_pool.use_volume_once), 1, 0,        0},
242    {"maximumvolumes",  store_pint,  ITEM(res_pool.max_volumes),     0, 0,        0},
243    {"maximumvolumejobs", store_pint,  ITEM(res_pool.MaxVolJobs),    0, 0,       0},
244    {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles),   0, 0,       0},
245    {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes),   0, 0,       0},
246    {"acceptanyvolume", store_yesno, ITEM(res_pool.accept_any_volume), 1, ITEM_DEFAULT,     1},
247    {"catalogfiles",    store_yesno, ITEM(res_pool.catalog_files),   1, ITEM_DEFAULT,  1},
248    {"volumeretention", store_time,  ITEM(res_pool.VolRetention),    0, ITEM_DEFAULT, 60*60*24*365},
249    {"volumeuseduration", store_time,  ITEM(res_pool.VolUseDuration),0, 0, 0},
250    {"autoprune",       store_yesno, ITEM(res_pool.AutoPrune), 1, ITEM_DEFAULT, 1},
251    {"recycle",         store_yesno, ITEM(res_pool.Recycle),     1, ITEM_DEFAULT, 1},
252    {NULL, NULL, NULL, 0, 0, 0} 
253 };
254
255 /* 
256  * Counter Resource
257  *   name             handler     value                        code flags default_value
258  */
259 static struct res_items counter_items[] = {
260    {"name",            store_name,    ITEM(res_counter.hdr.name),        0, ITEM_REQUIRED, 0},
261    {"description",     store_str,     ITEM(res_counter.hdr.desc),        0, 0,     0},
262    {"minimum",         store_int,     ITEM(res_counter.MinValue),        0, ITEM_DEFAULT, 0},
263    {"maximum",         store_pint,    ITEM(res_counter.MaxValue),        0, ITEM_DEFAULT, INT32_MAX},
264    {"global",          store_yesno,   ITEM(res_counter.Global),          0, ITEM_DEFAULT, 0},
265    {"wrapcounter",     store_strname, ITEM(res_counter.WrapCounter),     0, 0, 0},
266    {NULL, NULL, NULL, 0, 0, 0} 
267 };
268
269
270 /* Message resource */
271 extern struct res_items msgs_items[];
272
273 /* 
274  * This is the master resource definition.  
275  * It must have one item for each of the resources.
276  *
277  *  name             items        rcode        res_head
278  */
279 struct s_res resources[] = {
280    {"director",      dir_items,   R_DIRECTOR,  NULL},
281    {"client",        cli_items,   R_CLIENT,    NULL},
282    {"job",           job_items,   R_JOB,       NULL},
283    {"storage",       store_items, R_STORAGE,   NULL},
284    {"catalog",       cat_items,   R_CATALOG,   NULL},
285    {"schedule",      sch_items,   R_SCHEDULE,  NULL},
286    {"fileset",       fs_items,    R_FILESET,   NULL},
287    {"group",         group_items, R_GROUP,     NULL},
288    {"pool",          pool_items,  R_POOL,      NULL},
289    {"messages",      msgs_items,  R_MSGS,      NULL},
290    {"counter",       counter_items, R_COUNTER, NULL},
291    {NULL,            NULL,        0,           NULL}
292 };
293
294
295 /* Keywords (RHS) permitted in Job Level records   
296  *
297  *   level_name      level              job_type
298  */
299 struct s_jl joblevels[] = {
300    {"Full",          L_FULL,            JT_BACKUP},
301    {"Incremental",   L_INCREMENTAL,     JT_BACKUP},
302    {"Differential",  L_DIFFERENTIAL,    JT_BACKUP},
303    {"Since",         L_SINCE,           JT_BACKUP},
304    {"Catalog",       L_VERIFY_CATALOG,  JT_VERIFY},
305    {"Initcatalog",   L_VERIFY_INIT,     JT_VERIFY},
306    {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG,   JT_VERIFY},
307    {"Data",          L_VERIFY_DATA,     JT_VERIFY},
308    {NULL,            0}
309 };
310
311 /* Keywords (RHS) permitted in Job type records   
312  *
313  *   type_name       job_type
314  */
315 struct s_jt jobtypes[] = {
316    {"backup",        JT_BACKUP},
317    {"admin",         JT_ADMIN},
318    {"verify",        JT_VERIFY},
319    {"restore",       JT_RESTORE},
320    {NULL,            0}
321 };
322
323
324 /* Keywords (RHS) permitted in Backup and Verify records */
325 static struct s_kw BakVerFields[] = {
326    {"client",        'C'},
327    {"fileset",       'F'},
328    {"level",         'L'}, 
329    {NULL,            0}
330 };
331
332 /* Keywords (RHS) permitted in Restore records */
333 static struct s_kw RestoreFields[] = {
334    {"client",        'C'},
335    {"fileset",       'F'},
336    {"jobid",         'J'},            /* JobId to restore */
337    {"where",         'W'},            /* root of restore */
338    {"replace",       'R'},            /* replacement options */
339    {"bootstrap",     'B'},            /* bootstrap file */
340    {NULL,              0}
341 };
342
343 /* Options permitted in Restore replace= */
344 struct s_kw ReplaceOptions[] = {
345    {"always",         REPLACE_ALWAYS},
346    {"ifnewer",        REPLACE_IFNEWER},
347    {"ifolder",        REPLACE_IFOLDER},
348    {"never",          REPLACE_NEVER},
349    {NULL,               0}
350 };
351
352 char *level_to_str(int level)
353 {
354    int i;
355    static char level_no[30];
356    char *str = level_no;
357
358    sprintf(level_no, "%d", level);    /* default if not found */
359    for (i=0; joblevels[i].level_name; i++) {
360       if (level == joblevels[i].level) {
361          str = joblevels[i].level_name;
362          break;
363       }
364    }
365    return str;
366 }
367
368 /* Dump contents of resource */
369 void dump_resource(int type, RES *reshdr, void sendit(void *sock, char *fmt, ...), void *sock)
370 {
371    URES *res = (URES *)reshdr;
372    int recurse = 1;
373
374    if (res == NULL) {
375       sendit(sock, "No %s resource defined\n", res_to_str(type));
376       return;
377    }
378    if (type < 0) {                    /* no recursion */
379       type = - type;
380       recurse = 0;
381    }
382    switch (type) {
383       case R_DIRECTOR:
384          char ed1[30], ed2[30];
385          sendit(sock, "Director: name=%s maxjobs=%d FDtimeout=%s SDtimeout=%s\n", 
386             reshdr->name, res->res_dir.MaxConcurrentJobs, 
387             edit_uint64(res->res_dir.FDConnectTimeout, ed1),
388             edit_uint64(res->res_dir.SDConnectTimeout, ed2));
389          if (res->res_dir.query_file) {
390             sendit(sock, "   query_file=%s\n", res->res_dir.query_file);
391          }
392          if (res->res_dir.messages) {
393             sendit(sock, "  --> ");
394             dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
395          }
396          break;
397       case R_CLIENT:
398          sendit(sock, "Client: name=%s address=%s FDport=%d\n",
399             res->res_client.hdr.name, res->res_client.address, res->res_client.FDport);
400          sendit(sock, "      JobRetention=%" lld " FileRetention=%" lld " AutoPrune=%d\n",
401             res->res_client.JobRetention, res->res_client.FileRetention,
402             res->res_client.AutoPrune);
403          if (res->res_client.catalog) {
404             sendit(sock, "  --> ");
405             dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
406          }
407          break;
408       case R_STORAGE:
409          sendit(sock, "Storage: name=%s address=%s SDport=%d\n\
410          DeviceName=%s MediaType=%s\n",
411             res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
412             res->res_store.dev_name, res->res_store.media_type);
413          break;
414       case R_CATALOG:
415          sendit(sock, "Catalog: name=%s address=%s DBport=%d db_name=%s\n\
416          db_user=%s\n",
417             res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
418             res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user));
419          break;
420       case R_JOB:
421          sendit(sock, "Job: name=%s JobType=%d level=%s\n", res->res_job.hdr.name, 
422             res->res_job.JobType, level_to_str(res->res_job.level));
423          if (res->res_job.client) {
424             sendit(sock, "  --> ");
425             dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
426          }
427          if (res->res_job.fileset) {
428             sendit(sock, "  --> ");
429             dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
430          }
431          if (res->res_job.schedule) {
432             sendit(sock, "  --> ");
433             dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
434          }
435          if (res->res_job.RestoreWhere) {
436             sendit(sock, "  --> Where=%s\n", NPRT(res->res_job.RestoreWhere));
437          }
438          if (res->res_job.RestoreBootstrap) {
439             sendit(sock, "  --> Bootstrap=%s\n", NPRT(res->res_job.RestoreBootstrap));
440          }
441          if (res->res_job.RunBeforeJob) {
442             sendit(sock, "  --> RunBefore=%s\n", NPRT(res->res_job.RunBeforeJob));
443          }
444          if (res->res_job.RunAfterJob) {
445             sendit(sock, "  --> RunAfter=%s\n", NPRT(res->res_job.RunAfterJob));
446          }
447          if (res->res_job.WriteBootstrap) {
448             sendit(sock, "  --> WriteBootstrap=%s\n", NPRT(res->res_job.WriteBootstrap));
449          }
450          if (res->res_job.storage) {
451             sendit(sock, "  --> ");
452             dump_resource(-R_STORAGE, (RES *)res->res_job.storage, sendit, sock);
453          }
454          if (res->res_job.pool) {
455             sendit(sock, "  --> ");
456             dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
457          } else {
458             sendit(sock, "!!! No Pool resource\n");
459          }
460          if (res->res_job.messages) {
461             sendit(sock, "  --> ");
462             dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
463          }
464          break;
465       case R_FILESET:
466          sendit(sock, "FileSet: name=%s\n", res->res_fs.hdr.name);
467          for (int i=0; i<res->res_fs.num_includes; i++) {
468             INCEXE *incexe = res->res_fs.include_items[i];
469             for (int j=0; j<incexe->num_names; j++) {
470                sendit(sock, "      Inc: %s\n", incexe->name_list[j]);
471             }
472          }
473          for (int i=0; i<res->res_fs.num_excludes; i++) {
474             INCEXE *incexe = res->res_fs.exclude_items[i];
475             for (int j=0; j<incexe->num_names; j++) {
476                sendit(sock, "      Exc: %s\n", incexe->name_list[j]);
477             }
478          }
479          break;
480       case R_SCHEDULE:
481          if (res->res_sch.run) {
482             int i;
483             RUN *run = res->res_sch.run;
484             char buf[1000], num[10];
485             sendit(sock, "Schedule: name=%s\n", res->res_sch.hdr.name);
486             if (!run) {
487                break;
488             }
489 next_run:
490             sendit(sock, "  --> Run Level=%s\n", level_to_str(run->level));
491             strcpy(buf, "      hour=");
492             for (i=0; i<24; i++) {
493                if (bit_is_set(i, run->hour)) {
494                   sprintf(num, "%d ", i);
495                   strcat(buf, num);
496                }
497             }
498             strcat(buf, "\n");
499             sendit(sock, buf);
500             strcpy(buf, "      mday=");
501             for (i=0; i<31; i++) {
502                if (bit_is_set(i, run->mday)) {
503                   sprintf(num, "%d ", i+1);
504                   strcat(buf, num);
505                }
506             }
507             strcat(buf, "\n");
508             sendit(sock, buf);
509             strcpy(buf, "      month=");
510             for (i=0; i<12; i++) {
511                if (bit_is_set(i, run->month)) {
512                   sprintf(num, "%d ", i+1);
513                   strcat(buf, num);
514                }
515             }
516             strcat(buf, "\n");
517             sendit(sock, buf);
518             strcpy(buf, "      wday=");
519             for (i=0; i<7; i++) {
520                if (bit_is_set(i, run->wday)) {
521                   sprintf(num, "%d ", i+1);
522                   strcat(buf, num);
523                }
524             }
525             strcat(buf, "\n");
526             sendit(sock, buf);
527             strcpy(buf, "      wpos=");
528             for (i=0; i<5; i++) {
529                if (bit_is_set(i, run->wpos)) {
530                   sprintf(num, "%d ", i+1);
531                   strcat(buf, num);
532                }
533             }
534             strcat(buf, "\n");
535             sendit(sock, buf);
536             sendit(sock, "      mins=%d\n", run->minute);
537             if (run->pool) {
538                sendit(sock, "     --> ");
539                dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
540             }
541             if (run->storage) {
542                sendit(sock, "     --> ");
543                dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
544             }
545             if (run->msgs) {
546                sendit(sock, "     --> ");
547                dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
548             }
549             /* If another Run record is chained in, go print it */
550             if (run->next) {
551                run = run->next;
552                goto next_run;
553             }
554          } else {
555             sendit(sock, "Schedule: name=%s\n", res->res_sch.hdr.name);
556          }
557          break;
558       case R_GROUP:
559          sendit(sock, "Group: name=%s\n", res->res_group.hdr.name);
560          break;
561       case R_POOL:
562          sendit(sock, "Pool: name=%s PoolType=%s\n", res->res_pool.hdr.name,
563                  res->res_pool.pool_type);
564          sendit(sock, "      use_cat=%d use_once=%d acpt_any=%d cat_files=%d\n",
565                  res->res_pool.use_catalog, res->res_pool.use_volume_once,
566                  res->res_pool.accept_any_volume, res->res_pool.catalog_files);
567          sendit(sock, "      max_vols=%d auto_prune=%d VolRetention=%" lld "\n",
568                  res->res_pool.max_volumes, res->res_pool.AutoPrune,
569                  res->res_pool.VolRetention);
570          sendit(sock, "      recycle=%d LabelFormat=%s\n", res->res_pool.Recycle,
571                  NPRT(res->res_pool.label_format));
572          break;
573       case R_MSGS:
574          sendit(sock, "Messages: name=%s\n", res->res_msgs.hdr.name);
575          if (res->res_msgs.mail_cmd) 
576             sendit(sock, "      mailcmd=%s\n", res->res_msgs.mail_cmd);
577          if (res->res_msgs.operator_cmd) 
578             sendit(sock, "      opcmd=%s\n", res->res_msgs.operator_cmd);
579          break;
580       default:
581          sendit(sock, "Unknown resource type %d in dump_resource.\n", type);
582          break;
583    }
584    if (recurse && res->res_dir.hdr.next) {
585       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
586    }
587 }
588
589 /* 
590  * Free memory of resource.  
591  * NB, we don't need to worry about freeing any references
592  * to other resources as they will be freed when that 
593  * resource chain is traversed.  Mainly we worry about freeing
594  * allocated strings (names).
595  */
596 void free_resource(int type)
597 {
598    int num;
599    URES *res;
600    RES *nres;
601    int rindex = type - r_first;
602
603    res = (URES *)resources[rindex].res_head;
604
605    if (res == NULL)
606       return;
607
608    /* common stuff -- free the resource name and description */
609    nres = (RES *)res->res_dir.hdr.next;
610    if (res->res_dir.hdr.name) {
611       free(res->res_dir.hdr.name);
612    }
613    if (res->res_dir.hdr.desc) {
614       free(res->res_dir.hdr.desc);
615    }
616
617    switch (type) {
618       case R_DIRECTOR:
619          if (res->res_dir.working_directory) {
620             free(res->res_dir.working_directory);
621          }
622          if (res->res_dir.pid_directory) {
623             free(res->res_dir.pid_directory);
624          }
625          if (res->res_dir.subsys_directory) {
626             free(res->res_dir.subsys_directory);
627          }
628          if (res->res_dir.password) {
629             free(res->res_dir.password);
630          }
631          if (res->res_dir.query_file) {
632             free(res->res_dir.query_file);
633          }
634          if (res->res_dir.DIRaddr) {
635             free(res->res_dir.DIRaddr);
636          }
637          break;
638       case R_CLIENT:
639          if (res->res_client.address) {
640             free(res->res_client.address);
641          }
642          if (res->res_client.password) {
643             free(res->res_client.password);
644          }
645          break;
646       case R_STORAGE:
647          if (res->res_store.address) {
648             free(res->res_store.address);
649          }
650          if (res->res_store.password) {
651             free(res->res_store.password);
652          }
653          if (res->res_store.media_type) {
654             free(res->res_store.media_type);
655          }
656          if (res->res_store.dev_name) {
657             free(res->res_store.dev_name);
658          }
659          break;
660       case R_CATALOG:
661          if (res->res_cat.db_address) {
662             free(res->res_cat.db_address);
663          }
664          if (res->res_cat.db_socket) {
665             free(res->res_cat.db_socket);
666          }
667          if (res->res_cat.db_user) {
668             free(res->res_cat.db_user);
669          }
670          if (res->res_cat.db_name) {
671             free(res->res_cat.db_name);
672          }
673          if (res->res_cat.db_password) {
674             free(res->res_cat.db_password);
675          }
676          break;
677       case R_FILESET:
678          if ((num=res->res_fs.num_includes)) {
679             while (--num >= 0) {   
680                INCEXE *incexe = res->res_fs.include_items[num];
681                for (int i=0; i<incexe->num_names; i++) {
682                   free(incexe->name_list[i]);
683                }
684                if (incexe->name_list) {
685                   free(incexe->name_list);
686                }
687                for (int i=0; i<incexe->num_match; i++) {
688                   free(incexe->match_list[i]);
689                }
690                if (incexe->match_list) {
691                   free(incexe->match_list);
692                }
693                free(incexe);
694             }
695             free(res->res_fs.include_items);
696          }
697          res->res_fs.num_includes = 0;
698          if ((num=res->res_fs.num_excludes)) {
699             while (--num >= 0) {   
700                INCEXE *incexe = res->res_fs.exclude_items[num];
701                for (int i=0; i<incexe->num_names; i++) {
702                   free(incexe->name_list[i]);
703                }
704                if (incexe->name_list) {
705                   free(incexe->name_list);
706                }
707                for (int i=0; i<incexe->num_match; i++) {
708                   free(incexe->match_list[i]);
709                }
710                if (incexe->match_list) {
711                   free(incexe->match_list);
712                }
713                free(incexe);
714             }
715             free(res->res_fs.exclude_items);
716          }
717          res->res_fs.num_excludes = 0;
718          break;
719       case R_POOL:
720          if (res->res_pool.pool_type) {
721             free(res->res_pool.pool_type);
722          }
723          if (res->res_pool.label_format) {
724             free(res->res_pool.label_format);
725          }
726          break;
727       case R_SCHEDULE:
728          if (res->res_sch.run) {
729             RUN *nrun, *next;
730             nrun = res->res_sch.run;
731             while (nrun) {
732                next = nrun->next;
733                free(nrun);
734                nrun = next;
735             }
736          }
737          break;
738       case R_JOB:
739          if (res->res_job.RestoreWhere) {
740             free(res->res_job.RestoreWhere);
741          }
742          if (res->res_job.RestoreBootstrap) {
743             free(res->res_job.RestoreBootstrap);
744          }
745          if (res->res_job.WriteBootstrap) {
746             free(res->res_job.WriteBootstrap);
747          }
748          if (res->res_job.RunBeforeJob) {
749             free(res->res_job.RunBeforeJob);
750          }
751          if (res->res_job.RunAfterJob) {
752             free(res->res_job.RunAfterJob);
753          }
754          break;
755       case R_MSGS:
756          if (res->res_msgs.mail_cmd)
757             free(res->res_msgs.mail_cmd);
758          if (res->res_msgs.operator_cmd)
759             free(res->res_msgs.operator_cmd);
760          free_msgs_res((MSGS *)res);  /* free message resource */
761          res = NULL;
762          break;
763       case R_GROUP:
764          break;
765       default:
766          printf("Unknown resource type %d in free_resource.\n", type);
767    }
768    /* Common stuff again -- free the resource, recurse to next one */
769    if (res) {
770       free(res);
771    }
772    resources[rindex].res_head = nres;
773    if (nres) {
774       free_resource(type);
775    }
776 }
777
778 /*
779  * Save the new resource by chaining it into the head list for
780  * the resource. If this is pass 2, we update any resource
781  * pointers because they may not have been defined until 
782  * later in pass 1.
783  */
784 void save_resource(int type, struct res_items *items, int pass)
785 {
786    URES *res;
787    int rindex = type - r_first;
788    int i, size;
789    int error = 0;
790    
791    /* 
792     * Ensure that all required items are present
793     */
794    for (i=0; items[i].name; i++) {
795       if (items[i].flags & ITEM_REQUIRED) {
796             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {  
797                Emsg2(M_ERROR_TERM, 0, "%s item is required in %s resource, but not found.\n",
798                  items[i].name, resources[rindex]);
799              }
800       }
801       /* If this triggers, take a look at lib/parse_conf.h */
802       if (i >= MAX_RES_ITEMS) {
803          Emsg1(M_ERROR_TERM, 0, "Too many items in %s resource\n", resources[rindex]);
804       }
805    }
806
807    /* During pass 2 in each "store" routine, we looked up pointers 
808     * to all the resources referrenced in the current resource, now we
809     * must copy their addresses from the static record to the allocated
810     * record.
811     */
812    if (pass == 2) {
813       switch (type) {
814          /* Resources not containing a resource */
815          case R_CATALOG:
816          case R_STORAGE:
817          case R_GROUP:
818          case R_POOL:
819          case R_MSGS:
820          case R_FILESET:
821             break;
822
823          /* Resources containing another resource */
824          case R_DIRECTOR:
825             if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
826                Emsg1(M_ERROR_TERM, 0, "Cannot find Director resource %s\n", res_all.res_dir.hdr.name);
827             }
828             res->res_dir.messages = res_all.res_dir.messages;
829             break;
830          case R_JOB:
831             if ((res = (URES *)GetResWithName(R_JOB, res_all.res_dir.hdr.name)) == NULL) {
832                Emsg1(M_ERROR_TERM, 0, "Cannot find Job resource %s\n", res_all.res_dir.hdr.name);
833             }
834             res->res_job.messages = res_all.res_job.messages;
835             res->res_job.schedule = res_all.res_job.schedule;
836             res->res_job.client   = res_all.res_job.client;
837             res->res_job.fileset  = res_all.res_job.fileset;
838             res->res_job.storage  = res_all.res_job.storage;
839             res->res_job.pool     = res_all.res_job.pool;
840             if (res->res_job.JobType == 0) {
841                Emsg1(M_ERROR_TERM, 0, "Job Type not defined for Job resource %s\n", res_all.res_dir.hdr.name);
842             }
843             if (res->res_job.level != 0) {
844                int i;
845                for (i=0; joblevels[i].level_name; i++) {
846                   if (joblevels[i].level == res->res_job.level &&
847                       joblevels[i].job_type == res->res_job.JobType) {
848                      i = 0;
849                      break;
850                   }
851                }
852                if (i != 0) {
853                   Emsg1(M_ERROR_TERM, 0, "Inappropriate level specified in Job resource %s\n", 
854                      res_all.res_dir.hdr.name);
855                }
856             }
857             break;
858          case R_CLIENT:
859             if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
860                Emsg1(M_ERROR_TERM, 0, "Cannot find Client resource %s\n", res_all.res_client.hdr.name);
861             }
862             res->res_client.catalog = res_all.res_client.catalog;
863             break;
864          case R_SCHEDULE:
865             /* Schedule is a bit different in that it contains a RUN record
866              * chain which isn't a "named" resource. This chain was linked
867              * in by run_conf.c during pass 2, so here we jam the pointer 
868              * into the Schedule resource.                         
869              */
870             if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
871                Emsg1(M_ERROR_TERM, 0, "Cannot find Schedule resource %s\n", res_all.res_client.hdr.name);
872             }
873             res->res_sch.run = res_all.res_sch.run;
874             break;
875          default:
876             Emsg1(M_ERROR, 0, "Unknown resource type %d in save_resource.\n", type);
877             error = 1;
878             break;
879       }
880       /* Note, the resource name was already saved during pass 1,
881        * so here, we can just release it.
882        */
883       if (res_all.res_dir.hdr.name) {
884          free(res_all.res_dir.hdr.name);
885          res_all.res_dir.hdr.name = NULL;
886       }
887       if (res_all.res_dir.hdr.desc) {
888          free(res_all.res_dir.hdr.desc);
889          res_all.res_dir.hdr.desc = NULL;
890       }
891       return;
892    }
893
894    /* The following code is only executed for pass 1 */
895    switch (type) {
896       case R_DIRECTOR:
897          size = sizeof(DIRRES);
898          break;
899       case R_CLIENT:
900          size =sizeof(CLIENT);
901          break;
902       case R_STORAGE:
903          size = sizeof(STORE); 
904          break;
905       case R_CATALOG:
906          size = sizeof(CAT);
907          break;
908       case R_JOB:
909          size = sizeof(JOB);
910          break;
911       case R_FILESET:
912          size = sizeof(FILESET);
913          break;
914       case R_SCHEDULE:
915          size = sizeof(SCHED);
916          break;
917       case R_GROUP:
918          size = sizeof(GROUP);
919          break;
920       case R_POOL:
921          size = sizeof(POOL);
922          break;
923       case R_MSGS:
924          size = sizeof(MSGS);
925          break;
926       case R_COUNTER:
927          size = sizeof(COUNTER);
928          break;
929       default:
930          printf("Unknown resource type %d in save_resrouce.\n", type);
931          error = 1;
932          size = 1;
933          break;
934    }
935    /* Common */
936    if (!error) {
937       res = (URES *)malloc(size);
938       memcpy(res, &res_all, size);
939       if (!resources[rindex].res_head) {
940          resources[rindex].res_head = (RES *)res; /* store first entry */
941          Dmsg3(200, "Inserting first %s res: %s index=%d\n", res_to_str(type),
942                res->res_dir.hdr.name, rindex);
943       } else {
944          RES *next;
945          /* Add new res to end of chain */
946          for (next=resources[rindex].res_head; next->next; next=next->next)
947             { }
948          next->next = (RES *)res;
949          Dmsg3(200, "Inserting %s res: %s index=%d\n", res_to_str(type),
950                res->res_dir.hdr.name, rindex);
951       }
952    }
953 }
954
955 /* 
956  * Store JobType (backup, verify, restore)
957  *
958  */
959 static void store_jobtype(LEX *lc, struct res_items *item, int index, int pass)
960 {
961    int token, i;   
962
963    token = lex_get_token(lc, T_NAME);
964    /* Store the type both pass 1 and pass 2 */
965    for (i=0; jobtypes[i].type_name; i++) {
966       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
967          ((JOB *)(item->value))->JobType = jobtypes[i].job_type;
968          i = 0;
969          break;
970       }
971    }
972    if (i != 0) {
973       scan_err1(lc, "Expected a Job Type keyword, got: %s", lc->str);
974    }
975    scan_to_eol(lc);
976    set_bit(index, res_all.hdr.item_present);
977 }
978
979 /* 
980  * Store Job Level (Full, Incremental, ...)
981  *
982  */
983 static void store_level(LEX *lc, struct res_items *item, int index, int pass)
984 {
985    int token, i;
986
987    token = lex_get_token(lc, T_NAME);
988    /* Store the level pass 2 so that type is defined */
989    for (i=0; joblevels[i].level_name; i++) {
990       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
991          ((JOB *)(item->value))->level = joblevels[i].level;
992          i = 0;
993          break;
994       }
995    }
996    if (i != 0) {
997       scan_err1(lc, "Expected a Job Level keyword, got: %s", lc->str);
998    }
999    scan_to_eol(lc);
1000    set_bit(index, res_all.hdr.item_present);
1001 }
1002
1003 static void store_replace(LEX *lc, struct res_items *item, int index, int pass)
1004 {
1005    int token, i;
1006    token = lex_get_token(lc, T_NAME);
1007    /* Scan Replacement options */
1008    for (i=0; ReplaceOptions[i].name; i++) {
1009       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1010          *(int *)(item->value) = ReplaceOptions[i].token;
1011          i = 0;
1012          break;
1013       }
1014    }
1015    if (i != 0) {
1016       scan_err1(lc, "Expected a Restore replacement option, got: %s", lc->str);
1017    }
1018    scan_to_eol(lc);
1019    set_bit(index, res_all.hdr.item_present);
1020 }
1021
1022 /* 
1023  * Store backup/verify info for Job record 
1024  *
1025  * Note, this code is used for both BACKUP and VERIFY jobs
1026  *
1027  *    Backup = Client=<client-name> FileSet=<FileSet-name> Level=<level>
1028  */
1029 static void store_backup(LEX *lc, struct res_items *item, int index, int pass)
1030 {
1031    int token, i;
1032    RES *res;
1033    int options = lc->options;
1034
1035    lc->options |= LOPT_NO_IDENT;      /* make spaces significant */
1036
1037    
1038    ((JOB *)(item->value))->JobType = item->code;
1039    while ((token = lex_get_token(lc, T_ALL)) != T_EOL) {
1040       int found;
1041
1042       Dmsg1(150, "store_backup got token=%s\n", lex_tok_to_str(token));
1043       
1044       if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
1045          scan_err1(lc, "Expected a backup/verify keyword, got: %s", lc->str);
1046       }
1047       Dmsg1(190, "Got keyword: %s\n", lc->str);
1048       found = FALSE;
1049       for (i=0; BakVerFields[i].name; i++) {
1050          if (strcasecmp(lc->str, BakVerFields[i].name) == 0) {
1051             found = TRUE;
1052             if (lex_get_token(lc, T_ALL) != T_EQUALS) {
1053                scan_err1(lc, "Expected an equals, got: %s", lc->str);
1054             }
1055             token = lex_get_token(lc, T_NAME);
1056             Dmsg1(190, "Got value: %s\n", lc->str);
1057             switch (BakVerFields[i].token) {
1058                case 'C':
1059                   /* Find Client Resource */
1060                   if (pass == 2) {
1061                      res = GetResWithName(R_CLIENT, lc->str);
1062                      if (res == NULL) {
1063                         scan_err1(lc, "Could not find specified Client Resource: %s",
1064                                    lc->str);
1065                      }
1066                      res_all.res_job.client = (CLIENT *)res;
1067                   }
1068                   break;
1069                case 'F':
1070                   /* Find FileSet Resource */
1071                   if (pass == 2) {
1072                      res = GetResWithName(R_FILESET, lc->str);
1073                      if (res == NULL) {
1074                         scan_err1(lc, "Could not find specified FileSet Resource: %s\n",
1075                                     lc->str);
1076                      }
1077                      res_all.res_job.fileset = (FILESET *)res;
1078                   }
1079                   break;
1080                case 'L':
1081                   /* Get level */
1082                   for (i=0; joblevels[i].level_name; i++) {
1083                      if (joblevels[i].job_type == item->code && 
1084                           strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1085                         ((JOB *)(item->value))->level = joblevels[i].level;
1086                         i = 0;
1087                         break;
1088                      }
1089                   }
1090                   if (i != 0) {
1091                      scan_err1(lc, "Expected a Job Level keyword, got: %s", lc->str);
1092                   }
1093                   break;
1094             } /* end switch */
1095             break;
1096          } /* end if strcmp() */
1097       } /* end for */
1098       if (!found) {
1099          scan_err1(lc, "%s not a valid Backup/verify keyword", lc->str);
1100       }
1101    } /* end while */
1102    lc->options = options;             /* reset original options */
1103    set_bit(index, res_all.hdr.item_present);
1104 }
1105
1106 /* 
1107  * Store restore info for Job record 
1108  *
1109  *    Restore = JobId=<job-id> Where=<root-directory> Replace=<options> Bootstrap=<file>
1110  *
1111  */
1112 static void store_restore(LEX *lc, struct res_items *item, int index, int pass)
1113 {
1114    int token, i;
1115    RES *res;
1116    int options = lc->options;
1117
1118    lc->options |= LOPT_NO_IDENT;      /* make spaces significant */
1119
1120    Dmsg0(190, "Enter store_restore()\n");
1121    
1122    ((JOB *)(item->value))->JobType = item->code;
1123    while ((token = lex_get_token(lc, T_ALL)) != T_EOL) {
1124       int found; 
1125
1126       if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
1127          scan_err1(lc, "expected a name, got: %s", lc->str);
1128       }
1129       found = FALSE;
1130       for (i=0; RestoreFields[i].name; i++) {
1131          Dmsg1(190, "Restore kw=%s\n", lc->str);
1132          if (strcasecmp(lc->str, RestoreFields[i].name) == 0) {
1133             found = TRUE;
1134             if (lex_get_token(lc, T_ALL) != T_EQUALS) {
1135                scan_err1(lc, "Expected an equals, got: %s", lc->str);
1136             }
1137             token = lex_get_token(lc, T_ALL);
1138             Dmsg1(190, "Restore value=%s\n", lc->str);
1139             switch (RestoreFields[i].token) {
1140                case 'B':
1141                   /* Bootstrap */
1142                   if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
1143                      scan_err1(lc, "Expected a Restore bootstrap file, got: %s", lc->str);
1144                   }
1145                   if (pass == 1) {
1146                      res_all.res_job.RestoreBootstrap = bstrdup(lc->str);
1147                   }
1148                   break;
1149                case 'C':
1150                   /* Find Client Resource */
1151                   if (pass == 2) {
1152                      res = GetResWithName(R_CLIENT, lc->str);
1153                      if (res == NULL) {
1154                         scan_err1(lc, "Could not find specified Client Resource: %s",
1155                                    lc->str);
1156                      }
1157                      res_all.res_job.client = (CLIENT *)res;
1158                   }
1159                   break;
1160                case 'F':
1161                   /* Find FileSet Resource */
1162                   if (pass == 2) {
1163                      res = GetResWithName(R_FILESET, lc->str);
1164                      if (res == NULL) {
1165                         scan_err1(lc, "Could not find specified FileSet Resource: %s\n",
1166                                     lc->str);
1167                      }
1168                      res_all.res_job.fileset = (FILESET *)res;
1169                   }
1170                   break;
1171                case 'J':
1172                   /* JobId */
1173                   if (token != T_NUMBER) {
1174                      scan_err1(lc, "expected an integer number, got: %s", lc->str);
1175                   }
1176                   errno = 0;
1177                   res_all.res_job.RestoreJobId = strtol(lc->str, NULL, 0);
1178                   Dmsg1(190, "RestorJobId=%d\n", res_all.res_job.RestoreJobId);
1179                   if (errno != 0) {
1180                      scan_err1(lc, "expected an integer number, got: %s", lc->str);
1181                   }
1182                   break;
1183                case 'W':
1184                   /* Where */
1185                   if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
1186                      scan_err1(lc, "Expected a Restore root directory, got: %s", lc->str);
1187                   }
1188                   if (pass == 1) {
1189                      res_all.res_job.RestoreWhere = bstrdup(lc->str);
1190                   }
1191                   break;
1192                case 'R':
1193                   /* Replacement options */
1194                   if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
1195                      scan_err1(lc, "Expected a keyword name, got: %s", lc->str);
1196                   }
1197                   /* Fix to scan Replacement options */
1198                   for (i=0; ReplaceOptions[i].name; i++) {
1199                      if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1200                          ((JOB *)(item->value))->replace = ReplaceOptions[i].token;
1201                         i = 0;
1202                         break;
1203                      }
1204                   }
1205                   if (i != 0) {
1206                      scan_err1(lc, "Expected a Restore replacement option, got: %s", lc->str);
1207                   }
1208                   break;
1209             } /* end switch */
1210             break;
1211          } /* end if strcmp() */
1212       } /* end for */
1213       if (!found) {
1214          scan_err1(lc, "%s not a valid Restore keyword", lc->str);
1215       }
1216    } /* end while */
1217    lc->options = options;             /* reset original options */
1218    set_bit(index, res_all.hdr.item_present);
1219 }