]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/stored_conf.c
ebl update faketape driver to work with regress tests
[bacula/bacula] / bacula / src / stored / stored_conf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Configuration file parser for Bacula Storage daemon
30  *
31  *     Kern Sibbald, March MM
32  *
33  *   Version $Id$
34  */
35
36 #include "bacula.h"
37 #include "stored.h"
38
39 /* First and last resource ids */
40 int r_first = R_FIRST;
41 int r_last  = R_LAST;
42 static RES *sres_head[R_LAST - R_FIRST + 1];
43 RES **res_head = sres_head;
44
45
46 /* Forward referenced subroutines */
47 static void store_devtype(LEX *lc, RES_ITEM *item, int index, int pass);
48
49
50 /* We build the current resource here statically,
51  * then move it to dynamic memory */
52 #if defined(_MSC_VER)
53 extern "C" { // work around visual compiler mangling variables
54     URES res_all;
55 }
56 #else
57 URES res_all;
58 #endif
59 int res_all_size = sizeof(res_all);
60
61 /* Definition of records permitted within each
62  * resource with the routine to process the record
63  * information.
64  */
65
66 /* Globals for the Storage daemon. */
67 static RES_ITEM store_items[] = {
68    {"name",                  store_name, ITEM(res_store.hdr.name),   0, ITEM_REQUIRED, 0},
69    {"description",           store_str,  ITEM(res_dir.hdr.desc),     0, 0, 0},
70    {"sdaddress",             store_addresses_address,  ITEM(res_store.sdaddrs),     0, ITEM_DEFAULT, 9103},
71    {"sdaddresses",           store_addresses,  ITEM(res_store.sdaddrs), 0, ITEM_DEFAULT, 9103},
72    {"messages",              store_res,  ITEM(res_store.messages),   R_MSGS, 0, 0},
73    {"sdport",                store_addresses_port,  ITEM(res_store.sdaddrs),     0, ITEM_DEFAULT, 9103},
74    {"workingdirectory",      store_dir,  ITEM(res_store.working_directory), 0, ITEM_REQUIRED, 0},
75    {"piddirectory",          store_dir,  ITEM(res_store.pid_directory), 0, ITEM_REQUIRED, 0},
76    {"subsysdirectory",       store_dir,  ITEM(res_store.subsys_directory), 0, 0, 0},
77    {"plugindirectory",       store_dir,  ITEM(res_store.plugin_directory), 0, 0, 0},
78    {"scriptsdirectory",      store_dir,  ITEM(res_store.scripts_directory), 0, 0, 0},
79    {"maximumconcurrentjobs", store_pint, ITEM(res_store.max_concurrent_jobs), 0, ITEM_DEFAULT, 20},
80    {"heartbeatinterval",     store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 0},
81    {"tlsauthenticate",       store_bool,    ITEM(res_store.tls_authenticate), 0, 0, 0},
82    {"tlsenable",             store_bool,    ITEM(res_store.tls_enable), 0, 0, 0},
83    {"tlsrequire",            store_bool,    ITEM(res_store.tls_require), 0, 0, 0},
84    {"tlsverifypeer",         store_bool,    ITEM(res_store.tls_verify_peer), 1, ITEM_DEFAULT, 1},
85    {"tlscacertificatefile",  store_dir,       ITEM(res_store.tls_ca_certfile), 0, 0, 0},
86    {"tlscacertificatedir",   store_dir,       ITEM(res_store.tls_ca_certdir), 0, 0, 0},
87    {"tlscertificate",        store_dir,       ITEM(res_store.tls_certfile), 0, 0, 0},
88    {"tlskey",                store_dir,       ITEM(res_store.tls_keyfile), 0, 0, 0},
89    {"tlsdhfile",             store_dir,       ITEM(res_store.tls_dhfile), 0, 0, 0},
90    {"tlsallowedcn",          store_alist_str, ITEM(res_store.tls_allowed_cns), 0, 0, 0},
91    {"clientconnectwait",     store_time,  ITEM(res_store.client_wait), 0, ITEM_DEFAULT, 30 * 60},
92    {NULL, NULL, {0}, 0, 0, 0}
93 };
94
95
96 /* Directors that can speak to the Storage daemon */
97 static RES_ITEM dir_items[] = {
98    {"name",        store_name,     ITEM(res_dir.hdr.name),   0, ITEM_REQUIRED, 0},
99    {"description", store_str,      ITEM(res_dir.hdr.desc),   0, 0, 0},
100    {"password",    store_password, ITEM(res_dir.password),   0, ITEM_REQUIRED, 0},
101    {"monitor",     store_bool,     ITEM(res_dir.monitor),    0, 0, 0},
102    {"tlsauthenticate",      store_bool,    ITEM(res_dir.tls_authenticate), 0, 0, 0},
103    {"tlsenable",            store_bool,    ITEM(res_dir.tls_enable), 0, 0, 0},
104    {"tlsrequire",           store_bool,    ITEM(res_dir.tls_require), 0, 0, 0},
105    {"tlsverifypeer",        store_bool,    ITEM(res_dir.tls_verify_peer), 1, ITEM_DEFAULT, 1},
106    {"tlscacertificatefile", store_dir,       ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
107    {"tlscacertificatedir",  store_dir,       ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
108    {"tlscertificate",       store_dir,       ITEM(res_dir.tls_certfile), 0, 0, 0},
109    {"tlskey",               store_dir,       ITEM(res_dir.tls_keyfile), 0, 0, 0},
110    {"tlsdhfile",            store_dir,       ITEM(res_dir.tls_dhfile), 0, 0, 0},
111    {"tlsallowedcn",         store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
112    {NULL, NULL, {0}, 0, 0, 0}
113 };
114
115 /* Device definition */
116 static RES_ITEM dev_items[] = {
117    {"name",                  store_name,   ITEM(res_dev.hdr.name),    0, ITEM_REQUIRED, 0},
118    {"description",           store_str,    ITEM(res_dir.hdr.desc),    0, 0, 0},
119    {"mediatype",             store_strname,ITEM(res_dev.media_type),  0, ITEM_REQUIRED, 0},
120    {"devicetype",            store_devtype,ITEM(res_dev.dev_type),    0, 0, 0},
121    {"archivedevice",         store_strname,ITEM(res_dev.device_name), 0, ITEM_REQUIRED, 0},
122    {"hardwareendoffile",     store_bit,  ITEM(res_dev.cap_bits), CAP_EOF,  ITEM_DEFAULT, 1},
123    {"hardwareendofmedium",   store_bit,  ITEM(res_dev.cap_bits), CAP_EOM,  ITEM_DEFAULT, 1},
124    {"backwardspacerecord",   store_bit,  ITEM(res_dev.cap_bits), CAP_BSR,  ITEM_DEFAULT, 1},
125    {"backwardspacefile",     store_bit,  ITEM(res_dev.cap_bits), CAP_BSF,  ITEM_DEFAULT, 1},
126    {"bsfateom",              store_bit,  ITEM(res_dev.cap_bits), CAP_BSFATEOM, ITEM_DEFAULT, 0},
127    {"twoeof",                store_bit,  ITEM(res_dev.cap_bits), CAP_TWOEOF, ITEM_DEFAULT, 0},
128    {"forwardspacerecord",    store_bit,  ITEM(res_dev.cap_bits), CAP_FSR,  ITEM_DEFAULT, 1},
129    {"forwardspacefile",      store_bit,  ITEM(res_dev.cap_bits), CAP_FSF,  ITEM_DEFAULT, 1},
130    {"fastforwardspacefile",  store_bit,  ITEM(res_dev.cap_bits), CAP_FASTFSF, ITEM_DEFAULT, 1},
131    {"removablemedia",        store_bit,  ITEM(res_dev.cap_bits), CAP_REM,  ITEM_DEFAULT, 1},
132    {"randomaccess",          store_bit,  ITEM(res_dev.cap_bits), CAP_RACCESS, 0, 0},
133    {"automaticmount",        store_bit,  ITEM(res_dev.cap_bits), CAP_AUTOMOUNT,  ITEM_DEFAULT, 0},
134    {"labelmedia",            store_bit,  ITEM(res_dev.cap_bits), CAP_LABEL,      ITEM_DEFAULT, 0},
135    {"alwaysopen",            store_bit,  ITEM(res_dev.cap_bits), CAP_ALWAYSOPEN, ITEM_DEFAULT, 1},
136    {"autochanger",           store_bit,  ITEM(res_dev.cap_bits), CAP_AUTOCHANGER, ITEM_DEFAULT, 0},
137    {"closeonpoll",           store_bit,  ITEM(res_dev.cap_bits), CAP_CLOSEONPOLL, ITEM_DEFAULT, 0},
138    {"blockpositioning",      store_bit,  ITEM(res_dev.cap_bits), CAP_POSITIONBLOCKS, ITEM_DEFAULT, 1},
139    {"usemtiocget",           store_bit,  ITEM(res_dev.cap_bits), CAP_MTIOCGET, ITEM_DEFAULT, 1},
140    {"checklabels",           store_bit,  ITEM(res_dev.cap_bits), CAP_CHECKLABELS, ITEM_DEFAULT, 0},
141    {"requiresmount",         store_bit,  ITEM(res_dev.cap_bits), CAP_REQMOUNT, ITEM_DEFAULT, 0},
142    {"offlineonunmount",      store_bit,  ITEM(res_dev.cap_bits), CAP_OFFLINEUNMOUNT, ITEM_DEFAULT, 0},
143    {"autoselect",            store_bit,  ITEM(res_dev.autoselect), 1, ITEM_DEFAULT, 1},
144    {"changerdevice",         store_strname,ITEM(res_dev.changer_name), 0, 0, 0},
145    {"changercommand",        store_strname,ITEM(res_dev.changer_command), 0, 0, 0},
146    {"alertcommand",          store_strname,ITEM(res_dev.alert_command), 0, 0, 0},
147    {"maximumchangerwait",    store_time,   ITEM(res_dev.max_changer_wait), 0, ITEM_DEFAULT, 5 * 60},
148    {"maximumopenwait",       store_time,   ITEM(res_dev.max_open_wait), 0, ITEM_DEFAULT, 5 * 60},
149    {"maximumopenvolumes",    store_pint,   ITEM(res_dev.max_open_vols), 0, ITEM_DEFAULT, 1},
150    {"maximumnetworkbuffersize", store_pint, ITEM(res_dev.max_network_buffer_size), 0, 0, 0},
151    {"volumepollinterval",    store_time,   ITEM(res_dev.vol_poll_interval), 0, 0, 0},
152    {"maximumrewindwait",     store_time,   ITEM(res_dev.max_rewind_wait), 0, ITEM_DEFAULT, 5 * 60},
153    {"minimumblocksize",      store_pint,   ITEM(res_dev.min_block_size), 0, 0, 0},
154    {"maximumblocksize",      store_pint,   ITEM(res_dev.max_block_size), 0, 0, 0},
155    {"maximumvolumesize",     store_size,   ITEM(res_dev.max_volume_size), 0, 0, 0},
156    {"maximumfilesize",       store_size,   ITEM(res_dev.max_file_size), 0, ITEM_DEFAULT, 1000000000},
157    {"volumecapacity",        store_size,   ITEM(res_dev.volume_capacity), 0, 0, 0},
158    {"spooldirectory",        store_dir,    ITEM(res_dev.spool_directory), 0, 0, 0},
159    {"maximumspoolsize",      store_size,   ITEM(res_dev.max_spool_size), 0, 0, 0},
160    {"maximumjobspoolsize",   store_size,   ITEM(res_dev.max_job_spool_size), 0, 0, 0},
161    {"driveindex",            store_pint,   ITEM(res_dev.drive_index), 0, 0, 0},
162    {"maximumpartsize",       store_size,   ITEM(res_dev.max_part_size), 0, ITEM_DEFAULT, 0},
163    {"mountpoint",            store_strname,ITEM(res_dev.mount_point), 0, 0, 0},
164    {"mountcommand",          store_strname,ITEM(res_dev.mount_command), 0, 0, 0},
165    {"unmountcommand",        store_strname,ITEM(res_dev.unmount_command), 0, 0, 0},
166    {"writepartcommand",      store_strname,ITEM(res_dev.write_part_command), 0, 0, 0},
167    {"freespacecommand",      store_strname,ITEM(res_dev.free_space_command), 0, 0, 0},
168    {"labeltype",             store_label,  ITEM(res_dev.label_type), 0, 0, 0},
169    {NULL, NULL, {0}, 0, 0, 0}
170 };
171
172 /* Autochanger definition */
173 static RES_ITEM changer_items[] = {
174    {"name",              store_name,      ITEM(res_changer.hdr.name),        0, ITEM_REQUIRED, 0},
175    {"description",       store_str,       ITEM(res_changer.hdr.desc),        0, 0, 0},
176    {"device",            store_alist_res, ITEM(res_changer.device),   R_DEVICE, ITEM_REQUIRED, 0},
177    {"changerdevice",     store_strname,   ITEM(res_changer.changer_name),    0, ITEM_REQUIRED, 0},
178    {"changercommand",    store_strname,   ITEM(res_changer.changer_command), 0, ITEM_REQUIRED, 0},
179    {NULL, NULL, {0}, 0, 0, 0}
180 };
181
182
183 // {"mountanonymousvolumes", store_bit,  ITEM(res_dev.cap_bits), CAP_ANONVOLS,   ITEM_DEFAULT, 0},
184
185
186 /* Message resource */
187 extern RES_ITEM msgs_items[];
188
189
190 /* This is the master resource definition */
191 RES_TABLE resources[] = {
192    {"director",      dir_items,     R_DIRECTOR},
193    {"storage",       store_items,   R_STORAGE},
194    {"device",        dev_items,     R_DEVICE},
195    {"messages",      msgs_items,    R_MSGS},
196    {"autochanger",   changer_items, R_AUTOCHANGER},
197    {NULL,            NULL,          0}
198 };
199
200 /*
201  * Device types
202  *
203  *   device type     device code = token
204  */
205 struct s_kw {
206    const char *name;
207    int token;
208 };
209
210 static s_kw dev_types[] = {
211    {"file",          B_FILE_DEV},
212    {"tape",          B_TAPE_DEV},
213    {"dvd",           B_DVD_DEV},
214    {"fifo",          B_FIFO_DEV},
215    {"vtl",           B_VTL_DEV},
216    {"faketape",      B_FAKETAPE_DEV},
217    {NULL,            0}
218 };
219
220
221 /*
222  * Store Device Type (File, FIFO, Tape, DVD)
223  *
224  */
225 static void store_devtype(LEX *lc, RES_ITEM *item, int index, int pass)
226 {
227    int token, i;
228
229    token = lex_get_token(lc, T_NAME);
230    /* Store the label pass 2 so that type is defined */
231    for (i=0; dev_types[i].name; i++) {
232       if (strcasecmp(lc->str, dev_types[i].name) == 0) {
233          *(int *)(item->value) = dev_types[i].token;
234          i = 0;
235          break;
236       }
237    }
238    if (i != 0) {
239       scan_err1(lc, _("Expected a Device Type keyword, got: %s"), lc->str);
240    }
241    scan_to_eol(lc);
242    set_bit(index, res_all.hdr.item_present);
243 }
244
245
246 /* Dump contents of resource */
247 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
248 {
249    URES *res = (URES *)reshdr;
250    char buf[1000];
251    int recurse = 1;
252    IPADDR *p;
253    if (res == NULL) {
254       sendit(sock, _("Warning: no \"%s\" resource (%d) defined.\n"), res_to_str(type), type);
255       return;
256    }
257    sendit(sock, _("dump_resource type=%d\n"), type);
258    if (type < 0) {                    /* no recursion */
259       type = - type;
260       recurse = 0;
261    }
262    switch (type) {
263    case R_DIRECTOR:
264       sendit(sock, "Director: name=%s\n", res->res_dir.hdr.name);
265       break;
266    case R_STORAGE:
267       sendit(sock, "Storage: name=%s SDaddr=%s SDport=%d SDDport=%d HB=%s\n",
268              res->res_store.hdr.name,
269              NPRT(get_first_address(res->res_store.sdaddrs, buf, sizeof(buf))),
270              get_first_port_host_order(res->res_store.sdaddrs),
271              get_first_port_host_order(res->res_store.sddaddrs),
272              edit_utime(res->res_store.heartbeat_interval, buf, sizeof(buf)));
273       if (res->res_store.sdaddrs) {
274          foreach_dlist(p, res->res_store.sdaddrs) {
275             sendit(sock, "        SDaddr=%s SDport=%d\n",
276                    p->get_address(buf, sizeof(buf)), p->get_port_host_order());
277          }
278       }
279       if (res->res_store.sddaddrs) {
280          foreach_dlist(p, res->res_store.sddaddrs) {
281             sendit(sock, "        SDDaddr=%s SDDport=%d\n",
282                    p->get_address(buf, sizeof(buf)), p->get_port_host_order());
283          }
284       }
285       break;
286    case R_DEVICE:
287       sendit(sock, "Device: name=%s MediaType=%s Device=%s LabelType=%d\n",
288          res->res_dev.hdr.name,
289          res->res_dev.media_type, res->res_dev.device_name,
290          res->res_dev.label_type);
291       sendit(sock, "        rew_wait=%" lld " min_bs=%d max_bs=%d chgr_wait=%" lld "\n",
292          res->res_dev.max_rewind_wait, res->res_dev.min_block_size,
293          res->res_dev.max_block_size, res->res_dev.max_changer_wait);
294       sendit(sock, "        max_jobs=%d max_files=%" lld " max_size=%" lld "\n",
295          res->res_dev.max_volume_jobs, res->res_dev.max_volume_files,
296          res->res_dev.max_volume_size);
297       sendit(sock, "        max_file_size=%" lld " capacity=%" lld "\n",
298          res->res_dev.max_file_size, res->res_dev.volume_capacity);
299       sendit(sock, "        spool_directory=%s\n", NPRT(res->res_dev.spool_directory));
300       sendit(sock, "        max_spool_size=%" lld " max_job_spool_size=%" lld "\n",
301          res->res_dev.max_spool_size, res->res_dev.max_job_spool_size);
302       if (res->res_dev.changer_res) {
303          sendit(sock, "         changer=%p\n", res->res_dev.changer_res);
304       }
305       bstrncpy(buf, "        ", sizeof(buf));
306       if (res->res_dev.cap_bits & CAP_EOF) {
307          bstrncat(buf, "CAP_EOF ", sizeof(buf));
308       }
309       if (res->res_dev.cap_bits & CAP_BSR) {
310          bstrncat(buf, "CAP_BSR ", sizeof(buf));
311       }
312       if (res->res_dev.cap_bits & CAP_BSF) {
313          bstrncat(buf, "CAP_BSF ", sizeof(buf));
314       }
315       if (res->res_dev.cap_bits & CAP_FSR) {
316          bstrncat(buf, "CAP_FSR ", sizeof(buf));
317       }
318       if (res->res_dev.cap_bits & CAP_FSF) {
319          bstrncat(buf, "CAP_FSF ", sizeof(buf));
320       }
321       if (res->res_dev.cap_bits & CAP_EOM) {
322          bstrncat(buf, "CAP_EOM ", sizeof(buf));
323       }
324       if (res->res_dev.cap_bits & CAP_REM) {
325          bstrncat(buf, "CAP_REM ", sizeof(buf));
326       }
327       if (res->res_dev.cap_bits & CAP_RACCESS) {
328          bstrncat(buf, "CAP_RACCESS ", sizeof(buf));
329       }
330       if (res->res_dev.cap_bits & CAP_AUTOMOUNT) {
331          bstrncat(buf, "CAP_AUTOMOUNT ", sizeof(buf));
332       }
333       if (res->res_dev.cap_bits & CAP_LABEL) {
334          bstrncat(buf, "CAP_LABEL ", sizeof(buf));
335       }
336       if (res->res_dev.cap_bits & CAP_ANONVOLS) {
337          bstrncat(buf, "CAP_ANONVOLS ", sizeof(buf));
338       }
339       if (res->res_dev.cap_bits & CAP_ALWAYSOPEN) {
340          bstrncat(buf, "CAP_ALWAYSOPEN ", sizeof(buf));
341       }
342       if (res->res_dev.cap_bits & CAP_CHECKLABELS) {
343          bstrncat(buf, "CAP_CHECKLABELS ", sizeof(buf));
344       }
345       if (res->res_dev.cap_bits & CAP_REQMOUNT) {
346          bstrncat(buf, "CAP_REQMOUNT ", sizeof(buf));
347       }
348       if (res->res_dev.cap_bits & CAP_OFFLINEUNMOUNT) {
349          bstrncat(buf, "CAP_OFFLINEUNMOUNT ", sizeof(buf));
350       }
351       bstrncat(buf, "\n", sizeof(buf));
352       sendit(sock, buf);
353       break;
354    case R_AUTOCHANGER:
355       DEVRES *dev;
356       sendit(sock, "Changer: name=%s Changer_devname=%s\n      Changer_cmd=%s\n",
357          res->res_changer.hdr.name,
358          res->res_changer.changer_name, res->res_changer.changer_command);
359       foreach_alist(dev, res->res_changer.device) {
360          sendit(sock, "   --->Device: name=%s\n", dev->hdr.name);
361       }
362       bstrncat(buf, "\n", sizeof(buf));
363       sendit(sock, buf);
364       break;
365    case R_MSGS:
366       sendit(sock, "Messages: name=%s\n", res->res_msgs.hdr.name);
367       if (res->res_msgs.mail_cmd)
368          sendit(sock, "      mailcmd=%s\n", res->res_msgs.mail_cmd);
369       if (res->res_msgs.operator_cmd)
370          sendit(sock, "      opcmd=%s\n", res->res_msgs.operator_cmd);
371       break;
372    default:
373       sendit(sock, _("Warning: unknown resource type %d\n"), type);
374       break;
375    }
376    if (recurse && res->res_dir.hdr.next)
377       dump_resource(type, (RES *)res->res_dir.hdr.next, sendit, sock);
378 }
379
380 /*
381  * Free memory of resource.
382  * NB, we don't need to worry about freeing any references
383  * to other resources as they will be freed when that
384  * resource chain is traversed.  Mainly we worry about freeing
385  * allocated strings (names).
386  */
387 void free_resource(RES *sres, int type)
388 {
389    RES *nres;
390    URES *res = (URES *)sres;
391
392    if (res == NULL)
393       return;
394
395    /* common stuff -- free the resource name */
396    nres = (RES *)res->res_dir.hdr.next;
397    if (res->res_dir.hdr.name) {
398       free(res->res_dir.hdr.name);
399    }
400    if (res->res_dir.hdr.desc) {
401       free(res->res_dir.hdr.desc);
402    }
403
404
405    switch (type) {
406    case R_DIRECTOR:
407       if (res->res_dir.password) {
408          free(res->res_dir.password);
409       }
410       if (res->res_dir.address) {
411          free(res->res_dir.address);
412       }
413       if (res->res_dir.tls_ctx) { 
414          free_tls_context(res->res_dir.tls_ctx);
415       }
416       if (res->res_dir.tls_ca_certfile) {
417          free(res->res_dir.tls_ca_certfile);
418       }
419       if (res->res_dir.tls_ca_certdir) {
420          free(res->res_dir.tls_ca_certdir);
421       }
422       if (res->res_dir.tls_certfile) {
423          free(res->res_dir.tls_certfile);
424       }
425       if (res->res_dir.tls_keyfile) {
426          free(res->res_dir.tls_keyfile);
427       }
428       if (res->res_dir.tls_dhfile) {
429          free(res->res_dir.tls_dhfile);
430       }
431       if (res->res_dir.tls_allowed_cns) {
432          delete res->res_dir.tls_allowed_cns;
433       }
434       break;
435    case R_AUTOCHANGER:
436       if (res->res_changer.changer_name) {
437          free(res->res_changer.changer_name);
438       }
439       if (res->res_changer.changer_command) {
440          free(res->res_changer.changer_command);
441       }
442       if (res->res_changer.device) {
443          delete res->res_changer.device;
444       }
445       break; 
446    case R_STORAGE:
447       if (res->res_store.sdaddrs) {
448          free_addresses(res->res_store.sdaddrs);
449       }
450       if (res->res_store.sddaddrs) {
451          free_addresses(res->res_store.sddaddrs);
452       }
453       if (res->res_store.working_directory) {
454          free(res->res_store.working_directory);
455       }
456       if (res->res_store.pid_directory) {
457          free(res->res_store.pid_directory);
458       }
459       if (res->res_store.subsys_directory) {
460          free(res->res_store.subsys_directory);
461       }
462       if (res->res_store.scripts_directory) {
463          free(res->res_store.scripts_directory);
464       }
465       if (res->res_store.tls_ctx) { 
466          free_tls_context(res->res_store.tls_ctx);
467       }
468       if (res->res_store.tls_ca_certfile) {
469          free(res->res_store.tls_ca_certfile);
470       }
471       if (res->res_store.tls_ca_certdir) {
472          free(res->res_store.tls_ca_certdir);
473       }
474       if (res->res_store.tls_certfile) {
475          free(res->res_store.tls_certfile);
476       }
477       if (res->res_store.tls_keyfile) {
478          free(res->res_store.tls_keyfile);
479       }
480       if (res->res_store.tls_dhfile) {
481          free(res->res_store.tls_dhfile);
482       }
483       if (res->res_store.tls_allowed_cns) {
484          delete res->res_store.tls_allowed_cns;
485       }
486       break;
487    case R_DEVICE:
488       if (res->res_dev.media_type) {
489          free(res->res_dev.media_type);
490       }
491       if (res->res_dev.device_name) {
492          free(res->res_dev.device_name);
493       }
494       if (res->res_dev.changer_name) {
495          free(res->res_dev.changer_name);
496       }
497       if (res->res_dev.changer_command) {
498          free(res->res_dev.changer_command);
499       }
500       if (res->res_dev.alert_command) {
501          free(res->res_dev.alert_command);
502       }
503       if (res->res_dev.spool_directory) {
504          free(res->res_dev.spool_directory);
505       }
506       if (res->res_dev.mount_point) {
507          free(res->res_dev.mount_point);
508       }
509       if (res->res_dev.mount_command) {
510          free(res->res_dev.mount_command);
511       }
512       if (res->res_dev.unmount_command) {
513          free(res->res_dev.unmount_command);
514       }
515       if (res->res_dev.write_part_command) {
516          free(res->res_dev.write_part_command);
517       }
518       if (res->res_dev.free_space_command) {
519          free(res->res_dev.free_space_command);
520       }
521       break;
522    case R_MSGS:
523       if (res->res_msgs.mail_cmd) {
524          free(res->res_msgs.mail_cmd);
525       }
526       if (res->res_msgs.operator_cmd) {
527          free(res->res_msgs.operator_cmd);
528       }
529       free_msgs_res((MSGS *)res);  /* free message resource */
530       res = NULL;
531       break;
532    default:
533       Dmsg1(0, _("Unknown resource type %d\n"), type);
534       break;
535    }
536    /* Common stuff again -- free the resource, recurse to next one */
537    if (res) {
538       free(res);
539    }
540    if (nres) {
541       free_resource(nres, type);
542    }
543 }
544
545 /* Save the new resource by chaining it into the head list for
546  * the resource. If this is pass 2, we update any resource
547  * or alist pointers.  
548  */
549 void save_resource(int type, RES_ITEM *items, int pass)
550 {
551    URES *res;
552    int rindex = type - r_first;
553    int i, size;
554    int error = 0;
555
556    /*
557     * Ensure that all required items are present
558     */
559    for (i=0; items[i].name; i++) {
560       if (items[i].flags & ITEM_REQUIRED) {
561          if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
562             Emsg2(M_ERROR_TERM, 0, _("\"%s\" item is required in \"%s\" resource, but not found.\n"),
563               items[i].name, resources[rindex]);
564           }
565       }
566       /* If this triggers, take a look at lib/parse_conf.h */
567       if (i >= MAX_RES_ITEMS) {
568          Emsg1(M_ERROR_TERM, 0, _("Too many items in \"%s\" resource\n"), resources[rindex]);
569       }
570    }
571
572    /* During pass 2, we looked up pointers to all the resources
573     * referrenced in the current resource, , now we
574     * must copy their address from the static record to the allocated
575     * record.
576     */
577    if (pass == 2) {
578       DEVRES *dev;
579       int errstat;
580       switch (type) {
581       /* Resources not containing a resource */
582       case R_DEVICE:
583       case R_MSGS:
584          break;
585
586       /* Resources containing a resource or an alist */
587       case R_DIRECTOR:
588          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
589             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
590          }
591          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
592          break;
593       case R_STORAGE:
594          if ((res = (URES *)GetResWithName(R_STORAGE, res_all.res_dir.hdr.name)) == NULL) {
595             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"), res_all.res_dir.hdr.name);
596          }
597          res->res_store.messages = res_all.res_store.messages;
598          res->res_store.tls_allowed_cns = res_all.res_store.tls_allowed_cns;
599          break;
600       case R_AUTOCHANGER:
601          if ((res = (URES *)GetResWithName(type, res_all.res_changer.hdr.name)) == NULL) {
602             Emsg1(M_ERROR_TERM, 0, _("Cannot find AutoChanger resource %s\n"),
603                   res_all.res_changer.hdr.name);
604          }
605          /* we must explicitly copy the device alist pointer */
606          res->res_changer.device   = res_all.res_changer.device;
607          /*
608           * Now update each device in this resource to point back 
609           *  to the changer resource.
610           */
611          foreach_alist(dev, res->res_changer.device) {
612             dev->changer_res = (AUTOCHANGER *)&res->res_changer;
613          }
614          if ((errstat = pthread_mutex_init(&res->res_changer.changer_mutex, NULL)) != 0) {
615             berrno be;
616             Jmsg1(NULL, M_ERROR_TERM, 0, _("Unable to init mutex: ERR=%s\n"), 
617                   be.bstrerror(errstat));
618          }
619          break;
620       default:
621          printf(_("Unknown resource type %d\n"), type);
622          error = 1;
623          break;
624       }
625
626
627       if (res_all.res_dir.hdr.name) {
628          free(res_all.res_dir.hdr.name);
629          res_all.res_dir.hdr.name = NULL;
630       }
631       if (res_all.res_dir.hdr.desc) {
632          free(res_all.res_dir.hdr.desc);
633          res_all.res_dir.hdr.desc = NULL;
634       }
635       return;
636    }
637
638    /* The following code is only executed on pass 1 */
639    switch (type) {
640       case R_DIRECTOR:
641          size = sizeof(DIRRES);
642          break;
643       case R_STORAGE:
644          size = sizeof(STORES);
645          break;
646       case R_DEVICE:
647          size = sizeof(DEVRES);
648          break;
649       case R_MSGS:
650          size = sizeof(MSGS);
651          break;
652       case R_AUTOCHANGER:
653          size = sizeof(AUTOCHANGER);
654          break;
655       default:
656          printf(_("Unknown resource type %d\n"), type);
657          error = 1;
658          size = 1;
659          break;
660    }
661    /* Common */
662    if (!error) {
663       res = (URES *)malloc(size);
664       memcpy(res, &res_all, size);
665       if (!res_head[rindex]) {
666          res_head[rindex] = (RES *)res; /* store first entry */
667       } else {
668          RES *next, *last;
669          /* Add new res to end of chain */
670          for (last=next=res_head[rindex]; next; next=next->next) {
671             last = next;
672             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
673                Emsg2(M_ERROR_TERM, 0,
674                   _("Attempt to define second \"%s\" resource named \"%s\" is not permitted.\n"),
675                   resources[rindex].name, res->res_dir.hdr.name);
676             }
677          }
678          last->next = (RES *)res;
679          Dmsg2(90, "Inserting %s res: %s\n", res_to_str(type),
680                res->res_dir.hdr.name);
681       }
682    }
683 }