From 322cb260e2a68d5c1c133293c33b1229ca0fc5ad Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Wed, 11 Apr 2007 20:09:25 +0000 Subject: [PATCH] 11Apr07 kes Add exec external-command [wait-seconds] to bconsole. This executes the external-command. Note! normally external-command should be enclosed in double quotes. kes Turn the .die command on only if DEVELOPER is defined -- i.e. it should normally be off in a production system. 10Apr07 kes Implement die command for SD so that we can force it to dump. kes Implement SD lock debug code. kes Implement new algorithm for keeping Volume list in SD. It is now owned by the device. git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@4535 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/src/console/console.c | 45 +++- bacula/src/dird/ua_dotcmds.c | 148 +++++++++- bacula/src/lib/jcr.c | 50 ++-- bacula/src/stored/acquire.c | 44 +-- bacula/src/stored/ansi_label.c | 2 +- bacula/src/stored/askdir.c | 28 +- bacula/src/stored/autochanger.c | 8 +- bacula/src/stored/bacula-sd.conf.in | 2 +- bacula/src/stored/bcopy.c | 26 +- bacula/src/stored/block.c | 8 +- bacula/src/stored/btape.c | 12 +- bacula/src/stored/dev.c | 72 ++--- bacula/src/stored/dev.h | 41 ++- bacula/src/stored/device.c | 142 +++++++--- bacula/src/stored/dircmd.c | 49 +++- bacula/src/stored/label.c | 24 +- bacula/src/stored/match_bsr.c | 20 +- bacula/src/stored/protos.h | 39 ++- bacula/src/stored/reserve.c | 401 ++++++++++++++++++---------- bacula/src/stored/spool.c | 4 +- bacula/src/stored/stored.h | 14 +- bacula/src/stored/wait.c | 4 +- bacula/src/version.h | 6 +- bacula/technotes-2.1 | 11 + 24 files changed, 823 insertions(+), 377 deletions(-) diff --git a/bacula/src/console/console.c b/bacula/src/console/console.c index b01dea7b7f..34064f774f 100644 --- a/bacula/src/console/console.c +++ b/bacula/src/console/console.c @@ -102,6 +102,7 @@ static int quitcmd(FILE *input, BSOCK *UA_sock); static int echocmd(FILE *input, BSOCK *UA_sock); static int timecmd(FILE *input, BSOCK *UA_sock); static int sleepcmd(FILE *input, BSOCK *UA_sock); +static int execcmd(FILE *input, BSOCK *UA_sock); #define CONFIG_FILE "bconsole.conf" /* default configuration file */ @@ -166,6 +167,7 @@ static struct cmdstruct commands[] = { { N_("time"), timecmd, _("print current time")}, { N_("version"), versioncmd, _("print Console's version")}, { N_("echo"), echocmd, _("echo command string")}, + { N_("exec"), execcmd, _("execute an external command")}, { N_("exit"), quitcmd, _("exit = quit")}, { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")}, }; @@ -886,15 +888,54 @@ static int do_outputcmd(FILE *input, BSOCK *UA_sock) } fd = fopen(argk[1], mode); if (!fd) { + berrno be; senditf(_("Cannot open file %s for output. ERR=%s\n"), - argk[1], strerror(errno)); + argk[1], be.strerror(errno)); return 1; } output = fd; return 1; } -static int echocmd(FILE *intut, BSOCK *UA_sock) +/* + * exec "some-command" [wait-seconds] +*/ +static int execcmd(FILE *input, BSOCK *UA_sock) +{ + BPIPE *bpipe; + char line[5000]; + int stat; + int wait = 0; + + if (argc > 3) { + sendit(_("Too many arguments. Enclose command in double quotes.\n")); + return 1; + } + if (argc == 3) { + wait = atoi(argk[2]); + } + bpipe = open_bpipe(argk[1], wait, "r"); + if (!bpipe) { + berrno be; + senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"), + argk[1], be.strerror(errno)); + return 1; + } + + while (fgets(line, sizeof(line), bpipe->rfd)) { + senditf("%s", line); + } + stat = close_bpipe(bpipe); + if (stat != 0) { + berrno be; + be.set_errno(stat); + senditf(_("Autochanger error: ERR=%s\n"), be.strerror()); + } + return 1; +} + + +static int echocmd(FILE *input, BSOCK *UA_sock) { for (int i=1; i < argc; i++) { senditf("%s", argk[i]); diff --git a/bacula/src/dird/ua_dotcmds.c b/bacula/src/dird/ua_dotcmds.c index c8479129e4..8d41289b8a 100644 --- a/bacula/src/dird/ua_dotcmds.c +++ b/bacula/src/dird/ua_dotcmds.c @@ -162,20 +162,162 @@ static bool getmsgscmd(UAContext *ua, const char *cmd) return 1; } +#ifdef DEVELOPER +static void do_storage_die(UAContext *ua, STORE *store) +{ + BSOCK *sd; + JCR *jcr = ua->jcr; + USTORE lstore; + + lstore.store = store; + pm_strcpy(lstore.store_source, _("unknown source")); + set_wstorage(jcr, &lstore); + /* Try connecting for up to 15 seconds */ + ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"), + store->name(), store->address, store->SDport); + if (!connect_to_storage_daemon(jcr, 1, 15, 0)) { + ua->error_msg(_("Failed to connect to Storage daemon.\n")); + return; + } + Dmsg0(120, _("Connected to storage daemon\n")); + sd = jcr->store_bsock; + sd->fsend(".die"); + if (sd->recv() >= 0) { + ua->send_msg("%s", sd->msg); + } + sd->signal(BNET_TERMINATE); + sd->close(); + jcr->store_bsock = NULL; + return; +} + +static void do_client_die(UAContext *ua, CLIENT *client) +{ + BSOCK *fd; + + /* Connect to File daemon */ + + ua->jcr->client = client; + /* Try to connect for 15 seconds */ + ua->send_msg(_("Connecting to Client %s at %s:%d\n"), + client->name(), client->address, client->FDport); + if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) { + ua->error_msg(_("Failed to connect to Client.\n")); + return; + } + Dmsg0(120, "Connected to file daemon\n"); + fd = ua->jcr->file_bsock; + fd->fsend(".die"); + if (fd->recv() >= 0) { + ua->send_msg("%s", fd->msg); + } + fd->signal(BNET_TERMINATE); + fd->close(); + ua->jcr->file_bsock = NULL; + return; +} + /* * Create segmentation fault */ static bool diecmd(UAContext *ua, const char *cmd) { + STORE *store; + CLIENT *client; + int i; JCR *jcr = NULL; int a; - ua->send_msg(_("The Director will segment fault.\n")); - a = jcr->JobId; /* ref NULL pointer */ - jcr->JobId = 1000; /* another ref NULL pointer */ + Dmsg1(120, "diecmd:%s:\n", cmd); + + /* General debug? */ + for (i=1; iargc; i++) { + if (strcasecmp(ua->argk[i], "dir") == 0 || + strcasecmp(ua->argk[i], "director") == 0) { + ua->send_msg(_("The Director will segment fault.\n")); + a = jcr->JobId; /* ref NULL pointer */ + jcr->JobId = 1000; /* another ref NULL pointer */ + return 1; + } + if (strcasecmp(ua->argk[i], "client") == 0 || + strcasecmp(ua->argk[i], "fd") == 0) { + client = NULL; + if (ua->argv[i]) { + client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]); + if (client) { + do_client_die(ua, client); + return 1; + } + } + client = select_client_resource(ua); + if (client) { + do_client_die(ua, client); + return 1; + } + } + + if (strcasecmp(ua->argk[i], NT_("store")) == 0 || + strcasecmp(ua->argk[i], NT_("storage")) == 0 || + strcasecmp(ua->argk[i], NT_("sd")) == 0) { + store = NULL; + if (ua->argv[i]) { + store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]); + if (store) { + do_storage_die(ua, store); + return 1; + } + } + store = get_storage_resource(ua, false/*no default*/); + if (store) { + do_storage_die(ua, store); + return 1; + } + } + } + /* + * We didn't find an appropriate keyword above, so + * prompt the user. + */ + start_prompt(ua, _("Available daemons are: \n")); + add_prompt(ua, _("Director")); + add_prompt(ua, _("Storage")); + add_prompt(ua, _("Client")); + switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) { + case 0: /* Director */ + ua->send_msg(_("The Director will segment fault.\n")); + a = jcr->JobId; /* ref NULL pointer */ + jcr->JobId = 1000; /* another ref NULL pointer */ + break; + case 1: + store = get_storage_resource(ua, false/*no default*/); + if (store) { + do_storage_die(ua, store); + } + break; + case 2: + client = select_client_resource(ua); + if (client) { + do_client_die(ua, client); + } + break; + default: + break; + } + return true; +} + +#else + +/* + * Dummy routine for non-development version + */ +static bool diecmd(UAContext *ua, const char *cmd) +{ return true; } +#endif + static bool jobscmd(UAContext *ua, const char *cmd) { JOB *job; diff --git a/bacula/src/lib/jcr.c b/bacula/src/lib/jcr.c index 8f65f71f7f..982f4d0a9b 100644 --- a/bacula/src/lib/jcr.c +++ b/bacula/src/lib/jcr.c @@ -1,31 +1,7 @@ -/* - * Manipulation routines for Job Control Records and - * handling of last_jobs_list. - * - * Kern E. Sibbald, December 2000 - * - * Version $Id$ - * - * These routines are thread safe. - * - * The job list routines were re-written in May 2005 to - * eliminate the global lock while traversing the list, and - * to use the dlist subroutines. The locking is now done - * on the list each time the list is modified or traversed. - * That is it is "micro-locked" rather than globally locked. - * The result is that there is one lock/unlock for each entry - * in the list while traversing it rather than a single lock - * at the beginning of a traversal and one at the end. This - * incurs slightly more overhead, but effectively eliminates - * the possibilty of race conditions. In addition, with the - * exception of the global locking of the list during the - * re-reading of the config file, no recursion is needed. - * - */ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2006 Free Software Foundation Europe e.V. + 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. @@ -49,6 +25,30 @@ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ +/* + * Manipulation routines for Job Control Records and + * handling of last_jobs_list. + * + * Kern E. Sibbald, December 2000 + * + * Version $Id$ + * + * These routines are thread safe. + * + * The job list routines were re-written in May 2005 to + * eliminate the global lock while traversing the list, and + * to use the dlist subroutines. The locking is now done + * on the list each time the list is modified or traversed. + * That is it is "micro-locked" rather than globally locked. + * The result is that there is one lock/unlock for each entry + * in the list while traversing it rather than a single lock + * at the beginning of a traversal and one at the end. This + * incurs slightly more overhead, but effectively eliminates + * the possibilty of race conditions. In addition, with the + * exception of the global locking of the list during the + * re-reading of the config file, no recursion is needed. + * + */ #include "bacula.h" #include "jcr.h" diff --git a/bacula/src/stored/acquire.c b/bacula/src/stored/acquire.c index 436c39d235..8eac9dbc87 100644 --- a/bacula/src/stored/acquire.c +++ b/bacula/src/stored/acquire.c @@ -130,7 +130,7 @@ bool acquire_device_for_read(DCR *dcr) unlock_reservations(); if (stat == 1) { DCR *new_dcr = jcr->read_dcr; - dev->unblock(); + dev->unblock(dev_unlocked); detach_dcr_from_dev(dcr); /* release old device */ /* Copy important info from the new dcr */ dev = dcr->dev = new_dcr->dev; @@ -281,14 +281,13 @@ default_path: dcr->VolumeName, dev->print_name()); get_out: - dev->lock(); + dev->dlock(); if (dcr->reserved_device) { dev->reserved_device--; Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name()); dcr->reserved_device = false; } - dev->unlock(); - dev->unblock(); + dev->unblock(dev_locked); Dmsg1(50, "jcr->dcr=%p\n", jcr->dcr); return ok; } @@ -348,7 +347,7 @@ DCR *acquire_device_for_append(DCR *dcr) dcr->VolumeName); /* Release volume reserved by dir_find_next_appendable_volume() */ if (dcr->VolumeName[0]) { - free_unused_volume(dcr); + volume_unused(dcr); } if (dev->num_writers != 0) { Jmsg3(jcr, M_FATAL, 0, _("Wanted to append to Volume \"%s\", but device %s is busy writing on \"%s\" .\n"), @@ -431,28 +430,26 @@ DCR *acquire_device_for_append(DCR *dcr) } dev->VolCatInfo.VolCatJobs++; /* increment number of jobs on vol */ dir_update_volume_info(dcr, false); /* send Volume info to Director */ - dev->lock(); + dev->dlock(); if (dcr->reserved_device) { dev->reserved_device--; Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name()); dcr->reserved_device = false; } - dev->unlock(); - dev->unblock(); + dev->unblock(dev_locked); return dcr; /* * Error return */ get_out: - dev->lock(); + dev->dlock(); if (dcr->reserved_device) { dev->reserved_device--; Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name()); dcr->reserved_device = false; } - dev->unlock(); - dev->unblock(); + dev->unblock(dev_locked); return NULL; } @@ -473,7 +470,7 @@ bool release_device(DCR *dcr) /* lock only if not already locked by this thread */ if (!dcr->dev_locked) { - lock_device(dev); + dev->r_dlock(); } Dmsg2(100, "release_device device %s is %s\n", dev->print_name(), dev->is_tape()?"tape":"disk"); @@ -558,7 +555,7 @@ bool release_device(DCR *dcr) free_pool_memory(alert); } dcr->dev_locked = false; /* set no longer locked */ - dev->unlock(); + dev->dunlock(); if (jcr->read_dcr == dcr) { jcr->read_dcr = NULL; } @@ -579,6 +576,7 @@ DCR *new_dcr(JCR *jcr, DEVICE *dev) memset(dcr, 0, sizeof(DCR)); dcr->jcr = jcr; if (dev) { + dcr->tid = pthread_self(); dcr->dev = dev; dcr->device = dev->device; dcr->block = new_block(dev); @@ -629,24 +627,7 @@ static void attach_dcr_to_dev(DCR *dcr) void detach_dcr_from_dev(DCR *dcr) { - DEVICE *dev = dcr->dev; - - if (dcr->reserved_device) { - dcr->reserved_device = false; - lock_device(dev); - dev->reserved_device--; - Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name()); - dcr->reserved_device = false; - /* If we set read mode in reserving, remove it */ - if (dev->can_read()) { - dev->clear_read(); - } - if (dev->num_writers < 0) { - Jmsg1(dcr->jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers); - dev->num_writers = 0; - } - dev->unlock(); - } + unreserve_device(dcr); /* Detach this dcr only if attached */ if (dcr->attached_to_dev) { @@ -654,7 +635,6 @@ void detach_dcr_from_dev(DCR *dcr) dcr->attached_to_dev = false; // remove_dcr_from_dcrs(dcr); /* remove dcr from jcr list */ } - free_unused_volume(dcr); /* free unused vols attached to this dcr */ pthread_cond_broadcast(&dcr->dev->wait_next_vol); pthread_cond_broadcast(&wait_device_release); } diff --git a/bacula/src/stored/ansi_label.c b/bacula/src/stored/ansi_label.c index 3e1489278b..76005940a0 100644 --- a/bacula/src/stored/ansi_label.c +++ b/bacula/src/stored/ansi_label.c @@ -145,7 +145,7 @@ int read_ansi_ibm_label(DCR *dcr) *q++ = *p++; } *q = 0; - new_volume(dcr, dev->VolHdr.VolumeName); + reserve_volume(dcr, dev->VolHdr.VolumeName); Dmsg2(100, "Wanted ANSI Vol %s got %6s\n", VolName, dev->VolHdr.VolumeName); Mmsg2(jcr->errmsg, _("Wanted ANSI Volume \"%s\" got \"%s\"\n"), VolName, dev->VolHdr.VolumeName); return VOL_NAME_ERROR; diff --git a/bacula/src/stored/askdir.c b/bacula/src/stored/askdir.c index dbc2b8fe2e..3f728742b4 100644 --- a/bacula/src/stored/askdir.c +++ b/bacula/src/stored/askdir.c @@ -1,11 +1,3 @@ -/* - * Subroutines to handle Catalog reqests sent to the Director - * Reqests/commands from the Director are handled in dircmd.c - * - * Kern Sibbald, December 2000 - * - * Version $Id$ - */ /* Bacula® - The Network Backup Solution @@ -33,6 +25,14 @@ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ +/* + * Subroutines to handle Catalog reqests sent to the Director + * Reqests/commands from the Director are handled in dircmd.c + * + * Kern Sibbald, December 2000 + * + * Version $Id$ + */ #include "bacula.h" /* pull in global headers */ #include "stored.h" /* pull in Storage Deamon headers */ @@ -237,8 +237,10 @@ bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing) /* * Get info on the next appendable volume in the Director's database - * Returns: true on success - * false on failure + * + * Returns: true on success dcr->VolumeName is volume + * reserve_volume() called on Volume name + * false on failure dcr->VolumeName[0] == 0 * * Volume information returned in dcr * @@ -282,7 +284,7 @@ bool dir_find_next_appendable_volume(DCR *dcr) } if (found) { Dmsg0(400, "dir_find_next_appendable_volume return true\n"); - new_volume(dcr, dcr->VolumeName); /* reserve volume */ + reserve_volume(dcr, dcr->VolumeName); /* reserve volume */ V(vol_info_mutex); unlock_reservations(); return true; @@ -468,9 +470,9 @@ bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg); return false; } - dev->lock(); + dev->dlock(); got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */ - dev->unlock(); + dev->dunlock(); if (got_vol) { return true; } else { diff --git a/bacula/src/stored/autochanger.c b/bacula/src/stored/autochanger.c index 4c06a83315..be656ff337 100644 --- a/bacula/src/stored/autochanger.c +++ b/bacula/src/stored/autochanger.c @@ -264,7 +264,7 @@ int get_autochanger_loaded_slot(DCR *dcr) *results.c_str() = 0; Dmsg1(100, "Run program=%s\n", changer); status = run_program_full_output(changer, timeout, results.c_str()); - Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results.c_str()); + Dmsg3(100, "run_prog: %s stat=%d result=%s", changer, status, results.c_str()); if (status == 0) { loaded = str_to_int32(results.c_str()); if (loaded > 0) { @@ -414,14 +414,14 @@ static bool unload_other_drive(DCR *dcr, int slot) } break; } - dev->lock(); + dev->dlock(); if (dev->is_busy()) { Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"), dcr->VolumeName, dev->print_name()); Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n", dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot); Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device); - dev->unlock(); + dev->dunlock(); return false; } @@ -463,7 +463,7 @@ static bool unload_other_drive(DCR *dcr, int slot) Dmsg0(100, "Slot unloaded\n"); } unlock_changer(dcr); - dev->unlock(); + dev->dunlock(); free_pool_memory(changer_cmd); return ok; } diff --git a/bacula/src/stored/bacula-sd.conf.in b/bacula/src/stored/bacula-sd.conf.in index 367951ecb4..3ca34340be 100644 --- a/bacula/src/stored/bacula-sd.conf.in +++ b/bacula/src/stored/bacula-sd.conf.in @@ -160,7 +160,7 @@ Device { # A DVD device # #Device { -# Name = "DVD-Writer" +# Name = "DVD Writer" # Media Type = DVD # Device Type = DVD # Archive Device = /dev/hdc diff --git a/bacula/src/stored/bcopy.c b/bacula/src/stored/bcopy.c index b8a6bd5599..2412d84d6e 100644 --- a/bacula/src/stored/bcopy.c +++ b/bacula/src/stored/bcopy.c @@ -1,16 +1,7 @@ -/* - * - * Program to copy a Bacula from one volume to another. - * - * Kern E. Sibbald, October 2002 - * - * - * Version $Id$ - */ /* Bacula® - The Network Backup Solution - Copyright (C) 2002-2006 Free Software Foundation Europe e.V. + Copyright (C) 2002-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. @@ -34,6 +25,15 @@ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ +/* + * + * Program to copy a Bacula from one volume to another. + * + * Kern E. Sibbald, October 2002 + * + * + * Version $Id$ + */ #include "bacula.h" #include "stored.h" @@ -186,13 +186,13 @@ int main (int argc, char *argv[]) } Dmsg0(100, "About to acquire device for writing\n"); /* For we must now acquire the device for writing */ - lock_device(out_dev); + out_dev->r_dlock(); if (out_dev->open(out_jcr->dcr, OPEN_READ_WRITE) < 0) { Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg); - out_dev->unlock(); + out_dev->dunlock(); exit(1); } - out_dev->unlock(); + out_dev->dunlock(); if (!acquire_device_for_append(out_jcr->dcr)) { free_jcr(in_jcr); exit(1); diff --git a/bacula/src/stored/block.c b/bacula/src/stored/block.c index faa51c94d5..a2dd5a01cb 100644 --- a/bacula/src/stored/block.c +++ b/bacula/src/stored/block.c @@ -346,7 +346,7 @@ bool write_block_to_device(DCR *dcr) } if (!dcr->dev_locked) { /* device already locked? */ - lock_device(dev); /* no, lock it */ + dev->r_dlock(); /* no, lock it */ } /* @@ -388,7 +388,7 @@ bool write_block_to_device(DCR *dcr) bail_out: if (!dcr->dev_locked) { /* did we lock dev above? */ - dev->unlock(); /* unlock it now */ + dev->dunlock(); /* unlock it now */ } return stat; } @@ -896,9 +896,9 @@ bool read_block_from_device(DCR *dcr, bool check_block_numbers) bool ok; DEVICE *dev = dcr->dev; Dmsg0(200, "Enter read_block_from_device\n"); - lock_device(dev); + dev->r_dlock(); ok = read_block_from_dev(dcr, check_block_numbers); - unlock_device(dev); + dev->dunlock(); Dmsg0(200, "Leave read_block_from_device\n"); return ok; } diff --git a/bacula/src/stored/btape.c b/bacula/src/stored/btape.c index 76e73315b7..4f4560475d 100644 --- a/bacula/src/stored/btape.c +++ b/bacula/src/stored/btape.c @@ -358,7 +358,7 @@ static bool open_the_device() bool ok = true; block = new_block(dev); - lock_device(dev); + dev->r_dlock(); Dmsg1(200, "Opening device %s\n", dcr->VolumeName); if (dev->open(dcr, OPEN_READ_WRITE) < 0) { Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), dev->errmsg); @@ -369,7 +369,7 @@ static bool open_the_device() dev->set_append(); /* put volume in append mode */ bail_out: - dev->unlock(); + dev->dunlock(); free_block(block); return ok; } @@ -2330,7 +2330,7 @@ static int flush_block(DEV_BLOCK *block, int dump) DEV_BLOCK *tblock; uint32_t this_file, this_block_num; - lock_device(dev); + dev->r_dlock(); if (!this_block) { this_block = new_block(dev); } @@ -2385,12 +2385,12 @@ static int flush_block(DEV_BLOCK *block, int dump) if (!fixup_device_block_write_error(jcr->dcr)) { Pmsg1(000, _("Cannot fixup device error. %s\n"), dev->bstrerror()); ok = false; - dev->unlock(); + dev->dunlock(); return 0; } BlockNumber = 0; /* start counting for second tape */ } - dev->unlock(); + dev->dunlock(); return 1; /* end of tape reached */ } @@ -2409,7 +2409,7 @@ static int flush_block(DEV_BLOCK *block, int dump) last_file = this_file; last_block_num = this_block_num; - dev->unlock(); + dev->dunlock(); return 1; } diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index f63f70f79a..802b0647a9 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -1,3 +1,30 @@ +/* + 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 plus additions + that are listed 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. +*/ /* * * dev.c -- low level operations on device (storage device) @@ -5,7 +32,7 @@ * Kern Sibbald, MM * * NOTE!!!! None of these routines are reentrant. You must - * use lock_device() and dev->unlock() at a higher level, + * use dev->r_dlock() and dev->unlock() at a higher level, * or use the xxx_device() equivalents. By moving the * thread synchronization to a higher level, we permit * the higher level routines to "seize" the device and @@ -28,33 +55,6 @@ * * Version $Id$ */ -/* - 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 plus additions - that are listed 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. -*/ /* * Handling I/O errors and end of tape conditions are a bit tricky. @@ -782,18 +782,21 @@ bool DEVICE::rewind(DCR *dcr) void DEVICE::block(int why) { - lock_device(this); + r_dlock(); /* need recursive lock to block */ block_device(this, why); - unlock(); + r_dunlock(); } -void DEVICE::unblock() -{ - lock(); +void DEVICE::unblock(bool locked) +{ + if (!locked) { + dlock(); + } unblock_device(this); - unlock(); + dunlock(); } + const char *DEVICE::print_blocked() const { switch (m_blocked) { @@ -1850,6 +1853,7 @@ void DEVICE::clrerror(int func) void DEVICE::clear_volhdr() { free_volume(this); + Dmsg1(100, "Clear volhdr vol=%s\n", VolHdr.VolumeName); memset(&VolHdr, 0, sizeof(VolHdr)); } diff --git a/bacula/src/stored/dev.h b/bacula/src/stored/dev.h index b37c48d195..0fa2847e7a 100644 --- a/bacula/src/stored/dev.h +++ b/bacula/src/stored/dev.h @@ -39,10 +39,15 @@ #ifndef __DEV_H #define __DEV_H 1 +#ifdef SD_DEBUG_LOCK +#define r_dlock() _r_dlock(__FILE__, __LINE__); /* in device.c */ +#define r_dunlock() _r_dunlock(__FILE__, __LINE__); /* in device.c */ +#define dlock() _dlock(__FILE__, __LINE__); /* in device.c */ +#define dunlock() _dunlock(__FILE__, __LINE__); /* in device.c */ +#endif + #undef DCR /* used by Bacula */ -#define lock_device(d) _lock_device(__FILE__, __LINE__, (d)) -#define unlock_device(d) _unlock_device(__FILE__, __LINE__, (d)) #define block_device(d, s) _block_device(__FILE__, __LINE__, (d), s) #define unblock_device(d) _unblock_device(__FILE__, __LINE__, (d)) #define steal_device_lock(d, p, s) _steal_device_lock(__FILE__, __LINE__, (d), (p), s) @@ -190,6 +195,16 @@ typedef struct s_steal_lock { class DEVRES; /* Device resource defined in stored_conf.h */ class DCR; /* forward reference */ +class VOLRES; /* forward reference */ + +/* + * Used in unblock() call + */ +enum { + dev_locked = true, + dev_unlocked = false +}; + /* * Device structure definition. There is one of these for * each physical device. Everything here is "global" to @@ -199,6 +214,8 @@ class DEVICE { private: int m_fd; /* file descriptor */ int m_blocked; /* set if we must wait (i.e. change tape) */ + int m_count; /* Mutex use count -- DEBUG only */ + pthread_t m_pid; /* Thread that locked -- DEBUG only */ public: dlist *attached_dcrs; /* attached DCR list */ pthread_mutex_t m_mutex; /* access control */ @@ -258,6 +275,7 @@ public: utime_t vol_poll_interval; /* interval between polling Vol mount */ DEVRES *device; /* pointer to Device Resource */ + VOLRES *vol; /* Pointer to Volume reservation item */ btimer_t *tid; /* timer id */ VOLUME_CAT_INFO VolCatInfo; /* Volume Catalog Information */ @@ -360,12 +378,22 @@ public: void clear_freespace_ok() { state &= ~ST_FREESPACE_OK; }; char *bstrerror(void) { return errmsg; }; char *print_errmsg() { return errmsg; }; - void lock() { P(m_mutex); } - void unlock() { V(m_mutex); } + +#ifdef SD_DEBUG_LOCK + void _r_dlock(const char *, int); /* in device.c */ + void _r_dunlock(const char *, int); /* in device.c */ + void _dlock(const char *, int); /* in device.c */ + void _dunlock(const char *, int); /* in device.c */ +#else + void r_dlock(); /* in device.c */ + void r_dunlock(); /* in device.c */ + void dlock() { P(m_mutex); } + void dunlock() { V(m_mutex); } +#endif void clear_volhdr(); /* in dev.c */ void block(int why); /* in dev.c */ - void unblock(); /* in dev.c */ + void unblock(bool locked=false); /* in dev.c */ void close(); /* in dev.c */ void close_part(DCR *dcr); /* in dev.c */ bool truncate(DCR *dcr); /* in dev.c */ @@ -411,7 +439,6 @@ private: void open_dvd_device(DCR *dcr, int omode); /* in dev.c */ }; -/* Note, these return int not bool! */ inline const char *DEVICE::strerror() const { return errmsg; } inline const char *DEVICE::archive_name() const { return dev_name; } inline const char *DEVICE::print_name() const { return prt_name; } @@ -431,6 +458,7 @@ public: DEVRES *device; /* pointer to device resource */ DEV_BLOCK *block; /* pointer to block */ DEV_RECORD *rec; /* pointer to record */ + pthread_t tid; /* Thread running this dcr */ int spool_fd; /* fd if spooling */ bool spool_data; /* set to spool data */ bool spooling; /* set when actually spooling */ @@ -470,7 +498,6 @@ public: dlink link; char *vol_name; DEVICE *dev; - DCR *dcr; }; diff --git a/bacula/src/stored/device.c b/bacula/src/stored/device.c index 2696ecc0b5..ba90a76c37 100644 --- a/bacula/src/stored/device.c +++ b/bacula/src/stored/device.c @@ -39,7 +39,7 @@ * Obviously, no zzz_dev() is allowed to call * a www_device() or everything falls apart. * - * Concerning the routines lock_device() and block_device() + * Concerning the routines dev->r_lock()() and block_device() * see the end of this module for details. In general, * blocking a device leaves it in a state where all threads * other than the current thread block when they attempt to @@ -59,6 +59,13 @@ #include "bacula.h" /* pull in global headers */ #include "stored.h" /* pull in Storage Deamon headers */ +#ifdef SD_DEBUG_LOCK +const int dbglvl = 0; +#else +const int dbglvl = 500; +#endif + + /* Forward referenced functions */ /* @@ -99,7 +106,7 @@ bool fixup_device_block_write_error(DCR *dcr) block_device(dev, BST_DOING_ACQUIRE); /* Unlock, but leave BLOCKED */ - dev->unlock(); + dev->dunlock(); bstrncpy(PrevVolName, dev->VolCatInfo.VolCatName, sizeof(PrevVolName)); bstrncpy(dev->VolHdr.PrevVolumeName, PrevVolName, sizeof(dev->VolHdr.PrevVolumeName)); @@ -116,11 +123,11 @@ bool fixup_device_block_write_error(DCR *dcr) if (!mount_next_write_volume(dcr, 1)) { free_block(label_blk); dcr->block = block; - dev->lock(); + dev->dlock(); unblock_device(dev); return false; /* device locked */ } - dev->lock(); /* lock again */ + dev->dlock(); /* lock again */ dev->VolCatInfo.VolCatJobs++; /* increment number of jobs on vol */ dir_update_volume_info(dcr, false); /* send Volume info to Director */ @@ -261,7 +268,7 @@ bool first_open_device(DCR *dcr) return false; } - lock_device(dev); + dev->r_dlock(); /* Defer opening files */ if (!dev->is_tape()) { @@ -284,7 +291,7 @@ bool first_open_device(DCR *dcr) Dmsg1(129, "open dev %s OK\n", dev->print_name()); bail_out: - dev->unlock(); + dev->dunlock(); return ok; } @@ -316,28 +323,21 @@ bool open_device(DCR *dcr) return true; } - /* - * When dev_blocked is set, all threads EXCEPT thread with id no_wait_id - * must wait. The no_wait_id thread is out obtaining a new volume - * and preparing the label. + * Find which JobId corresponds to the current thread */ -void _lock_device(const char *file, int line, DEVICE *dev) +uint32_t get_jobid_from_tid() { - int stat; - Dmsg3(500, "lock %d from %s:%d\n", dev->blocked(), file, line); - dev->lock(); - if (dev->blocked() && !pthread_equal(dev->no_wait_id, pthread_self())) { - dev->num_waiting++; /* indicate that I am waiting */ - while (dev->blocked()) { - if ((stat = pthread_cond_wait(&dev->wait, &dev->m_mutex)) != 0) { - dev->unlock(); - Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"), - strerror(stat)); - } + JCR *jcr; + uint32_t JobId = 0; + foreach_jcr(jcr) { + if (pthread_equal(jcr->my_thread_id, pthread_self())) { + JobId = (uint32_t)jcr->JobId; + break; } - dev->num_waiting--; /* no longer waiting */ } + endeach_jcr(jcr); + return JobId; } /* @@ -352,23 +352,91 @@ bool is_device_unmounted(DEVICE *dev) return stat; } -void _unlock_device(const char *file, int line, DEVICE *dev) +void DEVICE::_dlock(const char *file, int line) { - Dmsg2(500, "unlock from %s:%d\n", file, line); - dev->unlock(); + Dmsg4(sd_dbglvl, "dlock from %s:%d precnt=%d JobId=%u\n", file, line, + m_count, get_jobid_from_tid()); + /* Note, this *really* should be protected by a mutex, but + * since it is only debug code we don't worry too much. + */ + if (m_count > 0 && pthread_equal(m_pid, pthread_self())) { + Dmsg2(sd_dbglvl, "DEADLOCK !!!!!!!!!! from %s:%d\n", file, line); + } + P(m_mutex); + m_pid = pthread_self(); + m_count++; +} + +void DEVICE::_dunlock(const char *file, int line) +{ + m_count--; + Dmsg4(sd_dbglvl, "dunlock from %s:%d postcnt=%d JobId=%u\n", file, line, + m_count, get_jobid_from_tid()); + V(m_mutex); +} + +#ifdef SD_DEBUG_LOCK +void DEVICE::_r_dunlock(const char *file, int line) +{ + this->_dunlock(file, line); +} +#else +void DEVICE::r_dunlock() +{ + this->dunlock(); +} +#endif + + +/* + * This is a recursive lock that checks if the device is blocked. + * + * When blocked is set, all threads EXCEPT thread with id no_wait_id + * must wait. The no_wait_id thread is out obtaining a new volume + * and preparing the label. + */ +#ifdef SD_DEBUG_LOCK +void DEVICE::_r_dlock(const char *file, int line) +#else +void DEVICE::r_dlock() +#endif +{ + int stat; +#ifdef SD_DEBUG_LOCK + Dmsg4(dbglvl, "r_dlock blked=%s from %s:%d JobId=%u\n", this->print_blocked(), + file, line, get_jobid_from_tid()); +#else + Dmsg1dbglvl, "reclock blked=%s\n", this->print_blocked()); +#endif + this->dlock(); + if (this->blocked() && !pthread_equal(this->no_wait_id, pthread_self())) { + this->num_waiting++; /* indicate that I am waiting */ + while (this->blocked()) { + Dmsg3(dbglvl, "r_dlock blked=%s no_wait=%p me=%p\n", this->print_blocked(), + this->no_wait_id, pthread_self()); + if ((stat = pthread_cond_wait(&this->wait, &m_mutex)) != 0) { + berrno be; + this->dunlock(); + Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"), + be.strerror(stat)); + } + } + this->num_waiting--; /* no longer waiting */ + } } /* * Block all other threads from using the device * Device must already be locked. After this call, - * the device is blocked to any thread calling lock_device(), + * the device is blocked to any thread calling dev->r_lock(), * but the device is not locked (i.e. no P on device). Also, - * the current thread can do slip through the lock_device() + * the current thread can do slip through the dev->r_lock() * calls without blocking. */ void _block_device(const char *file, int line, DEVICE *dev, int state) { - Dmsg3(500, "block set %d from %s:%d\n", state, file, line); + Dmsg3(dbglvl, "block set %d from %s:%d\n", state, file, line); + ASSERT(dev->blocked() == BST_NOT_BLOCKED); dev->set_blocked(state); /* make other threads wait */ dev->no_wait_id = pthread_self(); /* allow us to continue */ @@ -376,10 +444,12 @@ void _block_device(const char *file, int line, DEVICE *dev, int state) /* * Unblock the device, and wake up anyone who went to sleep. + * Enter: device locked + * Exit: device locked */ void _unblock_device(const char *file, int line, DEVICE *dev) { - Dmsg3(500, "unblock %s from %s:%d\n", dev->print_blocked(), file, line); + Dmsg3(dbglvl, "unblock %s from %s:%d\n", dev->print_blocked(), file, line); ASSERT(dev->blocked()); dev->set_blocked(BST_NOT_BLOCKED); dev->no_wait_id = 0; @@ -395,15 +465,15 @@ void _unblock_device(const char *file, int line, DEVICE *dev) void _steal_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state) { - Dmsg3(400, "steal lock. old=%s from %s:%d\n", dev->print_blocked(), + Dmsg3(dbglvl, "steal lock. old=%s from %s:%d\n", dev->print_blocked(), file, line); hold->dev_blocked = dev->blocked(); hold->dev_prev_blocked = dev->dev_prev_blocked; hold->no_wait_id = dev->no_wait_id; dev->set_blocked(state); - Dmsg1(400, "steal lock. new=%s\n", dev->print_blocked()); + Dmsg1(dbglvl, "steal lock. new=%s\n", dev->print_blocked()); dev->no_wait_id = pthread_self(); - dev->unlock(); + dev->dunlock(); } /* @@ -412,13 +482,13 @@ void _steal_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t * */ void _give_back_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold) { - Dmsg3(400, "return lock. old=%s from %s:%d\n", + Dmsg3(dbglvl, "return lock. old=%s from %s:%d\n", dev->print_blocked(), file, line); - dev->lock(); + dev->dlock(); dev->set_blocked(hold->dev_blocked); dev->dev_prev_blocked = hold->dev_prev_blocked; dev->no_wait_id = hold->no_wait_id; - Dmsg1(400, "return lock. new=%s\n", dev->print_blocked()); + Dmsg1(dbglvl, "return lock. new=%s\n", dev->print_blocked()); if (dev->num_waiting > 0) { pthread_cond_broadcast(&dev->wait); /* wake them up */ } diff --git a/bacula/src/stored/dircmd.c b/bacula/src/stored/dircmd.c index ba5a2140c6..77ff63e058 100644 --- a/bacula/src/stored/dircmd.c +++ b/bacula/src/stored/dircmd.c @@ -34,7 +34,7 @@ * in job.c. * * N.B. in this file, in general we must use P(dev->mutex) rather - * than lock_device(dev) so that we can examine the blocked + * than dev->r_lock() so that we can examine the blocked * state rather than blocking ourselves because a Job * thread has the device blocked. In some "safe" cases, * we can do things to a blocked device. CAREFUL!!!! @@ -75,6 +75,7 @@ extern bool qstatus_cmd(JCR *jcr); /* Forward referenced functions */ static bool label_cmd(JCR *jcr); +static bool die_cmd(JCR *jcr); static bool relabel_cmd(JCR *jcr); static bool readlabel_cmd(JCR *jcr); static bool release_cmd(JCR *jcr); @@ -107,6 +108,7 @@ static struct s_cmds cmds[] = { {"autochanger", changer_cmd, 0}, {"bootstrap", bootstrap_cmd, 0}, {"cancel", cancel_cmd, 0}, + {".die", die_cmd, 0}, {"label", label_cmd, 0}, /* label a tape */ {"mount", mount_cmd, 0}, {"readlabel", readlabel_cmd, 0}, @@ -168,6 +170,7 @@ void *handle_connection_request(void *arg) */ Dmsg1(110, "Conn: %s", bs->msg); if (sscanf(bs->msg, "Hello Start Job %127s", name) == 1) { + Dmsg0(110, "Got a FD connection\n"); handle_filed_connection(bs, name); return NULL; } @@ -175,7 +178,7 @@ void *handle_connection_request(void *arg) /* * This is a connection from the Director, so setup a JCR */ - Dmsg0(110, "Start Dir Job\n"); + Dmsg0(110, "Got a DIR connection\n"); jcr = new_jcr(sizeof(JCR), stored_free_jcr); /* create Job Control Record */ jcr->dir_bsock = bs; /* save Director bsock */ jcr->dir_bsock->set_jcr(jcr); @@ -239,6 +242,24 @@ bail_out: return NULL; } + +/* + * Force SD to die, and hopefully dump itself. Turned on only + * in development version. + */ +static bool die_cmd(JCR *jcr) +{ +#ifdef DEVELOPER + JCR *djcr = NULL; + int a; + Pmsg0(000, "I have been requested to die ..."); + a = djcr->JobId; /* ref NULL pointer */ +#endif + return 0; +} + + + /* * Set debug level as requested by the Director * @@ -355,7 +376,7 @@ static bool do_label(JCR *jcr, int relabel) dcr = find_device(jcr, dev_name, drive); if (dcr) { dev = dcr->dev; - dev->lock(); /* Use P to avoid indefinite block */ + dev->dlock(); /* Use P to avoid indefinite block */ if (!dev->is_open()) { Dmsg1(400, "Can %slabel. Device is not open\n", relabel?"re":""); label_volume_if_ok(dcr, oldname, newname, poolname, slot, relabel); @@ -370,7 +391,7 @@ static bool do_label(JCR *jcr, int relabel) Dmsg0(400, "Can relabel. device not used\n"); label_volume_if_ok(dcr, oldname, newname, poolname, slot, relabel); } - dev->unlock(); + dev->dunlock(); free_dcr(dcr); jcr->dcr = NULL; } else { @@ -619,7 +640,7 @@ static bool mount_cmd(JCR *jcr) dcr = find_device(jcr, devname, drive); if (dcr) { dev = dcr->dev; - dev->lock(); /* Use P to avoid indefinite block */ + dev->dlock(); /* Use P to avoid indefinite block */ Dmsg1(100, "mount cmd blocked=%d\n", dev->blocked()); switch (dev->blocked()) { /* device blocked? */ case BST_WAITING_FOR_SYSOP: @@ -726,7 +747,7 @@ static bool mount_cmd(JCR *jcr) bnet_fsend(dir, _("3905 Bizarre wait state %d\n"), dev->blocked()); break; } - dev->unlock(); + dev->dunlock(); free_dcr(dcr); jcr->dcr = NULL; } else { @@ -755,7 +776,7 @@ static bool unmount_cmd(JCR *jcr) dcr = find_device(jcr, devname, drive); if (dcr) { dev = dcr->dev; - dev->lock(); /* Use P to avoid indefinite block */ + dev->dlock(); /* Use P to avoid indefinite block */ if (!dev->is_open()) { if (!dev->is_busy()) { unload_autochanger(dcr, -1); @@ -817,7 +838,7 @@ static bool unmount_cmd(JCR *jcr) dev->print_name()); } } - dev->unlock(); + dev->dunlock(); free_dcr(dcr); jcr->dcr = NULL; } else { @@ -851,7 +872,7 @@ static bool release_cmd(JCR *jcr) dcr = find_device(jcr, devname, drive); if (dcr) { dev = dcr->dev; - dev->lock(); /* Use P to avoid indefinite block */ + dev->dlock(); /* Use P to avoid indefinite block */ if (!dev->is_open()) { if (!dev->is_busy()) { unload_autochanger(dcr, -1); @@ -890,7 +911,7 @@ static bool release_cmd(JCR *jcr) bnet_fsend(dir, _("3022 Device %s released.\n"), dev->print_name()); } - dev->unlock(); + dev->dunlock(); free_dcr(dcr); jcr->dcr = NULL; } else { @@ -942,7 +963,7 @@ static bool changer_cmd(JCR *jcr) dcr = find_device(jcr, devname, -1); if (dcr) { dev = dcr->dev; - dev->lock(); /* Use P to avoid indefinite block */ + dev->dlock(); /* Use P to avoid indefinite block */ if (!dev->device->changer_res) { bnet_fsend(dir, _("3995 Device %s is not an autochanger.\n"), dev->print_name()); @@ -954,7 +975,7 @@ static bool changer_cmd(JCR *jcr) } else { /* device not being used */ autochanger_cmd(dcr, dir, cmd); } - dev->unlock(); + dev->dunlock(); free_dcr(dcr); jcr->dcr = NULL; } else { @@ -986,7 +1007,7 @@ static bool readlabel_cmd(JCR *jcr) dcr = find_device(jcr, devname, drive); if (dcr) { dev = dcr->dev; - dev->lock(); /* Use P to avoid indefinite block */ + dev->dlock(); /* Use P to avoid indefinite block */ if (!dev->is_open()) { read_volume_label(jcr, dev, Slot); dev->close(); @@ -998,7 +1019,7 @@ static bool readlabel_cmd(JCR *jcr) } else { /* device not being used */ read_volume_label(jcr, dev, Slot); } - dev->unlock(); + dev->dunlock(); free_dcr(dcr); jcr->dcr = NULL; } else { diff --git a/bacula/src/stored/label.c b/bacula/src/stored/label.c index 063375c548..d99ab2c744 100644 --- a/bacula/src/stored/label.c +++ b/bacula/src/stored/label.c @@ -1,16 +1,7 @@ -/* - * - * label.c Bacula routines to handle labels - * - * Kern Sibbald, MM - * - * - * Version $Id$ - */ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2006 Free Software Foundation Europe e.V. + 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. @@ -34,6 +25,15 @@ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ +/* + * + * label.c Bacula routines to handle labels + * + * Kern Sibbald, MM + * + * + * Version $Id$ + */ #include "bacula.h" /* pull in global headers */ #include "stored.h" /* pull in Storage Deamon headers */ @@ -213,7 +213,7 @@ int read_dev_volume_label(DCR *dcr) } dev->set_labeled(); /* set has Bacula label */ - new_volume(dcr, dev->VolHdr.VolumeName); + reserve_volume(dcr, dev->VolHdr.VolumeName); /* Compare Volume Names */ Dmsg2(30, "Compare Vol names: VolName=%s hdr=%s\n", VolName?VolName:"*", dev->VolHdr.VolumeName); @@ -393,7 +393,7 @@ bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName, if (debug_level >= 20) { dump_volume_label(dev); } - new_volume(dcr, VolName); + reserve_volume(dcr, VolName); dev->clear_append(); /* remove append since this is PRE_LABEL */ return true; diff --git a/bacula/src/stored/match_bsr.c b/bacula/src/stored/match_bsr.c index e613c3e741..ffa858209b 100644 --- a/bacula/src/stored/match_bsr.c +++ b/bacula/src/stored/match_bsr.c @@ -1,15 +1,7 @@ -/* - * Match Bootstrap Records (used for restores) against - * Volume Records - * - * Kern Sibbald, June MMII - * - * Version $Id$ - */ /* Bacula® - The Network Backup Solution - Copyright (C) 2002-2006 Free Software Foundation Europe e.V. + Copyright (C) 2002-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. @@ -33,6 +25,14 @@ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ +/* + * Match Bootstrap Records (used for restores) against + * Volume Records + * + * Kern Sibbald, June MMII + * + * Version $Id$ + */ /* * ***FIXME*** @@ -60,7 +60,7 @@ #include "lib/fnmatch.h" #endif -const int dbglevel = 10; +const int dbglevel = 500; /* Forward references */ static int match_volume(BSR *bsr, BSR_VOLUME *volume, VOLUME_LABEL *volrec, bool done); diff --git a/bacula/src/stored/protos.h b/bacula/src/stored/protos.h index fdeeada249..b26eb2a5c9 100644 --- a/bacula/src/stored/protos.h +++ b/bacula/src/stored/protos.h @@ -1,8 +1,3 @@ -/* - * Protypes for stored -- Kern Sibbald MM - * - * Version $Id$ - */ /* Bacula® - The Network Backup Solution @@ -30,6 +25,11 @@ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ +/* + * Protypes for stored -- Kern Sibbald MM + * + * Version $Id$ + */ /* From stored.c */ uint32_t new_VolSessionId(); @@ -135,6 +135,7 @@ void _give_back_device_lock(const char *file, int line, DEVICE *dev, bsteal_ void set_new_volume_parameters(DCR *dcr); void set_new_file_parameters(DCR *dcr); bool is_device_unmounted(DEVICE *dev); +uint32_t get_jobid_from_tid(); /* From dircmd.c */ void *handle_connection_request(void *arg); @@ -210,13 +211,14 @@ bool read_records(DCR *dcr, /* From reserve.c */ void init_reservations_lock(); void term_reservations_lock(); -void lock_reservations(); -void unlock_reservations(); +void _lock_reservations(); +void _unlock_reservations(); void release_volume(DCR *dcr); -VOLRES *new_volume(DCR *dcr, const char *VolumeName); +VOLRES *reserve_volume(DCR *dcr, const char *VolumeName); VOLRES *find_volume(const char *VolumeName); bool free_volume(DEVICE *dev); -void free_unused_volume(DCR *dcr); +void unreserve_device(DCR *dcr); +bool volume_unused(DCR *dcr); void create_volume_list(); void free_volume_list(); void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg); @@ -226,6 +228,25 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx); int search_res_for_device(RCTX &rctx); void release_msgs(JCR *jcr); +extern int reservations_lock_count; + +#ifdef SD_DEBUG_LOCK +#define lock_reservations() \ + do { Dmsg4(sd_dbglvl, "lock_reservations at %s:%d precnt=%d JobId=%u\n", \ + __FILE__, __LINE__, \ + reservations_lock_count, get_jobid_from_tid()); \ + _lock_reservations(); } while (0) +#define unlock_reservations() \ + do { Dmsg4(sd_dbglvl, "unlock_reservations at %s:%d precnt=%d JobId=%u\n", \ + __FILE__, __LINE__, \ + reservations_lock_count, get_jobid_from_tid()); \ + _unlock_reservations(); } while (0) +#else +#define lock_reservations() _lock_reservations() +#define unlock_reservations() _unlock_reservations() +#endif + + /* From spool.c */ bool begin_data_spool (DCR *dcr); diff --git a/bacula/src/stored/reserve.c b/bacula/src/stored/reserve.c index 79d299dd29..bc590e210a 100644 --- a/bacula/src/stored/reserve.c +++ b/bacula/src/stored/reserve.c @@ -96,10 +96,13 @@ void term_reservations_lock() rwl_destroy(&reservation_lock); } +int reservations_lock_count = 0; + /* This applies to a drive and to Volumes */ -void lock_reservations() +void _lock_reservations() { int errstat; + reservations_lock_count++; if ((errstat=rwl_writelock(&reservation_lock)) != 0) { berrno be; Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n", @@ -107,9 +110,10 @@ void lock_reservations() } } -void unlock_reservations() +void _unlock_reservations() { int errstat; + reservations_lock_count--; if ((errstat=rwl_writeunlock(&reservation_lock)) != 0) { berrno be; Emsg2(M_ABORT, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n", @@ -117,20 +121,156 @@ void unlock_reservations() } } +/* + * List Volumes -- this should be moved to status.c + */ +enum { + debug_lock = true, + debug_nolock = false +}; + +static void debug_list_volumes(const char *imsg, bool do_lock) +{ + VOLRES *vol; + POOL_MEM msg(PM_MESSAGE); + int count = 0; + DEVICE *dev = NULL; + + if (do_lock) P(vol_list_lock); + for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) { + if (vol->dev) { + Mmsg(msg, "List from %s: %s at %p on device %s\n", imsg, + vol->vol_name, vol->vol_name, vol->dev->print_name()); + } else { + Mmsg(msg, "List from %s: %s at %p no dev\n", imsg, vol->vol_name, vol->vol_name); + } + Dmsg1(100, "%s", msg.c_str()); + count++; + } + + for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) { + if (vol->dev == dev) { + Dmsg0(000, "Two Volumes on same device.\n"); + ASSERT(1); + dev = vol->dev; + } + } + + Dmsg2(100, "List from %s: %d volumes\n", imsg, count); + if (do_lock) V(vol_list_lock); +} + + +/* + * List Volumes -- this should be moved to status.c + */ +void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg) +{ + VOLRES *vol; + POOL_MEM msg(PM_MESSAGE); + int len; + + P(vol_list_lock); + for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) { + if (vol->dev) { + len = Mmsg(msg, "%s on device %s\n", vol->vol_name, vol->dev->print_name()); + sendit(msg.c_str(), len, arg); + } else { + len = Mmsg(msg, "%s no dev\n", vol->vol_name); + sendit(msg.c_str(), len, arg); + } + } + V(vol_list_lock); +} + +/* + * Create a Volume item to put in the Volume list + * Ensure that the device points to it. + */ +static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName) +{ + VOLRES *vol; + vol = (VOLRES *)malloc(sizeof(VOLRES)); + memset(vol, 0, sizeof(VOLRES)); + vol->vol_name = bstrdup(VolumeName); + vol->dev = dcr->dev; + Dmsg4(100, "New Vol=%s at %p dev=%s JobId=%u\n", VolumeName, vol->vol_name, + vol->dev->print_name(), (int)dcr->jcr->JobId); + return vol; +} + +static void free_vol_item(VOLRES *vol) +{ + free(vol->vol_name); + free(vol); +} + /* * Put a new Volume entry in the Volume list. This * effectively reserves the volume so that it will * not be mounted again. + * + * If the device has any current volume associated with it, + * and it is a different Volume, and the device is not busy, + * we release the old Volume item and insert the new one. + * + * It is assumed that the device is free and locked so that + * we can change the device structure. + * + * Some details of the Volume list handling: + * + * 1. The Volume list entry must be attached to the drive (rather than + * attached to a job as it currently is. I.e. the drive that "owns" + * the volume (reserved, in use, mounted) + * must point to the volume (still to be maintained in a list). + * + * 2. The Volume is entered in the list when a drive is reserved. + * + * 3. When a drive is in use, the device code must appropriately update the + * volume name as it changes (currently the list is static -- an entry is + * removed when the Volume is no longer reserved, in use or mounted). + * The new code must keep the same list entry as long as the drive + * has any volume associated with it but the volume name in the list + * must be updated when the drive has a different volume mounted. + * + * 4. A job that has reserved a volume, can un-reserve the volume, and if the + * volume is not mounted, and not reserved, and not in use, it will be + * removed from the list. + * + * 5. If a job wants to reserve a drive with a different Volume from the one on + * the drive, it can re-use the drive for the new Volume. + * + * 6. If a job wants a Volume that is in a different drive, it can either use the + * other drive or take the volume, only if the other drive is not in use or + * not reserved. + * + * One nice aspect of this is that the reserve use count and the writer use count + * already exist and are correctly programmed and will need no changes -- use + * counts are always very tricky. + * + * The old code had a concept of "reserving" a Volume, but it needs to be changed + * to reserving and using a drive. A volume is must be attached to (owned by) a + * drive and can move from drive to drive or be unused given certain specific + * conditions of the drive. The key is that the drive must "own" the Volume. + * The old code has the job (dcr) owning the volume (more or less). The job is + * to change the insertion and removal of the volumes from the list to be based + * on the drive rather than the job. The new logic described above needs to be + * reviewed a couple more times for completeness and correctness. Then I can + * program it. + * * Return: VOLRES entry on success - * NULL if the Volume is already in the list + * NULL error */ -VOLRES *new_volume(DCR *dcr, const char *VolumeName) +VOLRES *reserve_volume(DCR *dcr, const char *VolumeName) { VOLRES *vol, *nvol; + DEVICE *dev = dcr->dev; + + ASSERT(dev != NULL); - Dmsg1(400, "new_volume %s\n", VolumeName); + Dmsg1(100, "reserve_volume %s\n", VolumeName); /* * We lock the reservations system here to ensure * when adding a new volume that no newly scheduled @@ -138,62 +278,67 @@ VOLRES *new_volume(DCR *dcr, const char *VolumeName) */ lock_reservations(); P(vol_list_lock); - /* - * First, if this dcr already is attached to any device, - * remove any old volumes attached to this device as they - * are no longer used (there should at max be one). + debug_list_volumes("begin reserve_volume", debug_nolock); + /* + * First, remove any old volume attached to this device as it + * is no longer used. */ - if (dcr->dev) { -again: - foreach_dlist(vol, vol_list) { - if (vol && vol->dev == dcr->dev) { - vol_list->remove(vol); - /* - * Make sure we don't remove the current volume we are inserting - * because it was probably inserted by another job. - */ - if (vol->vol_name && strcmp(vol->vol_name, VolumeName) != 0) { - Dmsg1(100, "new_vol free vol=%s\n", vol->vol_name); - free(vol->vol_name); - } - free(vol); - goto again; - } + if (dev->vol) { + vol = dev->vol; + /* + * Make sure we don't remove the current volume we are inserting + * because it was probably inserted by another job. + */ + if (strcmp(vol->vol_name, VolumeName) == 0) { + goto get_out; /* Volume already on this device */ + } else { + Dmsg3(100, "reserve_vol free vol=%s at %p JobId=%u\n", vol->vol_name, + vol->vol_name, (int)dcr->jcr->JobId); + debug_list_volumes("reserve_vol free", debug_nolock); + vol_list->remove(vol); + free_vol_item(vol); } } - vol = (VOLRES *)malloc(sizeof(VOLRES)); - memset(vol, 0, sizeof(VOLRES)); - vol->vol_name = bstrdup(VolumeName); - vol->dev = dcr->dev; - vol->dcr = dcr; - Dmsg2(100, "New Vol=%s dev=%s\n", VolumeName, dcr->dev->print_name()); + + /* Create a new Volume entry */ + nvol = new_vol_item(dcr, VolumeName); + /* * Now try to insert the new Volume */ - nvol = (VOLRES *)vol_list->binary_insert(vol, my_compare); - if (nvol != vol) { - Dmsg2(100, "Found vol=%s same dcr=%d\n", nvol->vol_name, dcr==nvol->dcr); + vol = (VOLRES *)vol_list->binary_insert(nvol, my_compare); + if (vol != nvol) { + Dmsg2(100, "Found vol=%s dev-same=%d\n", vol->vol_name, dev==vol->dev); /* - * At this point, a Volume with this name already is in the - * list, free our temp structure + * At this point, a Volume with this name already is in the list, + * so we simply release our new Volume entry. Note, this should + * only happen if we are moving the volume from one drive to another. */ - free(vol->vol_name); - free(vol); - vol = NULL; - if (dcr->dev) { - DEVICE *dev = nvol->dev; - /* ***FIXME*** don't we need a mutex here? */ + Dmsg3(100, "reserve_vol free-tmp vol=%s at %p JobId=%u\n", vol->vol_name, + vol->vol_name, (int)dcr->jcr->JobId); + free_vol_item(nvol); + + /* Check if we are trying to use the Volume on a different drive */ + if (dev != vol->dev) { + /* Caller wants to switch Volume to another device */ if (!dev->is_busy()) { + /* OK to move it */ Dmsg3(100, "Swap vol=%s from dev=%s to %s\n", VolumeName, - dev->print_name(), dcr->dev->print_name()); - nvol->dev = dcr->dev; + dev->print_name(), dev->print_name()); + vol->dev = dev; dev->VolHdr.VolumeName[0] = 0; } else { + vol = NULL; /* device busy */ Dmsg3(100, "Logic ERROR!!!! could not swap vol=%s from dev=%s to %s\n", VolumeName, dev->print_name(), dcr->dev->print_name()); + ASSERT(1); /* blow up!!! */ } } } + dev->vol = vol; + +get_out: + debug_list_volumes("end new volume", debug_nolock); V(vol_list_lock); unlock_reservations(); return vol; @@ -213,113 +358,88 @@ VOLRES *find_volume(const char *VolumeName) vol.vol_name = bstrdup(VolumeName); fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare); free(vol.vol_name); - V(vol_list_lock); Dmsg2(100, "find_vol=%s found=%d\n", VolumeName, fvol!=NULL); + debug_list_volumes("find_volume", debug_nolock); + V(vol_list_lock); return fvol; } +/* + * Remove any reservation from a drive and tell the system + * that the volume is unused at least by us. + */ +void unreserve_device(DCR *dcr) +{ + DEVICE *dev = dcr->dev; + dev->dlock(); + if (dcr->reserved_device) { + dcr->reserved_device = false; + dev->reserved_device--; + Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name()); + dcr->reserved_device = false; + /* If we set read mode in reserving, remove it */ + if (dev->can_read()) { + dev->clear_read(); + } + if (dev->num_writers < 0) { + Jmsg1(dcr->jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers); + dev->num_writers = 0; + } + } + + volume_unused(dcr); + dev->dunlock(); +} + /* - * Free a Volume from the Volume list + * Free a Volume from the Volume list if it is no longer used * * Returns: true if the Volume found and removed from the list - * false if the Volume is not in the list + * false if the Volume is not in the list or is in use */ -bool free_volume(DEVICE *dev) +bool volume_unused(DCR *dcr) { - VOLRES vol, *fvol; + DEVICE *dev = dcr->dev; - if (dev->VolHdr.VolumeName[0] == 0) { + if (dev->vol == NULL) { + Dmsg1(100, " unreserve_volume: no vol on %s\n", dev->print_name()); + debug_list_volumes("null return unreserve_volume", debug_lock); return false; } - P(vol_list_lock); -#ifdef xxx - Dmsg1(100, "free_volume: no vol on dev %s\n", dev->print_name()); - /* - * Our device has no VolumeName listed, but - * search the list for any Volume attached to - * this device and remove it. - */ - foreach_dlist(fvol, vol_list) { - if (fvol && fvol->dev == dev) { - vol_list->remove(fvol); - if (fvol->vol_name) { - Dmsg2(100, "free_volume %s dev=%s\n", fvol->vol_name, dev->print_name()); - free(fvol->vol_name); - } - free(fvol); - break; - } - } - goto bail_out; - } -#endif - Dmsg1(400, "free_volume %s\n", dev->VolHdr.VolumeName); - vol.vol_name = bstrdup(dev->VolHdr.VolumeName); - fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare); - if (fvol) { - vol_list->remove(fvol); - Dmsg2(100, "free_volume %s dev=%s\n", fvol->vol_name, dev->print_name()); - free(fvol->vol_name); - free(fvol); + if (dev->is_busy()) { + Dmsg1(100, "unreserve_volume: dev is busy %s\n", dev->print_name()); + debug_list_volumes("dev busy return unreserve_volume", debug_lock); + return false; } - free(vol.vol_name); -// dev->VolHdr.VolumeName[0] = 0; -//bail_out: - V(vol_list_lock); - return fvol != NULL; -} -/* Free volume reserved by this dcr but not attached to a dev */ -void free_unused_volume(DCR *dcr) -{ - VOLRES *vol; - - P(vol_list_lock); - for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) { - /* - * Releease this volume, but only if we inserted it (same dcr) and - * it is not attached to a device or the Volume in the device is - * different. Requiring a different name for the Volume in the - * device ensures that we don't free a volume in use. - */ - if (vol->dcr == dcr && (vol->dev == NULL || - strcmp(vol->vol_name, vol->dev->VolHdr.VolumeName) != 0)) { - vol_list->remove(vol); - Dmsg1(100, "free_unused_volume %s\n", vol->vol_name); - free(vol->vol_name); - free(vol); - break; - } - } - V(vol_list_lock); + return free_volume(dev); } /* - * List Volumes -- this should be moved to status.c + * Unconditionally release the volume */ -void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg) +bool free_volume(DEVICE *dev) { VOLRES *vol; - char *msg; - int len; - msg = (char *)get_pool_memory(PM_MESSAGE); - - P(vol_list_lock); - for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) { - if (vol->dev) { - len = Mmsg(msg, "%s on device %s\n", vol->vol_name, vol->dev->print_name()); - sendit(msg, len, arg); - } else { - len = Mmsg(msg, "%s\n", vol->vol_name); - sendit(msg, len, arg); - } + if (dev->vol == NULL) { + Dmsg1(100, "No vol on dev %s\n", dev->print_name()); + return false; } + P(vol_list_lock); + vol = dev->vol; + dev->vol = NULL; + Dmsg1(100, "free_volume %s\n", vol->vol_name); + vol_list->remove(vol); + Dmsg3(100, "free_volume %s at %p dev=%s\n", vol->vol_name, vol->vol_name, + dev->print_name()); + free_vol_item(vol); + debug_list_volumes("free_volume", debug_nolock); V(vol_list_lock); - - free_pool_memory(msg); + return vol != NULL; } + /* Create the Volume list */ void create_volume_list() @@ -339,8 +459,9 @@ void free_volume_list() } P(vol_list_lock); for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) { - Dmsg3(100, "Unreleased Volume=%s dcr=0x%x dev=0x%x\n", vol->vol_name, - vol->dcr, vol->dev); + Dmsg2(100, "Unreleased Volume=%s dev=%p\n", vol->vol_name, vol->dev); + free(vol->vol_name); + vol->vol_name = NULL; } delete vol_list; vol_list = NULL; @@ -354,10 +475,8 @@ bool is_volume_in_use(DCR *dcr) Dmsg1(100, "Vol=%s not in use.\n", dcr->VolumeName); return false; /* vol not in list */ } - if (!vol->dev) { /* vol not attached to device */ - Dmsg1(100, "Vol=%s has no dev.\n", dcr->VolumeName); - return false; - } + ASSERT(vol->dev != NULL); + if (dcr->dev == vol->dev) { /* same device OK */ Dmsg1(100, "Vol=%s on same dev.\n", dcr->VolumeName); return false; @@ -479,6 +598,7 @@ static bool use_storage_cmd(JCR *jcr) } rctx.suitable_device = false; rctx.have_volume = false; + rctx.VolumeName[0] = 0; rctx.any_drive = false; if (!jcr->PreferMountedVols) { /* Look for unused drives in autochangers */ @@ -625,7 +745,7 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) ok = true; break; } else if (stat == 0) { /* device busy */ - Dmsg1(110, "Suitable device found=%s, not used: busy\n", device_name); + Dmsg1(110, "Suitable device=%s, busy: not use\n", device_name); } else { /* otherwise error */ Dmsg0(110, "No suitable device found.\n"); @@ -759,14 +879,16 @@ static int reserve_device(RCTX &rctx) bstrncpy(dcr->media_type, rctx.store->media_type, name_len); bstrncpy(dcr->dev_name, rctx.device_name, name_len); if (rctx.store->append == SD_APPEND) { - if (rctx.exact_match && !rctx.have_volume) { + Dmsg2(100, "have_vol=%d vol=%s\n", rctx.have_volume, rctx.VolumeName); + if (!rctx.have_volume) { dcr->any_volume = true; if (dir_find_next_appendable_volume(dcr)) { bstrncpy(rctx.VolumeName, dcr->VolumeName, sizeof(rctx.VolumeName)); - Dmsg2(100, "JobId=%u looking for Volume=%s\n", rctx.jcr->JobId, rctx.VolumeName); + Dmsg2(100, "JobId=%u looking for Volume=%s\n", (int)rctx.jcr->JobId, rctx.VolumeName); rctx.have_volume = true; } else { Dmsg0(100, "No next volume found\n"); + rctx.have_volume = false; rctx.VolumeName[0] = 0; } } @@ -787,6 +909,7 @@ static int reserve_device(RCTX &rctx) } } if (!ok) { + rctx.have_volume = false; free_dcr(dcr); Dmsg0(110, "Not OK.\n"); return 0; @@ -810,7 +933,7 @@ static bool reserve_device_for_read(DCR *dcr) /* Get locks in correct order */ unlock_reservations(); - dev->lock(); + dev->dlock(); lock_reservations(); if (is_device_unmounted(dev)) { @@ -839,7 +962,7 @@ static bool reserve_device_for_read(DCR *dcr) dcr->reserved_device = true; bail_out: - dev->unlock(); + dev->dunlock(); return ok; } @@ -869,7 +992,7 @@ static bool reserve_device_for_append(DCR *dcr, RCTX &rctx) /* Get locks in correct order */ unlock_reservations(); - dev->lock(); + dev->dlock(); lock_reservations(); /* If device is being read, we cannot write it */ @@ -905,7 +1028,7 @@ static bool reserve_device_for_append(DCR *dcr, RCTX &rctx) ok = true; bail_out: - dev->unlock(); + dev->dunlock(); return ok; } diff --git a/bacula/src/stored/spool.c b/bacula/src/stored/spool.c index aaaed4d087..cb2b16ea4d 100644 --- a/bacula/src/stored/spool.c +++ b/bacula/src/stored/spool.c @@ -234,7 +234,7 @@ static bool despool_data(DCR *dcr, bool commit) } dcr->despool_wait = true; dcr->spooling = false; - lock_device(dcr->dev); + dcr->dev->r_dlock(); dcr->despool_wait = false; dcr->despooling = true; dcr->dev_locked = true; @@ -330,7 +330,7 @@ static bool despool_data(DCR *dcr, bool commit) /* If doing a commit, leave the device locked -- unlocked in release_device() */ if (!commit) { dcr->dev_locked = false; - dcr->dev->unlock(); + dcr->dev->dunlock(); } return ok; } diff --git a/bacula/src/stored/stored.h b/bacula/src/stored/stored.h index 0c458244cf..a530d9a2a5 100644 --- a/bacula/src/stored/stored.h +++ b/bacula/src/stored/stored.h @@ -1,8 +1,3 @@ -/* - * Storage daemon specific defines and includes - * - * Version $Id$ - */ /* Bacula® - The Network Backup Solution @@ -30,12 +25,21 @@ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ +/* + * Storage daemon specific defines and includes + * + * Version $Id$ + */ #ifndef __STORED_H_ #define __STORED_H_ #define STORAGE_DAEMON 1 +/* Set to debug mutexes */ +#define SD_DEBUG_LOCK +const int sd_dbglvl = 100; + #ifdef HAVE_MTIO_H #include #else diff --git a/bacula/src/stored/wait.c b/bacula/src/stored/wait.c index 6251a9b4ab..cd034ef1f5 100644 --- a/bacula/src/stored/wait.c +++ b/bacula/src/stored/wait.c @@ -61,7 +61,7 @@ int wait_for_sysop(DCR *dcr) DEVICE *dev = dcr->dev; JCR *jcr = dcr->jcr; - dev->lock(); + dev->dlock(); Dmsg1(100, "Enter blocked=%s\n", dev->print_blocked()); unmounted = is_device_unmounted(dev); @@ -184,7 +184,7 @@ int wait_for_sysop(DCR *dcr) Dmsg1(400, "set %s\n", dev->print_blocked()); } Dmsg1(400, "Exit blocked=%s\n", dev->print_blocked()); - dev->unlock(); + dev->dunlock(); return stat; } diff --git a/bacula/src/version.h b/bacula/src/version.h index d94769d542..408db2e472 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -3,9 +3,9 @@ */ #undef VERSION -#define VERSION "2.1.7" -#define BDATE "04 April 2007" -#define LSMDATE "04Apr07" +#define VERSION "2.1.8" +#define BDATE "11 April 2007" +#define LSMDATE "11Apr07" #define PROG_COPYRIGHT "Copyright (C) %d-2007 Free Software Foundation Europe e.V.\n" #define BYEAR "2007" /* year for copyright messages in progs */ diff --git a/bacula/technotes-2.1 b/bacula/technotes-2.1 index 06c694912d..c127e47370 100644 --- a/bacula/technotes-2.1 +++ b/bacula/technotes-2.1 @@ -1,6 +1,17 @@ Technical notes on version 2.1 General: +11Apr07 +kes Add exec external-command [wait-seconds] to bconsole. This + executes the external-command. Note! normally external-command + should be enclosed in double quotes. +kes Turn the .die command on only if DEVELOPER is defined -- i.e. + it should normally be off in a production system. +10Apr07 +kes Implement die command for SD so that we can force it to dump. +kes Implement SD lock debug code. +kes Implement new algorithm for keeping Volume list in SD. It + is now owned by the device. 04Apr07 kes Implement new code for freeing in use volumes that should resolve if not all, some of the problems of multiple drive -- 2.39.5