Release Notes for Bacula 1.33
- Bacula code: Total files = 281 Total lines = 83,815 (*.h *.c *.in)
+ Bacula code: Total files = 283 Total lines = 84,818 (*.h *.c *.in)
+
+New directives:
+- "Close on Poll = yes/no" in SD Device resource.
+- "Volume Poll Interval = time-interval" in SD Device resource.
+- "Two EOF = yes/no" in SD Device resource.
+- "Close on Poll = yes/no" in SD Device resource.
+- "Maximum Network Buffer Size = size" in SD Device resource.
+- "Maximum Network Buffer Size = size" in FD FileDaemon (or Client) resource.
+- "Console" new resource in Director conf file.
+ New directives: Name, Description, Password, JobACL, ClientACL,
+ StorageACL, ScheduleACL, RunACL, PoolACL, CommandACL,
+ FileSetACL, CatalogACL.
+- "Max Run Time = duration" in Director Job resource.
+- "Max Wait Time = duration" in Director Job resource (not fully implemented).
+- "JobDefs = name-of-resource" in Director Job resource.
+- "Jobdefs" new resource in Director. Same directives as for a Job.
+- "Full Backup Pool = xxx" in Job resource in the Director.
+- "Incremental Backup Pool = xxx" in Job resource in the Director.
+- "Differential Backup Pool = xxx" in Job resource in the Director.
+- Three new options on the Run override statement in a Schedule resource:
+ FullPool=xxx
+ IncrementalPool=xxx
+ DifferentialPool=xxx
+
+New Commands:
+- "SetIP"
+- Added "pool=xxx" to restore command line.
+- Added "fileset=xxx" to restore command line.
+- Fixed "storage=xxx" on restore command line.
+- "markdir" command in restore tree.
+- "unmarkdir" command in restore tree.
+- "quit" command in restore tree.
Most Significant Changes since 1.32d
+- The ability to ask the Storage daemon on a device by device basis
+ to "poll" the tape drive at a given interval (minimum 1 minute). If
+ a tape is found, its label is read and if appropriate it is used.
+ This eliminates the need to do "mount" commands.
+- The ability to close and re-open the device when a poll occurs.
+ This permits dealing with certain recalcitrant autochangers that
+ invalidate devices (typically on FreeBSD).
- Dan Langille has written a PostgreSQL driver for Bacula.
- Implement "update slots scan" that reads the volume label(s).
- The full form of the scan is "scan=1,2,4-5,7". With no specification,
# ------------------------------------------
# Where to place working dir
# ------------------------------------------
-working_dir=`eval echo ${sysconfdir}/working`
+working_dir=`eval echo ${prefix}/var/bacula/working`
AC_ARG_WITH(working-dir,
[ --with-working-dir=PATH specify path of Bacula working directory],
[
PFILES="platforms/Makefile"
PSCMD="ps -e"
WIN32=
-hostname=`uname -n`
+hostname=`uname -n | cut -d '.' -f 1`
case "$DISTNAME" in
aix)
DISTVER=`uname -r`
platforms/bsdi/bacula-fd \
platforms/bsdi/bacula-sd \
platforms/bsdi/bacula-dir"
- hostname=`hostname -s`
largefile_support="yes"
;;
cygwin)
DISTVER=`uname -r`
TAPEDRIVE="/dev/nst0"
PSCMD="ps -e -o pid,command"
- hostname=`hostname -s`
PFILES="${PFILES} \
platforms/darwin/Makefile"
;;
DISTVER=`cat /etc/debian_version`
TAPEDRIVE="/dev/nrst0"
PSCMD="ps -e -o pid,command"
- hostname=`hostname -s`
;;
freebsd)
DISTVER=`uname -a |awk '{print $3}'`
platforms/freebsd/bacula-fd \
platforms/freebsd/bacula-sd \
platforms/freebsd/bacula-dir"
- hostname=`hostname -s`
largefile_support="yes"
;;
hpux)
platforms/mandrake/bacula-dir \
platforms/mandrake/bacula.spec \
"
- hostname=`hostname -s`
;;
gentoo)
DISTVER=`awk '/version / {print $5}' < /etc/gentoo-release`
platforms/gentoo/bacula-fd \
platforms/gentoo/bacula-sd \
platforms/gentoo/bacula-dir"
- hostname=`hostname -s`
;;
slackware)
DISTVER=`cat /etc/slackware-version`
TAPEDRIVE="/dev/nst0"
PSCMD="ps -e -o pid,command"
- hostname=`hostname -s`
;;
solaris)
DISTVER=`uname -r`
cut -f 3 -d ' '`
TAPEDRIVE="/dev/nst0"
PSCMD="ps -e -o pid,command"
- hostname=`hostname -s`
PFILES="${PFILES} \
platforms/suse/Makefile \
platforms/suse/bacula-fd \
DISTVER=5.x
TAPEDRIVE="/dev/nst0"
PSCMD="ps -e -o pid,command"
- hostname=`hostname -s`
PFILES="${PFILES} \
platforms/suse/Makefile \
platforms/suse/bacula-fd \
# ------------------------------------------
# Where to place working dir
# ------------------------------------------
-working_dir=`eval echo ${sysconfdir}/working`
+working_dir=`eval echo ${prefix}/var/bacula/working`
# Check whether --with-working-dir or --without-working-dir was given.
if test "${with_working_dir+set}" = set; then
DISTVER=`cat /etc/slackware-version`
TAPEDRIVE="/dev/nst0"
PSCMD="ps -e -o pid,command"
- hostname=`hostname -s`
;;
solaris)
DISTVER=`uname -r`
- Test cancel at EOM.
For 1.33 Testing/Documentation:
+- Newly labeled tapes are chosen before ones already in use.
- Document new alias records in Director. SDAddress SDDeviceName, SDPassword.
FDPassword, FDAddress, DBAddress, DBPort, DBPassword.
- Document new Include/Exclude ...
purge, ...
- Add subsections to the Disaster Recovery index section.
- Document Pool keyword for restore.
-
+- If you use restore replace=never, the directory attributes for
+ non-existent directories will not be restored properly.
+- In the Bacula User Guide you write:"Note, one major disadvantage of
+ writing to a NFS mounted volume as I do isthat if the other machine goes
+ down, the OS will wait forever on the fopen()call that Bacula makes. As
+ a consequence, Bacula will completely stall untilthe machine exporting
+ the NSF mounts comes back up. If someone knows a wayaround this, please
+ let me know."I haven't tried using NFS in years, but I think that the
+ "soft" and "intr"remount options may well help you. The only way of
+ being sure would be totry it.See, for example,
+ http://howtos.linux.com/guides/nag2/x-087-2-nfs.mountd.shtml
+
For 1.33
-- Fix restore to only pull in last Differential and later Incrementals.
+- Rescue builds incorrect script files on Rufus.
+- Write a Qmsg() to be used in bnet.c to prevent recursion. Queue the
+ message. If dequeueing toss the messages. Lock while dequeuing so that
+ it cannot be called recursively and set dequeuing flag.
+- Look at ASSERT() at 384 src/lib/bnet.c
+- Add all pools in Dir conf to DB.
+- Symbolic link a directory to another one, then backup the symbolic
+ link.
+- Build console in client-only build.
+- Restore attributes of directory if replace=never set but directory
+ did not exist.
+- Check why Phil's Verify exclude does not work.
+- Phil says that Windows file sizes mismatch in Verify when they should,
+ and that either the file size or the catalog size was zero.
+- Fix option 2 of restore -- list where file is backed up -- require Client,
+ then list last 20 backups.
+- Allow browsing the catalog to see all versions of a file (with
+ stat data on each file).
- Finish code passing files=nnn to restore start.
- Add level to estimate command.
- Check time/dates printed during restore when using Win32 API.
F Number Number of filenames to follow
<file-name>
...
+
+- Spooling ideas taken from Volker Sauer's and other's emails:
+ > IMHO job spooling should be turned on
+ >
+ > 1) by job
+ > 2) by schedule
+ > 3) by sd
+ >
+ > where and 2) overrides 1) and 3) is independent.
+
+ Yes, this is the minimum that I think is necessary.
+
+ >
+ > Reason(s):
+ > It should be switched by job, because the job that backs up the machine
+ > with the bacula-sd on doesn't need spooling.
+ > It should be switched by schedule, because for full-backups I don't need
+ > spooling, so I can switch it off (because the network faster then the
+ > tapedrive)
+
+ True, with the exception that if you have enough disk spool space,
+ and you want to run concurrent jobs, spooling can eliminate the block
+ interleaving restore inefficiencies.
+
+ > And you should be able to turn it of by sd for sd-machines with low disk
+ > capacity or if you just don't need or want this feature.
+ >
+ > There should be:
+ > - definitly the possibility for multipe spool direcories
+
+ Having multiple directories is no problem -- having different maximum
+ sizes creates specification problems. At some point, I will probably
+ have a common SD pool of spool directories as well as a set of
+ private spool directories for each device. The first implementation
+ will be a set of private spool directories for each device since
+ managing a global pool with a bunch of threads writing into the same
+ directory is *much* more complicated and prone to error.
+
+ > - the ability to spool parts of a backup (not the whole client)
+
+ This may change in the future, but for the moment, it will spool
+ either to a job high water mark, or until the directory is full
+ (reaches max spool size or I/O error). It will then write to tape,
+ truncate the spool file, and begin spooling again.
+
+ > - spooling while writing to tape
+
+ Not within a job, but yes, if you run concurrent jobs -- each is a
+ different thread. Within a job could be a feature, but *much* later.
+
+ > - parallel spooling (like parallel jobs/ concurrent jobs) of clients
+
+ Yes, this is one of my main motivations for doing it (aside from
+ eliminating tape "shoe shine" during incremental backups.
+
+ > - flushing a backup that only went to disk (like amflush in amanda)
+
+ This will be a future feature, since spooling is different from backing
+ up to disk. The future feature will be "migration" which will move a job
+ from one backup Volume to another.
+
- New Storage specifications:
Passed to SD as a sort of BSR record called Storage Specification
Record or SSR.
Job report (Volker Sauer).
- Client does not show busy during Estimate command.
- Implement Console mtx commands.
-- Implement 3 Pools for a Job:
- Job {
- Name = ...
- Full Backup Pool = xxx
- Incremental Backup Pool = yyy
- Differential Backup Pool = zzz
- }
- Add a default DB password to MySQL.
GRANT all privileges ON bacula.* TO bacula@localhost IDENTIFIED BY
'bacula_password';
directories, setting them to be restored.
- Figure out a way to set restore on a directory without recursively
decending. (recurse off?).
+- Fix restore to only pull in last Differential and later Incrementals.
+- Implement 3 Pools for a Job:
+ Job {
+ Name = ...
+ Full Backup Pool = xxx
+ Incremental Backup Pool = yyy
+ Differential Backup Pool = zzz
+ }
#
# shell script to drop Bacula SQLite tables
-bindir=@SQL_BINDIR@
cd @working_dir@
-
-# how do we drop an sqlite database?
-#$bindir/sqlite $* bacula.db <<END-OF-DATA
-#END-OF-DATA
-exit 0
+rm -rf bacula.db
bstrncpy(mr->cLastWritten, row[18]!=NULL?row[18]:"", sizeof(mr->cLastWritten));
mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
bstrncpy(mr->VolStatus, row[19], sizeof(mr->VolStatus));
-
sql_free_result(mdb);
db_unlock(mdb);
db_lock(mdb);
Mmsg(&mdb->cmd,
"SELECT DISTINCT VolumeName FROM JobMedia,Media WHERE "
- "JobMedia.JobId=%u AND JobMedia.MediaId=Media.MediaId ",
+ "JobMedia.JobId=%u AND JobMedia.MediaId=Media.MediaId",
JobId);
Dmsg1(130, "VolNam=%s\n", mdb->cmd);
"MediaType,FirstWritten,LastWritten,LabelDate,VolJobs,"
"VolFiles,VolBlocks,VolMounts,VolBytes,VolErrors,VolWrites,"
"VolCapacityBytes,VolStatus,Recycle,VolRetention,"
- "VolUseDuration,MaxVolJobs,MaxVolFiles,MaxVolBytes "
+ "VolUseDuration,MaxVolJobs,MaxVolFiles,MaxVolBytes,InChanger "
"FROM Media WHERE Media.VolumeName='%s'", mdbr->VolumeName);
} else {
Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,Slot,PoolId,"
"MediaType,FirstWritten,LastWritten,LabelDate,VolJobs,"
"VolFiles,VolBlocks,VolMounts,VolBytes,VolErrors,VolWrites,"
"VolCapacityBytes,VolStatus,Recycle,VolRetention,"
- "VolUseDuration,MaxVolJobs,MaxVolFiles,MaxVolBytes "
+ "VolUseDuration,MaxVolJobs,MaxVolFiles,MaxVolBytes,InChanger "
"FROM Media WHERE Media.PoolId=%u ORDER BY MediaId", mdbr->PoolId);
}
} else {
jcr->JobId, jcr->Job);
/*
- * Get the Pool record
+ * Get the Pool record -- first apply any level defined pools
*/
+ switch (jcr->JobLevel) {
+ case L_FULL:
+ if (jcr->full_pool) {
+ jcr->pool = jcr->full_pool;
+ }
+ break;
+ case L_INCREMENTAL:
+ if (jcr->inc_pool) {
+ jcr->pool = jcr->inc_pool;
+ }
+ break;
+ case L_DIFFERENTIAL:
+ if (jcr->dif_pool) {
+ jcr->pool = jcr->dif_pool;
+ }
+ break;
+ }
memset(&pr, 0, sizeof(pr));
bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
+
while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
/* Try to create the pool */
if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
{"messages", store_res, ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
{"storage", store_res, ITEM(res_job.storage), R_STORAGE, ITEM_REQUIRED, 0},
{"pool", store_res, ITEM(res_job.pool), R_POOL, ITEM_REQUIRED, 0},
+ {"fullbackuppool", store_res, ITEM(res_job.full_pool), R_POOL, 0, 0},
+ {"incrementalbackuppool", store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
+ {"differentialbackuppool", store_res, ITEM(res_job.dif_pool), R_POOL, 0, 0},
{"client", store_res, ITEM(res_job.client), R_CLIENT, ITEM_REQUIRED, 0},
{"fileset", store_res, ITEM(res_job.fileset), R_FILESET, ITEM_REQUIRED, 0},
{"schedule", store_res, ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
if (res->res_job.pool) {
sendit(sock, " --> ");
dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
- } else {
- sendit(sock, "!!! No Pool resource\n");
+ }
+ if (res->res_job.full_pool) {
+ sendit(sock, " --> ");
+ dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
+ }
+ if (res->res_job.inc_pool) {
+ sendit(sock, " --> ");
+ dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
+ }
+ if (res->res_job.dif_pool) {
+ sendit(sock, " --> ");
+ dump_resource(-R_POOL, (RES *)res->res_job.dif_pool, sendit, sock);
}
if (res->res_job.verify_job) {
sendit(sock, " --> ");
res->res_job.fileset = res_all.res_job.fileset;
res->res_job.storage = res_all.res_job.storage;
res->res_job.pool = res_all.res_job.pool;
+ res->res_job.full_pool = res_all.res_job.full_pool;
+ res->res_job.inc_pool = res_all.res_job.inc_pool;
+ res->res_job.dif_pool = res_all.res_job.dif_pool;
res->res_job.verify_job = res_all.res_job.verify_job;
res->res_job.jobdefs = res_all.res_job.jobdefs;
break;
FILESET *fileset; /* What to backup -- Fileset */
STORE *storage; /* Where is device -- Storage daemon */
POOL *pool; /* Where is media -- Media Pool */
+ POOL *full_pool; /* Pool for Full backups */
+ POOL *inc_pool; /* Pool for Incremental backups */
+ POOL *dif_pool; /* Pool for Differental backups */
JOB *verify_job; /* Job name to verify */
JOB *jobdefs; /* Job defaults */
uint32_t NumConcurrentJobs; /* number of concurrent jobs running */
int Priority; /* priority override */
int job_type;
POOL *pool; /* Pool override */
+ POOL *full_pool; /* Pool override */
+ POOL *inc_pool; /* Pool override */
+ POOL *dif_pool; /* Pool override */
STORE *storage; /* Storage override */
MSGS *msgs; /* Messages override */
char *since;
}
pm_strcpy(&jcr->client_name, jcr->client->hdr.name);
jcr->pool = job->pool;
+ jcr->full_pool = job->full_pool;
+ jcr->inc_pool = job->inc_pool;
+ jcr->dif_pool = job->dif_pool;
jcr->catalog = job->client->catalog;
jcr->fileset = job->fileset;
jcr->messages = job->messages;
/* fd_cmds.c */
extern int connect_to_file_daemon(JCR *jcr, int retry_interval,
- int max_retry_time, int verbose);
+ int max_retry_time, int verbose);
extern int send_include_list(JCR *jcr);
extern int send_exclude_list(JCR *jcr);
extern int send_bootstrap_file(JCR *jcr);
extern int get_attributes_and_put_in_catalog(JCR *jcr);
extern int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId);
extern int put_file_into_catalog(JCR *jcr, long file_index, char *fname,
- char *link, char *attr, int stream);
+ char *link, char *attr, int stream);
extern void get_level_since_time(JCR *jcr, char *since, int since_len);
extern int send_run_before_and_after_commands(JCR *jcr);
/* msgchan.c */
extern int connect_to_storage_daemon(JCR *jcr, int retry_interval,
- int max_retry_time, int verbose);
+ int max_retry_time, int verbose);
extern int start_storage_daemon_job(JCR *jcr);
extern int start_storage_daemon_message_thread(JCR *jcr);
extern int bget_dirmsg(BSOCK *bs);
void free_ua_context(UAContext *ua);
/* ua_select.c */
-STORE *select_storage_resource(UAContext *ua);
-JOB *select_job_resource(UAContext *ua);
-JOB *select_restore_job_resource(UAContext *ua);
-CLIENT *select_client_resource(UAContext *ua);
+STORE *select_storage_resource(UAContext *ua);
+JOB *select_job_resource(UAContext *ua);
+JOB *select_restore_job_resource(UAContext *ua);
+CLIENT *select_client_resource(UAContext *ua);
FILESET *select_fileset_resource(UAContext *ua);
-int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr);
-int select_media_dbr(UAContext *ua, MEDIA_DBR *mr);
-int select_pool_dbr(UAContext *ua, POOL_DBR *pr);
-int select_client_dbr(UAContext *ua, CLIENT_DBR *cr);
-
-void start_prompt(UAContext *ua, char *msg);
-void add_prompt(UAContext *ua, char *prompt);
-int do_prompt(UAContext *ua, char *automsg, char *msg, char *prompt, int max_prompt);
-CAT *get_catalog_resource(UAContext *ua);
+int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr);
+int select_media_dbr(UAContext *ua, MEDIA_DBR *mr);
+int select_pool_dbr(UAContext *ua, POOL_DBR *pr);
+int select_client_dbr(UAContext *ua, CLIENT_DBR *cr);
+
+void start_prompt(UAContext *ua, char *msg);
+void add_prompt(UAContext *ua, char *prompt);
+int do_prompt(UAContext *ua, char *automsg, char *msg, char *prompt, int max_prompt);
+CAT *get_catalog_resource(UAContext *ua);
STORE *get_storage_resource(UAContext *ua, int use_default);
-int get_media_type(UAContext *ua, char *MediaType, int max_media);
-int get_pool_dbr(UAContext *ua, POOL_DBR *pr);
-int get_client_dbr(UAContext *ua, CLIENT_DBR *cr);
+int get_media_type(UAContext *ua, char *MediaType, int max_media);
+int get_pool_dbr(UAContext *ua, POOL_DBR *pr);
+int get_client_dbr(UAContext *ua, CLIENT_DBR *cr);
POOL *get_pool_resource(UAContext *ua);
POOL *select_pool_resource(UAContext *ua);
CLIENT *get_client_resource(UAContext *ua);
-int get_job_dbr(UAContext *ua, JOB_DBR *jr);
+int get_job_dbr(UAContext *ua, JOB_DBR *jr);
int find_arg_keyword(UAContext *ua, char **list);
int find_arg(UAContext *ua, char *keyword);
/* Keywords (RHS) permitted in Run records */
static struct s_kw RunFields[] = {
- {"pool", 'P'},
- {"level", 'L'},
- {"storage", 'S'},
- {"messages", 'M'},
- {"priority", 'p'},
- {NULL, 0}
+ {"pool", 'P'},
+ {"fullpool", 'f'},
+ {"incrementalpool", 'i'},
+ {"differentialpool", 'd'},
+ {"level", 'L'},
+ {"storage", 'S'},
+ {"messages", 'M'},
+ {"priority", 'p'},
+ {NULL, 0}
};
/*
}
break;
case 'P': /* Pool */
+ case 'f': /* FullPool */
+ case 'i': /* IncPool */
+ case 'd': /* DifPool */
token = lex_get_token(lc, T_NAME);
if (pass == 2) {
res = GetResWithName(R_POOL, lc->str);
lc->str);
/* NOT REACHED */
}
- lrun.pool = (POOL *)res;
+ switch(RunFields[i].token) {
+ case 'P':
+ lrun.pool = (POOL *)res;
+ break;
+ case 'f':
+ lrun.full_pool = (POOL *)res;
+ break;
+ case 'i':
+ lrun.inc_pool = (POOL *)res;
+ break;
+ case 'd':
+ lrun.dif_pool = (POOL *)res;
+ break;
+ }
}
break;
case 'S': /* storage */
if (run->pool) {
jcr->pool = run->pool; /* override pool */
}
+ if (run->full_pool) {
+ jcr->pool = run->full_pool; /* override full pool */
+ }
+ if (run->inc_pool) {
+ jcr->pool = run->inc_pool; /* override inc pool */
+ }
+ if (run->dif_pool) {
+ jcr->pool = run->dif_pool; /* override dif pool */
+ }
if (run->storage) {
jcr->store = run->storage; /* override storage */
}
"AND JobMedia.JobId=Job.JobId "
"AND JobMedia.MediaId=Media.MediaId";
-char *uar_dec =
+char *uar_dif =
"INSERT INTO temp SELECT Job.JobId,Job.JobTDate,Job.ClientId,"
"Job.Level,Job.JobFiles,Job.StartTime,Media.VolumeName,JobMedia.StartFile,"
"Job.VolSessionId,Job.VolSessionTime "
static int release_cmd(UAContext *ua, char *cmd);
static int update_cmd(UAContext *ua, char *cmd);
static int wait_cmd(UAContext *ua, char *cmd);
+static int setip_cmd(UAContext *ua, char *cmd);
int quit_cmd(UAContext *ua, char *cmd);
{ N_("run"), run_cmd, _("run <job-name>")},
{ N_("status"), status_cmd, _("status [storage | client]=<name>")},
{ N_("setdebug"), setdebug_cmd, _("sets debug level")},
+ { N_("setip"), setip_cmd, _("sets new client address -- if authorized")},
{ N_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")},
{ N_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")},
{ N_("time"), time_cmd, _("print current time")},
switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
case 0:
- bsendmsg(ua, _("Error: Pool %s already exists.\n\
-Use update to change it.\n"), pool->hdr.name);
+ bsendmsg(ua, _("Error: Pool %s already exists.\n"
+ "Use update to change it.\n"), pool->hdr.name);
break;
case -1:
}
+/*
+ * Set a new address in a Client resource. We do this only
+ * if the Console name is the same as the Client name
+ * and the Console can access the client.
+ */
+static int setip_cmd(UAContext *ua, char *cmd)
+{
+ CLIENT *client;
+ if (!ua->cons && acl_access_ok(ua, Client_ACL, ua->cons->hdr.name)) {
+ bsendmsg(ua, _("Illegal command from this console.\n"));
+ return 1;
+ }
+ client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->hdr.name);
+
+ if (!client) {
+ bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->hdr.name);
+ return 1;
+ }
+ LockRes();
+ if (client->address) {
+ free(client->address);
+ }
+ client->address = bstrdup(inet_ntoa(ua->UA_sock->client_addr.sin_addr));
+ bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
+ client->hdr.name, client->address);
+ UnlockRes();
+ return 1;
+}
/*
if (cnt.count == 0) {
if (ua->verbose) {
- bsendmsg(ua, "There are no Jobs associated with Volume %s. Marking it purged.\n",
+ bsendmsg(ua, "There are no Jobs associated with Volume \"%s\". Marking it purged.\n",
mr->VolumeName);
}
stat = mark_media_purged(ua, mr);
free(del.JobId);
}
if (ua->verbose && del.num_del != 0) {
- bsendmsg(ua, _("Pruned %d %s on Volume %s from catalog.\n"), del.num_del,
+ bsendmsg(ua, _("Pruned %d %s on Volume \"%s\" from catalog.\n"), del.num_del,
del.num_del == 1 ? "Job" : "Jobs", mr->VolumeName);
}
extern char *uar_create_temp1, *uar_last_full, *uar_full;
extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
-extern char *uar_jobid_fileindex, *uar_dec, *uar_sel_all_temp;
+extern char *uar_jobid_fileindex, *uar_dif, *uar_sel_all_temp;
struct NAME_LIST {
int restore_cmd(UAContext *ua, char *cmd)
{
RESTORE_CTX rx; /* restore context */
- JOB *job = NULL;
+ JOB *job;
int i;
memset(&rx, 0, sizeof(rx));
-
rx.path = get_pool_memory(PM_FNAME);
rx.fname = get_pool_memory(PM_FNAME);
rx.JobIds = get_pool_memory(PM_FNAME);
/* Ensure there is at least one Restore Job */
LockRes();
- while ( (job = (JOB *)GetNextRes(R_JOB, (RES *)job)) ) {
+ foreach_res(job, R_JOB) {
if (job->JobType == JT_RESTORE) {
if (!rx.restore_job) {
rx.restore_job = job;
break;
case 5: /* pool specified */
i = find_arg_with_value(ua, "pool");
- if (i >= 0) {
+ if (i >= 0 && acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
} else {
bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
JobId, db_strerror(ua->db));
return 0;
}
+ if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
+ bsendmsg(ua, _("No authorization. Job \"%s\" not selected.\n"),
+ jr.Name);
+ continue;
+ }
rx->TotalFiles += jr.JobFiles;
}
return 1;
goto bail_out;
}
- /* Now find most recent Decremental Job after Full save, if any */
- Mmsg(&rx->query, uar_dec, edit_uint64(rx->JobTDate, ed1), date,
+ /* Now find most recent Differental Job after Full save, if any */
+ Mmsg(&rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
cr.ClientId, fsr.FileSet, pool_select);
if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
bsendmsg(ua, "%s\n", db_strerror(ua->db));
}
- /* Now update JobTDate to lock onto Decremental, if any */
+ /* Now update JobTDate to lock onto Differental, if any */
rx->JobTDate = 0;
if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
bsendmsg(ua, "%s\n", db_strerror(ua->db));
goto bail_out;
}
- /* Now find all Incremental Jobs after Full/Dec save */
+ /* Now find all Incremental Jobs after Full/dif save */
Mmsg(&rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
cr.ClientId, fsr.FileSet, pool_select);
if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
bsendmsg(ua, "%s", db_strerror(ua->db));
return 0;
}
+ if (!acl_access_ok(ua, Pool_ACL, pr->Name)) {
+ bsendmsg(ua, _("No access to Pool \"%s\"\n"), pr->Name);
+ return 0;
+ }
return 1;
}
i = 0;
foreach_res(store, R_STORAGE) {
found = false;
+ if (!acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
+ continue;
+ }
for (j=0; j<i; j++) {
if (strcmp(unique_store[j]->address, store->address) == 0 &&
unique_store[j]->SDport == store->SDport) {
i = 0;
foreach_res(client, R_CLIENT) {
found = false;
+ if (!acl_access_ok(ua, Client_ACL, client->hdr.name)) {
+ continue;
+ }
for (j=0; j<i; j++) {
if (strcmp(unique_client[j]->address, client->address) == 0 &&
unique_client[j]->FDport == client->FDport) {
/* Loop through all jobs */
LockRes();
foreach_res(job, R_JOB) {
+ if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
+ continue;
+ }
for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
level = job->level;
if (run->level) {
bsendmsg(ua, _("Level JobId Job Status\n"));
bsendmsg(ua, _("====================================================================\n"));
for (jcr=NULL; (jcr=get_next_jcr(jcr)); njobs++) {
+ if (!acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
+ continue;
+ }
if (jcr->JobId == 0) { /* this is us */
njobs--;
free_locked_jcr(jcr);
STORE *store; /* Storage resource */
CLIENT *client; /* Client resource */
POOL *pool; /* Pool resource */
+ POOL *full_pool; /* Full backup pool resource */
+ POOL *inc_pool; /* Incremental backup pool resource */
+ POOL *dif_pool; /* Differential backup pool resource */
FILESET *fileset; /* FileSet resource */
CAT *catalog; /* Catalog resource */
MSGS *messages; /* Default message handler */
extern CURES res_all;
extern int res_all_size;
-static int res_locked = 0; /* set when resource chains locked */
+static bool res_locked = false; /* set when resource chains locked */
/* Forward referenced subroutines */
static void scan_types(LEX *lc, MSGS *msg, int dest, char *where, char *cmd);
void LockRes()
{
P(res_mutex);
- res_locked = 1;
+ res_locked = true;
}
void UnlockRes()
{
- res_locked = 0;
+ res_locked = false;
V(res_mutex);
}
}
-
/*
* Request the sysop to create an appendable volume
*
{
int stat = 0, jstat;
bool unmounted;
+ bool first = true;
Dmsg0(130, "enter dir_ask_sysop_to_create_appendable_volume\n");
ASSERT(dev->dev_blocked);
Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
return 0;
}
- if (dir_find_next_appendable_volume(jcr)) { /* get suggested volume */
+ /* First pass, we *know* there are no appendable volumes, so no need to call */
+ if (!first && dir_find_next_appendable_volume(jcr)) { /* get suggested volume */
jstat = JS_WaitMount;
unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
(dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
Dmsg0(100, "Return 1 from mount without wait.\n");
return 1;
}
- Jmsg(jcr, M_MOUNT, 0, _(
+ if (!dev->poll) {
+ Jmsg(jcr, M_MOUNT, 0, _(
"Please mount Volume \"%s\" on Storage Device \"%s\" for Job %s\n"
"Use \"mount\" command to release Job.\n"),
jcr->VolumeName, jcr->dev_name, jcr->Job);
- Dmsg3(190, "Mount %s on %s for Job %s\n",
- jcr->VolumeName, jcr->dev_name, jcr->Job);
+ Dmsg3(190, "Mount %s on %s for Job %s\n",
+ jcr->VolumeName, jcr->dev_name, jcr->Job);
+ }
} else {
jstat = JS_WaitMedia;
if (!dev->poll) {
jcr->pool_name);
}
}
+ first = false;
jcr->JobStatus = jstat;
dir_send_job_status(jcr);
/* #define NEW_LOCK 1 */
-#define new_lock_device(dev) _new_lock_device(__FILE__, __LINE__, (dev))
+#define new_lock_device(dev) _new_lock_device(__FILE__, __LINE__, (dev))
#define new_lock_device_state(dev,state) _new_lock_device(__FILE__, __LINE__, (dev), (state))
-#define new_unlock_device(dev) _new_unlock_device(__FILE__, __LINE__, (dev))
+#define new_unlock_device(dev) _new_unlock_device(__FILE__, __LINE__, (dev))
#define lock_device(d) _lock_device(__FILE__, __LINE__, (d))
#define unlock_device(d) _unlock_device(__FILE__, __LINE__, (d))
#define give_back_device_lock(d, p) _give_back_device_lock(__FILE__, __LINE__, (d), (p))
/* Arguments to open_dev() */
-#define READ_WRITE 0
-#define READ_ONLY 1
+#define READ_WRITE 0
+#define READ_ONLY 1
#define OPEN_READ_WRITE 0
-#define OPEN_READ_ONLY 1
+#define OPEN_READ_ONLY 1
#define OPEN_WRITE_ONLY 2
/* Generic status bits returned from status_dev() */
-#define BMT_TAPE (1<<0) /* is tape device */
-#define BMT_EOF (1<<1) /* just read EOF */
-#define BMT_BOT (1<<2) /* at beginning of tape */
-#define BMT_EOT (1<<3) /* end of tape reached */
-#define BMT_SM (1<<4) /* DDS setmark */
-#define BMT_EOD (1<<5) /* DDS at end of data */
-#define BMT_WR_PROT (1<<6) /* tape write protected */
-#define BMT_ONLINE (1<<7) /* tape online */
-#define BMT_DR_OPEN (1<<8) /* tape door open */
-#define BMT_IM_REP_EN (1<<9) /* immediate report enabled */
+#define BMT_TAPE (1<<0) /* is tape device */
+#define BMT_EOF (1<<1) /* just read EOF */
+#define BMT_BOT (1<<2) /* at beginning of tape */
+#define BMT_EOT (1<<3) /* end of tape reached */
+#define BMT_SM (1<<4) /* DDS setmark */
+#define BMT_EOD (1<<5) /* DDS at end of data */
+#define BMT_WR_PROT (1<<6) /* tape write protected */
+#define BMT_ONLINE (1<<7) /* tape online */
+#define BMT_DR_OPEN (1<<8) /* tape door open */
+#define BMT_IM_REP_EN (1<<9) /* immediate report enabled */
/* Test capabilities */
#define dev_cap(dev, cap) ((dev)->capabilities & (cap))
/* Bits for device capabilities */
-#define CAP_EOF (1<<0) /* has MTWEOF */
-#define CAP_BSR (1<<1) /* has MTBSR */
-#define CAP_BSF (1<<2) /* has MTBSF */
-#define CAP_FSR (1<<3) /* has MTFSR */
-#define CAP_FSF (1<<4) /* has MTFSF */
-#define CAP_EOM (1<<5) /* has MTEOM */
-#define CAP_REM (1<<6) /* is removable media */
-#define CAP_RACCESS (1<<7) /* is random access device */
-#define CAP_AUTOMOUNT (1<<8) /* Read device at start to see what is there */
-#define CAP_LABEL (1<<9) /* Label blank tapes */
-#define CAP_ANONVOLS (1<<10) /* Mount without knowing volume name */
-#define CAP_ALWAYSOPEN (1<<11) /* always keep device open */
+#define CAP_EOF (1<<0) /* has MTWEOF */
+#define CAP_BSR (1<<1) /* has MTBSR */
+#define CAP_BSF (1<<2) /* has MTBSF */
+#define CAP_FSR (1<<3) /* has MTFSR */
+#define CAP_FSF (1<<4) /* has MTFSF */
+#define CAP_EOM (1<<5) /* has MTEOM */
+#define CAP_REM (1<<6) /* is removable media */
+#define CAP_RACCESS (1<<7) /* is random access device */
+#define CAP_AUTOMOUNT (1<<8) /* Read device at start to see what is there */
+#define CAP_LABEL (1<<9) /* Label blank tapes */
+#define CAP_ANONVOLS (1<<10) /* Mount without knowing volume name */
+#define CAP_ALWAYSOPEN (1<<11) /* always keep device open */
#define CAP_AUTOCHANGER (1<<12) /* AutoChanger */
#define CAP_OFFLINEUNMOUNT (1<<13) /* Offline before unmount */
-#define CAP_STREAM (1<<14) /* Stream device */
-#define CAP_BSFATEOM (1<<15) /* Backspace file at EOM */
-#define CAP_FASTFSF (1<<16) /* Fast forward space file */
-#define CAP_TWOEOF (1<<17) /* Write two eofs for EOM */
+#define CAP_STREAM (1<<14) /* Stream device */
+#define CAP_BSFATEOM (1<<15) /* Backspace file at EOM */
+#define CAP_FASTFSF (1<<16) /* Fast forward space file */
+#define CAP_TWOEOF (1<<17) /* Write two eofs for EOM */
+#define CAP_CLOSEONPOLL (1<<18) /* Close device on polling */
/* Test state */
#define dev_state(dev, st_state) ((dev)->state & (st_state))
/* Device state bits */
-#define ST_OPENED (1<<0) /* set when device opened */
-#define ST_TAPE (1<<1) /* is a tape device */
-#define ST_FILE (1<<2) /* is a file device */
-#define ST_FIFO (1<<3) /* is a fifo device */
-#define ST_PROG (1<<4) /* is a program device */
-#define ST_LABEL (1<<5) /* label found */
+#define ST_OPENED (1<<0) /* set when device opened */
+#define ST_TAPE (1<<1) /* is a tape device */
+#define ST_FILE (1<<2) /* is a file device */
+#define ST_FIFO (1<<3) /* is a fifo device */
+#define ST_PROG (1<<4) /* is a program device */
+#define ST_LABEL (1<<5) /* label found */
#define ST_MALLOC (1<<6) /* dev packet malloc'ed in init_dev() */
-#define ST_APPEND (1<<7) /* ready for Bacula append */
-#define ST_READ (1<<8) /* ready for Bacula read */
-#define ST_EOT (1<<9) /* at end of tape */
-#define ST_WEOT (1<<10) /* Got EOT on write */
-#define ST_EOF (1<<11) /* Read EOF i.e. zero bytes */
-#define ST_NEXTVOL (1<<12) /* Start writing on next volume */
-#define ST_SHORT (1<<13) /* Short block read */
+#define ST_APPEND (1<<7) /* ready for Bacula append */
+#define ST_READ (1<<8) /* ready for Bacula read */
+#define ST_EOT (1<<9) /* at end of tape */
+#define ST_WEOT (1<<10) /* Got EOT on write */
+#define ST_EOF (1<<11) /* Read EOF i.e. zero bytes */
+#define ST_NEXTVOL (1<<12) /* Start writing on next volume */
+#define ST_SHORT (1<<13) /* Short block read */
/* dev_blocked states (mutually exclusive) */
#define BST_NOT_BLOCKED 0 /* not blocked */
-#define BST_UNMOUNTED 1 /* User unmounted device */
+#define BST_UNMOUNTED 1 /* User unmounted device */
#define BST_WAITING_FOR_SYSOP 2 /* Waiting for operator to mount tape */
#define BST_DOING_ACQUIRE 3 /* Opening/validating/moving tape */
#define BST_WRITING_LABEL 4 /* Labeling a tape */
#define BST_UNMOUNTED_WAITING_FOR_SYSOP 5 /* Closed by user during mount request */
-#define BST_MOUNT 6 /* Mount request */
+#define BST_MOUNT 6 /* Mount request */
/* Volume Catalog Information structure definition */
struct VOLUME_CAT_INFO {
/* Media info for the current Volume */
- uint32_t VolCatJobs; /* number of jobs on this Volume */
- uint32_t VolCatFiles; /* Number of files */
- uint32_t VolCatBlocks; /* Number of blocks */
- uint64_t VolCatBytes; /* Number of bytes written */
- uint32_t VolCatMounts; /* Number of mounts this volume */
- uint32_t VolCatErrors; /* Number of errors this volume */
- uint32_t VolCatWrites; /* Number of writes this volume */
- uint32_t VolCatReads; /* Number of reads this volume */
- uint64_t VolCatRBytes; /* Number of bytes read */
- uint32_t VolCatRecycles; /* Number of recycles this volume */
- int32_t Slot; /* Slot in changer */
- bool InChanger; /* Set if vol in current magazine */
- uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
- uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
- uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
+ uint32_t VolCatJobs; /* number of jobs on this Volume */
+ uint32_t VolCatFiles; /* Number of files */
+ uint32_t VolCatBlocks; /* Number of blocks */
+ uint64_t VolCatBytes; /* Number of bytes written */
+ uint32_t VolCatMounts; /* Number of mounts this volume */
+ uint32_t VolCatErrors; /* Number of errors this volume */
+ uint32_t VolCatWrites; /* Number of writes this volume */
+ uint32_t VolCatReads; /* Number of reads this volume */
+ uint64_t VolCatRBytes; /* Number of bytes read */
+ uint32_t VolCatRecycles; /* Number of recycles this volume */
+ int32_t Slot; /* Slot in changer */
+ bool InChanger; /* Set if vol in current magazine */
+ uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
+ uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
+ uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
uint64_t VolCatCapacityBytes; /* capacity estimate */
- uint64_t VolReadTime; /* time spent reading */
- uint64_t VolWriteTime; /* time spent writing this Volume */
- char VolCatStatus[20]; /* Volume status */
+ uint64_t VolReadTime; /* time spent reading */
+ uint64_t VolWriteTime; /* time spent writing this Volume */
+ char VolCatStatus[20]; /* Volume status */
char VolCatName[MAX_NAME_LENGTH]; /* Desired volume to mount */
-};
+};
typedef struct s_steal_lock {
- pthread_t no_wait_id; /* id of no wait thread */
- int dev_blocked; /* state */
- int dev_prev_blocked; /* previous blocked state */
+ pthread_t no_wait_id; /* id of no wait thread */
+ int dev_blocked; /* state */
+ int dev_prev_blocked; /* previous blocked state */
} bsteal_lock_t;
-struct DEVRES; /* Device resource defined in stored_conf.h */
+struct DEVRES; /* Device resource defined in stored_conf.h */
/* Device structure definition */
struct DEVICE {
public:
- DEVICE *next; /* pointer to next open device */
- DEVICE *prev; /* pointer to prev open device */
- JCR *attached_jcrs; /* attached JCR list */
- pthread_mutex_t mutex; /* access control */
- pthread_cond_t wait; /* thread wait variable */
+ DEVICE *next; /* pointer to next open device */
+ DEVICE *prev; /* pointer to prev open device */
+ JCR *attached_jcrs; /* attached JCR list */
+ pthread_mutex_t mutex; /* access control */
+ pthread_cond_t wait; /* thread wait variable */
pthread_cond_t wait_next_vol; /* wait for tape to be mounted */
- pthread_t no_wait_id; /* this thread must not wait */
- int dev_blocked; /* set if we must wait (i.e. change tape) */
- int dev_prev_blocked; /* previous blocked state */
- int num_waiting; /* number of threads waiting */
- int num_writers; /* number of writing threads */
- int use_count; /* usage count on this device */
- int fd; /* file descriptor */
- int capabilities; /* capabilities mask */
- int state; /* state mask */
- int dev_errno; /* Our own errno */
- int mode; /* read/write modes */
- char *dev_name; /* device name */
- char *errmsg; /* nicely edited error message */
- uint32_t block_num; /* current block number base 0 */
- uint32_t file; /* current file number base 0 */
- uint64_t file_addr; /* Current file read/write address */
- uint32_t EndBlock; /* last block written */
- uint32_t EndFile; /* last file written */
- uint32_t min_block_size; /* min block size */
- uint32_t max_block_size; /* max block size */
- uint64_t max_volume_size; /* max bytes to put on one volume */
- uint64_t max_file_size; /* max file size to put in one file on volume */
- uint64_t volume_capacity; /* advisory capacity */
- uint32_t max_rewind_wait; /* max secs to allow for rewind */
- uint32_t max_open_wait; /* max secs to allow for open */
- uint32_t max_open_vols; /* max simultaneous open volumes */
- utime_t vol_poll_interval; /* interval between polling Vol mount */
- DEVRES *device; /* pointer to Device Resource */
- btimer_id tid; /* timer id */
-
- VOLUME_CAT_INFO VolCatInfo; /* Volume Catalog Information */
- VOLUME_LABEL VolHdr; /* Actual volume label */
+ pthread_t no_wait_id; /* this thread must not wait */
+ int dev_blocked; /* set if we must wait (i.e. change tape) */
+ int dev_prev_blocked; /* previous blocked state */
+ int num_waiting; /* number of threads waiting */
+ int num_writers; /* number of writing threads */
+ int use_count; /* usage count on this device */
+ int fd; /* file descriptor */
+ int capabilities; /* capabilities mask */
+ int state; /* state mask */
+ int dev_errno; /* Our own errno */
+ int mode; /* read/write modes */
+ char *dev_name; /* device name */
+ char *errmsg; /* nicely edited error message */
+ uint32_t block_num; /* current block number base 0 */
+ uint32_t file; /* current file number base 0 */
+ uint64_t file_addr; /* Current file read/write address */
+ uint32_t EndBlock; /* last block written */
+ uint32_t EndFile; /* last file written */
+ uint32_t min_block_size; /* min block size */
+ uint32_t max_block_size; /* max block size */
+ uint64_t max_volume_size; /* max bytes to put on one volume */
+ uint64_t max_file_size; /* max file size to put in one file on volume */
+ uint64_t volume_capacity; /* advisory capacity */
+ uint32_t max_rewind_wait; /* max secs to allow for rewind */
+ uint32_t max_open_wait; /* max secs to allow for open */
+ uint32_t max_open_vols; /* max simultaneous open volumes */
+ utime_t vol_poll_interval; /* interval between polling Vol mount */
+ DEVRES *device; /* pointer to Device Resource */
+ btimer_id tid; /* timer id */
+
+ VOLUME_CAT_INFO VolCatInfo; /* Volume Catalog Information */
+ VOLUME_LABEL VolHdr; /* Actual volume label */
/* Device wait times ***FIXME*** look at durations */
char BadVolName[MAX_NAME_LENGTH]; /* Last wrong Volume mounted */
- bool poll; /* set to poll Volume */
+ bool poll; /* set to poll Volume */
int min_wait;
int max_wait;
int max_num_wait;
* dependent. Arrgggg!
*/
#ifndef MTEOM
-#ifdef MTSEOD
+#ifdef MTSEOD
#define MTEOM MTSEOD
#endif
#ifdef MTEOD
* Get Director's idea of what tape we should have mounted.
* in jcr->VolCatInfo
*/
- Dmsg0(100, "Before dir_find_next\n");
+ Dmsg0(200, "Before dir_find_next_appendable_volume.\n");
while (!dir_find_next_appendable_volume(jcr)) {
- Dmsg0(100, "not dir_find_next\n");
+ Dmsg0(200, "not dir_find_next\n");
if (!dir_ask_sysop_to_create_appendable_volume(jcr, dev)) {
return 0;
}
+ Dmsg0(200, "Again dir_find_next_append...\n");
}
if (job_canceled(jcr)) {
return 0;
}
Dmsg1(100, "want vol=%s\n", jcr->VolumeName);
+ if (dev->poll && dev_cap(dev, CAP_CLOSEONPOLL)) {
+ force_close_dev(dev);
+ }
+
/* Open device */
if (!(dev_state(dev, ST_OPENED))) {
int mode;
{"labelmedia", store_yesno, ITEM(res_dev.cap_bits), CAP_LABEL, ITEM_DEFAULT, 0},
{"alwaysopen", store_yesno, ITEM(res_dev.cap_bits), CAP_ALWAYSOPEN, ITEM_DEFAULT, 1},
{"autochanger", store_yesno, ITEM(res_dev.cap_bits), CAP_AUTOCHANGER, ITEM_DEFAULT, 0},
+ {"closeonpoll", store_yesno, ITEM(res_dev.cap_bits), CAP_CLOSEONPOLL, ITEM_DEFAULT, 0},
{"changerdevice", store_strname,ITEM(res_dev.changer_name), 0, 0, 0},
{"changercommand", store_strname,ITEM(res_dev.changer_command), 0, 0, 0},
{"maximumchangerwait", store_pint, ITEM(res_dev.max_changer_wait), 0, ITEM_DEFAULT, 5 * 60},
#undef VERSION
#define VERSION "1.33"
#define VSTRING "1"
-#define BDATE "16 Jan 2004"
-#define LSMDATE "16Jan04"
+#define BDATE "22 Jan 2004"
+#define LSMDATE "22Jan04"
/* Debug flags */
#undef DEBUG