/*
- Bacula® - The Network Backup Solution
-
- Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
-
- The main author of Bacula is Kern Sibbald, with contributions from
- many others, a complete list can be found in the file AUTHORS.
- This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
- License as published by the Free Software Foundation and included
- in the file LICENSE.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
- Bacula® is a registered trademark of John Walker.
- The licensor of Bacula is the Free Software Foundation Europe
- (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
- Switzerland, email:ftf@fsfeurope.org.
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2015 Kern Sibbald
+ Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
*/
/*
* Second generation Storage daemon.
*
- * Kern Sibbald, MM
+ * Written by Kern Sibbald, MM
*
* It accepts a number of simple commands from the File daemon
* and acts on them. When a request to append data is made,
* it opens a data channel and accepts data from the
* File daemon.
*
- * Version $Id$
- *
*/
#include "bacula.h"
#include "stored.h"
-/* Imported functions */
+/* TODO: fix problem with bls, bextract
+ * that use findlib and already declare
+ * filed plugins
+ */
+#include "sd_plugins.h"
+/* Imported functions */
+extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
/* Forward referenced functions */
void terminate_stored(int sig);
char OK_msg[] = "3000 OK\n";
char TERM_msg[] = "3999 Terminate\n";
STORES *me = NULL; /* our Global resource */
+
bool forge_on = false; /* proceed inspite of I/O errors */
pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
uint32_t VolSessionTime;
char *configfile = NULL;
bool init_done = false;
+static pthread_t server_tid;
+static bool server_tid_valid = false;
/* Global static variables */
static bool foreground = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static workq_t dird_workq; /* queue for processing connections */
+static CONFIG *config;
static void usage()
{
fprintf(stderr, _(
-PROG_COPYRIGHT
-"\nVersion: %s (%s)\n\n"
-"Usage: stored [options] [-c config_file] [config_file]\n"
-" -c <file> use <file> as configuration file\n"
-" -d <nn> set debug level to <nn>\n"
-" -dt print timestamp in debug output\n"
-" -f run in foreground (for debugging)\n"
-" -g <group> set groupid to group\n"
-" -p proceed despite I/O errors\n"
-" -s no signals (for debugging)\n"
-" -t test - read config and exit\n"
-" -u <user> userid to <user>\n"
-" -v verbose user messages\n"
-" -? print this message.\n"
-"\n"), 2000, VERSION, BDATE);
+ PROG_COPYRIGHT
+ "\n%sVersion: %s (%s)\n\n"
+ "Usage: bacula-sd [options] [-c config_file] [config_file]\n"
+ " -c <file> use <file> as configuration file\n"
+ " -d <nn>[,<tags>] set debug level to <nn>, debug tags to <tags>\n"
+ " -dt print timestamp in debug output\n"
+ " -T set trace on\n"
+ " -f run in foreground (for debugging)\n"
+ " -g <group> set groupid to group\n"
+ " -m print kaboom output (for debugging)\n"
+ " -p proceed despite I/O errors\n"
+ " -s no signals (for debugging)\n"
+ " -t test - read config and exit\n"
+ " -u <user> userid to <user>\n"
+ " -v verbose user messages\n"
+ " -? print this message.\n"
+ "\n"), 2000, "", VERSION, BDATE);
exit(1);
}
Emsg1(M_ABORT, 0, _("Tape block size (%d) is not a power of 2\n"), TAPE_BSIZE);
}
- while ((ch = getopt(argc, argv, "c:d:fg:pstu:v?")) != -1) {
+ while ((ch = getopt(argc, argv, "c:d:fg:mpstu:v?T")) != -1) {
switch (ch) {
case 'c': /* configuration file */
if (configfile != NULL) {
if (*optarg == 't') {
dbg_timestamp = true;
} else {
+ char *p;
+ /* We probably find a tag list -d 10,sql,bvfs */
+ if ((p = strchr(optarg, ',')) != NULL) {
+ *p = 0;
+ }
debug_level = atoi(optarg);
if (debug_level <= 0) {
debug_level = 1;
}
+ if (p) {
+ debug_parse_tags(p+1, &debug_level_tags);
+ }
}
break;
+ case 'T':
+ set_trace(true);
+ break;
+
case 'f': /* run in foreground */
foreground = true;
break;
gid = optarg;
break;
+ case 'm': /* print kaboom output */
+ prt_kaboom = true;
+ break;
+
case 'p': /* proceed in spite of I/O errors */
forge_on = true;
break;
if (argc)
usage();
+ if (!foreground) {
+ daemon_start(); /* become daemon */
+ init_stack_dump(); /* pick up new pid */
+ }
+
if (!no_signals) {
init_signals(terminate_stored);
}
configfile = bstrdup(CONFIG_FILE);
}
- parse_config(configfile);
+ config = new_config_parser();
+ parse_sd_config(config, configfile, M_ERROR_TERM);
if (init_crypto() != 0) {
Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
my_name_is(0, (char **)NULL, me->hdr.name); /* Set our real name */
- if (!foreground) {
- daemon_start(); /* become daemon */
- init_stack_dump(); /* pick up new pid */
- }
- create_pid_file(me->pid_directory, "bacula-sd", get_first_port_host_order(me->sdaddrs));
- read_state_file(me->working_directory, "bacula-sd", get_first_port_host_order(me->sdaddrs));
+ create_pid_file(me->pid_directory, "bacula-sd",
+ get_first_port_host_order(me->sdaddrs));
+ read_state_file(me->working_directory, "bacula-sd",
+ get_first_port_host_order(me->sdaddrs));
- drop(uid, gid);
+ set_jcr_in_tsd(INVALID_JCR);
+ /* Make sure on Solaris we can run concurrent, watch dog + servers + misc */
+ set_thread_concurrency(me->max_concurrent_jobs * 2 + 4);
+ lmgr_init_thread(); /* initialize the lockmanager stack */
- cleanup_old_files();
+ load_sd_plugins(me->plugin_directory);
+ drop(uid, gid, false);
+
+ cleanup_old_files();
/* Ensure that Volume Session Time and Id are both
* set and are both non-zero.
Jmsg0(NULL, M_ABORT, 0, _("Volume Session Time is ZERO!\n"));
}
- init_python_interpreter(me->hdr.name, me->scripts_directory, "SDStartUp");
-
- /* Make sure on Solaris we can run concurrent, watch dog + servers + misc */
- set_thread_concurrency(me->max_concurrent_jobs * 2 + 4);
-
- /*
- * Start the device allocation thread
- */
- create_volume_list(); /* do before device_init */
+ /*
+ * Start the device allocation thread
+ */
+ create_volume_lists(); /* do before device_init */
if (pthread_create(&thid, NULL, device_initialization, NULL) != 0) {
berrno be;
Emsg1(M_ABORT, 0, _("Unable to create thread. ERR=%s\n"), be.bstrerror());
init_jcr_subsystem(); /* start JCR watchdogs etc. */
/* Single server used for Director and File daemon */
+ server_tid = pthread_self();
+ server_tid_valid = true;
bnet_thread_server(me->sdaddrs, me->max_concurrent_jobs * 2 + 1,
&dird_workq, handle_connection_request);
exit(1); /* to keep compiler quiet */
bool OK = true;
bool tls_needed;
-
me = (STORES *)GetNextRes(R_STORAGE, NULL);
if (!me) {
Jmsg1(NULL, M_ERROR, 0, _("No Storage resource defined in %s. Cannot continue.\n"),
DIRRES *director;
STORES *store;
- foreach_res(store, R_STORAGE) {
+ foreach_res(store, R_STORAGE) {
/* tls_require implies tls_enable */
if (store->tls_require) {
if (have_tls) {
store->tls_keyfile, NULL, NULL, store->tls_dhfile,
store->tls_verify_peer);
- if (!store->tls_ctx) {
+ if (!store->tls_ctx) {
Jmsg(NULL, M_FATAL, 0, _("Failed to initialize TLS context for Storage \"%s\" in %s.\n"),
store->hdr.name, configfile);
OK = false;
}
}
- foreach_res(director, R_DIRECTOR) {
+ foreach_res(director, R_DIRECTOR) {
/* tls_require implies tls_enable */
if (director->tls_require) {
director->tls_enable = true;
director->tls_keyfile, NULL, NULL, director->tls_dhfile,
director->tls_verify_peer);
- if (!director->tls_ctx) {
+ if (!director->tls_ctx) {
Jmsg(NULL, M_FATAL, 0, _("Failed to initialize TLS context for Director \"%s\" in %s.\n"),
director->hdr.name, configfile);
OK = false;
OK = init_autochangers();
-
+
if (OK) {
close_msg(NULL); /* close temp message handler */
init_msg(NULL, me->messages); /* open daemon message handler */
return OK;
}
+/*
+ * Remove old .spool files written by me from the working directory.
+ */
static void cleanup_old_files()
{
- POOLMEM *cleanup = get_pool_memory(PM_MESSAGE);
+ DIR* dp;
+ struct dirent *entry, *result;
+ int rc, name_max;
+ int my_name_len = strlen(my_name);
int len = strlen(me->working_directory);
-#if defined(HAVE_WIN32)
- pm_strcpy(cleanup, "del /q ");
-#else
- pm_strcpy(cleanup, "/bin/rm -f ");
-#endif
- pm_strcat(cleanup, me->working_directory);
+ POOLMEM *cleanup = get_pool_memory(PM_MESSAGE);
+ POOLMEM *basename = get_pool_memory(PM_MESSAGE);
+ regex_t preg1;
+ char prbuf[500];
+ const int nmatch = 30;
+ regmatch_t pmatch[nmatch];
+ berrno be;
+
+ /* Look for .spool files but don't allow spaces */
+ const char *pat1 = "^[^ ]+\\.spool$";
+
+ /* Setup working directory prefix */
+ pm_strcpy(basename, me->working_directory);
if (len > 0 && !IsPathSeparator(me->working_directory[len-1])) {
- pm_strcat(cleanup, "/");
+ pm_strcat(basename, "/");
+ }
+
+ /* Compile regex expressions */
+ rc = regcomp(&preg1, pat1, REG_EXTENDED);
+ if (rc != 0) {
+ regerror(rc, &preg1, prbuf, sizeof(prbuf));
+ Pmsg2(000, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
+ pat1, prbuf);
+ goto get_out2;
+ }
+
+ name_max = pathconf(".", _PC_NAME_MAX);
+ if (name_max < 1024) {
+ name_max = 1024;
+ }
+
+ if (!(dp = opendir(me->working_directory))) {
+ berrno be;
+ Pmsg2(000, "Failed to open working dir %s for cleanup: ERR=%s\n",
+ me->working_directory, be.bstrerror());
+ goto get_out1;
+ }
+
+ entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
+ while (1) {
+ if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
+ break;
+ }
+ /* Exclude any name with ., .., not my_name or containing a space */
+ if (strcmp(result->d_name, ".") == 0 || strcmp(result->d_name, "..") == 0 ||
+ strncmp(result->d_name, my_name, my_name_len) != 0) {
+ Dmsg1(500, "Skipped: %s\n", result->d_name);
+ continue;
+ }
+
+ /* Unlink files that match regex */
+ if (regexec(&preg1, result->d_name, nmatch, pmatch, 0) == 0) {
+ pm_strcpy(cleanup, basename);
+ pm_strcat(cleanup, result->d_name);
+ Dmsg1(500, "Unlink: %s\n", cleanup);
+ unlink(cleanup);
+ }
}
- pm_strcat(cleanup, my_name);
- pm_strcat(cleanup, "*.spool");
- run_program(cleanup, 0, NULL);
+ free(entry);
+ closedir(dp);
+
+get_out1:
+ regfree(&preg1);
+get_out2:
free_pool_memory(cleanup);
+ free_pool_memory(basename);
}
-
/*
* Here we attempt to init and open each device. This is done
* once at startup in a separate thread.
pthread_detach(pthread_self());
jcr = new_jcr(sizeof(JCR), stored_free_jcr);
- jcr->JobType = JT_SYSTEM;
+ new_plugins(jcr); /* instantiate plugins */
+ jcr->setJobType(JT_SYSTEM);
/* Initialize FD start condition variable */
int errstat = pthread_cond_init(&jcr->job_start_wait, NULL);
if (errstat != 0) {
}
jcr->dcr = dcr = new_dcr(jcr, NULL, dev);
- if (dev->is_autochanger()) {
- /* If autochanger set slot in dev sturcture */
- get_autochanger_loaded_slot(dcr);
- }
+ generate_plugin_event(jcr, bsdEventDeviceInit, dcr);
if (device->cap_bits & CAP_ALWAYSOPEN) {
+ if (dev->is_autochanger()) {
+ /* If autochanger set slot in dev sturcture */
+ get_autochanger_loaded_slot(dcr);
+ }
Dmsg1(20, "calling first_open_device %s\n", dev->print_name());
+ if (generate_plugin_event(jcr, bsdEventDeviceOpen, dcr) != bRC_OK) {
+ Jmsg(jcr, M_FATAL, 0, _("generate_plugin_event(bsdEventDeviceOpen) Failed\n"));
+ continue;
+ }
+
if (!first_open_device(dcr)) {
Jmsg1(NULL, M_ERROR, 0, _("Could not open device %s\n"), dev->print_name());
Dmsg1(20, "Could not open device %s\n", dev->print_name());
+ generate_plugin_event(jcr, bsdEventDeviceClose, dcr);
free_dcr(dcr);
jcr->dcr = NULL;
continue;
}
+ } else {
+ /* If not always open, we don't know what is in the drive */
+ dev->clear_slot();
}
if (device->cap_bits & CAP_AUTOMOUNT && dev->is_open()) {
switch (read_dev_volume_label(dcr)) {
break;
}
}
+
free_dcr(dcr);
jcr->dcr = NULL;
}
+
+
#ifdef xxx
if (jcr->dcr) {
- Dmsg1(000, "free_dcr=%p\n", jcr->dcr);
+ Pmsg1(000, "free_dcr=%p\n", jcr->dcr);
free_dcr(jcr->dcr);
jcr->dcr = NULL;
}
#endif
- free_jcr(jcr);
+ free_plugins(jcr);
+ free_jcr(jcr);
init_done = true;
UnlockRes();
return NULL;
exit(1);
}
in_here = true;
+ debug_level = 0; /* turn off any debug */
stop_watchdog();
- if (sig == SIGTERM) { /* normal shutdown request? */
+ if (sig == SIGTERM || sig == SIGINT) { /* normal shutdown request? or ^C */
/*
* This is a normal shutdown request. We wiffle through
* all open jobs canceling them and trying to wake
free_jcr(jcr);
continue; /* ignore console */
}
- set_jcr_job_status(jcr, JS_Canceled);
+ if (jcr->dcr) {
+ /* Make sure no device remains locked */
+ generate_plugin_event(jcr, bsdEventDeviceClose, jcr->dcr);
+ }
+ jcr->setJobStatus(JS_Canceled);
fd = jcr->file_bsock;
if (fd) {
fd->set_timed_out();
+ jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
Dmsg1(100, "term_stored killing JobId=%d\n", jcr->JobId);
- pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
/* ***FIXME*** wiffle through all dcrs */
if (jcr->dcr && jcr->dcr->dev && jcr->dcr->dev->blocked()) {
pthread_cond_broadcast(&jcr->dcr->dev->wait_next_vol);
Dmsg1(200, "In terminate_stored() sig=%d\n", sig);
+ unload_plugins();
+ free_volume_lists();
+
foreach_res(device, R_DEVICE) {
Dmsg1(10, "Term device %s\n", device->device_name);
if (device->dev) {
Dmsg1(10, "No dev structure %s\n", device->device_name);
}
}
+ if (server_tid_valid) {
+ bnet_stop_thread_server(server_tid);
+ }
if (configfile) {
free(configfile);
configfile = NULL;
}
- free_config_resources();
+ if (config) {
+ config->free_resources();
+ free(config);
+ config = NULL;
+ }
- if (debug_level > 10) {
+ if (chk_dbglvl(10)) {
print_memory_pool_stats();
}
term_msg();
cleanup_crypto();
- free_volume_list();
term_reservations_lock();
close_memory_pool();
+ lmgr_cleanup_main();
sm_dump(false); /* dump orphaned buffers */
exit(sig);