Release Notes for Bacula 1.30
- Bacula code: Total files = 231 Total lines = 65,064 (*.h *.c *.in)
+ Bacula code: Total files = 233 Total lines = 65,765 (*.h *.c *.in)
Major Changes this Release:
-- The Windows Client now uses Cygwin 1.3.20 and should be much more reliable.
+- The Windows Client now uses Cygwin 1.3.20 and should be much
+ more reliable.
- Implemented save/restore of chflags for FreeBSD systems.
- Support for FreeBSD tape drives.
- Support for SHA1 signatures in addition to MD5 (more secure).
+- The btape "test" command is much more comprehensive and automatically
+ tries different options.
Other Changes this Release:
- Added "BSF at EOM = yes/no" for supporting FreeBSD tape drives.
- The | and < options are now available for Excludes.
- Multiple Directors in the GNOME Console now work (thanks Lutz Kttler).
-- The btape "test" command is much more comprehensive and automatically
- tries different options.
- Installation on Cygwin systems is now supported.
- GNOME Console has font support thanks to Phil Stracchino.
- Solaris 2.6 now supported.
- Partial support for AIX systems.
- Cycle through tapes with "RecycleOldestVolume=yes" (dangerous).
- Support for Win95 systems (I hope).
-- Console program now supported on Win32 systems.
+- Console program now supported on Win32 systems as well as
+ several other utility programs.
+- Full listing of most catalog records (llist command).
+- Relabel Purged tapes with the relabel command.
Items to note:
- multiple simultaneous Volumes
For 1.30 release:
-- Pool requested twice in "relabel" command.
- Must delete old volume from catalog.
-- Make non-zero status from RunJobBefore/After error the job.
+- Add IP address to authentication failures. Implement M_SECURITY
+ message class.
- Remove kern and kelvin from mysql_grant...
- Install grant_mysql...
- Look at Python for a Bacula scripting language -- www.python.org
- add #define ENABLE_NLS for Gnome compile on SuSE.
-- Need define of int_least16_t in sha1.h for SuSE.
- Figure out how to use ssh to protect Bacula communications.
- Fix "access not allowed" for backup of files on WinXP.
- Issue message to mount a new tape before the rewind.
that no longer exist on the machine -- so that we maintain
say backups for 30 days, but if the file is deleted, we maintain
the last copy for 1 year. -- answer Volume retention.
+- Make non-zero status from RunJobBefore/After error the job.
+- Need define of int_least16_t in sha1.h for SuSE.
*/
int db_delete_media_record(void *jcr, B_DB *mdb, MEDIA_DBR *mr)
{
+ db_lock(mdb);
if (mr->MediaId == 0 && !db_get_media_record(jcr, mdb, mr)) {
+ db_unlock(mdb);
return 0;
}
- /* Delete associated records */
- do_media_purge(mdb, mr);
+ /* Do purge if not already purged */
+ if (strcmp(mr->VolStatus, "Purged") != 0) {
+ /* Delete associated records */
+ do_media_purge(mdb, mr);
+ }
Mmsg(&mdb->cmd, "DELETE FROM Media WHERE MediaId=%d", mr->MediaId);
db_sql_query(mdb, mdb->cmd, NULL, (void *)NULL);
+ db_unlock(mdb);
return 1;
}
*/
int db_purge_media_record(void *jcr, B_DB *mdb, MEDIA_DBR *mr)
{
+ db_lock(mdb);
if (mr->MediaId == 0 && !db_get_media_record(jcr, mdb, mr)) {
+ db_unlock(mdb);
return 0;
}
/* Delete associated records */
- do_media_purge(mdb, mr);
+ do_media_purge(mdb, mr); /* Note, always purge */
/* Mark Volume as purged */
strcpy(mr->VolStatus, "Purged");
if (!db_update_media_record(jcr, mdb, mr)) {
+ db_unlock(mdb);
return 0;
}
+ db_unlock(mdb);
return 1;
}
scheduler.c sql_cmds.c \
ua_cmds.c ua_dotcmds.c \
ua_db_query.c ua_retention.c \
- ua_input.c ua_output.c ua_prune.c \
+ ua_input.c ua_label.c ua_output.c ua_prune.c \
ua_purge.c ua_restore.c ua_run.c \
ua_select.c ua_server.c \
ua_status.c verify.c
scheduler.o sql_cmds.o \
ua_cmds.o ua_dotcmds.o \
ua_db_query.o ua_retention.o \
- ua_input.o ua_output.o ua_prune.o \
+ ua_input.o ua_label.o ua_output.o ua_prune.o \
ua_purge.o ua_restore.o ua_run.o \
ua_select.o ua_server.o \
ua_status.o verify.o
sm_check(__FILE__, __LINE__, True);
if (!acquire_resource_locks(jcr)) {
- set_jcr_job_status(jcr, JS_Cancelled);
+ set_jcr_job_status(jcr, JS_Canceled);
}
Dmsg0(200, "=====Start Job=========\n");
jcr->start_time = time(NULL); /* set the real start time */
- Dmsg2(200, "jcr->JobStatus=%d %c\n", jcr->JobStatus, (char)jcr->JobStatus);
- if (job_cancelled(jcr)) {
+
+ if (job_canceled(jcr)) {
update_job_end_record(jcr);
} else if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
(utime_t)(jcr->start_time - jcr->sched_time)) {
- Jmsg(jcr, M_FATAL, 0, _("Job cancelled because max start delay time exceeded.\n"));
- set_jcr_job_status(jcr, JS_Cancelled);
+ Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
+ set_jcr_job_status(jcr, JS_Canceled);
update_job_end_record(jcr);
} else {
before = edit_run_codes(jcr, before, jcr->job->RunBeforeJob);
status = run_program(before, 0, NULL);
+ if (status != 0) {
+ Jmsg(jcr, M_FATAL, 0, _("RunBeforeJob returned non-zero status=%d\n"),
+ status);
+ set_jcr_job_status(jcr, JS_FatalError);
+ update_job_end_record(jcr);
+ free_pool_memory(before);
+ goto bail_out;
+ }
free_pool_memory(before);
}
switch (jcr->JobType) {
after = edit_run_codes(jcr, after, jcr->job->RunAfterJob);
status = run_program(after, 0, NULL);
+ if (status != 0) {
+ Jmsg(jcr, M_FATAL, 0, _("RunAfterJob returned non-zero status=%d\n"),
+ status);
+ set_jcr_job_status(jcr, JS_FatalError);
+ update_job_end_record(jcr);
+ }
free_pool_memory(after);
}
}
+bail_out:
release_resource_locks(jcr);
Dmsg0(50, "Before free jcr\n");
free_jcr(jcr);
extern int prunecmd(UAContext *ua, char *cmd);
extern int purgecmd(UAContext *ua, char *cmd);
extern int restorecmd(UAContext *ua, char *cmd);
+extern int labelcmd(UAContext *ua, char *cmd);
+extern int relabelcmd(UAContext *ua, char *cmd);
/* Forward referenced functions */
static int addcmd(UAContext *ua, char *cmd), createcmd(UAContext *ua, char *cmd), cancelcmd(UAContext *ua, char *cmd);
static int helpcmd(UAContext *ua, char *cmd);
static int deletecmd(UAContext *ua, char *cmd);
static int usecmd(UAContext *ua, char *cmd), unmountcmd(UAContext *ua, char *cmd);
-static int labelcmd(UAContext *ua, char *cmd), mountcmd(UAContext *ua, char *cmd), updatecmd(UAContext *ua, char *cmd);
-static int relabelcmd(UAContext *ua, char *cmd), mountcmd(UAContext *ua, char *cmd), updatecmd(UAContext *ua, char *cmd);
static int versioncmd(UAContext *ua, char *cmd), automountcmd(UAContext *ua, char *cmd);
static int timecmd(UAContext *ua, char *cmd);
static int update_volume(UAContext *ua);
static int update_pool(UAContext *ua);
static int delete_volume(UAContext *ua);
static int delete_pool(UAContext *ua);
-static int do_label(UAContext *ua, char *cmd, int relabel);
+static int mountcmd(UAContext *ua, char *cmd);
+static int updatecmd(UAContext *ua, char *cmd);
int quitcmd(UAContext *ua, char *cmd);
if (strcasecmp(ua->argk[i], _("client")) == 0) {
client = NULL;
if (ua->argv[i]) {
- client = (CLIENT *) GetResWithName(R_CLIENT, ua->argv[i]);
+ client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
if (client) {
do_client_setdebug(ua, client, level);
return 1;
do_client_setdebug(ua, client, level);
return 1;
}
+ }
+ if (strcasecmp(ua->argk[i], _("store")) == 0 ||
+ strcasecmp(ua->argk[i], _("storage")) == 0) {
+ store = NULL;
+ if (ua->argv[i]) {
+ store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
+ if (store) {
+ do_storage_setdebug(ua, store, level);
+ return 1;
+ }
+ }
store = get_storage_resource(ua, cmd);
if (store) {
do_storage_setdebug(ua, store, level);
}
-/*
- * Label a tape
- *
- * label storage=xxx volume=vvv
- */
-static int labelcmd(UAContext *ua, char *cmd)
-{
- return do_label(ua, cmd, 0); /* standard label */
-}
-
-static int relabelcmd(UAContext *ua, char *cmd)
-{
- return do_label(ua, cmd, 1); /* relabel tape */
-}
-
-
-/*
- * Common routine for both label and relabel
- */
-static int do_label(UAContext *ua, char *cmd, int relabel)
-{
- STORE *store;
- BSOCK *sd;
- char dev_name[MAX_NAME_LENGTH];
- MEDIA_DBR mr, omr;
- POOL_DBR pr;
- int ok = FALSE;
- int mounted = FALSE;
- int i;
- int slot = 0;
- static char *name_keyword[] = {
- "name",
- NULL};
-
- static char *vol_keyword[] = {
- "volume",
- NULL};
-
-
- if (!open_db(ua)) {
- return 1;
- }
- store = get_storage_resource(ua, cmd);
- if (!store) {
- return 1;
- }
-
- /* If relabel get name of Volume to relabel */
- if (relabel) {
- i = find_arg_keyword(ua, vol_keyword);
- if (i >= 0 && ua->argv[i]) {
- memset(&omr, 0, sizeof(omr));
- bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
- if (!db_get_media_record(ua->jcr, ua->db, &omr)) {
- bsendmsg(ua, "%s", db_strerror(ua->db));
- goto getVol;
- }
- goto gotVol;
- }
-getVol:
- if (!select_pool_and_media_dbr(ua, &pr, &omr)) {
- return 1;
- }
-
-gotVol:
- if (strcmp(omr.VolStatus, "Purged") != 0) {
- bsendmsg(ua, _("Volume \"%s\" has VolStatus %s. It must be purged before relabeling.\n"),
- omr.VolumeName, omr.VolStatus);
- return 1;
- }
- }
-
- i = find_arg_keyword(ua, name_keyword);
- if (i >=0 && ua->argv[i]) {
- strcpy(ua->cmd, ua->argv[i]);
- goto gotName;
- }
-
-getName:
- if (!get_cmd(ua, _("Enter new Volume name: "))) {
- return 1;
- }
-gotName:
- /* ****FIXME*** be much more restrictive in the name */
- if (strpbrk(ua->cmd, "`~!@#$%^&*()[]{}|\\;'\"<>?,/")) {
- bsendmsg(ua, _("Illegal character | in a volume name.\n"));
- goto getName;
- }
- if (strlen(ua->cmd) >= MAX_NAME_LENGTH) {
- bsendmsg(ua, _("Volume name too long.\n"));
- goto getVol;
- }
- if (strlen(ua->cmd) == 0) {
- bsendmsg(ua, _("Volume name must be at least one character long.\n"));
- goto getName;
- }
-
- memset(&mr, 0, sizeof(mr));
- strcpy(mr.VolumeName, ua->cmd);
- if (db_get_media_record(ua->jcr, ua->db, &mr)) {
- bsendmsg(ua, _("Media record for new Volume \"%s\" already exists.\n"),
- mr.VolumeName);
- goto getName;
- }
-
- /* Do some more checking on slot ****FIXME**** */
- if (store->autochanger) {
- if (!get_cmd(ua, _("Enter slot (0 for none): "))) {
- return 1;
- }
- slot = atoi(ua->cmd);
- }
- strcpy(mr.MediaType, store->media_type);
- mr.Slot = slot;
-
- memset(&pr, 0, sizeof(pr));
- if (!select_pool_dbr(ua, &pr)) {
- return 1;
- }
-
- ua->jcr->store = store;
- bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"),
- store->hdr.name, store->address, store->SDport);
- if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
- bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
- return 1;
- }
- sd = ua->jcr->store_bsock;
- strcpy(dev_name, store->dev_name);
- bash_spaces(dev_name);
- bash_spaces(mr.VolumeName);
- bash_spaces(mr.MediaType);
- bash_spaces(pr.Name);
- if (relabel) {
- bash_spaces(omr.VolumeName);
- bnet_fsend(sd, _("relabel %s OldName=%s NewName=%s PoolName=%s MediaType=%s Slot=%d"),
- dev_name, omr.VolumeName, mr.VolumeName, pr.Name, mr.MediaType, mr.Slot);
- bsendmsg(ua, _("Sending relabel command ...\n"));
- } else {
- bnet_fsend(sd, _("label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d"),
- dev_name, mr.VolumeName, pr.Name, mr.MediaType, mr.Slot);
- bsendmsg(ua, _("Sending label command ...\n"));
- }
- while (bget_msg(sd, 0) >= 0) {
- bsendmsg(ua, "%s", sd->msg);
- if (strncmp(sd->msg, "3000 OK label.", 14) == 0) {
- ok = TRUE;
- } else {
- bsendmsg(ua, _("Label command failed.\n"));
- }
- }
- ua->jcr->store_bsock = NULL;
- unbash_spaces(dev_name);
- unbash_spaces(mr.VolumeName);
- unbash_spaces(mr.MediaType);
- unbash_spaces(pr.Name);
- mr.LabelDate = time(NULL);
- if (ok) {
- set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
- if (db_create_media_record(ua->jcr, ua->db, &mr)) {
- bsendmsg(ua, _("Media record for Volume \"%s\" successfully created.\n"),
- mr.VolumeName);
- if (ua->automount) {
- bsendmsg(ua, _("Requesting mount %s ...\n"), dev_name);
- bash_spaces(dev_name);
- bnet_fsend(sd, "mount %s", dev_name);
- unbash_spaces(dev_name);
- while (bnet_recv(sd) >= 0) {
- bsendmsg(ua, "%s", sd->msg);
- /* Here we can get
- * 3001 OK mount. Device=xxx or
- * 3001 Mounted Volume vvvv
- */
- if (strncmp(sd->msg, "3001 ", 5) == 0) {
- mounted = TRUE;
- /***** ****FIXME***** find job waiting for
- ***** mount, and change to waiting for SD
- */
- }
- }
- }
- } else {
- bsendmsg(ua, "%s", db_strerror(ua->db));
- }
- }
- if (!mounted) {
- bsendmsg(ua, _("Do not forget to mount the drive!!!\n"));
- }
- bnet_sig(sd, BNET_TERMINATE);
- bnet_close(sd);
- return 1;
-}
-
static void do_mount_cmd(int mount, UAContext *ua, char *cmd)
{
STORE *store;
--- /dev/null
+/*
+ *
+ * Bacula Director -- Tape labeling commands
+ *
+ * Kern Sibbald, April MMIII
+ *
+ * Version $Id$
+ */
+
+/*
+ Copyright (C) 2000-2003 Kern Sibbald and John Walker
+
+ 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.
+
+ 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.
+
+ */
+
+#include "bacula.h"
+#include "dird.h"
+
+/* Imported subroutines */
+
+/* Imported variables */
+
+
+/* Imported functions */
+
+/* Forward referenced functions */
+static int do_label(UAContext *ua, char *cmd, int relabel);
+static void label_from_barcodes(UAContext *ua);
+
+/*
+ * Label a tape
+ *
+ * label storage=xxx volume=vvv
+ */
+int labelcmd(UAContext *ua, char *cmd)
+{
+ return do_label(ua, cmd, 0); /* standard label */
+}
+
+int relabelcmd(UAContext *ua, char *cmd)
+{
+ return do_label(ua, cmd, 1); /* relabel tape */
+}
+
+
+/*
+ * Common routine for both label and relabel
+ */
+static int do_label(UAContext *ua, char *cmd, int relabel)
+{
+ STORE *store;
+ BSOCK *sd;
+ char dev_name[MAX_NAME_LENGTH];
+ MEDIA_DBR mr, omr;
+ POOL_DBR pr;
+ int ok = FALSE;
+ int mounted = FALSE;
+ int i;
+ int slot = 0;
+ static char *name_keyword[] = {
+ "name",
+ NULL};
+ static char *vol_keyword[] = {
+ "volume",
+ NULL};
+ static char *barcode_keyword[] = {
+ "barcode",
+ "barcodes",
+ NULL};
+
+
+ memset(&pr, 0, sizeof(pr));
+ if (!open_db(ua)) {
+ return 1;
+ }
+ store = get_storage_resource(ua, cmd);
+ if (!store) {
+ return 1;
+ }
+ ua->jcr->store = store;
+
+ if (!relabel && find_arg_keyword(ua, barcode_keyword) >= 0) {
+ label_from_barcodes(ua);
+ return 1;
+ }
+
+ /* If relabel get name of Volume to relabel */
+ if (relabel) {
+ /* Check for volume=OldVolume */
+ i = find_arg_keyword(ua, vol_keyword);
+ if (i >= 0 && ua->argv[i]) {
+ memset(&omr, 0, sizeof(omr));
+ bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName));
+ if (db_get_media_record(ua->jcr, ua->db, &omr)) {
+ goto checkVol;
+ }
+ bsendmsg(ua, "%s", db_strerror(ua->db));
+ }
+ /* No keyword or Vol not found, ask user to select */
+ if (!select_pool_and_media_dbr(ua, &pr, &omr)) {
+ return 1;
+ }
+
+ /* Require Volume to be Purged */
+checkVol:
+ if (strcmp(omr.VolStatus, "Purged") != 0) {
+ bsendmsg(ua, _("Volume \"%s\" has VolStatus %s. It must be purged before relabeling.\n"),
+ omr.VolumeName, omr.VolStatus);
+ return 1;
+ }
+ }
+
+ /* Check for name=NewVolume */
+ i = find_arg_keyword(ua, name_keyword);
+ if (i >=0 && ua->argv[i]) {
+ strcpy(ua->cmd, ua->argv[i]);
+ goto checkName;
+ }
+
+ /* Get a new Volume name */
+ for ( ;; ) {
+ if (!get_cmd(ua, _("Enter new Volume name: "))) {
+ return 1;
+ }
+checkName:
+ /* Restrict the characters permitted in the Volume name */
+ if (strpbrk(ua->cmd, "`~!@#$%^&*()[]{}|\\;'\"<>?,/")) {
+ bsendmsg(ua, _("Illegal character | in a volume name.\n"));
+ continue;
+ }
+ if (strlen(ua->cmd) >= MAX_NAME_LENGTH) {
+ bsendmsg(ua, _("Volume name too long.\n"));
+ continue;
+ }
+ if (strlen(ua->cmd) == 0) {
+ bsendmsg(ua, _("Volume name must be at least one character long.\n"));
+ continue;
+ }
+
+ memset(&mr, 0, sizeof(mr));
+ strcpy(mr.VolumeName, ua->cmd);
+ if (db_get_media_record(ua->jcr, ua->db, &mr)) {
+ bsendmsg(ua, _("Media record for new Volume \"%s\" already exists.\n"),
+ mr.VolumeName);
+ continue;
+ }
+ break; /* Got it */
+ }
+
+ /* Do some more checking on slot ****FIXME**** */
+ if (store->autochanger) {
+ for ( ;; ) {
+ if (!get_cmd(ua, _("Enter slot (0 for none): "))) {
+ return 1;
+ }
+ slot = atoi(ua->cmd);
+ if (slot >= 0) { /* OK */
+ break;
+ }
+ bsendmsg(ua, _("Slot numbers must be positive.\n"));
+ }
+ }
+ strcpy(mr.MediaType, store->media_type);
+ mr.Slot = slot;
+
+ /* Must select Pool if not already done */
+ if (pr.PoolId == 0) {
+ memset(&pr, 0, sizeof(pr));
+ if (!select_pool_dbr(ua, &pr)) {
+ return 1;
+ }
+ }
+
+ bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"),
+ store->hdr.name, store->address, store->SDport);
+ if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
+ bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
+ return 1;
+ }
+ sd = ua->jcr->store_bsock;
+ strcpy(dev_name, store->dev_name);
+ bash_spaces(dev_name);
+ bash_spaces(mr.VolumeName);
+ bash_spaces(mr.MediaType);
+ bash_spaces(pr.Name);
+ if (relabel) {
+ bash_spaces(omr.VolumeName);
+ bnet_fsend(sd, _("relabel %s OldName=%s NewName=%s PoolName=%s MediaType=%s Slot=%d"),
+ dev_name, omr.VolumeName, mr.VolumeName, pr.Name, mr.MediaType, mr.Slot);
+ bsendmsg(ua, _("Sending relabel command ...\n"));
+ } else {
+ bnet_fsend(sd, _("label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d"),
+ dev_name, mr.VolumeName, pr.Name, mr.MediaType, mr.Slot);
+ bsendmsg(ua, _("Sending label command ...\n"));
+ }
+ while (bget_msg(sd, 0) >= 0) {
+ bsendmsg(ua, "%s", sd->msg);
+ if (strncmp(sd->msg, "3000 OK label.", 14) == 0) {
+ ok = TRUE;
+ } else {
+ bsendmsg(ua, _("Label command failed.\n"));
+ }
+ }
+ ua->jcr->store_bsock = NULL;
+ unbash_spaces(dev_name);
+ unbash_spaces(mr.VolumeName);
+ unbash_spaces(mr.MediaType);
+ unbash_spaces(pr.Name);
+ mr.LabelDate = time(NULL);
+ if (ok) {
+ set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
+ if (db_create_media_record(ua->jcr, ua->db, &mr)) {
+ bsendmsg(ua, _("Media record for Volume \"%s\" successfully created.\n"),
+ mr.VolumeName);
+ if (ua->automount) {
+ bsendmsg(ua, _("Requesting mount %s ...\n"), dev_name);
+ bash_spaces(dev_name);
+ bnet_fsend(sd, "mount %s", dev_name);
+ unbash_spaces(dev_name);
+ while (bnet_recv(sd) >= 0) {
+ bsendmsg(ua, "%s", sd->msg);
+ /* Here we can get
+ * 3001 OK mount. Device=xxx or
+ * 3001 Mounted Volume vvvv
+ */
+ if (strncmp(sd->msg, "3001 ", 5) == 0) {
+ mounted = TRUE;
+ /***** ****FIXME***** find job waiting for
+ ***** mount, and change to waiting for SD
+ */
+ }
+ }
+ }
+ } else {
+ bsendmsg(ua, "%s", db_strerror(ua->db));
+ }
+ if (!db_delete_media_record(ua->jcr, ua->db, &omr)) {
+ bsendmsg(ua, _("Delete of Volume \"%s\" failed. ERR=%s"),
+ omr.VolumeName, db_strerror(ua->db));
+ } else {
+ bsendmsg(ua, _("Volume \"%s\" deleted from catalog.\n"));
+ }
+ }
+ if (!mounted) {
+ bsendmsg(ua, _("Do not forget to mount the drive!!!\n"));
+ }
+ bnet_sig(sd, BNET_TERMINATE);
+ bnet_close(sd);
+ ua->jcr->store_bsock = NULL;
+
+ return 1;
+}
+
+static void label_from_barcodes(UAContext *ua)
+{
+ STORE *store = ua->jcr->store;
+ BSOCK *sd;
+ POOL_DBR pr;
+ char dev_name[MAX_NAME_LENGTH];
+
+ bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"),
+ store->hdr.name, store->address, store->SDport);
+ if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
+ bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
+ return;
+ }
+
+ strcpy(dev_name, store->dev_name);
+ bash_spaces(dev_name);
+ sd = ua->jcr->store_bsock;
+ bnet_fsend(sd, _("autochanger list %s \n"), dev_name);
+ while (bget_msg(sd, 0) >= 0) {
+ bsendmsg(ua, "%s", sd->msg);
+ }
+ bnet_sig(sd, BNET_TERMINATE);
+ bnet_close(sd);
+ ua->jcr->store_bsock = NULL;
+
+ memset(&pr, 0, sizeof(pr));
+ if (!select_pool_dbr(ua, &pr)) {
+ return;
+ }
+
+ return;
+}
/*
* We scan what the user has entered looking for
- * <storage-resource>
* device=<device-name> ???? does this work ????
* storage=<storage-resource>
* job=<job_name>
JCR *jcr;
int i;
- if (ua->argc == 1) {
- return select_storage_resource(ua);
- }
-
device_name = NULL;
store_name = NULL;
#define JS_FatalError 'f' /* Fatal error */
#define JS_Differences 'D' /* Verify differences */
#define JS_Cancelled 'A' /* cancelled by user */
+#define JS_Canceled 'A' /* canceled by user */
#define JS_WaitFD 'F' /* waiting on File daemon */
#define JS_WaitSD 'S' /* waiting on the Storage daemon */
#define JS_WaitMedia 'm' /* waiting for new media */
#define JS_WaitMaxJobs 'd' /* Waiting for maximum jobs */
#define job_cancelled(jcr) \
- (jcr->JobStatus == JS_Cancelled || \
+ (jcr->JobStatus == JS_Canceled || \
jcr->JobStatus == JS_ErrorTerminated || \
jcr->JobStatus == JS_FatalError)
+#define job_canceled(jcr) \
+ (jcr->JobStatus == JS_Canceled || \
+ jcr->JobStatus == JS_ErrorTerminated || \
+ jcr->JobStatus == JS_FatalError)
+
+
typedef void (JCR_free_HANDLER)(struct s_jcr *jcr);
/* Job Control Record (JCR) */
dummy:
# bacula-sd
-SVRSRCS = stored.c acquire.c append.c askdir.c authenticate.c \
+SVRSRCS = stored.c autochanger.c acquire.c append.c \
+ askdir.c authenticate.c \
block.c dev.c \
device.c dircmd.c fd_cmds.c fdmsg.c job.c \
label.c match_bsr.c parse_bsr.c \
read.c record.c stored_conf.c mount.c
-SVROBJS = stored.o acquire.o append.o askdir.o authenticate.o \
+SVROBJS = stored.o autochanger.o acquire.o append.o \
+ askdir.o authenticate.o \
block.o dev.o \
device.o dircmd.o fd_cmds.o fdmsg.o job.o \
label.o match_bsr.o mount.o parse_bsr.o \
acquire.c mount.c record.c read_record.c \
stored_conf.c match_bsr.c parse_bsr.o
TAPEOBJS = btape.o block.o butil.o dev.o device.o label.o \
- acquire.o mount.o record.o read_record.o \
+ autochanger.o acquire.o mount.o record.o read_record.o \
stored_conf.o match_bsr.o parse_bsr.o
# bls
BLSOBJS = bls.o block.o butil.o device.o dev.o label.o match_bsr.o \
- acquire.o mount.o parse_bsr.o record.o \
+ autochanger.o acquire.o mount.o parse_bsr.o record.o \
read_record.o stored_conf.o
# bextract
BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o \
- acquire.o mount.o match_bsr.o parse_bsr.o butil.o \
+ autochanger.o acquire.o mount.o match_bsr.o parse_bsr.o butil.o \
read_record.o stored_conf.o
# bscan
SCNOBJS = bscan.o block.o device.o dev.o label.o \
- acquire.o mount.o record.o match_bsr.o parse_bsr.o \
+ autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \
butil.o read_record.o stored_conf.o
# bcopy
COPYOBJS = bcopy.o block.o device.o dev.o label.o \
- acquire.o mount.o record.o match_bsr.o parse_bsr.o \
+ autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \
butil.o read_record.o stored_conf.o
--- /dev/null
+/*
+ *
+ * Routines for handling the autochanger.
+ *
+ * Kern Sibbald, August MMII
+ *
+ * Version $Id$
+ */
+/*
+ Copyright (C) 2000-2003 Kern Sibbald and John Walker
+
+ 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.
+
+ 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.
+
+ */
+
+#include "bacula.h" /* pull in global headers */
+#include "stored.h" /* pull in Storage Deamon headers */
+
+/* Forward referenced functions */
+static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd);
+
+
+/*
+ * Called here to do an autoload using the autochanger, if
+ * configured, and if a Slot has been defined for this Volume.
+ * On success this routine loads the indicated tape, but the
+ * label is not read, so it must be verified.
+ *
+ * Note if dir is not NULL, it is the console requesting the
+ * autoload for labeling, so we respond directly to the
+ * dir bsock.
+ *
+ * Returns: 1 on success
+ * 0 on failure
+ */
+int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir)
+{
+ int slot = jcr->VolCatInfo.Slot;
+ int rtn_stat = 0;
+
+ /*
+ * Handle autoloaders here. If we cannot autoload it, we
+ * will return FALSE to ask the sysop.
+ */
+ if (writing && dev_cap(dev, CAP_AUTOCHANGER) && slot <= 0) {
+ if (dir) {
+ return 0; /* For user, bail out right now */
+ }
+ if (dir_find_next_appendable_volume(jcr)) {
+ slot = jcr->VolCatInfo.Slot;
+ }
+ }
+ Dmsg1(100, "Want changer slot=%d\n", slot);
+
+ if (slot > 0 && jcr->device->changer_name && jcr->device->changer_command) {
+ uint32_t timeout = jcr->device->max_changer_wait;
+ POOLMEM *changer, *results;
+ int status, loaded;
+
+ results = get_pool_memory(PM_MESSAGE);
+ changer = get_pool_memory(PM_FNAME);
+
+ /* Find out what is loaded, zero means device is unloaded */
+ changer = edit_device_codes(jcr, changer, jcr->device->changer_command,
+ "loaded");
+ status = run_program(changer, timeout, results);
+ Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results);
+ if (status == 0) {
+ loaded = atoi(results);
+ } else {
+ loaded = -1; /* force unload */
+ }
+ Dmsg1(100, "loaded=%s\n", results);
+
+ /* If bad status or tape we want is not loaded, load it. */
+ if (status != 0 || loaded != slot) {
+ if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
+ offline_dev(dev);
+ }
+ /* We are going to load a new tape, so close the device */
+ force_close_dev(dev);
+ if (loaded != 0) { /* must unload drive */
+ Dmsg0(100, "Doing changer unload.\n");
+ if (dir) {
+ bnet_fsend(dir, _("3902 Issuing autochanger \"unload\" command.\n"));
+ } else {
+ Jmsg(jcr, M_INFO, 0, _("Issuing autochanger \"unload\" command.\n"));
+ }
+ changer = edit_device_codes(jcr, changer,
+ jcr->device->changer_command, "unload");
+ status = run_program(changer, timeout, NULL);
+ Dmsg1(100, "unload status=%d\n", status);
+ }
+ /*
+ * Load the desired cassette
+ */
+ Dmsg1(100, "Doing changer load slot %d\n", slot);
+ if (dir) {
+ bnet_fsend(dir, _("3903 Issuing autochanger \"load slot %d\" command.\n"),
+ slot);
+ } else {
+ Jmsg(jcr, M_INFO, 0, _("Issuing autochanger \"load slot %d\" command.\n"),
+ slot);
+ }
+ changer = edit_device_codes(jcr, changer,
+ jcr->device->changer_command, "load");
+ status = run_program(changer, timeout, NULL);
+ if (status == 0) {
+ Jmsg(jcr, M_INFO, 0, _("Autochanger \"load slot\" status is OK.\n"));
+ } else {
+ Jmsg(jcr, M_INFO, 0, _("Bad autochanger \"load slot\" status = %d.\n"),
+ status);
+ }
+ Dmsg2(100, "load slot %d status=%d\n", slot, status);
+ }
+ free_pool_memory(changer);
+ free_pool_memory(results);
+ Dmsg1(100, "After changer, status=%d\n", status);
+ if (status == 0) { /* did we succeed? */
+ rtn_stat = 1; /* tape loaded by changer */
+ }
+ }
+ return rtn_stat;
+}
+
+int autochanger_list(JCR *jcr, DEVICE *dev, BSOCK *dir)
+{
+ uint32_t timeout = jcr->device->max_changer_wait;
+ POOLMEM *changer;
+ BPIPE *bpipe;
+ int len = sizeof_pool_memory(dir->msg) - 1;
+
+ if (!dev_cap(dev, CAP_AUTOCHANGER) || !jcr->device->changer_name ||
+ !jcr->device->changer_command) {
+ bnet_fsend(dir, _("Not a changer device.\n"));
+ return 0;
+ }
+
+ changer = get_pool_memory(PM_FNAME);
+ if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
+ offline_dev(dev);
+ }
+ /* We are going to load a new tape, so close the device */
+ force_close_dev(dev);
+
+ /* First unload any tape */
+ bnet_fsend(dir, _("3902 Issuing autochanger \"unload\" command.\n"));
+ changer = edit_device_codes(jcr, changer, jcr->device->changer_command, "unload");
+ run_program(changer, timeout, NULL);
+
+ /* Now list slots occupied */
+ changer = edit_device_codes(jcr, changer, jcr->device->changer_command, "list");
+ bnet_fsend(dir, _("3903 Issuing autochanger \"list\" command.\n"));
+ bpipe = open_bpipe(changer, timeout, "r");
+ if (!bpipe) {
+ bnet_fsend(dir, _("Open bpipe failed.\n"));
+ goto bail_out;
+ }
+ /* Get output from changer */
+ while (fgets(dir->msg, len, bpipe->rfd)) {
+ dir->msglen = strlen(dir->msg);
+ bnet_send(dir);
+ }
+ bnet_sig(dir, BNET_EOD);
+ close_bpipe(bpipe);
+
+bail_out:
+ free_pool_memory(changer);
+ return 1;
+}
+
+
+
+/*
+ * Edit codes into ChangerCommand
+ * %% = %
+ * %a = archive device name
+ * %c = changer device name
+ * %f = Client's name
+ * %j = Job name
+ * %o = command
+ * %s = Slot base 0
+ * %S = Slot base 1
+ * %v = Volume name
+ *
+ *
+ * omsg = edited output message
+ * imsg = input string containing edit codes (%x)
+ * cmd = command string (load, unload, ...)
+ *
+ */
+static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd)
+{
+ char *p;
+ const char *str;
+ char add[20];
+
+ *omsg = 0;
+ Dmsg1(200, "edit_device_codes: %s\n", imsg);
+ for (p=imsg; *p; p++) {
+ if (*p == '%') {
+ switch (*++p) {
+ case '%':
+ str = "%";
+ break;
+ case 'a':
+ str = jcr->device->dev->dev_name;
+ break;
+ case 'c':
+ str = NPRT(jcr->device->changer_name);
+ break;
+ case 'o':
+ str = NPRT(cmd);
+ break;
+ case 's':
+ sprintf(add, "%d", jcr->VolCatInfo.Slot - 1);
+ str = add;
+ break;
+ case 'S':
+ sprintf(add, "%d", jcr->VolCatInfo.Slot);
+ str = add;
+ break;
+ case 'j': /* Job name */
+ str = jcr->Job;
+ break;
+ case 'v':
+ str = NPRT(jcr->VolumeName);
+ break;
+ case 'f':
+ str = NPRT(jcr->client_name);
+ break;
+
+ default:
+ add[0] = '%';
+ add[1] = *p;
+ add[2] = 0;
+ str = add;
+ break;
+ }
+ } else {
+ add[0] = *p;
+ add[1] = 0;
+ str = add;
+ }
+ Dmsg1(200, "add_str %s\n", str);
+ pm_strcat(&omsg, (char *)str);
+ Dmsg1(200, "omsg=%s\n", omsg);
+ }
+ return omsg;
+}
static int mount_cmd(JCR *jcr);
static int unmount_cmd(JCR *jcr);
static int status_cmd(JCR *sjcr);
+static int autochanger_cmd(JCR *sjcr);
static int do_label(JCR *jcr, int relabel);
static void label_volume_if_ok(JCR *jcr, DEVICE *dev, char *oldname,
char *newname, char *poolname,
{"mount", mount_cmd},
{"unmount", unmount_cmd},
{"status", status_cmd},
+ {"autochanger", autochanger_cmd},
{NULL, NULL} /* list terminator */
};
break;
}
}
+
+/*
+ * Autochanger command from Director
+ */
+static int autochanger_cmd(JCR *jcr)
+{
+ char *devname;
+ BSOCK *dir = jcr->dir_bsock;
+ DEVRES *device;
+ DEVICE *dev;
+ int found = 0;
+
+ devname = (char *)get_memory(dir->msglen);
+ if (sscanf(dir->msg, "autochanger list %s ", devname) == 1) {
+ unbash_spaces(devname);
+ device = NULL;
+ LockRes();
+ while ((device=(DEVRES *)GetNextRes(R_DEVICE, (RES *)device))) {
+ /* Find resource, and make sure we were able to open it */
+ if (strcmp(device->hdr.name, devname) == 0 && device->dev) {
+ Dmsg1(20, "Found device %s\n", device->hdr.name);
+ found = 1;
+ break;
+ }
+ }
+ UnlockRes();
+ if (found) {
+ jcr->device = device;
+ dev = device->dev;
+ P(dev->mutex); /* Use P to avoid indefinite block */
+ if (!(dev->state & ST_OPENED)) {
+ if (open_dev(dev, NULL, READ_WRITE) < 0) {
+ bnet_fsend(dir, _("3994 Connot open device: %s\n"), strerror_dev(dev));
+ } else {
+ autochanger_list(jcr, dev, dir);
+ force_close_dev(dev);
+ }
+ /* Under certain "safe" conditions, we can steal the lock */
+ } else if (dev->dev_blocked &&
+ (dev->dev_blocked == BST_UNMOUNTED ||
+ dev->dev_blocked == BST_WAITING_FOR_SYSOP ||
+ dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP)) {
+ autochanger_list(jcr, dev, dir);
+ } else if (dev->state & ST_READ || dev->num_writers) {
+ if (dev->state & ST_READ) {
+ bnet_fsend(dir, _("3901 Device %s is busy with 1 reader.\n"),
+ dev_name(dev));
+ } else {
+ bnet_fsend(dir, _("3902 Device %s is busy with %d writer(s).\n"),
+ dev_name(dev), dev->num_writers);
+ }
+ } else { /* device not being used */
+ autochanger_list(jcr, dev, dir);
+ }
+ V(dev->mutex);
+ } else {
+ bnet_fsend(dir, _("3999 Device %s not found\n"), devname);
+ }
+ } else {
+ strcpy(devname, dir->msg);
+ bnet_fsend(dir, _("3907 Error scanning autocharger list command: %s\n"), devname);
+ }
+ free_memory(devname);
+ bnet_sig(dir, BNET_EOD);
+ return 1;
+}
#include "stored.h" /* pull in Storage Deamon headers */
/* Forward referenced functions */
-static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd);
/*
Dmsg0(90, "End of Device reached.\n");
return 0;
}
-
-/*
- * Called here to do an autoload using the autochanger, if
- * configured, and if a Slot has been defined for this Volume.
- * On success this routine loads the indicated tape, but the
- * label is not read, so it must be verified.
- *
- * Note if dir is not NULL, it is the console requesting the
- * autoload for labeling, so we respond directly to the
- * dir bsock.
- *
- * Returns: 1 on success
- * 0 on failure
- */
-int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir)
-{
- int slot = jcr->VolCatInfo.Slot;
- int rtn_stat = 0;
-
- /*
- * Handle autoloaders here. If we cannot autoload it, we
- * will return FALSE to ask the sysop.
- */
- if (writing && dev_cap(dev, CAP_AUTOCHANGER) && slot <= 0) {
- if (dir) {
- return 0; /* For user, bail out right now */
- }
- if (dir_find_next_appendable_volume(jcr)) {
- slot = jcr->VolCatInfo.Slot;
- }
- }
- Dmsg1(100, "Want changer slot=%d\n", slot);
-
- if (slot > 0 && jcr->device->changer_name && jcr->device->changer_command) {
- uint32_t timeout = jcr->device->max_changer_wait;
- POOLMEM *changer, *results;
- int status, loaded;
-
- results = get_pool_memory(PM_MESSAGE);
- changer = get_pool_memory(PM_FNAME);
-
- /* Find out what is loaded, zero means device is unloaded */
- changer = edit_device_codes(jcr, changer, jcr->device->changer_command,
- "loaded");
- status = run_program(changer, timeout, results);
- Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results);
- if (status == 0) {
- loaded = atoi(results);
- } else {
- loaded = -1; /* force unload */
- }
- Dmsg1(100, "loaded=%s\n", results);
-
- /* If bad status or tape we want is not loaded, load it. */
- if (status != 0 || loaded != slot) {
- if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
- offline_dev(dev);
- }
- /* We are going to load a new tape, so close the device */
- force_close_dev(dev);
- if (loaded != 0) { /* must unload drive */
- Dmsg0(100, "Doing changer unload.\n");
- if (dir) {
- bnet_fsend(dir, _("3902 Issuing autochanger \"unload\" command.\n"));
- } else {
- Jmsg(jcr, M_INFO, 0, _("Issuing autochanger \"unload\" command.\n"));
- }
- changer = edit_device_codes(jcr, changer,
- jcr->device->changer_command, "unload");
- status = run_program(changer, timeout, NULL);
- Dmsg1(100, "unload status=%d\n", status);
- }
- /*
- * Load the desired cassette
- */
- Dmsg1(100, "Doing changer load slot %d\n", slot);
- if (dir) {
- bnet_fsend(dir, _("3903 Issuing autochanger \"load slot %d\" command.\n"),
- slot);
- } else {
- Jmsg(jcr, M_INFO, 0, _("Issuing autochanger \"load slot %d\" command.\n"),
- slot);
- }
- changer = edit_device_codes(jcr, changer,
- jcr->device->changer_command, "load");
- status = run_program(changer, timeout, NULL);
- if (status == 0) {
- Jmsg(jcr, M_INFO, 0, _("Autochanger \"load slot\" status is OK.\n"));
- } else {
- Jmsg(jcr, M_INFO, 0, _("Bad autochanger \"load slot\" status = %d.\n"),
- status);
- }
- Dmsg2(100, "load slot %d status=%d\n", slot, status);
- }
- free_pool_memory(changer);
- free_pool_memory(results);
- Dmsg1(100, "After changer, status=%d\n", status);
- if (status == 0) { /* did we succeed? */
- rtn_stat = 1; /* tape loaded by changer */
- }
- }
- return rtn_stat;
-}
-
-
-
-/*
- * Edit codes into ChangerCommand
- * %% = %
- * %a = archive device name
- * %c = changer device name
- * %f = Client's name
- * %j = Job name
- * %o = command
- * %s = Slot base 0
- * %S = Slot base 1
- * %v = Volume name
- *
- *
- * omsg = edited output message
- * imsg = input string containing edit codes (%x)
- * cmd = command string (load, unload, ...)
- *
- */
-static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd)
-{
- char *p;
- const char *str;
- char add[20];
-
- *omsg = 0;
- Dmsg1(200, "edit_device_codes: %s\n", imsg);
- for (p=imsg; *p; p++) {
- if (*p == '%') {
- switch (*++p) {
- case '%':
- str = "%";
- break;
- case 'a':
- str = jcr->device->dev->dev_name;
- break;
- case 'c':
- str = NPRT(jcr->device->changer_name);
- break;
- case 'o':
- str = NPRT(cmd);
- break;
- case 's':
- sprintf(add, "%d", jcr->VolCatInfo.Slot - 1);
- str = add;
- break;
- case 'S':
- sprintf(add, "%d", jcr->VolCatInfo.Slot);
- str = add;
- break;
- case 'j': /* Job name */
- str = jcr->Job;
- break;
- case 'v':
- str = NPRT(jcr->VolumeName);
- break;
- case 'f':
- str = NPRT(jcr->client_name);
- break;
-
- default:
- add[0] = '%';
- add[1] = *p;
- add[2] = 0;
- str = add;
- break;
- }
- } else {
- add[0] = *p;
- add[1] = 0;
- str = add;
- }
- Dmsg1(200, "add_str %s\n", str);
- pm_strcat(&omsg, (char *)str);
- Dmsg1(200, "omsg=%s\n", omsg);
- }
- return omsg;
-}
uint32_t new_VolSessionId();
/* From acquire.c */
-DEVICE *acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int release_device(JCR *jcr, DEVICE *dev);
+DEVICE *acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int release_device(JCR *jcr, DEVICE *dev);
/* From askdir.c */
-int dir_get_volume_info(JCR *jcr, int writing);
-int dir_find_next_appendable_volume(JCR *jcr);
-int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel);
-int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev);
-int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev);
-int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec);
-int dir_send_job_status(JCR *jcr);
-int dir_create_jobmedia_record(JCR *jcr);
+int dir_get_volume_info(JCR *jcr, int writing);
+int dir_find_next_appendable_volume(JCR *jcr);
+int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel);
+int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev);
+int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev);
+int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec);
+int dir_send_job_status(JCR *jcr);
+int dir_create_jobmedia_record(JCR *jcr);
/* authenticate.c */
-int authenticate_director(JCR *jcr);
-int authenticate_filed(JCR *jcr);
+int authenticate_director(JCR *jcr);
+int authenticate_filed(JCR *jcr);
/* From block.c */
-void dump_block(DEV_BLOCK *b, char *msg);
+void dump_block(DEV_BLOCK *b, char *msg);
DEV_BLOCK *new_block(DEVICE *dev);
-void init_block_write(DEV_BLOCK *block);
-void empty_block(DEV_BLOCK *block);
-void free_block(DEV_BLOCK *block);
-int write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int read_block_from_device(DEVICE *dev, DEV_BLOCK *block);
-int read_block_from_dev(DEVICE *dev, DEV_BLOCK *block);
+void init_block_write(DEV_BLOCK *block);
+void empty_block(DEV_BLOCK *block);
+void free_block(DEV_BLOCK *block);
+int write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int read_block_from_device(DEVICE *dev, DEV_BLOCK *block);
+int read_block_from_dev(DEVICE *dev, DEV_BLOCK *block);
/* From butil.c -- utilities for SD tool programs */
-void print_ls_output(char *fname, char *link, int type, struct stat *statp);
+void print_ls_output(char *fname, char *link, int type, struct stat *statp);
JCR *setup_jcr(char *name, char *device, BSR *bsr, char *VolumeName);
DEVICE *setup_to_access_device(JCR *jcr, int read_access);
-void display_error_status(DEVICE *dev);
+void display_error_status(DEVICE *dev);
DEVRES *find_device_res(char *device_name, int read_access);
/* From dev.c */
-DEVICE *init_dev(DEVICE *dev, DEVRES *device);
-int open_dev(DEVICE *dev, char *VolName, int mode);
-void close_dev(DEVICE *dev);
-void force_close_dev(DEVICE *dev);
-int truncate_dev(DEVICE *dev);
-void term_dev(DEVICE *dev);
-char * strerror_dev(DEVICE *dev);
-void clrerror_dev(DEVICE *dev, int func);
-int update_pos_dev(DEVICE *dev);
-int rewind_dev(DEVICE *dev);
-int load_dev(DEVICE *dev);
-int offline_dev(DEVICE *dev);
-int flush_dev(DEVICE *dev);
-int weof_dev(DEVICE *dev, int num);
-int write_block(DEVICE *dev);
-int write_dev(DEVICE *dev, char *buf, size_t len);
-int read_dev(DEVICE *dev, char *buf, size_t len);
-int status_dev(DEVICE *dev, uint32_t *status);
-int eod_dev(DEVICE *dev);
-int fsf_dev(DEVICE *dev, int num);
-int fsr_dev(DEVICE *dev, int num);
-int bsf_dev(DEVICE *dev, int num);
-int bsr_dev(DEVICE *dev, int num);
-void attach_jcr_to_device(DEVICE *dev, JCR *jcr);
-void detach_jcr_from_device(DEVICE *dev, JCR *jcr);
-JCR *next_attached_jcr(DEVICE *dev, JCR *jcr);
+DEVICE *init_dev(DEVICE *dev, DEVRES *device);
+int open_dev(DEVICE *dev, char *VolName, int mode);
+void close_dev(DEVICE *dev);
+void force_close_dev(DEVICE *dev);
+int truncate_dev(DEVICE *dev);
+void term_dev(DEVICE *dev);
+char * strerror_dev(DEVICE *dev);
+void clrerror_dev(DEVICE *dev, int func);
+int update_pos_dev(DEVICE *dev);
+int rewind_dev(DEVICE *dev);
+int load_dev(DEVICE *dev);
+int offline_dev(DEVICE *dev);
+int flush_dev(DEVICE *dev);
+int weof_dev(DEVICE *dev, int num);
+int write_block(DEVICE *dev);
+int write_dev(DEVICE *dev, char *buf, size_t len);
+int read_dev(DEVICE *dev, char *buf, size_t len);
+int status_dev(DEVICE *dev, uint32_t *status);
+int eod_dev(DEVICE *dev);
+int fsf_dev(DEVICE *dev, int num);
+int fsr_dev(DEVICE *dev, int num);
+int bsf_dev(DEVICE *dev, int num);
+int bsr_dev(DEVICE *dev, int num);
+void attach_jcr_to_device(DEVICE *dev, JCR *jcr);
+void detach_jcr_from_device(DEVICE *dev, JCR *jcr);
+JCR *next_attached_jcr(DEVICE *dev, JCR *jcr);
/* Get info about device */
-char * dev_name(DEVICE *dev);
-char * dev_vol_name(DEVICE *dev);
+char * dev_name(DEVICE *dev);
+char * dev_vol_name(DEVICE *dev);
uint32_t dev_block(DEVICE *dev);
uint32_t dev_file(DEVICE *dev);
-int dev_is_tape(DEVICE *dev);
+int dev_is_tape(DEVICE *dev);
/* From device.c */
-int open_device(DEVICE *dev);
-int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int open_device(DEVICE *dev);
+int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
void _lock_device(char *file, int line, DEVICE *dev);
void _unlock_device(char *file, int line, DEVICE *dev);
void _block_device(char *file, int line, DEVICE *dev, int state);
void new_return_device_lock(DEVICE *dev, brwsteal_t *hold);
/* From dircmd.c */
-void *connection_request(void *arg);
+void *connection_request(void *arg);
/* From fd_cmds.c */
-void run_job(JCR *jcr);
+void run_job(JCR *jcr);
/* From fdmsg.c */
-int bget_msg(BSOCK *sock);
+int bget_msg(BSOCK *sock);
/* From job.c */
-void stored_free_jcr(JCR *jcr);
-void connection_from_filed(void *arg);
-void handle_filed_connection(BSOCK *fd, char *job_name);
+void stored_free_jcr(JCR *jcr);
+void connection_from_filed(void *arg);
+void handle_filed_connection(BSOCK *fd, char *job_name);
/* From label.c */
-int read_dev_volume_label(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-void create_session_label(JCR *jcr, DEV_RECORD *rec, int label);
-void create_volume_label(DEVICE *dev, char *VolName);
-int write_volume_label_to_dev(JCR *jcr, DEVRES *device, char *VolName, char *PoolName);
-int write_session_label(JCR *jcr, DEV_BLOCK *block, int label);
-int write_volume_label_to_block(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-void dump_volume_label(DEVICE *dev);
-void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose);
-int unser_volume_label(DEVICE *dev, DEV_RECORD *rec);
-int unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec);
+int read_dev_volume_label(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+void create_session_label(JCR *jcr, DEV_RECORD *rec, int label);
+void create_volume_label(DEVICE *dev, char *VolName);
+int write_volume_label_to_dev(JCR *jcr, DEVRES *device, char *VolName, char *PoolName);
+int write_session_label(JCR *jcr, DEV_BLOCK *block, int label);
+int write_volume_label_to_block(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+void dump_volume_label(DEVICE *dev);
+void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose);
+int unser_volume_label(DEVICE *dev, DEV_RECORD *rec);
+int unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec);
/* From match_bsr.c */
int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
- SESSION_LABEL *sesrec);
+ SESSION_LABEL *sesrec);
/* From mount.c */
-int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release);
-int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir);
+int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release);
+int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+
+/* From autochanger.c */
+int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir);
+int autochanger_list(JCR *jcr, DEVICE *dev, BSOCK *dir);
/* From parse_bsr.c */
/* From record.c */
char *FI_to_ascii(int fi);
char *stream_to_ascii(int stream, int fi);
-int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
-int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
-int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec);
DEV_RECORD *new_record();
-void free_record(DEV_RECORD *rec);
+void free_record(DEV_RECORD *rec);
/* From read_record.c */
int read_records(JCR *jcr, DEVICE *dev,