;;
slots)
- ${MTX} -f $CHANGER status slot | wc -l | bc
+ ${MTX} -f $CHANGER status slot | wc -l
;;
esac
Kern's ToDo List
- 07 January 2006
+ 11 January 2006
Major development:
Project Developer
- %d and %v only valid on Director, not for ClientRunBefore/After.
Priority:
-- Implement status that shows why a job is being held in reserve, or
- rather why none of the drives are suitable.
- Implement a way to disable a drive (so you can use the second
drive of an autochanger, and the first one will not be used or
even defined).
- Queue warning/error messages during restore so that they
are reported at the end of the report rather than being
hidden in the file listing ...
-- A Volume taken from Scratch should take on the retention period
- of the new pool.
-- Correct doc for Maximum Changer Wait (and others) accepting only
- integers.
- Fix Maximum Changer Wait (and others) to accept qualifiers.
- Look at -D_FORTIFY_SOURCE=2
- Add Win32 FileSet definition somewhere
for ( ; jcr; (jcr=jcr_walk_next(jcr)) )
...
jcr_walk_end(jcr);
-
+- A Volume taken from Scratch should take on the retention period
+ of the new pool.
+- Correct doc for Maximum Changer Wait (and others) accepting only
+ integers.
+- Implement status that shows why a job is being held in reserve, or
+ rather why none of the drives are suitable.
General:
-1.38.4:
+Release 1.38.4:
+15Jan06
+- Add periodic (every 24 hours) garbage collection of memory
+ pool by releasing free buffers.
+14Jan06
+- Correct bug counting sized (for display only) in smartall.c
+- Print FD mempool stats if debug > 0 rather than 5.
+12Jan06
+- Make db_lock() mutex error fail the job rather than abort
+ Bacula. Canceling the job caused the mutex to fail.
+- Correct bug in alist.c that re-allocated the list if the
+ number of items goes to zero.
+- Move the reservation system thread locking to the top level
+ so that one job at a time tries all possible drives before
+ waiting.
+- Implement a reservation 'fail' message queue that is built
+ and destroyed on each pass through the reservation system.
+ These messages are displayed in a 'Jobs waiting to reserve
+ a drive' list during a 'status storage='. Note, multiple
+ messages will generally print for each JobId because they
+ represent the different problems with either the same drive
+ or different drives. If this output proves too confusing
+ of voluminous, I will display it only when debug level 1
+ or greater is enabled in the SD.
+11Jan06
+- Add enable/disable job=<job-name>. This command prevents
+ the specified job from being scheduled. Even when disabled,
+ the job can be manually started from the console.
+- During 'update slots' clear all InChanger flags where the
+ StorageId is zero (old Media records).
+
+Beta release 1.38.4:
+09Jan06
+- Fix autochanger code to strip leading spaces from returned
+ slots number. Remove bc from chio-changer.
+- Back port a bit of 1.39 crypto code to reduce diffs.
+- Fix first call to autochanger that missed close()ing the
+ drive. Put close() just before each run_program(). Fixes
+ Arno's changer bug.
07Jan06
- Add PoolId to Job record when updating it at job start time.
06Jan06
volume so that it can handle multiple returns from the wait
code.
- Modify the wait code to permit multiple returns.
-- Return a zero when "autochanger drives" is called and
+- Return a zero when 'autochanger drives' is called and
it is not an autochanger.
- Make rewind_dev() a method taking a DCR as an argument.
This permits closing and reopening the drive if the
04Dec05
- Apply days keyword patch from Alexander.Bergolth at wu-wien.ac.at
If this patch is applied, the number of days can be specified with
- "list nextvol days=xx"
+ 'list nextvol days=xx'
or
- "status dir days=xx"
+ 'status dir days=xx'
My use case is to be able to preview the next scheduled job (and the
next tape to be used) on fridays if there are no scheduled jobs during
the weekend.
- Add Solaris ACL detection in configure.in as supplied by
Attila Fulop.
12Nov05
-- Implement "autochanger drives" protocol so that Dir knows
+- Implement 'autochanger drives' protocol so that Dir knows
how many drives an autochanger has.
- Do not request drive number in label, ... if only one drive.
- Turn off debug code.
* Version $Id$
*/
/*
- Copyright (C) 2000-2005 Kern Sibbald
+ Copyright (C) 2000-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
#define STREAM_NONE 0 /* Reserved Non-Stream */
#define STREAM_UNIX_ATTRIBUTES 1 /* Generic Unix attributes */
#define STREAM_FILE_DATA 2 /* Standard uncompressed data */
+#define STREAM_MD5_SIGNATURE 3 /* deprecated */
#define STREAM_MD5_DIGEST 3 /* MD5 digest for the file */
#define STREAM_GZIP_DATA 4 /* GZip compressed file data */
/* Extended Unix attributes with Win32 Extended data. Deprecated. */
#define STREAM_SPARSE_GZIP_DATA 7
#define STREAM_PROGRAM_NAMES 8 /* program names for program data */
#define STREAM_PROGRAM_DATA 9 /* Data needing program */
+#define STREAM_SHA1_SIGNATURE 10 /* deprecated */
#define STREAM_SHA1_DIGEST 10 /* SHA1 digest for the file */
#define STREAM_WIN32_DATA 11 /* Win32 BackupRead data */
#define STREAM_WIN32_GZIP_DATA 12 /* Gzipped Win32 BackupRead data */
/*
- Copyright (C) 2000-2005 Kern Sibbald
+ Copyright (C) 2000-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
int errstat;
if ((errstat=rwl_writelock(&mdb->lock)) != 0) {
berrno be;
- e_msg(file, line, M_ABORT, 0, "rwl_writelock failure. ERR=%s\n",
- be.strerror(errstat));
+ e_msg(file, line, M_FATAL, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
+ errstat, be.strerror(errstat));
}
}
int errstat;
if ((errstat=rwl_writeunlock(&mdb->lock)) != 0) {
berrno be;
- e_msg(file, line, M_ABORT, 0, "rwl_writeunlock failure. ERR=%s\n",
- be.strerror(errstat));
+ e_msg(file, line, M_FATAL, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n",
+ errstat, be.strerror(errstat));
}
}
}
for ( ; run; run=run->next) {
/*
- * Find runs in next 24 hours
+ * Find runs in next 24 hours. Day 0 is today, so if
+ * ndays=1, look at today and tomorrow.
*/
for (day = 0; day <= ndays; day++) {
future = now + (day * 60 * 60 * 24);
if (configfile != NULL) {
free(configfile);
}
- if (debug_level > 5) {
+ if (debug_level > 0) {
print_memory_pool_stats();
}
free_config_resources();
if ((me->pki_encrypt || me->pki_sign) && !me->pki_keypairfile) {
Emsg2(M_FATAL, 0, _("\"PKI Key Pair\" must be defined for File"
" daemon \"%s\" in %s if either \"PKI Sign\" or"
- " \"PKI Encrypt\" are enabled.\n"), me->hdr.name, configfile);
+ " \"PKI Encrypt\" are enabled.\n"), me->hdr.name, configfile);
OK = false;
}
me->pki_signers->append(crypto_keypair_dup(me->pki_keypair));
/* If additional trusted keys have been specified, load them up */
- if (me->pki_trustedkeys) {
+ if (me->pki_trustedkeys) {
foreach_alist(filepath, me->pki_trustedkeys) {
X509_KEYPAIR *keypair;
}
}
}
- }
+ }
if (me->pki_encrypt) {
/*
bool spool_data; /* set to spool data */
int CurVol; /* Current Volume count */
DIRRES* director; /* Director resource */
- alist *dirstore;
+ alist *dirstore; /* list of storage devices sent by DIR */
+ alist *reserve_msgs; /* reserve fail messages */
bool write_part_after_job; /* Set to write part after job */
bool PreferMountedVols; /* Prefer mounted vols rather than new */
*
*/
/*
- Copyright (C) 2003-2005 Kern Sibbald
+ Copyright (C) 2003-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
*/
void alist::grow_list()
{
- if (num_items == 0) {
+ if (items == NULL) {
if (num_grow == 0) {
num_grow = 1; /* default if not initialized */
}
* Kern Sibbald, June MMIII
*/
/*
- Copyright (C) 2003-2005 Kern Sibbald
+ Copyright (C) 2003-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
#ifdef the_easy_way
#define foreach_alist(var, list) \
- for(((void*)(var))=(list)->first(); (var); ((void*)(var))=(list)->next(var)); )
+ for(((void*)(var))=(list)->first(); (var); ((void*)(var))=(list)->next()); )
#endif
/* Use it as a stack, pushing and poping from the end */
void push(void *item) { append(item); };
- void pop() { num_items?NULL:remove(num_items-1); };
+ void *pop() { return remove(num_items-1); };
};
inline void * alist::operator [](int index) const {
}
free_common_jcr(jcr);
close_msg(NULL); /* flush any daemon messages */
+ garbage_collect_memory_pool();
Dmsg0(3400, "Exit free_jcr\n");
}
*/
/*
- Copyright (C) 2000-2004 Kern Sibbald
+ Copyright (C) 2000-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
+ modify it under the terms of the GNU General Public License
+ version 2 as amended with additional clauses defined in the
+ file LICENSE in the main source directory.
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., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ the file LICENSE for additional details.
*/
#endif /* SMARTALLOC */
+/*
+ * Clean up memory pool periodically
+ *
+ */
+static time_t last_garbage_collection = 0;
+const int garbage_interval = 24 * 60 * 60; /* garbage collect every 24 hours */
+
+void garbage_collect_memory_pool()
+{
+ time_t now;
+
+ Dmsg0(200, "garbage collect memory pool\n");
+ P(mutex);
+ if (last_garbage_collection == 0) {
+ last_garbage_collection = time(NULL);
+ V(mutex);
+ return;
+ }
+ now = time(NULL);
+ if (now >= last_garbage_collection + garbage_interval) {
+ last_garbage_collection = now;
+ V(mutex);
+ close_memory_pool();
+ } else {
+ V(mutex);
+ }
+}
void close_memory_pool()
{
struct abufhead *buf, *next;
+ int count = 0;
+ uint64_t bytes = 0;
+ char ed1[50];
sm_check(__FILE__, __LINE__, false);
P(mutex);
buf = pool_ctl[i].free_buf;
while (buf) {
next = buf->next;
+ count++;
+ bytes += sizeof_pool_memory((char *)buf);
free((char *)buf);
buf = next;
}
pool_ctl[i].free_buf = NULL;
}
+ Dmsg2(100, "Freed mem_pool count=%d size=%s\n", count, edit_uint64_with_commas(bytes, ed1));
V(mutex);
+
}
#ifdef DEBUG
* Version $Id$
*/
/*
- Copyright (C) 2000-2004 Kern Sibbald and John Walker
+ Copyright (C) 2000-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
+ modify it under the terms of the GNU General Public License
+ version 2 as amended with additional clauses defined in the
+ file LICENSE in the main source directory.
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., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ the file LICENSE for additional details.
*/
#endif
+extern void garbage_collect_memory_pool();
extern void close_memory_pool();
extern void print_memory_pool_stats();
/* edit.c */
uint64_t str_to_uint64(char *str);
int64_t str_to_int64(char *str);
+#define str_to_int32(str) ((int32_t)str_to_int64(str))
char * edit_uint64_with_commas (uint64_t val, char *buf);
char * add_commas (char *val, char *buf);
char * edit_uint64 (uint64_t val, char *buf);
/*
- S M A R T A L L O C
- Smart Memory Allocator
+ S M A R T A L L O C
+ Smart Memory Allocator
- Evolved over several years, starting with the initial
- SMARTALLOC code for AutoSketch in 1986, guided by the Blind
- Watchbreaker, John Walker. Isolated in this general-purpose
- form in September of 1989. Updated with be more POSIX
- compliant and to include Web-friendly HTML documentation in
- October of 1998 by the same culprit. For additional
- information and the current version visit the Web page:
+ Evolved over several years, starting with the initial
+ SMARTALLOC code for AutoSketch in 1986, guided by the Blind
+ Watchbreaker, John Walker. Isolated in this general-purpose
+ form in September of 1989. Updated with be more POSIX
+ compliant and to include Web-friendly HTML documentation in
+ October of 1998 by the same culprit. For additional
+ information and the current version visit the Web page:
- http://www.fourmilab.ch/smartall/
+ http://www.fourmilab.ch/smartall/
- Version $Id$
+ Version $Id$
*/
/*
- Copyright (C) 2000-2005 Kern Sibbald
+ Copyright (C) 2000-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-extern char my_name[]; /* daemon name */
+extern char my_name[]; /* daemon name */
typedef unsigned short sm_ushort;
/* Memory allocation control structures and storage. */
struct abufhead {
- struct b_queue abq; /* Links on allocated queue */
- unsigned ablen; /* Buffer length in bytes */
+ struct b_queue abq; /* Links on allocated queue */
+ unsigned ablen; /* Buffer length in bytes */
const char *abfname; /* File name pointer */
- sm_ushort ablineno; /* Line number of allocation */
+ sm_ushort ablineno; /* Line number of allocation */
};
static struct b_queue abqueue = { /* Allocated buffer queue */
};
-static bool bufimode = false; /* Buffers not tracked when True */
+static bool bufimode = false; /* Buffers not tracked when True */
#define HEAD_SIZE BALIGN(sizeof(struct abufhead))
/* SMALLOC -- Allocate buffer, enqueing on the orphaned buffer
- tracking list. */
+ tracking list. */
static void *smalloc(const char *fname, int lineno, unsigned int nbytes)
{
buf[nbytes - 1] = (uint8_t)((((long) buf) & 0xFF) ^ 0xC5);
buf += HEAD_SIZE; /* Increment to user data start */
if (++sm_buffers > sm_max_buffers) {
- sm_max_buffers = sm_buffers;
+ sm_max_buffers = sm_buffers;
}
sm_bytes += nbytes;
if (sm_bytes > sm_max_bytes) {
- sm_max_bytes = sm_bytes;
+ sm_max_bytes = sm_bytes;
}
V(mutex);
} else {
}
/* SM_NEW_OWNER -- Update the File and line number for a buffer
- This is to accomodate mem_pool. */
+ This is to accomodate mem_pool. */
void sm_new_owner(const char *fname, int lineno, char *buf)
{
}
/* SM_FREE -- Update free pool availability. FREE is never called
- except through this interface or by actuallyfree().
- free(x) is defined to generate a call to this
- routine. */
+ except through this interface or by actuallyfree().
+ free(x) is defined to generate a call to this
+ routine. */
void sm_free(const char *file, int line, void *fp)
{
P(mutex);
Dmsg4(1150, "sm_free %d at %x from %s:%d\n",
- head->ablen, fp,
- head->abfname, head->ablineno);
+ head->ablen, fp,
+ head->abfname, head->ablineno);
/* The following assertions will catch virtually every release
of an address which isn't an allocated buffer. */
Emsg2(M_ABORT, 0, _("qp->qprev->qnext != qp called from %s:%d\n"), file, line);
}
- /* The following assertion detects storing off the end of the
+ /* The following assertion detects storing off the end of the
allocated space in the buffer by comparing the end of buffer
- checksum with the address of the buffer. */
+ checksum with the address of the buffer. */
if (((unsigned char *)cp)[head->ablen - 1] != ((((long) cp) & 0xFF) ^ 0xC5)) {
V(mutex);
qdchain(qp);
V(mutex);
- /* Now we wipe the contents of the just-released buffer with
+ /* Now we wipe the contents of the just-released buffer with
"designer garbage" (Duff Kurland's phrase) of alternating
bits. This is intended to ruin the day for any miscreant who
attempts to access data through a pointer into storage that's
}
/* SM_MALLOC -- Allocate buffer. NULL is returned if no memory
- was available. */
+ was available. */
void *sm_malloc(const char *fname, int lineno, unsigned int nbytes)
{
if ((buf = smalloc(fname, lineno, nbytes)) != NULL) {
/* To catch sloppy code that assumes buffers obtained from
- malloc() are zeroed, we preset the buffer contents to
+ malloc() are zeroed, we preset the buffer contents to
"designer garbage" consisting of alternating bits. */
memset(buf, 0x55, (int) nbytes);
/* SM_CALLOC -- Allocate an array and clear it to zero. */
void *sm_calloc(const char *fname, int lineno,
- unsigned int nelem, unsigned int elsize)
+ unsigned int nelem, unsigned int elsize)
{
void *buf;
return buf;
}
-/* SM_REALLOC -- Adjust the size of a previously allocated buffer.
+/* SM_REALLOC -- Adjust the size of a previously allocated buffer.
Note that the trick of "resurrecting" a previously
- freed buffer with realloc() is NOT supported by this
- function. Further, because of the need to maintain
- our control storage, SM_REALLOC must always allocate
- a new block and copy the data in the old block.
- This may result in programs which make heavy use of
- realloc() running much slower than normally. */
+ freed buffer with realloc() is NOT supported by this
+ function. Further, because of the need to maintain
+ our control storage, SM_REALLOC must always allocate
+ a new block and copy the data in the old block.
+ This may result in programs which make heavy use of
+ realloc() running much slower than normally. */
void *sm_realloc(const char *fname, int lineno, void *ptr, unsigned int size)
{
e_msg(fname, lineno, M_ABORT, 0, _("sm_realloc size: %d\n"), size);
}
- /* If the old block pointer is NULL, treat realloc() as a
+ /* If the old block pointer is NULL, treat realloc() as a
malloc(). SVID is silent on this, but many C libraries
permit this. */
/* Sizes differ. Allocate a new buffer of the requested size.
If we can't obtain such a buffer, act as defined in SVID:
- return NULL from realloc() and leave the buffer in PTR
+ return NULL from realloc() and leave the buffer in PTR
intact. */
sm_buffers--;
- sm_bytes -= osize;
+ sm_bytes -= head->ablen;
if ((buf = smalloc(fname, lineno, size)) != NULL) {
memcpy(buf, ptr, (int) sm_min(size, osize));
/* If the new buffer is larger than the old, fill the balance
of it with "designer garbage". */
if (size > osize) {
- memset(((char *) buf) + osize, 0x55, (int) (size - osize));
+ memset(((char *) buf) + osize, 0x55, (int) (size - osize));
}
/* All done. Free and dechain the original buffer. */
return buf;
}
-/* ACTUALLYMALLOC -- Call the system malloc() function to obtain
- storage which will eventually be released
- by system or library routines not compiled
- using SMARTALLOC. */
+/* ACTUALLYMALLOC -- Call the system malloc() function to obtain
+ storage which will eventually be released
+ by system or library routines not compiled
+ using SMARTALLOC. */
void *actuallymalloc(unsigned int size)
{
return malloc(size);
}
-/* ACTUALLYCALLOC -- Call the system calloc() function to obtain
- storage which will eventually be released
- by system or library routines not compiled
- using SMARTALLOC. */
+/* ACTUALLYCALLOC -- Call the system calloc() function to obtain
+ storage which will eventually be released
+ by system or library routines not compiled
+ using SMARTALLOC. */
void *actuallycalloc(unsigned int nelem, unsigned int elsize)
{
}
/* ACTUALLYREALLOC -- Call the system realloc() function to obtain
- storage which will eventually be released
- by system or library routines not compiled
- using SMARTALLOC. */
+ storage which will eventually be released
+ by system or library routines not compiled
+ using SMARTALLOC. */
void *actuallyrealloc(void *ptr, unsigned int size)
{
}
/* ACTUALLYFREE -- Interface to system free() function to release
- buffers allocated by low-level routines. */
+ buffers allocated by low-level routines. */
void actuallyfree(void *cp)
{
}
/* SM_DUMP -- Print orphaned buffers (and dump them if BUFDUMP is
- * True).
+ * True).
* N.B. DO NOT USE any Bacula print routines (Dmsg, Jmsg, Emsg, ...)
* as they have all been shut down at this point.
*/
while (ap != (struct abufhead *) &abqueue) {
if ((ap == NULL) ||
- (ap->abq.qnext->qprev != (struct b_queue *) ap) ||
- (ap->abq.qprev->qnext != (struct b_queue *) ap)) {
- fprintf(stderr, _(
+ (ap->abq.qnext->qprev != (struct b_queue *) ap) ||
+ (ap->abq.qprev->qnext != (struct b_queue *) ap)) {
+ fprintf(stderr, _(
"\nOrphaned buffers exist. Dump terminated following\n"
" discovery of bad links in chain of orphaned buffers.\n"
" Buffer address with bad links: %lx\n"), (long) ap);
- break;
+ break;
}
if (ap->abfname != NULL) {
- unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
- char errmsg[500];
+ unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
+ char errmsg[500];
- bsnprintf(errmsg, sizeof(errmsg),
+ bsnprintf(errmsg, sizeof(errmsg),
_("Orphaned buffer: %6u bytes allocated at line %d of %s %s\n"),
- memsize, ap->ablineno, my_name, ap->abfname
- );
+ memsize, ap->ablineno, my_name, ap->abfname
+ );
fprintf(stderr, "%s", errmsg);
- if (bufdump) {
- char buf[20];
- unsigned llen = 0;
- char *cp = ((char *) ap) + HEAD_SIZE;
-
- errmsg[0] = EOS;
- while (memsize) {
- if (llen >= 16) {
+ if (bufdump) {
+ char buf[20];
+ unsigned llen = 0;
+ char *cp = ((char *) ap) + HEAD_SIZE;
+
+ errmsg[0] = EOS;
+ while (memsize) {
+ if (llen >= 16) {
bstrncat(errmsg, "\n", sizeof(errmsg));
- llen = 0;
+ llen = 0;
fprintf(stderr, "%s", errmsg);
- errmsg[0] = EOS;
- }
+ errmsg[0] = EOS;
+ }
bsnprintf(buf, sizeof(buf), " %02X",
- (*cp++) & 0xFF);
- bstrncat(errmsg, buf, sizeof(errmsg));
- llen++;
- memsize--;
- }
+ (*cp++) & 0xFF);
+ bstrncat(errmsg, buf, sizeof(errmsg));
+ llen++;
+ memsize--;
+ }
fprintf(stderr, "%s\n", errmsg);
- }
+ }
}
ap = (struct abufhead *) ap->abq.qnext;
}
/* SM_CHECK -- Check the buffers and dump if any damage exists. */
void sm_check(const char *fname, int lineno, bool bufdump)
{
- if (!sm_check_rtn(fname, lineno, bufdump)) {
+ if (!sm_check_rtn(fname, lineno, bufdump)) {
Emsg2(M_ABORT, 0, _("Damaged buffer found. Called from %s:%d\n"),
- fname, lineno);
- }
+ fname, lineno);
+ }
}
#undef sm_check_rtn
while (ap != (struct abufhead *) &abqueue) {
bad = 0;
if ((ap == NULL) ||
- (ap->abq.qnext->qprev != (struct b_queue *) ap)) {
- bad = 0x1;
+ (ap->abq.qnext->qprev != (struct b_queue *) ap)) {
+ bad = 0x1;
}
if (ap->abq.qprev->qnext != (struct b_queue *) ap) {
- bad |= 0x2;
+ bad |= 0x2;
}
if (((unsigned char *) ap)[((struct abufhead *) ap)->ablen - 1] !=
- ((((long) ap) & 0xFF) ^ 0xC5)) {
- bad |= 0x4;
+ ((((long) ap) & 0xFF) ^ 0xC5)) {
+ bad |= 0x4;
}
badbuf |= bad;
if (bad) {
- fprintf(stderr,
+ fprintf(stderr,
_("\nDamaged buffers found at %s:%d\n"), fname, lineno);
- if (bad & 0x1) {
+ if (bad & 0x1) {
fprintf(stderr, _(" discovery of bad prev link.\n"));
- }
- if (bad & 0x2) {
+ }
+ if (bad & 0x2) {
fprintf(stderr, _(" discovery of bad next link.\n"));
- }
- if (bad & 0x4) {
+ }
+ if (bad & 0x4) {
fprintf(stderr, _(" discovery of data overrun.\n"));
- }
+ }
fprintf(stderr, _(" Buffer address: %lx\n"), (long) ap);
- if (ap->abfname != NULL) {
- unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
- char errmsg[80];
+ if (ap->abfname != NULL) {
+ unsigned memsize = ap->ablen - (HEAD_SIZE + 1);
+ char errmsg[80];
- fprintf(stderr,
+ fprintf(stderr,
_("Damaged buffer: %6u bytes allocated at line %d of %s %s\n"),
- memsize, ap->ablineno, my_name, ap->abfname
- );
- if (bufdump) {
- unsigned llen = 0;
- char *cp = ((char *) ap) + HEAD_SIZE;
-
- errmsg[0] = EOS;
- while (memsize) {
- if (llen >= 16) {
+ memsize, ap->ablineno, my_name, ap->abfname
+ );
+ if (bufdump) {
+ unsigned llen = 0;
+ char *cp = ((char *) ap) + HEAD_SIZE;
+
+ errmsg[0] = EOS;
+ while (memsize) {
+ if (llen >= 16) {
strcat(errmsg, "\n");
- llen = 0;
+ llen = 0;
fprintf(stderr, "%s", errmsg);
- errmsg[0] = EOS;
- }
- if (*cp < 0x20) {
+ errmsg[0] = EOS;
+ }
+ if (*cp < 0x20) {
sprintf(errmsg + strlen(errmsg), " %02X",
- (*cp++) & 0xFF);
- } else {
+ (*cp++) & 0xFF);
+ } else {
sprintf(errmsg + strlen(errmsg), " %c ",
- (*cp++) & 0xFF);
- }
- llen++;
- memsize--;
- }
+ (*cp++) & 0xFF);
+ }
+ llen++;
+ memsize--;
+ }
fprintf(stderr, "%s\n", errmsg);
- }
- }
+ }
+ }
}
ap = (struct abufhead *) ap->abq.qnext;
}
/* SM_STATIC -- Orphaned buffer detection can be disabled (for such
- items as buffers allocated during initialisation) by
- calling sm_static(1). Normal orphaned buffer
- detection can be re-enabled with sm_static(0). Note
- that all the other safeguards still apply to buffers
- allocated when sm_static(1) mode is in effect. */
+ items as buffers allocated during initialisation) by
+ calling sm_static(1). Normal orphaned buffer
+ detection can be re-enabled with sm_static(0). Note
+ that all the other safeguards still apply to buffers
+ allocated when sm_static(1) mode is in effect. */
void sm_static(int mode)
{
* Version $Id$
*/
/*
- Copyright (C) 2000-2005 Kern Sibbald
+ Copyright (C) 2000-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
* Version $Id$
*/
/*
- Copyright (C) 2002-2005 Kern Sibbald
+ Copyright (C) 2002-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
dcr->VolCatInfo.Slot = slot; /* slot to be loaded */
changer = edit_device_codes(dcr, changer,
dcr->device->changer_command, "load");
+ offline_or_rewind_dev(dev);
+ force_close_device(dev);
status = run_program(changer, timeout, NULL);
if (status == 0) {
Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
status = run_program(changer, timeout, results);
Dmsg3(50, "run_prog: %s stat=%d result=%s\n", changer, status, results);
if (status == 0) {
- loaded = atoi(results);
+ loaded = str_to_int32(results);
if (loaded > 0) {
Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
drive, loaded);
loaded = get_autochanger_loaded_slot(dcr);
}
- /* We are going to load a new tape, so close the device */
- offline_or_rewind_dev(dev);
- force_close_device(dev);
-
if (loaded > 0) {
POOLMEM *changer = get_pool_memory(PM_FNAME);
lock_changer(dcr);
dcr->VolCatInfo.Slot = loaded;
changer = edit_device_codes(dcr, changer,
dcr->device->changer_command, "unload");
+ offline_or_rewind_dev(dev);
+ force_close_device(dev);
int stat = run_program(changer, timeout, NULL);
dcr->VolCatInfo.Slot = slot;
if (stat != 0) {
return false;
}
- /* We are going to unload a tape, so close the device */
- offline_or_rewind_dev(dev);
- force_close_device(dev);
-
POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
lock_changer(dcr);
Jmsg(jcr, M_INFO, 0,
changer_cmd = edit_device_codes(dcr, changer_cmd,
dcr->device->changer_command, "unload");
Dmsg1(200, "Run program=%s\n", changer_cmd);
+ offline_or_rewind_dev(dev);
+ force_close_device(dev);
int stat = run_program(changer_cmd, timeout, NULL);
dcr->VolCatInfo.Slot = save_slot;
dcr->dev = save_dev;
bnet_send(dir);
}
} else if (strcmp(cmd, "slots") == 0 ) {
+ char buf[100], *p;
/* For slots command, read a single line */
- bstrncpy(dir->msg, "slots=", len);
- fgets(dir->msg+6, len-6, bpipe->rfd);
- dir->msglen = strlen(dir->msg);
+ buf[0] = 0;
+ fgets(buf, sizeof(buf)-1, bpipe->rfd);
+ buf[sizeof(buf)-1] = 0;
+ /* Strip any leading space in front of # of slots */
+ for (p=buf; B_ISSPACE(*p); p++)
+ { }
+ bnet_fsend(dir, "slots=%s", p);
Dmsg1(100, "<stored: %s", dir->msg);
- bnet_send(dir);
}
stat = close_bpipe(bpipe);
int fd;
uint32_t i;
uint32_t min_block_size;
+ struct tm tm;
ok = true;
stop = 0;
*/
jcr->dcr->VolFirstIndex = 0;
time(&jcr->run_time); /* start counting time for rates */
+ localtime_r(&jcr->run_time, &tm);
+ strftime(buf1, sizeof(buf1), "%T", &tm);
if (simple) {
- Pmsg0(-1, _("Begin writing Bacula records to tape ...\n"));
+ Pmsg1(-1, _("%s Begin writing Bacula records to tape ...\n"), buf1);
} else {
- Pmsg0(-1, _("Begin writing Bacula records to first tape ...\n"));
+ Pmsg1(-1, _("%s Begin writing Bacula records to first tape ...\n"), buf1);
}
for (file_index = 0; ok && !job_canceled(jcr); ) {
rec.VolSessionId = jcr->VolSessionId;
block->BlockNumber, dev->block_num,
edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), (float)kbs);
}
- /* Every 15000 blocks (approx 1GB) write an EOF.
+ /* Every 32000 blocks (approx 2GB) write an EOF.
*/
- if ((block->BlockNumber % 15000) == 0) {
- Pmsg0(-1, _("Flush block, write EOF\n"));
+ if ((block->BlockNumber % 32000) == 0) {
+ now = time(NULL);
+ localtime_r(&now, &tm);
+ strftime(buf1, sizeof(buf1), "%T", &tm);
+ Pmsg1(-1, _("%s Flush block, write EOF\n"), buf1);
flush_block(block, 0);
weof_dev(dev, 1);
}
be.strerror());
}
+ now = time(NULL);
+ localtime_r(&now, &tm);
+ strftime(buf1, sizeof(buf1), "%T", &tm);
if (simple) {
- Pmsg2(-1, _("\n\nDone filling tape at %d:%d. Now beginning re-read of tape ...\n"),
- jcr->dcr->dev->file, jcr->dcr->dev->block_num);
+ Pmsg3(-1, _("\n\n%s Done filling tape at %d:%d. Now beginning re-read of tape ...\n"),
+ buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num);
}
else {
- Pmsg2(-1, _("\n\nDone filling tapes at %d:%d. Now beginning re-read of first tape ...\n"),
- jcr->dcr->dev->file, jcr->dcr->dev->block_num);
+ Pmsg3(-1, _("\n\n%s Done filling tapes at %d:%d. Now beginning re-read of first tape ...\n"),
+ buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num);
}
jcr->dcr->block = block;
*
*/
/*
- Copyright (C) 2000-2005 Kern Sibbald
+ Copyright (C) 2000-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
* Version $Id$
*/
/*
- Copyright (C) 2000-2005 Kern Sibbald
+ Copyright (C) 2000-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
void free_volume_list();
void list_volumes(BSOCK *user);
bool is_volume_in_use(DCR *dcr);
+void send_drive_reserve_messages(JCR *jcr, BSOCK *user);
/* From spool.c */
static bool reserve_device_for_append(DCR *dcr, RCTX &rctx);
static bool use_storage_cmd(JCR *jcr);
bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx);
+static void queue_reserve_message(JCR *jcr);
/* Requests from the Director daemon */
static char use_storage[] = "use storage=%127s media_type=%127s "
int Copy, Stripe;
DIRSTORE *store;
RCTX rctx;
+ char *msg;
+ alist *msgs;
memset(&rctx, 0, sizeof(RCTX));
rctx.jcr = jcr;
* use_device for each device that it wants to use.
*/
jcr->dirstore = New(alist(10, not_owned_by_alist));
+ msgs = jcr->reserve_msgs = New(alist(10, not_owned_by_alist));
do {
Dmsg1(100, "<dird: %s", dir->msg);
ok = sscanf(dir->msg, use_storage, store_name.c_str(),
}
#endif
+ init_jcr_device_wait_timers(jcr);
/*
* At this point, we have a list of all the Director's Storage
* resources indicated for this Job, which include Pool, PoolType,
if (ok) {
bool first = true; /* print wait message once */
for ( ; !job_canceled(jcr); ) {
+ P(search_lock); /* only one thread at a time */
+ while ((msg = (char *)msgs->pop())) {
+ free(msg);
+ }
rctx.suitable_device = false;
rctx.have_volume = false;
rctx.any_drive = false;
rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
rctx.autochanger_only, rctx.any_drive);
if ((ok = find_suitable_device_for_job(jcr, rctx))) {
- goto done;
+ break;
}
/* Look through all drives possibly for low_use drive */
if (rctx.low_use_drive) {
rctx.try_low_use_drive = true;
if ((ok = find_suitable_device_for_job(jcr, rctx))) {
- goto done;
+ break;
}
rctx.try_low_use_drive = false;
}
rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
rctx.autochanger_only, rctx.any_drive);
if ((ok = find_suitable_device_for_job(jcr, rctx))) {
- goto done;
+ break;
}
}
/* Look for an exact match all drives */
rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
rctx.autochanger_only, rctx.any_drive);
if ((ok = find_suitable_device_for_job(jcr, rctx))) {
- goto done;
+ break;
}
/* Look for any mounted drive */
rctx.exact_match = false;
rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
rctx.autochanger_only, rctx.any_drive);
if ((ok = find_suitable_device_for_job(jcr, rctx))) {
- goto done;
+ break;
}
/* Try any drive */
rctx.any_drive = true;
rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
rctx.autochanger_only, rctx.any_drive);
if ((ok = find_suitable_device_for_job(jcr, rctx))) {
- goto done;
+ break;
}
+ /* Unlock before possible wait */
+ V(search_lock);
if (!rctx.suitable_device || !wait_for_device(jcr, first)) {
break; /* Get out, failure ... */
}
first = false;
bnet_sig(dir, BNET_HEARTBEAT); /* Inform Dir that we are alive */
}
+ /* Note if !ok then search_lock is already cleared */
+ if (ok) {
+ V(search_lock);
+ goto all_done;
+ }
/*
* If we get here, there are no suitable devices available, which
Dmsg1(100, ">dird: %s", dir->msg);
}
-done:
+all_done:
foreach_alist(store, jcr->dirstore) {
delete store->device;
delete store;
}
delete jcr->dirstore;
-#ifdef implemented
- while (error=(char*)rctx->errors.first()) {
- free(error);
+ P(search_lock);
+ while ((msg = (char *)msgs->pop())) {
+ free(msg);
}
-#endif
+ delete msgs;
+ jcr->reserve_msgs = NULL;
+ V(search_lock);
return ok;
}
Dmsg4(100, "PrefMnt=%d exact=%d suitable=%d chgronly=%d\n",
rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
rctx.autochanger_only);
- init_jcr_device_wait_timers(jcr);
ok = false;
- P(search_lock);
foreach_alist(store, jcr->dirstore) {
rctx.store = store;
foreach_alist(device_name, store->device) {
break;
}
}
- V(search_lock);
return ok;
}
}
rctx.suitable_device = true;
- Dmsg2(100, "Try reserve %s jobid=%d\n", rctx.device->hdr.name,
+ Dmsg2(100, "Try reserve %s JobId=%u\n", rctx.device->hdr.name,
rctx.jcr->JobId);
dcr = new_dcr(rctx.jcr, rctx.device->dev);
if (!dcr) {
if (rctx.exact_match && !rctx.have_volume) {
dcr->any_volume = true;
if (dir_find_next_appendable_volume(dcr)) {
- Dmsg1(100, "Looking for Volume=%s\n", dcr->VolumeName);
bstrncpy(rctx.VolumeName, dcr->VolumeName, sizeof(rctx.VolumeName));
+ Dmsg2(100, "JobId=%u looking for Volume=%s\n", rctx.jcr->JobId, rctx.VolumeName);
rctx.have_volume = true;
} else {
Dmsg0(100, "No next volume found\n");
if (is_device_unmounted(dev)) {
Dmsg1(200, "Device %s is BLOCKED due to user unmount.\n", dev->print_name());
- Mmsg(jcr->errmsg, _("Device %s is BLOCKED due to user unmount.\n"),
- dev->print_name());
+ Mmsg(jcr->errmsg, _("3601 JobId=%u device %s is BLOCKED due to user unmount.\n"),
+ jcr->JobId, dev->print_name());
+ queue_reserve_message(jcr);
goto bail_out;
}
if (dev->is_busy()) {
Dmsg4(200, "Device %s is busy ST_READ=%d num_writers=%d reserved=%d.\n", dev->print_name(),
dev->state & ST_READ?1:0, dev->num_writers, dev->reserved_device);
- Mmsg1(jcr->errmsg, _("Device %s is busy.\n"),
- dev->print_name());
+ Mmsg(jcr->errmsg, _("3602 JobId=%u device %s is busy (already reading/writing).\n"),
+ jcr->JobId, dev->print_name());
+ queue_reserve_message(jcr);
goto bail_out;
}
/* If device is being read, we cannot write it */
if (dev->can_read()) {
- Mmsg1(jcr->errmsg, _("Device %s is busy reading.\n"), dev->print_name());
+ Mmsg(jcr->errmsg, _("3603 JobId=%u device %s is busy reading.\n"),
+ jcr->JobId, dev->print_name());
Dmsg1(100, "%s", jcr->errmsg);
+ queue_reserve_message(jcr);
goto bail_out;
}
/* If device is unmounted, we are out of luck */
if (is_device_unmounted(dev)) {
- Mmsg(jcr->errmsg, _("Device %s is BLOCKED due to user unmount.\n"), dev->print_name());
+ Mmsg(jcr->errmsg, _("3604 JobId=%u device %s is BLOCKED due to user unmount.\n"),
+ jcr->JobId, dev->print_name());
Dmsg1(100, "%s", jcr->errmsg);
+ queue_reserve_message(jcr);
goto bail_out;
}
* helps spread the load to the least used drives.
*/
if (rctx.try_low_use_drive && dev == rctx.low_use_drive) {
- Dmsg3(100, "OK dev=%s == low_drive=%s. JobId=%d\n",
+ Dmsg3(100, "OK dev=%s == low_drive=%s. JobId=%u\n",
dev->print_name(), rctx.low_use_drive->print_name(), jcr->JobId);
return 1;
}
Dmsg1(100, "not low use num_writers=%d\n", dev->num_writers+
dev->reserved_device);
}
- Dmsg1(100, "failed: !prefMnt && busy. JobId=%d\n", jcr->JobId);
+ Dmsg1(100, "failed: !prefMnt && busy. JobId=%u\n", jcr->JobId);
+ Mmsg(jcr->errmsg, _("3605 JobId=%u wants free drive but device %s is busy.\n"),
+ jcr->JobId, dev->print_name());
+ queue_reserve_message(jcr);
return 0;
}
/* Check for prefer mounted volumes */
if (rctx.PreferMountedVols && !dev->VolHdr.VolumeName[0] && dev->is_tape()) {
- Dmsg1(100, "failed: want mounted -- no vol JobId=%d\n", jcr->JobId);
+ Mmsg(jcr->errmsg, _("3606 JobId=%u wants mounted, but drive %s has no Volume.\n"),
+ jcr->JobId, dev->print_name());
+ queue_reserve_message(jcr);
+ Dmsg1(100, "failed: want mounted -- no vol JobId=%u\n", jcr->JobId);
return 0; /* No volume mounted */
}
/* Check for exact Volume name match */
if (rctx.exact_match && rctx.have_volume &&
strcmp(dev->VolHdr.VolumeName, rctx.VolumeName) != 0) {
+ Mmsg(jcr->errmsg, _("3607 JobId=%u wants Vol=\"%s\" drive has Vol=\"%s\" on drive %s.\n"),
+ jcr->JobId, rctx.VolumeName, dev->VolHdr.VolumeName,
+ dev->print_name());
+ queue_reserve_message(jcr);
Dmsg2(100, "failed: Not exact match have=%s want=%s\n",
dev->VolHdr.VolumeName, rctx.VolumeName);
return 0;
if (rctx.autochanger_only && dev->num_writers == 0 &&
dev->VolHdr.VolumeName[0] == 0) {
/* Device is available but not yet reserved, reserve it for us */
- Dmsg2(100, "OK Res Unused autochanger %s JobId=%d.\n",
+ Dmsg2(100, "OK Res Unused autochanger %s JobId=%u.\n",
dev->print_name(), jcr->JobId);
bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
strcmp(dev->pool_type, dcr->pool_type) == 0) {
/* OK, compatible device */
- Dmsg2(100, "OK dev: %s num_writers=0, reserved, pool matches JobId=%d\n",
+ Dmsg2(100, "OK dev: %s num_writers=0, reserved, pool matches JobId=%u\n",
dev->print_name(), jcr->JobId);
return 1;
} else {
- /* Drive not suitable for us */
+ /* Drive Pool not suitable for us */
+ Mmsg(jcr->errmsg, _("3608 JobId=%u wants Pool=\"%s\" but have Pool=\"%s\" on drive %s.\n"),
+ jcr->JobId, dcr->pool_name, dev->pool_name, dev->print_name());
+ queue_reserve_message(jcr);
Dmsg2(100, "failed: busy num_writers=0, reserved, pool=%s wanted=%s\n",
dev->pool_name, dcr->pool_name);
return 0; /* wait */
/* Device in append mode, check if changing pool */
if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
strcmp(dev->pool_type, dcr->pool_type) == 0) {
- Dmsg2(100, "OK dev: %s num_writers=0, can_append, pool matches. JobId=%d\n",
+ Dmsg2(100, "OK dev: %s num_writers=0, can_append, pool matches. JobId=%u\n",
dev->print_name(), jcr->JobId);
/* OK, compatible device */
return 1;
}
}
/* Device is available but not yet reserved, reserve it for us */
- Dmsg2(100, "OK Dev avail reserved %s JobId=%d\n", dev->print_name(),
+ Dmsg2(100, "OK Dev avail reserved %s JobId=%u\n", dev->print_name(),
jcr->JobId);
bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
/* Yes, now check if we want the same Pool and pool type */
if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
strcmp(dev->pool_type, dcr->pool_type) == 0) {
- Dmsg2(100, "OK dev: %s num_writers>=0, can_append, pool matches. JobId=%d\n",
+ Dmsg2(100, "OK dev: %s num_writers>=0, can_append, pool matches. JobId=%u\n",
dev->print_name(), jcr->JobId);
/* OK, compatible device */
return 1;
} else {
- /* Drive not suitable for us */
+ /* Drive Pool not suitable for us */
+ Mmsg(jcr->errmsg, _("3609 JobId=%u wants Pool=\"%s\" but have Pool=\"%s\" on drive %s.\n"),
+ jcr->JobId, dcr->pool_name, dev->pool_name, dev->print_name());
+ queue_reserve_message(jcr);
Dmsg2(100, "failed: busy num_writers>0, can_append, pool=%s wanted=%s\n",
dev->pool_name, dcr->pool_name);
return 0; /* wait */
}
} else {
Pmsg0(000, _("Logic error!!!! Should not get here.\n"));
+ Mmsg(jcr->errmsg, _("3910 JobId=%u Logic error!!!! drive %s Should not get here.\n"),
+ jcr->JobId, dev->print_name());
+ queue_reserve_message(jcr);
Jmsg0(jcr, M_FATAL, 0, _("Logic error!!!! Should not get here.\n"));
return -1; /* error, should not get here */
}
- Dmsg2(100, "failed: No reserve %s JobId=%d\n", dev->print_name(), jcr->JobId);
+ Mmsg(jcr->errmsg, _("3911 JobId=%u failed reserve drive %s.\n"),
+ jcr->JobId, dev->print_name());
+ queue_reserve_message(jcr);
+ Dmsg2(100, "failed: No reserve %s JobId=%u\n", dev->print_name(), jcr->JobId);
return 0;
}
+
+/*
+ * search_lock is already set on entering this routine
+ */
+static void queue_reserve_message(JCR *jcr)
+{
+ int i;
+ alist *msgs = jcr->reserve_msgs;
+ char *msg;
+
+ if (!msgs) {
+ return;
+ }
+ /*
+ * Look for duplicate message. If found, do
+ * not insert
+ */
+ for (i=msgs->size()-1; i >= 0; i--) {
+ msg = (char *)msgs->get(i);
+ if (!msg) {
+ return;
+ }
+ /* Comparison based on 4 digit message number */
+ if (strncmp(msg, jcr->errmsg, 4) == 0) {
+ return;
+ }
+ }
+ /* Message unique, so insert it */
+ jcr->reserve_msgs->push(bstrdup(jcr->errmsg));
+}
+
+/*
+ * Send any reservation messages queued for this jcr
+ */
+void send_drive_reserve_messages(JCR *jcr, BSOCK *user)
+{
+ int i;
+ alist *msgs;
+ char *msg;
+
+ P(search_lock);
+ msgs = jcr->reserve_msgs;
+ if (!msgs || msgs->size() == 0) {
+ V(search_lock);
+ return;
+ }
+ for (i=msgs->size()-1; i >= 0; i--) {
+ msg = (char *)msgs->get(i);
+ if (msg) {
+ bnet_fsend(user, " %s", msg);
+ } else {
+ break;
+ }
+ }
+ V(search_lock);
+}
static void send_blocked_status(JCR *jcr, DEVICE *dev);
static void list_terminated_jobs(void *arg);
static void list_running_jobs(BSOCK *user);
+static void list_jobs_waiting_on_reservation(BSOCK *user);
static void sendit(const char *msg, int len, void *arg);
static const char *level_to_str(int level);
*/
list_running_jobs(user);
+ /*
+ * List jobs stuck in reservation system
+ */
+ list_jobs_waiting_on_reservation(user);
+
/*
* List terminated jobs
*/
bnet_fsend(user, _("====\n"));
}
+static void list_jobs_waiting_on_reservation(BSOCK *user)
+{
+ JCR *jcr;
+
+ bnet_fsend(user, _("\nJobs waiting to reserve a drive:\n"));
+ foreach_jcr(jcr) {
+ if (!jcr->reserve_msgs) {
+ continue;
+ }
+ send_drive_reserve_messages(jcr, user);
+ }
+ endeach_jcr(jcr);
+
+ bnet_fsend(user, _("====\n"));
+}
+
+
static void list_terminated_jobs(void *arg)
{
char dt[MAX_TIME_LENGTH], b1[30], b2[30];
#undef VERSION
#define VERSION "1.39.4"
-#define BDATE "06 January 2006"
-#define LSMDATE "06Jan06"
+#define BDATE "09 January 2006"
+#define LSMDATE "09Jan06"
/* Debug flags */
#undef DEBUG