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