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