]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/stored_conf.c
Implement Plugin Directory and plugin events.
[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    {"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    {NULL,            0}
216 };
217
218
219 /*
220  * Store Device Type (File, FIFO, Tape, DVD)
221  *
222  */
223 static void store_devtype(LEX *lc, RES_ITEM *item, int index, int pass)
224 {
225    int token, i;
226
227    token = lex_get_token(lc, T_NAME);
228    /* Store the label pass 2 so that type is defined */
229    for (i=0; dev_types[i].name; i++) {
230       if (strcasecmp(lc->str, dev_types[i].name) == 0) {
231          *(int *)(item->value) = dev_types[i].token;
232          i = 0;
233          break;
234       }
235    }
236    if (i != 0) {
237       scan_err1(lc, _("Expected a Device Type keyword, got: %s"), lc->str);
238    }
239    scan_to_eol(lc);
240    set_bit(index, res_all.hdr.item_present);
241 }
242
243
244 /* Dump contents of resource */
245 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
246 {
247    URES *res = (URES *)reshdr;
248    char buf[1000];
249    int recurse = 1;
250    IPADDR *p;
251    if (res == NULL) {
252       sendit(sock, _("Warning: no \"%s\" resource (%d) defined.\n"), res_to_str(type), type);
253       return;
254    }
255    sendit(sock, _("dump_resource type=%d\n"), type);
256    if (type < 0) {                    /* no recursion */
257       type = - type;
258       recurse = 0;
259    }
260    switch (type) {
261    case R_DIRECTOR:
262       sendit(sock, "Director: name=%s\n", res->res_dir.hdr.name);
263       break;
264    case R_STORAGE:
265       sendit(sock, "Storage: name=%s SDaddr=%s SDport=%d SDDport=%d HB=%s\n",
266              res->res_store.hdr.name,
267              NPRT(get_first_address(res->res_store.sdaddrs, buf, sizeof(buf))),
268              get_first_port_host_order(res->res_store.sdaddrs),
269              get_first_port_host_order(res->res_store.sddaddrs),
270              edit_utime(res->res_store.heartbeat_interval, buf, sizeof(buf)));
271       if (res->res_store.sdaddrs) {
272          foreach_dlist(p, res->res_store.sdaddrs) {
273             sendit(sock, "        SDaddr=%s SDport=%d\n",
274                    p->get_address(buf, sizeof(buf)), p->get_port_host_order());
275          }
276       }
277       if (res->res_store.sddaddrs) {
278          foreach_dlist(p, res->res_store.sddaddrs) {
279             sendit(sock, "        SDDaddr=%s SDDport=%d\n",
280                    p->get_address(buf, sizeof(buf)), p->get_port_host_order());
281          }
282       }
283       break;
284    case R_DEVICE:
285       sendit(sock, "Device: name=%s MediaType=%s Device=%s LabelType=%d\n",
286          res->res_dev.hdr.name,
287          res->res_dev.media_type, res->res_dev.device_name,
288          res->res_dev.label_type);
289       sendit(sock, "        rew_wait=%" lld " min_bs=%d max_bs=%d chgr_wait=%" lld "\n",
290          res->res_dev.max_rewind_wait, res->res_dev.min_block_size,
291          res->res_dev.max_block_size, res->res_dev.max_changer_wait);
292       sendit(sock, "        max_jobs=%d max_files=%" lld " max_size=%" lld "\n",
293          res->res_dev.max_volume_jobs, res->res_dev.max_volume_files,
294          res->res_dev.max_volume_size);
295       sendit(sock, "        max_file_size=%" lld " capacity=%" lld "\n",
296          res->res_dev.max_file_size, res->res_dev.volume_capacity);
297       sendit(sock, "        spool_directory=%s\n", NPRT(res->res_dev.spool_directory));
298       sendit(sock, "        max_spool_size=%" lld " max_job_spool_size=%" lld "\n",
299          res->res_dev.max_spool_size, res->res_dev.max_job_spool_size);
300       if (res->res_dev.changer_res) {
301          sendit(sock, "         changer=%p\n", res->res_dev.changer_res);
302       }
303       bstrncpy(buf, "        ", sizeof(buf));
304       if (res->res_dev.cap_bits & CAP_EOF) {
305          bstrncat(buf, "CAP_EOF ", sizeof(buf));
306       }
307       if (res->res_dev.cap_bits & CAP_BSR) {
308          bstrncat(buf, "CAP_BSR ", sizeof(buf));
309       }
310       if (res->res_dev.cap_bits & CAP_BSF) {
311          bstrncat(buf, "CAP_BSF ", sizeof(buf));
312       }
313       if (res->res_dev.cap_bits & CAP_FSR) {
314          bstrncat(buf, "CAP_FSR ", sizeof(buf));
315       }
316       if (res->res_dev.cap_bits & CAP_FSF) {
317          bstrncat(buf, "CAP_FSF ", sizeof(buf));
318       }
319       if (res->res_dev.cap_bits & CAP_EOM) {
320          bstrncat(buf, "CAP_EOM ", sizeof(buf));
321       }
322       if (res->res_dev.cap_bits & CAP_REM) {
323          bstrncat(buf, "CAP_REM ", sizeof(buf));
324       }
325       if (res->res_dev.cap_bits & CAP_RACCESS) {
326          bstrncat(buf, "CAP_RACCESS ", sizeof(buf));
327       }
328       if (res->res_dev.cap_bits & CAP_AUTOMOUNT) {
329          bstrncat(buf, "CAP_AUTOMOUNT ", sizeof(buf));
330       }
331       if (res->res_dev.cap_bits & CAP_LABEL) {
332          bstrncat(buf, "CAP_LABEL ", sizeof(buf));
333       }
334       if (res->res_dev.cap_bits & CAP_ANONVOLS) {
335          bstrncat(buf, "CAP_ANONVOLS ", sizeof(buf));
336       }
337       if (res->res_dev.cap_bits & CAP_ALWAYSOPEN) {
338          bstrncat(buf, "CAP_ALWAYSOPEN ", sizeof(buf));
339       }
340       if (res->res_dev.cap_bits & CAP_CHECKLABELS) {
341          bstrncat(buf, "CAP_CHECKLABELS ", sizeof(buf));
342       }
343       if (res->res_dev.cap_bits & CAP_REQMOUNT) {
344          bstrncat(buf, "CAP_REQMOUNT ", sizeof(buf));
345       }
346       if (res->res_dev.cap_bits & CAP_OFFLINEUNMOUNT) {
347          bstrncat(buf, "CAP_OFFLINEUNMOUNT ", sizeof(buf));
348       }
349       bstrncat(buf, "\n", sizeof(buf));
350       sendit(sock, buf);
351       break;
352    case R_AUTOCHANGER:
353       DEVRES *dev;
354       sendit(sock, "Changer: name=%s Changer_devname=%s\n      Changer_cmd=%s\n",
355          res->res_changer.hdr.name,
356          res->res_changer.changer_name, res->res_changer.changer_command);
357       foreach_alist(dev, res->res_changer.device) {
358          sendit(sock, "   --->Device: name=%s\n", dev->hdr.name);
359       }
360       bstrncat(buf, "\n", sizeof(buf));
361       sendit(sock, buf);
362       break;
363    case R_MSGS:
364       sendit(sock, "Messages: name=%s\n", res->res_msgs.hdr.name);
365       if (res->res_msgs.mail_cmd)
366          sendit(sock, "      mailcmd=%s\n", res->res_msgs.mail_cmd);
367       if (res->res_msgs.operator_cmd)
368          sendit(sock, "      opcmd=%s\n", res->res_msgs.operator_cmd);
369       break;
370    default:
371       sendit(sock, _("Warning: unknown resource type %d\n"), type);
372       break;
373    }
374    if (recurse && res->res_dir.hdr.next)
375       dump_resource(type, (RES *)res->res_dir.hdr.next, sendit, sock);
376 }
377
378 /*
379  * Free memory of resource.
380  * NB, we don't need to worry about freeing any references
381  * to other resources as they will be freed when that
382  * resource chain is traversed.  Mainly we worry about freeing
383  * allocated strings (names).
384  */
385 void free_resource(RES *sres, int type)
386 {
387    RES *nres;
388    URES *res = (URES *)sres;
389
390    if (res == NULL)
391       return;
392
393    /* common stuff -- free the resource name */
394    nres = (RES *)res->res_dir.hdr.next;
395    if (res->res_dir.hdr.name) {
396       free(res->res_dir.hdr.name);
397    }
398    if (res->res_dir.hdr.desc) {
399       free(res->res_dir.hdr.desc);
400    }
401
402
403    switch (type) {
404    case R_DIRECTOR:
405       if (res->res_dir.password) {
406          free(res->res_dir.password);
407       }
408       if (res->res_dir.address) {
409          free(res->res_dir.address);
410       }
411       if (res->res_dir.tls_ctx) { 
412          free_tls_context(res->res_dir.tls_ctx);
413       }
414       if (res->res_dir.tls_ca_certfile) {
415          free(res->res_dir.tls_ca_certfile);
416       }
417       if (res->res_dir.tls_ca_certdir) {
418          free(res->res_dir.tls_ca_certdir);
419       }
420       if (res->res_dir.tls_certfile) {
421          free(res->res_dir.tls_certfile);
422       }
423       if (res->res_dir.tls_keyfile) {
424          free(res->res_dir.tls_keyfile);
425       }
426       if (res->res_dir.tls_dhfile) {
427          free(res->res_dir.tls_dhfile);
428       }
429       if (res->res_dir.tls_allowed_cns) {
430          delete res->res_dir.tls_allowed_cns;
431       }
432       break;
433    case R_AUTOCHANGER:
434       if (res->res_changer.changer_name) {
435          free(res->res_changer.changer_name);
436       }
437       if (res->res_changer.changer_command) {
438          free(res->res_changer.changer_command);
439       }
440       if (res->res_changer.device) {
441          delete res->res_changer.device;
442       }
443       break; 
444    case R_STORAGE:
445       if (res->res_store.sdaddrs) {
446          free_addresses(res->res_store.sdaddrs);
447       }
448       if (res->res_store.sddaddrs) {
449          free_addresses(res->res_store.sddaddrs);
450       }
451       if (res->res_store.working_directory) {
452          free(res->res_store.working_directory);
453       }
454       if (res->res_store.pid_directory) {
455          free(res->res_store.pid_directory);
456       }
457       if (res->res_store.subsys_directory) {
458          free(res->res_store.subsys_directory);
459       }
460       if (res->res_store.scripts_directory) {
461          free(res->res_store.scripts_directory);
462       }
463       if (res->res_store.tls_ctx) { 
464          free_tls_context(res->res_store.tls_ctx);
465       }
466       if (res->res_store.tls_ca_certfile) {
467          free(res->res_store.tls_ca_certfile);
468       }
469       if (res->res_store.tls_ca_certdir) {
470          free(res->res_store.tls_ca_certdir);
471       }
472       if (res->res_store.tls_certfile) {
473          free(res->res_store.tls_certfile);
474       }
475       if (res->res_store.tls_keyfile) {
476          free(res->res_store.tls_keyfile);
477       }
478       if (res->res_store.tls_dhfile) {
479          free(res->res_store.tls_dhfile);
480       }
481       if (res->res_store.tls_allowed_cns) {
482          delete res->res_store.tls_allowed_cns;
483       }
484       break;
485    case R_DEVICE:
486       if (res->res_dev.media_type) {
487          free(res->res_dev.media_type);
488       }
489       if (res->res_dev.device_name) {
490          free(res->res_dev.device_name);
491       }
492       if (res->res_dev.changer_name) {
493          free(res->res_dev.changer_name);
494       }
495       if (res->res_dev.changer_command) {
496          free(res->res_dev.changer_command);
497       }
498       if (res->res_dev.alert_command) {
499          free(res->res_dev.alert_command);
500       }
501       if (res->res_dev.spool_directory) {
502          free(res->res_dev.spool_directory);
503       }
504       if (res->res_dev.mount_point) {
505          free(res->res_dev.mount_point);
506       }
507       if (res->res_dev.mount_command) {
508          free(res->res_dev.mount_command);
509       }
510       if (res->res_dev.unmount_command) {
511          free(res->res_dev.unmount_command);
512       }
513       if (res->res_dev.write_part_command) {
514          free(res->res_dev.write_part_command);
515       }
516       if (res->res_dev.free_space_command) {
517          free(res->res_dev.free_space_command);
518       }
519       break;
520    case R_MSGS:
521       if (res->res_msgs.mail_cmd) {
522          free(res->res_msgs.mail_cmd);
523       }
524       if (res->res_msgs.operator_cmd) {
525          free(res->res_msgs.operator_cmd);
526       }
527       free_msgs_res((MSGS *)res);  /* free message resource */
528       res = NULL;
529       break;
530    default:
531       Dmsg1(0, _("Unknown resource type %d\n"), type);
532       break;
533    }
534    /* Common stuff again -- free the resource, recurse to next one */
535    if (res) {
536       free(res);
537    }
538    if (nres) {
539       free_resource(nres, type);
540    }
541 }
542
543 /* Save the new resource by chaining it into the head list for
544  * the resource. If this is pass 2, we update any resource
545  * or alist pointers.  
546  */
547 void save_resource(int type, RES_ITEM *items, int pass)
548 {
549    URES *res;
550    int rindex = type - r_first;
551    int i, size;
552    int error = 0;
553
554    /*
555     * Ensure that all required items are present
556     */
557    for (i=0; items[i].name; i++) {
558       if (items[i].flags & ITEM_REQUIRED) {
559          if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
560             Emsg2(M_ERROR_TERM, 0, _("\"%s\" item is required in \"%s\" resource, but not found.\n"),
561               items[i].name, resources[rindex]);
562           }
563       }
564       /* If this triggers, take a look at lib/parse_conf.h */
565       if (i >= MAX_RES_ITEMS) {
566          Emsg1(M_ERROR_TERM, 0, _("Too many items in \"%s\" resource\n"), resources[rindex]);
567       }
568    }
569
570    /* During pass 2, we looked up pointers to all the resources
571     * referrenced in the current resource, , now we
572     * must copy their address from the static record to the allocated
573     * record.
574     */
575    if (pass == 2) {
576       DEVRES *dev;
577       int errstat;
578       switch (type) {
579       /* Resources not containing a resource */
580       case R_DEVICE:
581       case R_MSGS:
582          break;
583
584       /* Resources containing a resource or an alist */
585       case R_DIRECTOR:
586          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
587             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
588          }
589          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
590          break;
591       case R_STORAGE:
592          if ((res = (URES *)GetResWithName(R_STORAGE, res_all.res_dir.hdr.name)) == NULL) {
593             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"), res_all.res_dir.hdr.name);
594          }
595          res->res_store.messages = res_all.res_store.messages;
596          res->res_store.tls_allowed_cns = res_all.res_store.tls_allowed_cns;
597          break;
598       case R_AUTOCHANGER:
599          if ((res = (URES *)GetResWithName(type, res_all.res_changer.hdr.name)) == NULL) {
600             Emsg1(M_ERROR_TERM, 0, _("Cannot find AutoChanger resource %s\n"),
601                   res_all.res_changer.hdr.name);
602          }
603          /* we must explicitly copy the device alist pointer */
604          res->res_changer.device   = res_all.res_changer.device;
605          /*
606           * Now update each device in this resource to point back 
607           *  to the changer resource.
608           */
609          foreach_alist(dev, res->res_changer.device) {
610             dev->changer_res = (AUTOCHANGER *)&res->res_changer;
611          }
612          if ((errstat = pthread_mutex_init(&res->res_changer.changer_mutex, NULL)) != 0) {
613             berrno be;
614             Jmsg1(NULL, M_ERROR_TERM, 0, _("Unable to init mutex: ERR=%s\n"), 
615                   be.bstrerror(errstat));
616          }
617          break;
618       default:
619          printf(_("Unknown resource type %d\n"), type);
620          error = 1;
621          break;
622       }
623
624
625       if (res_all.res_dir.hdr.name) {
626          free(res_all.res_dir.hdr.name);
627          res_all.res_dir.hdr.name = NULL;
628       }
629       if (res_all.res_dir.hdr.desc) {
630          free(res_all.res_dir.hdr.desc);
631          res_all.res_dir.hdr.desc = NULL;
632       }
633       return;
634    }
635
636    /* The following code is only executed on pass 1 */
637    switch (type) {
638       case R_DIRECTOR:
639          size = sizeof(DIRRES);
640          break;
641       case R_STORAGE:
642          size = sizeof(STORES);
643          break;
644       case R_DEVICE:
645          size = sizeof(DEVRES);
646          break;
647       case R_MSGS:
648          size = sizeof(MSGS);
649          break;
650       case R_AUTOCHANGER:
651          size = sizeof(AUTOCHANGER);
652          break;
653       default:
654          printf(_("Unknown resource type %d\n"), type);
655          error = 1;
656          size = 1;
657          break;
658    }
659    /* Common */
660    if (!error) {
661       res = (URES *)malloc(size);
662       memcpy(res, &res_all, size);
663       if (!res_head[rindex]) {
664          res_head[rindex] = (RES *)res; /* store first entry */
665       } else {
666          RES *next, *last;
667          /* Add new res to end of chain */
668          for (last=next=res_head[rindex]; next; next=next->next) {
669             last = next;
670             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
671                Emsg2(M_ERROR_TERM, 0,
672                   _("Attempt to define second \"%s\" resource named \"%s\" is not permitted.\n"),
673                   resources[rindex].name, res->res_dir.hdr.name);
674             }
675          }
676          last->next = (RES *)res;
677          Dmsg2(90, "Inserting %s res: %s\n", res_to_str(type),
678                res->res_dir.hdr.name);
679       }
680    }
681 }