]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
c13a348ae607010792b4102f964d025ac850c41f
[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
58
59 /* Forward referenced subroutines */
60
61 static void store_inc(LEX *lc, struct res_items *item, int index, int pass);
62 static void store_applyto(LEX *lc, struct res_items *item, int index, int pass);
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 /* FileOptions resource
196  *
197  *   name          handler     value                 code flags    default_value
198  */
199 static struct res_items fo_items[] = {
200    {"name",        store_name, ITEM(res_fo.hdr.name), 0, ITEM_REQUIRED, 0},
201    {"description", store_str,  ITEM(res_fo.hdr.desc), 0, 0, 0},
202    {"replace",     store_replace, ITEM(res_fo.replace), REPLACE_ALWAYS, ITEM_DEFAULT, 0},
203    {"applyto",     store_applyto, NULL,               0, 0, 0},
204    {NULL,          NULL,       NULL,                  0, 0, 0} 
205 };
206
207
208 /* FileSet resource
209  *
210  *   name          handler     value                 code flags    default_value
211  */
212 static struct res_items fs_items[] = {
213    {"name",        store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
214    {"description", store_str,  ITEM(res_fs.hdr.desc), 0, 0, 0},
215    {"include",     store_inc,  NULL,                  0, 0, 0},
216    {"exclude",     store_inc,  NULL,                  1, 0, 0},
217    {NULL,          NULL,       NULL,                  0, 0, 0} 
218 };
219
220 /* Schedule -- see run_conf.c */
221 /* Schedule
222  *
223  *   name          handler     value                 code flags    default_value
224  */
225 static struct res_items sch_items[] = {
226    {"name",     store_name,  ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
227    {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
228    {"run",      store_run,   ITEM(res_sch.run),      0, 0, 0},
229    {NULL, NULL, NULL, 0, 0, 0} 
230 };
231
232 /* Group resource -- not implemented
233  *
234  *   name          handler     value                 code flags    default_value
235  */
236 static struct res_items group_items[] = {
237    {"name",        store_name, ITEM(res_group.hdr.name), 0, ITEM_REQUIRED, 0},
238    {"description", store_str,  ITEM(res_group.hdr.desc), 0, 0, 0},
239    {NULL, NULL, NULL, 0, 0, 0} 
240 };
241
242 /* Pool resource
243  *
244  *   name             handler     value                        code flags default_value
245  */
246 static struct res_items pool_items[] = {
247    {"name",            store_name,    ITEM(res_pool.hdr.name),      0, ITEM_REQUIRED, 0},
248    {"description",     store_str,     ITEM(res_pool.hdr.desc),      0, 0,     0},
249    {"pooltype",        store_strname, ITEM(res_pool.pool_type),     0, ITEM_REQUIRED, 0},
250    {"labelformat",     store_strname, ITEM(res_pool.label_format),  0, 0,     0},
251    {"usecatalog",      store_yesno, ITEM(res_pool.use_catalog),     1, ITEM_DEFAULT,  1},
252    {"usevolumeonce",   store_yesno, ITEM(res_pool.use_volume_once), 1, 0,        0},
253    {"maximumvolumes",  store_pint,  ITEM(res_pool.max_volumes),     0, 0,        0},
254    {"maximumvolumejobs", store_pint,  ITEM(res_pool.MaxVolJobs),    0, 0,       0},
255    {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles),   0, 0,       0},
256    {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes),   0, 0,       0},
257    {"acceptanyvolume", store_yesno, ITEM(res_pool.accept_any_volume), 1, ITEM_DEFAULT,     1},
258    {"catalogfiles",    store_yesno, ITEM(res_pool.catalog_files),   1, ITEM_DEFAULT,  1},
259    {"volumeretention", store_time,  ITEM(res_pool.VolRetention),    0, ITEM_DEFAULT, 60*60*24*365},
260    {"volumeuseduration", store_time,  ITEM(res_pool.VolUseDuration),0, 0, 0},
261    {"autoprune",       store_yesno, ITEM(res_pool.AutoPrune), 1, ITEM_DEFAULT, 1},
262    {"recycle",         store_yesno, ITEM(res_pool.Recycle),     1, ITEM_DEFAULT, 1},
263    {NULL, NULL, NULL, 0, 0, 0} 
264 };
265
266 /* 
267  * Counter Resource
268  *   name             handler     value                        code flags default_value
269  */
270 static struct res_items counter_items[] = {
271    {"name",            store_name,    ITEM(res_counter.hdr.name),        0, ITEM_REQUIRED, 0},
272    {"description",     store_str,     ITEM(res_counter.hdr.desc),        0, 0,     0},
273    {"minimum",         store_int,     ITEM(res_counter.MinValue),        0, ITEM_DEFAULT, 0},
274    {"maximum",         store_pint,    ITEM(res_counter.MaxValue),        0, ITEM_DEFAULT, INT32_MAX},
275    {"global",          store_yesno,   ITEM(res_counter.Global),          0, ITEM_DEFAULT, 0},
276    {"wrapcounter",     store_strname, ITEM(res_counter.WrapCounter),     0, 0, 0},
277    {NULL, NULL, NULL, 0, 0, 0} 
278 };
279
280
281 /* Message resource */
282 extern struct res_items msgs_items[];
283
284 /* 
285  * This is the master resource definition.  
286  * It must have one item for each of the resources.
287  *
288  *  name             items        rcode        res_head
289  */
290 struct s_res resources[] = {
291    {"director",      dir_items,   R_DIRECTOR,  NULL},
292    {"client",        cli_items,   R_CLIENT,    NULL},
293    {"job",           job_items,   R_JOB,       NULL},
294    {"storage",       store_items, R_STORAGE,   NULL},
295    {"catalog",       cat_items,   R_CATALOG,   NULL},
296    {"schedule",      sch_items,   R_SCHEDULE,  NULL},
297    {"fileoptions",   fo_items,    R_FILEOPTIONS, NULL},
298    {"fileset",       fs_items,    R_FILESET,   NULL},
299    {"group",         group_items, R_GROUP,     NULL},
300    {"pool",          pool_items,  R_POOL,      NULL},
301    {"messages",      msgs_items,  R_MSGS,      NULL},
302    {"counter",       counter_items, R_COUNTER, NULL},
303    {NULL,            NULL,        0,           NULL}
304 };
305
306
307 /* Keywords (RHS) permitted in Job Level records   
308  *
309  *   level_name      level              job_type
310  */
311 struct s_jl joblevels[] = {
312    {"Full",          L_FULL,            JT_BACKUP},
313    {"Incremental",   L_INCREMENTAL,     JT_BACKUP},
314    {"Differential",  L_DIFFERENTIAL,    JT_BACKUP},
315    {"Since",         L_SINCE,           JT_BACKUP},
316    {"Catalog",       L_VERIFY_CATALOG,  JT_VERIFY},
317    {"Initcatalog",   L_VERIFY_INIT,     JT_VERIFY},
318    {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG,   JT_VERIFY},
319    {"Data",          L_VERIFY_DATA,     JT_VERIFY},
320    {NULL,            0}
321 };
322
323 /* Keywords (RHS) permitted in Job type records   
324  *
325  *   type_name       job_type
326  */
327 struct s_jt jobtypes[] = {
328    {"backup",        JT_BACKUP},
329    {"admin",         JT_ADMIN},
330    {"verify",        JT_VERIFY},
331    {"restore",       JT_RESTORE},
332    {NULL,            0}
333 };
334
335
336 /* Keywords (RHS) permitted in Backup and Verify records */
337 static struct s_kw BakVerFields[] = {
338    {"client",        'C'},
339    {"fileset",       'F'},
340    {"level",         'L'}, 
341    {NULL,            0}
342 };
343
344 /* Keywords (RHS) permitted in Restore records */
345 static struct s_kw RestoreFields[] = {
346    {"client",        'C'},
347    {"fileset",       'F'},
348    {"jobid",         'J'},            /* JobId to restore */
349    {"where",         'W'},            /* root of restore */
350    {"replace",       'R'},            /* replacement options */
351    {"bootstrap",     'B'},            /* bootstrap file */
352    {NULL,              0}
353 };
354
355 /* Options permitted in Restore replace= */
356 struct s_kw ReplaceOptions[] = {
357    {"always",         REPLACE_ALWAYS},
358    {"ifnewer",        REPLACE_IFNEWER},
359    {"ifolder",        REPLACE_IFOLDER},
360    {"never",          REPLACE_NEVER},
361    {NULL,               0}
362 };
363
364
365
366 /* Define FileSet KeyWord values */
367
368 #define INC_KW_NONE         0
369 #define INC_KW_COMPRESSION  1
370 #define INC_KW_SIGNATURE    2
371 #define INC_KW_ENCRYPTION   3
372 #define INC_KW_VERIFY       4
373 #define INC_KW_ONEFS        5
374 #define INC_KW_RECURSE      6
375 #define INC_KW_SPARSE       7
376 #define INC_KW_REPLACE      8         /* restore options */
377 #define INC_KW_READFIFO     9         /* Causes fifo data to be read */
378
379 /* Include keywords */
380 static struct s_kw FS_option_kw[] = {
381    {"compression", INC_KW_COMPRESSION},
382    {"signature",   INC_KW_SIGNATURE},
383    {"encryption",  INC_KW_ENCRYPTION},
384    {"verify",      INC_KW_VERIFY},
385    {"onefs",       INC_KW_ONEFS},
386    {"recurse",     INC_KW_RECURSE},
387    {"sparse",      INC_KW_SPARSE},
388    {"replace",     INC_KW_REPLACE},
389    {"readfifo",    INC_KW_READFIFO},
390    {NULL,          0}
391 };
392
393 /* Options for FileSet keywords */
394
395 struct s_fs_opt {
396    char *name;
397    int keyword;
398    char *option;
399 };
400
401 /* Options permitted for each keyword and resulting value */
402 static struct s_fs_opt FS_options[] = {
403    {"md5",      INC_KW_SIGNATURE,    "M"},
404    {"sha1",     INC_KW_SIGNATURE,    "S"},
405    {"gzip",     INC_KW_COMPRESSION,  "Z6"},
406    {"gzip1",    INC_KW_COMPRESSION,  "Z1"},
407    {"gzip2",    INC_KW_COMPRESSION,  "Z2"},
408    {"gzip3",    INC_KW_COMPRESSION,  "Z3"},
409    {"gzip4",    INC_KW_COMPRESSION,  "Z4"},
410    {"gzip5",    INC_KW_COMPRESSION,  "Z5"},
411    {"gzip6",    INC_KW_COMPRESSION,  "Z6"},
412    {"gzip7",    INC_KW_COMPRESSION,  "Z7"},
413    {"gzip8",    INC_KW_COMPRESSION,  "Z8"},
414    {"gzip9",    INC_KW_COMPRESSION,  "Z9"},
415    {"blowfish", INC_KW_ENCRYPTION,    "B"},   /* ***FIXME*** not implemented */
416    {"3des",     INC_KW_ENCRYPTION,    "3"},   /* ***FIXME*** not implemented */
417    {"yes",      INC_KW_ONEFS,         "0"},
418    {"no",       INC_KW_ONEFS,         "f"},
419    {"yes",      INC_KW_RECURSE,       "0"},
420    {"no",       INC_KW_RECURSE,       "h"},
421    {"yes",      INC_KW_SPARSE,        "s"},
422    {"no",       INC_KW_SPARSE,        "0"},
423    {"always",   INC_KW_REPLACE,       "a"},
424    {"ifnewer",  INC_KW_REPLACE,       "w"},
425    {"never",    INC_KW_REPLACE,       "n"},
426    {"yes",      INC_KW_READFIFO,      "r"},
427    {"no",       INC_KW_READFIFO,      "0"},
428    {NULL,       0,                   0}
429 };
430
431 char *level_to_str(int level)
432 {
433    int i;
434    static char level_no[30];
435    char *str = level_no;
436
437    sprintf(level_no, "%d", level);    /* default if not found */
438    for (i=0; joblevels[i].level_name; i++) {
439       if (level == joblevels[i].level) {
440          str = joblevels[i].level_name;
441          break;
442       }
443    }
444    return str;
445 }
446
447
448
449 /* Dump contents of resource */
450 void dump_resource(int type, RES *reshdr, void sendit(void *sock, char *fmt, ...), void *sock)
451 {
452    int i;
453    URES *res = (URES *)reshdr;
454    int recurse = 1;
455
456    if (res == NULL) {
457       sendit(sock, "No %s resource defined\n", res_to_str(type));
458       return;
459    }
460    if (type < 0) {                    /* no recursion */
461       type = - type;
462       recurse = 0;
463    }
464    switch (type) {
465       case R_DIRECTOR:
466          char ed1[30], ed2[30];
467          sendit(sock, "Director: name=%s maxjobs=%d FDtimeout=%s SDtimeout=%s\n", 
468             reshdr->name, res->res_dir.MaxConcurrentJobs, 
469             edit_uint64(res->res_dir.FDConnectTimeout, ed1),
470             edit_uint64(res->res_dir.SDConnectTimeout, ed2));
471          if (res->res_dir.query_file) {
472             sendit(sock, "   query_file=%s\n", res->res_dir.query_file);
473          }
474          if (res->res_dir.messages) {
475             sendit(sock, "  --> ");
476             dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
477          }
478          break;
479       case R_CLIENT:
480          sendit(sock, "Client: name=%s address=%s FDport=%d\n",
481             res->res_client.hdr.name, res->res_client.address, res->res_client.FDport);
482          sendit(sock, "JobRetention=%" lld " FileRetention=%" lld " AutoPrune=%d\n",
483             res->res_client.JobRetention, res->res_client.FileRetention,
484             res->res_client.AutoPrune);
485          if (res->res_client.catalog) {
486             sendit(sock, "  --> ");
487             dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
488          }
489          break;
490       case R_STORAGE:
491          sendit(sock, "Storage: name=%s address=%s SDport=%d\n\
492          DeviceName=%s MediaType=%s\n",
493             res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
494             res->res_store.dev_name, res->res_store.media_type);
495          break;
496       case R_CATALOG:
497          sendit(sock, "Catalog: name=%s address=%s DBport=%d db_name=%s\n\
498          db_user=%s\n",
499             res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
500             res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user));
501          break;
502       case R_JOB:
503          sendit(sock, "Job: name=%s JobType=%d level=%s\n", res->res_job.hdr.name, 
504             res->res_job.JobType, level_to_str(res->res_job.level));
505          if (res->res_job.client) {
506             sendit(sock, "  --> ");
507             dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
508          }
509          if (res->res_job.fileset) {
510             sendit(sock, "  --> ");
511             dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
512          }
513          if (res->res_job.schedule) {
514             sendit(sock, "  --> ");
515             dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
516          }
517          if (res->res_job.RestoreWhere) {
518             sendit(sock, "  --> Where=%s\n", NPRT(res->res_job.RestoreWhere));
519          }
520          if (res->res_job.RestoreBootstrap) {
521             sendit(sock, "  --> Bootstrap=%s\n", NPRT(res->res_job.RestoreBootstrap));
522          }
523          if (res->res_job.RunBeforeJob) {
524             sendit(sock, "  --> RunBefore=%s\n", NPRT(res->res_job.RunBeforeJob));
525          }
526          if (res->res_job.RunAfterJob) {
527             sendit(sock, "  --> RunAfter=%s\n", NPRT(res->res_job.RunAfterJob));
528          }
529          if (res->res_job.WriteBootstrap) {
530             sendit(sock, "  --> WriteBootstrap=%s\n", NPRT(res->res_job.WriteBootstrap));
531          }
532          if (res->res_job.storage) {
533             sendit(sock, "  --> ");
534             dump_resource(-R_STORAGE, (RES *)res->res_job.storage, sendit, sock);
535          }
536          if (res->res_job.pool) {
537             sendit(sock, "  --> ");
538             dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
539          } else {
540             sendit(sock, "!!! No Pool resource\n");
541          }
542          if (res->res_job.messages) {
543             sendit(sock, "  --> ");
544             dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
545          }
546          break;
547       case R_FILESET:
548          sendit(sock, "FileSet: name=%s\n", res->res_fs.hdr.name);
549          for (i=0; i<res->res_fs.num_includes; i++)
550             sendit(sock, "      Inc: %s\n", res->res_fs.include_array[i]->name);
551          for (i=0; i<res->res_fs.num_excludes; i++)
552             sendit(sock, "      Exc: %s\n", res->res_fs.exclude_array[i]->name);
553          break;
554       case R_SCHEDULE:
555          if (res->res_sch.run) {
556             int i;
557             RUN *run = res->res_sch.run;
558             char buf[1000], num[10];
559             sendit(sock, "Schedule: name=%s\n", res->res_sch.hdr.name);
560             if (!run) {
561                break;
562             }
563 next_run:
564             sendit(sock, "  --> Run Level=%s\n", level_to_str(run->level));
565             strcpy(buf, "      hour=");
566             for (i=0; i<24; i++) {
567                if (bit_is_set(i, run->hour)) {
568                   sprintf(num, "%d ", i);
569                   strcat(buf, num);
570                }
571             }
572             strcat(buf, "\n");
573             sendit(sock, buf);
574             strcpy(buf, "      mday=");
575             for (i=0; i<31; i++) {
576                if (bit_is_set(i, run->mday)) {
577                   sprintf(num, "%d ", i+1);
578                   strcat(buf, num);
579                }
580             }
581             strcat(buf, "\n");
582             sendit(sock, buf);
583             strcpy(buf, "      month=");
584             for (i=0; i<12; i++) {
585                if (bit_is_set(i, run->month)) {
586                   sprintf(num, "%d ", i+1);
587                   strcat(buf, num);
588                }
589             }
590             strcat(buf, "\n");
591             sendit(sock, buf);
592             strcpy(buf, "      wday=");
593             for (i=0; i<7; i++) {
594                if (bit_is_set(i, run->wday)) {
595                   sprintf(num, "%d ", i+1);
596                   strcat(buf, num);
597                }
598             }
599             strcat(buf, "\n");
600             sendit(sock, buf);
601             strcpy(buf, "      wpos=");
602             for (i=0; i<5; i++) {
603                if (bit_is_set(i, run->wpos)) {
604                   sprintf(num, "%d ", i+1);
605                   strcat(buf, num);
606                }
607             }
608             strcat(buf, "\n");
609             sendit(sock, buf);
610             sendit(sock, "      mins=%d\n", run->minute);
611             if (run->pool) {
612                sendit(sock, "     --> ");
613                dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
614             }
615             if (run->storage) {
616                sendit(sock, "     --> ");
617                dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
618             }
619             if (run->msgs) {
620                sendit(sock, "     --> ");
621                dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
622             }
623             /* If another Run record is chained in, go print it */
624             if (run->next) {
625                run = run->next;
626                goto next_run;
627             }
628          } else {
629             sendit(sock, "Schedule: name=%s\n", res->res_sch.hdr.name);
630          }
631          break;
632       case R_GROUP:
633          sendit(sock, "Group: name=%s\n", res->res_group.hdr.name);
634          break;
635       case R_POOL:
636          sendit(sock, "Pool: name=%s PoolType=%s\n", res->res_pool.hdr.name,
637                  res->res_pool.pool_type);
638          sendit(sock, "      use_cat=%d use_once=%d acpt_any=%d cat_files=%d\n",
639                  res->res_pool.use_catalog, res->res_pool.use_volume_once,
640                  res->res_pool.accept_any_volume, res->res_pool.catalog_files);
641          sendit(sock, "      max_vols=%d auto_prune=%d VolRetention=%" lld "\n",
642                  res->res_pool.max_volumes, res->res_pool.AutoPrune,
643                  res->res_pool.VolRetention);
644          sendit(sock, "      recycle=%d LabelFormat=%s\n", res->res_pool.Recycle,
645                  NPRT(res->res_pool.label_format));
646          break;
647       case R_MSGS:
648          sendit(sock, "Messages: name=%s\n", res->res_msgs.hdr.name);
649          if (res->res_msgs.mail_cmd) 
650             sendit(sock, "      mailcmd=%s\n", res->res_msgs.mail_cmd);
651          if (res->res_msgs.operator_cmd) 
652             sendit(sock, "      opcmd=%s\n", res->res_msgs.operator_cmd);
653          break;
654       default:
655          sendit(sock, "Unknown resource type %d\n", type);
656          break;
657    }
658    if (recurse && res->res_dir.hdr.next) {
659       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
660    }
661 }
662
663 /* 
664  * Free memory of resource.  
665  * NB, we don't need to worry about freeing any references
666  * to other resources as they will be freed when that 
667  * resource chain is traversed.  Mainly we worry about freeing
668  * allocated strings (names).
669  */
670 void free_resource(int type)
671 {
672    int num;
673    URES *res;
674    RES *nres;
675    int rindex = type - r_first;
676
677    res = (URES *)resources[rindex].res_head;
678
679    if (res == NULL)
680       return;
681
682    /* common stuff -- free the resource name and description */
683    nres = (RES *)res->res_dir.hdr.next;
684    if (res->res_dir.hdr.name) {
685       free(res->res_dir.hdr.name);
686    }
687    if (res->res_dir.hdr.desc) {
688       free(res->res_dir.hdr.desc);
689    }
690
691    switch (type) {
692       case R_DIRECTOR:
693          if (res->res_dir.working_directory) {
694             free(res->res_dir.working_directory);
695          }
696          if (res->res_dir.pid_directory) {
697             free(res->res_dir.pid_directory);
698          }
699          if (res->res_dir.subsys_directory) {
700             free(res->res_dir.subsys_directory);
701          }
702          if (res->res_dir.password) {
703             free(res->res_dir.password);
704          }
705          if (res->res_dir.query_file) {
706             free(res->res_dir.query_file);
707          }
708          if (res->res_dir.DIRaddr) {
709             free(res->res_dir.DIRaddr);
710          }
711          break;
712       case R_CLIENT:
713          if (res->res_client.address) {
714             free(res->res_client.address);
715          }
716          if (res->res_client.password) {
717             free(res->res_client.password);
718          }
719          break;
720       case R_STORAGE:
721          if (res->res_store.address) {
722             free(res->res_store.address);
723          }
724          if (res->res_store.password) {
725             free(res->res_store.password);
726          }
727          if (res->res_store.media_type) {
728             free(res->res_store.media_type);
729          }
730          if (res->res_store.dev_name) {
731             free(res->res_store.dev_name);
732          }
733          break;
734       case R_CATALOG:
735          if (res->res_cat.db_address) {
736             free(res->res_cat.db_address);
737          }
738          if (res->res_cat.db_socket) {
739             free(res->res_cat.db_socket);
740          }
741          if (res->res_cat.db_user) {
742             free(res->res_cat.db_user);
743          }
744          if (res->res_cat.db_name) {
745             free(res->res_cat.db_name);
746          }
747          if (res->res_cat.db_password) {
748             free(res->res_cat.db_password);
749          }
750          break;
751       case R_FILESET:
752          if ((num=res->res_fs.num_includes)) {
753             while (--num >= 0) {   
754                free(res->res_fs.include_array[num]);
755             }
756             free(res->res_fs.include_array);
757          }
758          if ((num=res->res_fs.num_excludes)) {
759             while (--num >= 0) {   
760                free(res->res_fs.exclude_array[num]);
761             }
762             free(res->res_fs.exclude_array);
763          }
764          break;
765       case R_POOL:
766          if (res->res_pool.pool_type) {
767             free(res->res_pool.pool_type);
768          }
769          if (res->res_pool.label_format) {
770             free(res->res_pool.label_format);
771          }
772          break;
773       case R_SCHEDULE:
774          if (res->res_sch.run) {
775             RUN *nrun, *next;
776             nrun = res->res_sch.run;
777             while (nrun) {
778                next = nrun->next;
779                free(nrun);
780                nrun = next;
781             }
782          }
783          break;
784       case R_JOB:
785          if (res->res_job.RestoreWhere) {
786             free(res->res_job.RestoreWhere);
787          }
788          if (res->res_job.RestoreBootstrap) {
789             free(res->res_job.RestoreBootstrap);
790          }
791          if (res->res_job.WriteBootstrap) {
792             free(res->res_job.WriteBootstrap);
793          }
794          if (res->res_job.RunBeforeJob) {
795             free(res->res_job.RunBeforeJob);
796          }
797          if (res->res_job.RunAfterJob) {
798             free(res->res_job.RunAfterJob);
799          }
800          break;
801       case R_MSGS:
802          if (res->res_msgs.mail_cmd)
803             free(res->res_msgs.mail_cmd);
804          if (res->res_msgs.operator_cmd)
805             free(res->res_msgs.operator_cmd);
806          free_msgs_res((MSGS *)res);  /* free message resource */
807          res = NULL;
808          break;
809       case R_GROUP:
810          break;
811       default:
812          printf("Unknown resource type %d\n", type);
813    }
814    /* Common stuff again -- free the resource, recurse to next one */
815    if (res) {
816       free(res);
817    }
818    resources[rindex].res_head = nres;
819    if (nres) {
820       free_resource(type);
821    }
822 }
823
824 /*
825  * Save the new resource by chaining it into the head list for
826  * the resource. If this is pass 2, we update any resource
827  * pointers because they may not have been defined until 
828  * later in pass 1.
829  */
830 void save_resource(int type, struct res_items *items, int pass)
831 {
832    URES *res;
833    int rindex = type - r_first;
834    int i, size;
835    int error = 0;
836    
837    /* 
838     * Ensure that all required items are present
839     */
840    for (i=0; items[i].name; i++) {
841       if (items[i].flags & ITEM_REQUIRED) {
842             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {  
843                Emsg2(M_ERROR_TERM, 0, "%s item is required in %s resource, but not found.\n",
844                  items[i].name, resources[rindex]);
845              }
846       }
847       /* If this triggers, take a look at lib/parse_conf.h */
848       if (i >= MAX_RES_ITEMS) {
849          Emsg1(M_ERROR_TERM, 0, "Too many items in %s resource\n", resources[rindex]);
850       }
851    }
852
853    /* During pass 2 in each "store" routine, we looked up pointers 
854     * to all the resources referrenced in the current resource, now we
855     * must copy their addresses from the static record to the allocated
856     * record.
857     */
858    if (pass == 2) {
859       switch (type) {
860          /* Resources not containing a resource */
861          case R_CATALOG:
862          case R_STORAGE:
863          case R_FILESET:
864          case R_GROUP:
865          case R_POOL:
866          case R_MSGS:
867             break;
868
869          /* Resources containing another resource */
870          case R_DIRECTOR:
871             if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
872                Emsg1(M_ERROR_TERM, 0, "Cannot find Director resource %s\n", res_all.res_dir.hdr.name);
873             }
874             res->res_dir.messages = res_all.res_dir.messages;
875             break;
876          case R_JOB:
877             if ((res = (URES *)GetResWithName(R_JOB, res_all.res_dir.hdr.name)) == NULL) {
878                Emsg1(M_ERROR_TERM, 0, "Cannot find Job resource %s\n", res_all.res_dir.hdr.name);
879             }
880             res->res_job.messages = res_all.res_job.messages;
881             res->res_job.schedule = res_all.res_job.schedule;
882             res->res_job.client   = res_all.res_job.client;
883             res->res_job.fileset  = res_all.res_job.fileset;
884             res->res_job.storage  = res_all.res_job.storage;
885             res->res_job.pool     = res_all.res_job.pool;
886             if (res->res_job.JobType == 0) {
887                Emsg1(M_ERROR_TERM, 0, "Job Type not defined for Job resource %s\n", res_all.res_dir.hdr.name);
888             }
889             if (res->res_job.level != 0) {
890                int i;
891                for (i=0; joblevels[i].level_name; i++) {
892                   if (joblevels[i].level == res->res_job.level &&
893                       joblevels[i].job_type == res->res_job.JobType) {
894                      i = 0;
895                      break;
896                   }
897                }
898                if (i != 0) {
899                   Emsg1(M_ERROR_TERM, 0, "Inappropriate level specified in Job resource %s\n", 
900                      res_all.res_dir.hdr.name);
901                }
902             }
903             break;
904          case R_CLIENT:
905             if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
906                Emsg1(M_ERROR_TERM, 0, "Cannot find Client resource %s\n", res_all.res_client.hdr.name);
907             }
908             res->res_client.catalog = res_all.res_client.catalog;
909             break;
910          case R_SCHEDULE:
911             /* Schedule is a bit different in that it contains a RUN record
912              * chain which isn't a "named" resource. This chain was linked
913              * in by run_conf.c during pass 2, so here we jam the pointer 
914              * into the Schedule resource.                         
915              */
916             if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
917                Emsg1(M_ERROR_TERM, 0, "Cannot find Schedule resource %s\n", res_all.res_client.hdr.name);
918             }
919             res->res_sch.run = res_all.res_sch.run;
920             break;
921          default:
922             Emsg1(M_ERROR, 0, "Unknown resource type %d\n", type);
923             error = 1;
924             break;
925       }
926       /* Note, the resource name was already saved during pass 1,
927        * so here, we can just release it.
928        */
929       if (res_all.res_dir.hdr.name) {
930          free(res_all.res_dir.hdr.name);
931          res_all.res_dir.hdr.name = NULL;
932       }
933       if (res_all.res_dir.hdr.desc) {
934          free(res_all.res_dir.hdr.desc);
935          res_all.res_dir.hdr.desc = NULL;
936       }
937       return;
938    }
939
940    /* The following code is only executed for pass 1 */
941    switch (type) {
942       case R_DIRECTOR:
943          size = sizeof(DIRRES);
944          break;
945       case R_CLIENT:
946          size =sizeof(CLIENT);
947          break;
948       case R_STORAGE:
949          size = sizeof(STORE); 
950          break;
951       case R_CATALOG:
952          size = sizeof(CAT);
953          break;
954       case R_JOB:
955          size = sizeof(JOB);
956          break;
957       case R_FILESET:
958          size = sizeof(FILESET);
959          break;
960       case R_SCHEDULE:
961          size = sizeof(SCHED);
962          break;
963       case R_GROUP:
964          size = sizeof(GROUP);
965          break;
966       case R_POOL:
967          size = sizeof(POOL);
968          break;
969       case R_MSGS:
970          size = sizeof(MSGS);
971          break;
972       default:
973          printf("Unknown resource type %d\n", type);
974          error = 1;
975          size = 1;
976          break;
977    }
978    /* Common */
979    if (!error) {
980       res = (URES *)malloc(size);
981       memcpy(res, &res_all, size);
982       if (!resources[rindex].res_head) {
983          resources[rindex].res_head = (RES *)res; /* store first entry */
984       } else {
985          RES *next;
986          /* Add new res to end of chain */
987          for (next=resources[rindex].res_head; next->next; next=next->next)
988             { }
989          next->next = (RES *)res;
990          Dmsg2(90, "Inserting %s res: %s\n", res_to_str(type),
991                res->res_dir.hdr.name);
992       }
993    }
994 }
995
996 /* 
997  * Store JobType (backup, verify, restore)
998  *
999  */
1000 static void store_jobtype(LEX *lc, struct res_items *item, int index, int pass)
1001 {
1002    int token, i;   
1003
1004    token = lex_get_token(lc, T_NAME);
1005    /* Store the type both pass 1 and pass 2 */
1006    for (i=0; jobtypes[i].type_name; i++) {
1007       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1008          ((JOB *)(item->value))->JobType = jobtypes[i].job_type;
1009          i = 0;
1010          break;
1011       }
1012    }
1013    if (i != 0) {
1014       scan_err1(lc, "Expected a Job Type keyword, got: %s", lc->str);
1015    }
1016    scan_to_eol(lc);
1017    set_bit(index, res_all.hdr.item_present);
1018 }
1019
1020 /* 
1021  * Store Job Level (Full, Incremental, ...)
1022  *
1023  */
1024 static void store_level(LEX *lc, struct res_items *item, int index, int pass)
1025 {
1026    int token, i;
1027
1028    token = lex_get_token(lc, T_NAME);
1029    /* Store the level pass 2 so that type is defined */
1030    for (i=0; joblevels[i].level_name; i++) {
1031       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1032          ((JOB *)(item->value))->level = joblevels[i].level;
1033          i = 0;
1034          break;
1035       }
1036    }
1037    if (i != 0) {
1038       scan_err1(lc, "Expected a Job Level keyword, got: %s", lc->str);
1039    }
1040    scan_to_eol(lc);
1041    set_bit(index, res_all.hdr.item_present);
1042 }
1043
1044 static void store_replace(LEX *lc, struct res_items *item, int index, int pass)
1045 {
1046    int token, i;
1047    token = lex_get_token(lc, T_NAME);
1048    /* Scan Replacement options */
1049    for (i=0; ReplaceOptions[i].name; i++) {
1050       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1051          ((JOB *)(item->value))->replace = ReplaceOptions[i].token;
1052          i = 0;
1053          break;
1054       }
1055    }
1056    if (i != 0) {
1057       scan_err1(lc, "Expected a Restore replacement option, got: %s", lc->str);
1058    }
1059    scan_to_eol(lc);
1060    set_bit(index, res_all.hdr.item_present);
1061 }
1062
1063 /* 
1064  * Store backup/verify info for Job record 
1065  *
1066  * Note, this code is used for both BACKUP and VERIFY jobs
1067  *
1068  *    Backup = Client=<client-name> FileSet=<FileSet-name> Level=<level>
1069  */
1070 static void store_backup(LEX *lc, struct res_items *item, int index, int pass)
1071 {
1072    int token, i;
1073    RES *res;
1074    int options = lc->options;
1075
1076    lc->options |= LOPT_NO_IDENT;      /* make spaces significant */
1077
1078    
1079    ((JOB *)(item->value))->JobType = item->code;
1080    while ((token = lex_get_token(lc, T_ALL)) != T_EOL) {
1081       int found;
1082
1083       Dmsg1(150, "store_backup got token=%s\n", lex_tok_to_str(token));
1084       
1085       if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
1086          scan_err1(lc, "Expected a backup/verify keyword, got: %s", lc->str);
1087       }
1088       Dmsg1(190, "Got keyword: %s\n", lc->str);
1089       found = FALSE;
1090       for (i=0; BakVerFields[i].name; i++) {
1091          if (strcasecmp(lc->str, BakVerFields[i].name) == 0) {
1092             found = TRUE;
1093             if (lex_get_token(lc, T_ALL) != T_EQUALS) {
1094                scan_err1(lc, "Expected an equals, got: %s", lc->str);
1095             }
1096             token = lex_get_token(lc, T_NAME);
1097             Dmsg1(190, "Got value: %s\n", lc->str);
1098             switch (BakVerFields[i].token) {
1099                case 'C':
1100                   /* Find Client Resource */
1101                   if (pass == 2) {
1102                      res = GetResWithName(R_CLIENT, lc->str);
1103                      if (res == NULL) {
1104                         scan_err1(lc, "Could not find specified Client Resource: %s",
1105                                    lc->str);
1106                      }
1107                      res_all.res_job.client = (CLIENT *)res;
1108                   }
1109                   break;
1110                case 'F':
1111                   /* Find FileSet Resource */
1112                   if (pass == 2) {
1113                      res = GetResWithName(R_FILESET, lc->str);
1114                      if (res == NULL) {
1115                         scan_err1(lc, "Could not find specified FileSet Resource: %s\n",
1116                                     lc->str);
1117                      }
1118                      res_all.res_job.fileset = (FILESET *)res;
1119                   }
1120                   break;
1121                case 'L':
1122                   /* Get level */
1123                   for (i=0; joblevels[i].level_name; i++) {
1124                      if (joblevels[i].job_type == item->code && 
1125                           strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1126                         ((JOB *)(item->value))->level = joblevels[i].level;
1127                         i = 0;
1128                         break;
1129                      }
1130                   }
1131                   if (i != 0) {
1132                      scan_err1(lc, "Expected a Job Level keyword, got: %s", lc->str);
1133                   }
1134                   break;
1135             } /* end switch */
1136             break;
1137          } /* end if strcmp() */
1138       } /* end for */
1139       if (!found) {
1140          scan_err1(lc, "%s not a valid Backup/verify keyword", lc->str);
1141       }
1142    } /* end while */
1143    lc->options = options;             /* reset original options */
1144    set_bit(index, res_all.hdr.item_present);
1145 }
1146
1147 /* 
1148  * Store restore info for Job record 
1149  *
1150  *    Restore = JobId=<job-id> Where=<root-directory> Replace=<options> Bootstrap=<file>
1151  *
1152  */
1153 static void store_restore(LEX *lc, struct res_items *item, int index, int pass)
1154 {
1155    int token, i;
1156    RES *res;
1157    int options = lc->options;
1158
1159    lc->options |= LOPT_NO_IDENT;      /* make spaces significant */
1160
1161    Dmsg0(190, "Enter store_restore()\n");
1162    
1163    ((JOB *)(item->value))->JobType = item->code;
1164    while ((token = lex_get_token(lc, T_ALL)) != T_EOL) {
1165       int found; 
1166
1167       if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
1168          scan_err1(lc, "expected a name, got: %s", lc->str);
1169       }
1170       found = FALSE;
1171       for (i=0; RestoreFields[i].name; i++) {
1172          Dmsg1(190, "Restore kw=%s\n", lc->str);
1173          if (strcasecmp(lc->str, RestoreFields[i].name) == 0) {
1174             found = TRUE;
1175             if (lex_get_token(lc, T_ALL) != T_EQUALS) {
1176                scan_err1(lc, "Expected an equals, got: %s", lc->str);
1177             }
1178             token = lex_get_token(lc, T_ALL);
1179             Dmsg1(190, "Restore value=%s\n", lc->str);
1180             switch (RestoreFields[i].token) {
1181                case 'B':
1182                   /* Bootstrap */
1183                   if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
1184                      scan_err1(lc, "Expected a Restore bootstrap file, got: %s", lc->str);
1185                   }
1186                   if (pass == 1) {
1187                      res_all.res_job.RestoreBootstrap = bstrdup(lc->str);
1188                   }
1189                   break;
1190                case 'C':
1191                   /* Find Client Resource */
1192                   if (pass == 2) {
1193                      res = GetResWithName(R_CLIENT, lc->str);
1194                      if (res == NULL) {
1195                         scan_err1(lc, "Could not find specified Client Resource: %s",
1196                                    lc->str);
1197                      }
1198                      res_all.res_job.client = (CLIENT *)res;
1199                   }
1200                   break;
1201                case 'F':
1202                   /* Find FileSet Resource */
1203                   if (pass == 2) {
1204                      res = GetResWithName(R_FILESET, lc->str);
1205                      if (res == NULL) {
1206                         scan_err1(lc, "Could not find specified FileSet Resource: %s\n",
1207                                     lc->str);
1208                      }
1209                      res_all.res_job.fileset = (FILESET *)res;
1210                   }
1211                   break;
1212                case 'J':
1213                   /* JobId */
1214                   if (token != T_NUMBER) {
1215                      scan_err1(lc, "expected an integer number, got: %s", lc->str);
1216                   }
1217                   errno = 0;
1218                   res_all.res_job.RestoreJobId = strtol(lc->str, NULL, 0);
1219                   Dmsg1(190, "RestorJobId=%d\n", res_all.res_job.RestoreJobId);
1220                   if (errno != 0) {
1221                      scan_err1(lc, "expected an integer number, got: %s", lc->str);
1222                   }
1223                   break;
1224                case 'W':
1225                   /* Where */
1226                   if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
1227                      scan_err1(lc, "Expected a Restore root directory, got: %s", lc->str);
1228                   }
1229                   if (pass == 1) {
1230                      res_all.res_job.RestoreWhere = bstrdup(lc->str);
1231                   }
1232                   break;
1233                case 'R':
1234                   /* Replacement options */
1235                   if (token != T_IDENTIFIER && token != T_UNQUOTED_STRING && token != T_QUOTED_STRING) {
1236                      scan_err1(lc, "Expected a keyword name, got: %s", lc->str);
1237                   }
1238                   /* Fix to scan Replacement options */
1239                   for (i=0; ReplaceOptions[i].name; i++) {
1240                      if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1241                          ((JOB *)(item->value))->replace = ReplaceOptions[i].token;
1242                         i = 0;
1243                         break;
1244                      }
1245                   }
1246                   if (i != 0) {
1247                      scan_err1(lc, "Expected a Restore replacement option, got: %s", lc->str);
1248                   }
1249                   break;
1250             } /* end switch */
1251             break;
1252          } /* end if strcmp() */
1253       } /* end for */
1254       if (!found) {
1255          scan_err1(lc, "%s not a valid Restore keyword", lc->str);
1256       }
1257    } /* end while */
1258    lc->options = options;             /* reset original options */
1259    set_bit(index, res_all.hdr.item_present);
1260 }
1261
1262
1263
1264 /* 
1265  * Scan for Include options (keyword=option) is converted into one or
1266  *  two characters. Verifyopts=xxxx is Vxxxx:
1267  */
1268 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
1269 {
1270    int token, i;
1271    char option[3];
1272
1273    option[0] = 0;                     /* default option = none */
1274    option[2] = 0;                     /* terminate options */
1275    token = lex_get_token(lc, T_NAME);             /* expect at least one option */       
1276    if (keyword == INC_KW_VERIFY) { /* special case */
1277       /* ***FIXME**** ensure these are in permitted set */
1278       bstrncat(opts, "V", optlen);         /* indicate Verify */
1279       bstrncat(opts, lc->str, optlen);
1280       bstrncat(opts, ":", optlen);         /* terminate it */
1281    } else {
1282       for (i=0; FS_options[i].name; i++) {
1283          if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
1284             /* NOTE! maximum 2 letters here or increase option[3] */
1285             option[0] = FS_options[i].option[0];
1286             option[1] = FS_options[i].option[1];
1287             i = 0;
1288             break;
1289          }
1290       }
1291       if (i != 0) {
1292          scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str);
1293       } else { /* add option */
1294          bstrncat(opts, option, optlen);
1295          Dmsg3(200, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
1296       }
1297    }
1298
1299    /* If option terminated by comma, eat it */
1300    if (lc->ch == ',') {
1301       token = lex_get_token(lc, T_ALL);      /* yes, eat comma */
1302    }
1303 }
1304
1305
1306 /* Store FileSet Include/Exclude info */
1307 static void store_inc(LEX *lc, struct res_items *item, int index, int pass)
1308 {
1309    int token, i;
1310    int options = lc->options;
1311    int keyword;
1312    char inc_opts[100];
1313    int inc_opts_len;
1314
1315    lc->options |= LOPT_NO_IDENT;      /* make spaces significant */
1316
1317    /* Get include options */
1318    inc_opts[0] = 0;
1319    while ((token=lex_get_token(lc, T_ALL)) != T_BOB) {
1320       keyword = INC_KW_NONE;
1321       for (i=0; FS_option_kw[i].name; i++) {
1322          if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
1323             keyword = FS_option_kw[i].token;
1324             break;
1325          }
1326       }
1327       if (keyword == INC_KW_NONE) {
1328          scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
1329       }
1330       /* Option keyword should be following by = <option> */
1331       if ((token=lex_get_token(lc, T_ALL)) != T_EQUALS) {
1332          scan_err1(lc, "expected an = following keyword, got: %s", lc->str);
1333       }
1334       scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
1335       if (token == T_BOB) {
1336          break;
1337       }
1338    }
1339    if (!inc_opts[0]) {
1340       strcat(inc_opts, "0 ");         /* set no options */
1341    } else {
1342       strcat(inc_opts, " ");          /* add field separator */
1343    }
1344    inc_opts_len = strlen(inc_opts);
1345
1346    if (pass == 1) {
1347       if (!res_all.res_fs.have_MD5) {
1348          MD5Init(&res_all.res_fs.md5c);
1349          res_all.res_fs.have_MD5 = TRUE;
1350       }
1351       /* Pickup include/exclude names. Note, they are stored as
1352        * XYZ fname
1353        * where XYZ are the include/exclude options for the FileSet
1354        *     a "0 " (zero) indicates no options,
1355        * and fname is the file/directory name given
1356        */
1357       while ((token = lex_get_token(lc, T_ALL)) != T_EOB) {
1358          switch (token) {
1359             case T_COMMA:
1360             case T_EOL:
1361                continue;
1362
1363             case T_IDENTIFIER:
1364             case T_UNQUOTED_STRING:
1365             case T_QUOTED_STRING:
1366                INCEXE *incexe;
1367                incexe = (INCEXE *)malloc(lc->str_len + sizeof(INCEXE));
1368                memset(incexe, 0, sizeof(INCEXE));
1369                bstrncpy(incexe->opts, inc_opts, sizeof(incexe->opts));
1370                strcpy(incexe->name, lc->str);
1371                if (res_all.res_fs.have_MD5) {
1372                   MD5Update(&res_all.res_fs.md5c, (unsigned char *)incexe, 
1373                             sizeof(INCEXE) + lc->str_len);
1374                }
1375                if (item->code == 0) { /* include */
1376                   if (res_all.res_fs.num_includes == res_all.res_fs.include_size) {
1377                      res_all.res_fs.include_size += 10;
1378                      if (res_all.res_fs.include_array == NULL) {
1379                         res_all.res_fs.include_array = (INCEXE **)malloc(sizeof(INCEXE *) * res_all.res_fs.include_size);
1380                      } else {
1381                         res_all.res_fs.include_array = (INCEXE **)realloc(res_all.res_fs.include_array,
1382                            sizeof(INCEXE *) * res_all.res_fs.include_size);
1383                      }
1384                   }
1385                   res_all.res_fs.include_array[res_all.res_fs.num_includes++] = incexe;
1386                } else {                /* exclude */
1387                   if (res_all.res_fs.num_excludes == res_all.res_fs.exclude_size) {
1388                      res_all.res_fs.exclude_size += 10;
1389                      if (res_all.res_fs.exclude_array == NULL) {
1390                         res_all.res_fs.exclude_array = (INCEXE **)malloc(sizeof(INCEXE *) * res_all.res_fs.exclude_size);
1391                      } else {
1392                         res_all.res_fs.exclude_array = (INCEXE **)realloc(res_all.res_fs.exclude_array,
1393                            sizeof(INCEXE *) * res_all.res_fs.exclude_size);
1394                      }
1395                   }
1396                   res_all.res_fs.exclude_array[res_all.res_fs.num_excludes++] = incexe;
1397                }
1398                break;
1399             default:
1400                scan_err1(lc, "Expected a filename, got: %s", lc->str);
1401          }                                 
1402       }
1403    } else { /* pass 2 */
1404       while (lex_get_token(lc, T_ALL) != T_EOB) 
1405          {}
1406    }
1407    scan_to_eol(lc);
1408    lc->options = options;
1409    set_bit(index, res_all.hdr.item_present);
1410 }
1411
1412 /* Store FileOptions ApplyTo info */
1413 static void store_applyto(LEX *lc, struct res_items *item, int index, int pass)
1414 {
1415    int token;
1416    char *applyto;
1417
1418    if (pass == 1) {
1419       /* Pickup ApplyTo string
1420        */
1421       while ((token = lex_get_token(lc, T_ALL)) != T_EOB) {
1422          switch (token) {
1423             case T_COMMA:
1424             case T_EOL:
1425                continue;
1426
1427             case T_IDENTIFIER:
1428             case T_UNQUOTED_STRING:
1429             case T_QUOTED_STRING:
1430                applyto = (char *)malloc(lc->str_len + 1);
1431                strcpy(applyto, lc->str);
1432                res_all.res_fo.num_applyto++;
1433                if (res_all.res_fo.applyto == NULL) {
1434                   res_all.res_fo.applyto = (char **)malloc(sizeof(char *) * res_all.res_fo.num_applyto);
1435                } else {
1436                   res_all.res_fo.applyto = (char **)realloc(res_all.res_fo.applyto,
1437                      sizeof(char *) * res_all.res_fo.num_applyto);
1438                }
1439                res_all.res_fo.applyto[res_all.res_fo.num_applyto-1] = applyto;
1440                break;
1441             default:
1442                scan_err1(lc, "Expected a filename, got: %s", lc->str);
1443          }                                 
1444       }
1445    } else { /* pass 2 */
1446       while (lex_get_token(lc, T_ALL) != T_EOB) 
1447          {}
1448    }
1449    scan_to_eol(lc);
1450    set_bit(index, res_all.hdr.item_present);
1451 }