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