Kern's ToDo List
- 31 May 2003
+ 06 June 2003
Documentation to do: (any release a little bit at a time)
- Document running a test version.
- Test cancel at EOM.
- Test not zeroing Autochanger slot when it is wrong.
- Test multiple simultaneous Volumes
+- That restoring a hard link that already exists works correctly.
+ Same for soft link.
+- Test of last block is correct in JobMedia when splitting file
+ over two volumes.
- Figure out how to use ssh or stunnel to protect Bacula communications.
For 1.31 release:
+- Something is not right in last block of fill command.
+- Check this below from Phil.
+ > When the job was done, Bacula reported 11084 files restored:
+ >
+ > JobId: 527
+ > Job: Zocalo_Restore.2003-06-05_16.42.01
+ > Client: Zocalo
+ > Start time: 05-Jun-2003 16:42
+ > End time: 06-Jun-2003 01:21
+ > Files Restored: 11,084
+ > Bytes Restored: 65,474,772
+ > Rate: 2.1 KB/s
+ > FD termination status: OK
+ > Termination: Restore OK
+ >
+ > when it should probably have reported 11084 files scanned, 250 restored.
+ > The bytes restored count looks about right.
+ >
+- If during a restore, a hard linked file already exists (on option), delete
+ the file and re-link it. This is to avoid the possibility that the
+ user had re-linked the file between the backup and the restore.
+ Do lstat() to see if it is already properly linked.
+ Same for symlinked file.
+- Remove the Jmsg() in sql_find.c:102 or only print on hard error.
+- Implement List Volume Job=xxx or List scheduled volumes or
+ Status Director
- Instrument use_count on DEVICE packets and ensure that the device is
being close()ed at the appropriate time.
- Check if Incremental is working correctly when it looks for the previous Job
(Phil's problem).
- Add next Volume to be used to status output.
- Add a recycle command.
-- Command to determine next volume needed for a particular job.
- Make bootstrap filename unique.
-- Implement FileSet VolIndex.
+- Implement FileSet VolIndex -- done, but must update old records.
- Sort JobIds entered into recover tree.
- The bsr for Dan's job has file indexes covering the whole range rather
than only the range contained on the volume.
After 1.31:
+- When doing a Backup send all attributes back to the Director, who
+ would then figure out what files have been deleted.
- Currently in mount.c:236 the SD simply creates a Volume. It should have
explicit permission to do so. It should also mark the tape in error
if there is an error.
- Make bcopy copy with a single tape drive.
- Permit changing ownership during restore.
+- From Phil:
+ > My suggestion: Add a feature on the systray menu-icon menu to request
+ > an immediate backup now. This would be useful for laptop users who may
+ > not be on the network when the regular scheduled backup is run.
+ >
+ > My wife's suggestion: Add a setting to the win32 client to allow it to
+ > shut down the machine after backup is complete (after, of course,
+ > displaying a "System will shut down in one minute, click here to cancel"
+ > warning dialog). This would be useful for sites that want user
+ > woorkstations to be shut down overnight to save power.
+ >
+
- Autolabel should be specified by DIR instead of SD.
- Storage daemon
- Add media capacity
- The "List last 20 Jobs run" doesnt work correctly in restore.
It doesnt show the last 20 jobs , but some older ones.
- Fix Verify VolumeToCatalog to use BSRs -- it is broken.
+- Implement Release Storage=xxx
db_create_jobmedia_record(JCR *jcr, B_DB *mdb, JOBMEDIA_DBR *jm)
{
int stat;
+ int count;
db_lock(mdb);
Mmsg(&mdb->cmd, "SELECT JobId, MediaId FROM JobMedia WHERE \
sql_free_result(mdb);
}
+ /* Now get count for VolIndex */
+ Mmsg(&mdb->cmd, "SELECT count(*) from JobMedia");
+ count = get_sql_record_max(jcr, mdb);
+ if (count < 0) {
+ count = 0;
+ }
+ count++;
+
/* Must create it */
Mmsg(&mdb->cmd,
"INSERT INTO JobMedia (JobId,MediaId,FirstIndex,LastIndex,\
-StartFile,EndFile,StartBlock,EndBlock) \
-VALUES (%u,%u,%u,%u,%u,%u,%u,%u)",
+StartFile,EndFile,StartBlock,EndBlock,VolIndex) \
+VALUES (%u,%u,%u,%u,%u,%u,%u,%u,%u)",
jm->JobId, jm->MediaId, jm->FirstIndex, jm->LastIndex,
- jm->StartFile, jm->EndFile, jm->StartBlock, jm->EndBlock);
+ jm->StartFile, jm->EndFile, jm->StartBlock, jm->EndBlock,count);
Dmsg0(30, mdb->cmd);
if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
}
if ((row = sql_fetch_row(mdb)) == NULL) {
sql_free_result(mdb);
- Mmsg0(&mdb->errmsg, _("No Job Found.\n"));
+ Mmsg(&mdb->errmsg, _("No prior Job record found.\n"));
db_unlock(mdb);
return 0;
}
Mmsg(&mdb->cmd,
"SELECT VolumeName,FirstIndex,LastIndex,StartFile,EndFile,StartBlock,EndBlock"
" FROM JobMedia,Media WHERE JobMedia.JobId=%u"
-" AND JobMedia.MediaId=Media.MediaId ORDER BY JobMediaId", JobId);
+" AND JobMedia.MediaId=Media.MediaId ORDER BY VolIndex,JobMediaId", JobId);
Dmsg1(130, "VolNam=%s\n", mdb->cmd);
if (QUERY_DB(jcr, mdb, mdb->cmd)) {
# Define the main nightly save backup job
# By default, this job will back up to disk in /tmp
Job {
- Name = "NightlySave"
+ Name = "Client1"
Type = Backup
- Client=@hostname@-fd
- FileSet="Full Set"
+ Client = @hostname@-fd
+ FileSet = "Full Set"
Schedule = "WeeklyCycle"
Storage = File
Messages = Standard
Pool = Default
- Write Bootstrap = "@working_dir@/NightlySave.bsr"
+ Write Bootstrap = "@working_dir@/Client1.bsr"
}
# Backup the catalog database (after the nightly save)
catalog->db_password, catalog->db_address,
catalog->db_port, catalog->db_socket);
if (!db_open_database(NULL, db)) {
- OK = FALSE; /* Error message already printed */
+ Jmsg(NULL, M_FATAL, 0, _("Could not open database \"%s\".\n"),
+ catalog->db_name);
+ OK = FALSE;
} else {
/* If a pool is defined for this job, create the pool DB
* record if it is not already created.
*Enter path with trailing slash:
*Enter filename:
*Enter Client name:
-SELECT Job.JobId, StartTime AS JobStartTime, VolumeName, Client.Name AS ClientName
+SELECT Job.JobId,StartTime AS JobStartTime,VolumeName,Client.Name AS ClientName
FROM Job,File,Path,Filename,Media,JobMedia,Client
WHERE File.JobId=Job.JobId
AND Path.Path='%1'
*Enter path with trailing slash:
*Enter filename:
*Enter Client name:
-SELECT Job.JobId, StartTime AS JobStartTime, VolumeName, Client.Name AS ClientName
+SELECT Job.JobId,StartTime AS JobStartTime,VolumeName,Client.Name AS ClientName
FROM Job,File,Path,Filename,Media,JobMedia,Client
WHERE File.JobId=Job.JobId
AND Path.Path='%1'
AND Client.ClientId=Job.ClientId
AND Level='F' AND JobStatus='T'
AND JobMedia.JobId=Job.JobId AND JobMedia.MediaId=Media.MediaId
- ORDER BY JobId DESC LIMIT 20;
+ ORDER BY Job.StartTime DESC LIMIT 20;
+#
+:List all backups for a Client after a specified time
+*Enter Client Name:
+*Enter time in YYYY-MM-DD HH:MM:SS format:
+Select Job.JobId,Client.Name as Client,Level,StartTime,JobFiles,JobBytes,VolumeName
+ FROM Client,Job,JobMedia,Media
+ WHERE Client.Name='%1'
+ AND Client.ClientId=Job.ClientId
+ AND JobStatus='T'
+ AND JobMedia.JobId=Job.JobId AND JobMedia.MediaId=Media.MediaId
+ AND Job.StartTime >= '%2'
+ ORDER BY Job.StartTime;
+#
+:List all backups for a Client
+*Enter Client Name:
+Select Job.JobId,Client.Name as Client,Level,StartTime,JobFiles,JobBytes,VolumeName
+ FROM Client,Job,JobMedia,Media
+ WHERE Client.Name='%1'
+ AND Client.ClientId=Job.ClientId
+ AND JobStatus='T'
+ AND JobMedia.JobId=Job.JobId AND JobMedia.MediaId=Media.MediaId
+ ORDER BY Job.StartTime;
#
:List Volume Attributes for a selected Volume:
*Enter Volume name:
FROM Client,Job,File,Filename,Path WHERE Client.ClientId=Job.ClientId
AND JobStatus='T' AND Job.JobId=File.JobId
AND Path.PathId=File.PathId AND Filename.FilenameId=File.FilenameId
- AND Filename.Name='%1' ORDER BY Job.JobId LIMIT 20;
+ AND Filename.Name='%1' ORDER BY Job.StartTime LIMIT 20;
#
:List total files/bytes by Job:
SELECT count(*) AS Jobs, sum(JobFiles) AS Files,
extern int update_slots(UAContext *ua); /* ua_label.c */
/* Forward referenced functions */
-static int addcmd(UAContext *ua, char *cmd), createcmd(UAContext *ua, char *cmd), cancelcmd(UAContext *ua, char *cmd);
-static int setdebugcmd(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 versioncmd(UAContext *ua, char *cmd), automountcmd(UAContext *ua, char *cmd);
-static int timecmd(UAContext *ua, char *cmd);
+static int add_cmd(UAContext *ua, char *cmd), createcmd(UAContext *ua, char *cmd), cancelcmd(UAContext *ua, char *cmd);
+static int setdebug_cmd(UAContext *ua, char *cmd);
+static int help_cmd(UAContext *ua, char *cmd);
+static int delete_cmd(UAContext *ua, char *cmd);
+static int use_cmd(UAContext *ua, char *cmd), unmount_cmd(UAContext *ua, char *cmd);
+static int version_cmd(UAContext *ua, char *cmd), automount_cmd(UAContext *ua, char *cmd);
+static int time_cmd(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 mountcmd(UAContext *ua, char *cmd);
-static int updatecmd(UAContext *ua, char *cmd);
-static int waitcmd(UAContext *ua, char *cmd);
+static int mount_cmd(UAContext *ua, char *cmd);
+static int release_cmd(UAContext *ua, char *cmd);
+static int update_cmd(UAContext *ua, char *cmd);
+static int wait_cmd(UAContext *ua, char *cmd);
-int quitcmd(UAContext *ua, char *cmd);
+int quit_cmd(UAContext *ua, char *cmd);
struct cmdstruct { char *key; int (*func)(UAContext *ua, char *cmd); char *help; };
static struct cmdstruct commands[] = {
- { N_("add"), addcmd, _("add media to a pool")},
+ { N_("add"), add_cmd, _("add media to a pool")},
{ N_("autodisplay"), autodisplaycmd, _("autodisplay [on/off] -- console messages")},
- { N_("automount"), automountcmd, _("automount [on/off] -- after label")},
+ { N_("automount"), automount_cmd, _("automount [on/off] -- after label")},
{ N_("cancel"), cancelcmd, _("cancel job=nnn -- cancel a job")},
{ N_("create"), createcmd, _("create DB Pool from resource")},
- { N_("delete"), deletecmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
- { N_("help"), helpcmd, _("print this command")},
+ { N_("delete"), delete_cmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
+ { N_("help"), help_cmd, _("print this command")},
{ N_("label"), labelcmd, _("label a tape")},
{ N_("relabel"), relabelcmd, _("relabel a tape")},
{ N_("list"), listcmd, _("list [pools | jobs | jobtotals | media <pool> | files job=<nn>]; from catalog")},
{ N_("llist"), llistcmd, _("full or long list like list command")},
{ N_("messages"), messagescmd, _("messages")},
- { N_("mount"), mountcmd, _("mount <storage-name>")},
+ { N_("mount"), mount_cmd, _("mount <storage-name>")},
{ N_("restore"), restorecmd, _("restore files")},
{ N_("prune"), prunecmd, _("prune expired records from catalog")},
{ N_("purge"), purgecmd, _("purge records from catalog")},
{ N_("run"), runcmd, _("run <job-name>")},
- { N_("setdebug"), setdebugcmd, _("sets debug level")},
+ { N_("setdebug"), setdebug_cmd, _("sets debug level")},
{ N_("show"), showcmd, _("show (resource records) [jobs | pools | ... | all]")},
{ N_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")},
{ N_("status"), statuscmd, _("status [storage | client]=<name>")},
- { N_("unmount"), unmountcmd, _("unmount <storage-name>")},
- { N_("update"), updatecmd, _("update Volume or Pool")},
- { N_("use"), usecmd, _("use catalog xxx")},
- { N_("version"), versioncmd, _("print Director version")},
- { N_("quit"), quitcmd, _("quit")},
+ { N_("unmount"), unmount_cmd, _("unmount <storage-name>")},
+ { N_("update"), update_cmd, _("update Volume or Pool")},
+ { N_("use"), use_cmd, _("use catalog xxx")},
+ { N_("version"), version_cmd, _("print Director version")},
+ { N_("quit"), quit_cmd, _("quit")},
{ N_("query"), querycmd, _("query catalog")},
- { N_("time"), timecmd, _("print current time")},
- { N_("exit"), quitcmd, _("exit = quit")},
- { N_("wait"), waitcmd, _("wait until no jobs are running")},
+ { N_("release"), release_cmd, _("release <storage-name>")},
+ { N_("time"), time_cmd, _("print current time")},
+ { N_("exit"), quit_cmd, _("exit = quit")},
+ { N_("wait"), wait_cmd, _("wait until no jobs are running")},
};
#define comsize (sizeof(commands)/sizeof(struct cmdstruct))
/*
* Add Volumes to an existing Pool
*/
-static int addcmd(UAContext *ua, char *cmd)
+static int add_cmd(UAContext *ua, char *cmd)
{
POOL_DBR pr;
MEDIA_DBR mr;
* automount on
* automount off
*/
-int automountcmd(UAContext *ua, char *cmd)
+int automount_cmd(UAContext *ua, char *cmd)
{
char *onoff;
* update media pool=<pool-name> volume=<volume-name>
* changes pool info for volume
*/
-static int updatecmd(UAContext *ua, char *cmd)
+static int update_cmd(UAContext *ua, char *cmd)
{
static char *kw[] = {
N_("media"), /* 0 */
/*
* setdebug level=nn all
*/
-static int setdebugcmd(UAContext *ua, char *cmd)
+static int setdebug_cmd(UAContext *ua, char *cmd)
{
STORE *store;
CLIENT *client;
/*
* print time
*/
-static int timecmd(UAContext *ua, char *cmd)
+static int time_cmd(UAContext *ua, char *cmd)
{
char sdt[50];
time_t ttime = time(NULL);
* delete pool=<pool-name>
* delete media pool=<pool-name> volume=<name>
*/
-static int deletecmd(UAContext *ua, char *cmd)
+static int delete_cmd(UAContext *ua, char *cmd)
{
static char *keywords[] = {
N_("volume"),
}
-static void do_mount_cmd(int mount, UAContext *ua, char *cmd)
+static void do_mount_cmd(UAContext *ua, char *command)
{
STORE *store;
BSOCK *sd;
if (!open_db(ua)) {
return;
}
- Dmsg1(120, "mount: %s\n", ua->UA_sock->msg);
+ Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
store = get_storage_resource(ua, 1);
if (!store) {
sd = ua->jcr->store_bsock;
strcpy(dev_name, store->dev_name);
bash_spaces(dev_name);
- if (mount) {
- bnet_fsend(sd, "mount %s", dev_name);
- } else {
- bnet_fsend(sd, "unmount %s", dev_name);
- }
+ bnet_fsend(sd, "%s %s", command, dev_name);
while (bnet_recv(sd) >= 0) {
bsendmsg(ua, "%s", sd->msg);
- if (strncmp(sd->msg, "3001 OK mount.", 14) == 0) {
- /***** ****FIXME**** fix JobStatus */
- }
}
bnet_sig(sd, BNET_TERMINATE);
bnet_close(sd);
/*
* mount [storage | device] <name>
*/
-static int mountcmd(UAContext *ua, char *cmd)
+static int mount_cmd(UAContext *ua, char *cmd)
{
- do_mount_cmd(1, ua, cmd); /* mount */
+ do_mount_cmd(ua, "mount"); /* mount */
return 1;
}
/*
* unmount [storage | device] <name>
*/
-static int unmountcmd(UAContext *ua, char *cmd)
+static int unmount_cmd(UAContext *ua, char *cmd)
+{
+ do_mount_cmd(ua, "unmount"); /* unmount */
+ return 1;
+}
+
+
+/*
+ * release [storage | device] <name>
+ */
+static int release_cmd(UAContext *ua, char *cmd)
{
- do_mount_cmd(0, ua, cmd); /* unmount */
+ do_mount_cmd(ua, "release"); /* release */
return 1;
}
* Switch databases
* use catalog=<name>
*/
-static int usecmd(UAContext *ua, char *cmd)
+static int use_cmd(UAContext *ua, char *cmd)
{
CAT *oldcatalog, *catalog;
return 1;
}
-int quitcmd(UAContext *ua, char *cmd)
+int quit_cmd(UAContext *ua, char *cmd)
{
ua->quit = TRUE;
return 1;
/*
* Wait until no job is running
*/
-int waitcmd(UAContext *ua, char *cmd)
+int wait_cmd(UAContext *ua, char *cmd)
{
bmicrosleep(0, 200000); /* let job actually start */
for (int running=1; running; ) {
}
-static int helpcmd(UAContext *ua, char *cmd)
+static int help_cmd(UAContext *ua, char *cmd)
{
unsigned int i;
return 1;
}
-static int versioncmd(UAContext *ua, char *cmd)
+static int version_cmd(UAContext *ua, char *cmd)
{
bsendmsg(ua, "%s Version: " VERSION " (" BDATE ")\n", my_name);
return 1;
/* Imported functions */
extern int qmessagescmd(UAContext *ua, char *cmd);
-extern int quitcmd(UAContext *ua, char *cmd);
+extern int quit_cmd(UAContext *ua, char *cmd);
/* Forward referenced functions */
static int diecmd(UAContext *ua, char *cmd);
{ N_(".storage"), storagecmd, NULL},
{ N_(".defaults"), defaultscmd, NULL},
{ N_(".messages"), qmessagescmd, NULL},
- { N_(".quit"), quitcmd, NULL},
- { N_(".exit"), quitcmd, NULL}
+ { N_(".quit"), quit_cmd, NULL},
+ { N_(".exit"), quit_cmd, NULL}
};
#define comsize (sizeof(commands)/sizeof(struct cmdstruct))
return CF_CREATED;
case FT_LNKSAVED: /* Hard linked, file already saved */
- Dmsg2(130, "Hard link %s => %s\n", ofile, lname);
- if (link(lname, ofile) != 0) {
- Jmsg3(jcr, M_ERROR, 0, _("Could not hard link %s -> %s: ERR=%s\n"),
- ofile, lname, strerror(errno));
- return CF_ERROR;
- }
- return CF_CREATED;
+ Dmsg2(130, "Hard link %s => %s\n", ofile, lname);
+ if (link(lname, ofile) != 0) {
+ Jmsg3(jcr, M_ERROR, 0, _("Could not hard link %s -> %s: ERR=%s\n"),
+ ofile, lname, strerror(errno));
+ return CF_ERROR;
+ }
+ return CF_CREATED;
} /* End inner switch */
Jmsg(jcr, M_INFO, 0, _("User defined maximum volume capacity %s exceeded on device %s.\n"),
edit_uint64(max_cap, ed1), dev->dev_name);
block->write_failed = true;
- dev->EndBlock = dev->block_num;
- dev->EndFile = dev->file;
weof_dev(dev, 1); /* end the tape */
weof_dev(dev, 1); /* write second eof */
dev->state |= (ST_EOF | ST_EOT | ST_WEOT);
wlen, stat, dev->dev_errno, strerror(dev->dev_errno));
block->write_failed = true;
- dev->EndBlock = dev->block_num;
- dev->EndFile = dev->file;
weof_dev(dev, 1); /* end the tape */
weof_dev(dev, 1); /* write second eof */
dev->state |= (ST_EOF | ST_EOT | ST_WEOT);
struct DEV_BLOCK {
DEV_BLOCK *next; /* pointer to next one */
void *dev; /* pointer to device (DEVICE not defined yet) */
- /* binbuf is the number of bytes remaining
- * in the buffer. For writes, it is bytes not yet written.
- * For reads, it is remaining bytes not yet read.
+ /* binbuf is the number of bytes remaining in the buffer.
+ * For writes, it is bytes not yet written.
+ * For reads, it is remaining bytes not yet read.
*/
uint32_t binbuf; /* bytes in buffer */
uint32_t block_len; /* length of current block read */
uint32_t buf_len; /* max/default block length */
- uint32_t BlockNumber; /* sequential block number */
+ uint32_t BlockNumber; /* sequential Bacula block number */
uint32_t read_len; /* bytes read into buffer, if zero, block empty */
uint32_t VolSessionId; /* */
uint32_t VolSessionTime; /* */
int BlockVer; /* block version 1 or 2 */
bool write_failed; /* set if write failed */
bool block_read; /* set when block read */
- char *bufp; /* pointer into buffer */
- POOLMEM *buf; /* actual data buffer. This is a
- * Pool buffer!
- */
+ char *bufp; /* pointer into buffer */
+ POOLMEM *buf; /* actual data buffer */
};
#define block_is_empty(block) !((block)->read_len)
static int my_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
static void scan_blocks();
static void set_volume_name(char *VolName, int volnum);
+static void rawfill_cmd();
/* Static variables */
int get_cmd(char *prompt);
-int write_dev(DEVICE *dev, char *buf, size_t len)
-{
- Emsg0(M_ABORT, 0, "write_dev not implemented.\n");
- return 0;
-}
-
-int read_dev(DEVICE *dev, char *buf, size_t len)
-{
- Emsg0(M_ABORT, 0, "read_dev not implemented.\n");
- return 0;
-}
-
-
/*********************************************************************
*
* Main Bacula Pool Creation Program
}
update_pos_dev(dev);
tot_files = dev->file;
+ Pmsg1(0, _("Starting scan at file %u\n"), dev->file);
for (;;) {
if ((stat = read(dev->fd, buf, sizeof(buf))) < 0) {
clrerror_dev(dev, -1);
/* Get out after writing 10 blocks to the second tape */
if (BlockNumber > 10 && stop != 0) { /* get out */
- Pmsg0(-1, "Done writing 10 blocks to second tape.\n");
+ Pmsg0(-1, "Done writing ...\n");
break;
}
}
end_of_tape = 0;
- /* Close device so user can use autochanger if desired */
- if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
- offline_dev(dev);
- }
- force_close_dev(dev);
- get_cmd(_("Mount first tape. Press enter when ready: "));
+ if (!simple) {
+ /* Close device so user can use autochanger if desired */
+ if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
+ offline_dev(dev);
+ }
+ force_close_dev(dev);
+ get_cmd(_("Mount first tape. Press enter when ready: "));
- free_vol_list(jcr);
- set_volume_name("TestVolume1", 1);
- jcr->bsr = NULL;
- create_vol_list(jcr);
- close_dev(dev);
- dev->state &= ~ST_READ;
- if (!acquire_device_for_read(jcr, dev, block)) {
- Pmsg1(-1, "%s", dev->errmsg);
- return;
+ free_vol_list(jcr);
+ set_volume_name("TestVolume1", 1);
+ jcr->bsr = NULL;
+ create_vol_list(jcr);
+ close_dev(dev);
+ dev->state &= ~ST_READ;
+ if (!acquire_device_for_read(jcr, dev, block)) {
+ Pmsg1(-1, "%s", dev->errmsg);
+ return;
+ }
}
time(&jcr->run_time); /* start counting time for rates */
Pmsg1(-1, _("Forward space to file %u complete. Reading blocks ...\n"),
last_file);
Pmsg1(-1, _("Now reading to block %u.\n"), last_block_num);
- for (uint32_t i= 0; i < last_block_num; i++) {
+ for (uint32_t i=0; i <= last_block_num; i++) {
if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) {
Pmsg1(-1, _("Error reading blocks: ERR=%s\n"), strerror_dev(dev));
Pmsg2(-1, _("Wanted block %u error at block %u\n"), last_block_num, i);
Pmsg0(000, strerror_dev(dev));
Pmsg3(000, "Block not written: FileIndex=%u Block=%u Size=%u\n",
(unsigned)file_index, block->BlockNumber, block->block_len);
+ Pmsg2(000, "last_block_num=%u this_block_num=%d\n", last_block_num,
+ this_block_num);
if (dump) {
dump_block(block, "Block not written");
}
}
+static void rawfill_cmd()
+{
+ DEV_BLOCK *block;
+ int stat;
+ int fd;
+ uint32_t block_num = 0;
+ uint32_t *p;
+ int my_errno;
+ block = new_block(dev);
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd) {
+ read(fd, block->buf, block->buf_len);
+ } else {
+ Pmsg0(0, "Cannot open /dev/urandom.\n");
+ free_block(block);
+ return;
+ }
+ p = (uint32_t *)block->buf;
+ Pmsg1(0, "Begin writing blocks of %u bytes.\n", block->buf_len);
+ for ( ;; ) {
+ *p = block_num;
+ stat = write(dev->fd, block->buf, block->buf_len);
+ if (stat == (int)block->buf_len) {
+ if ((block_num++ % 100) == 0) {
+ printf("+");
+ }
+ continue;
+ }
+ break;
+ }
+ my_errno = errno;
+ printf("\n");
+ weofcmd();
+ printf("Write failed at block %u. stat=%d ERR=%s\n", block_num, stat,
+ strerror(my_errno));
+ free_block(block);
+
+}
struct cmdstruct { char *key; void (*func)(); char *help; };
static struct cmdstruct commands[] = {
{"label", labelcmd, "write a Bacula label to the tape"},
{"load", loadcmd, "load a tape"},
{"quit", quitcmd, "quit btape"},
+ {"rawfill", rawfill_cmd, "use write() to fill tape"},
{"readlabel", readlabelcmd, "read and print the Bacula tape label"},
{"rectest", rectestcmd, "test record handling functions"},
{"rewind", rewindcmd, "rewind the tape"},
- {"scan", scancmd, "read tape block by block to EOT and report"},
+ {"scan", scancmd, "read() tape block by block to EOT and report"},
+ {"scanblocks", scan_blocks, "Bacula read block by block to EOT and report"},
{"status", statcmd, "print tape status"},
{"test", testcmd, "General test Bacula tape functions"},
{"weof", weofcmd, "write an EOF on the tape"},
#include "stored.h"
/* Forward referenced functions */
-int dev_is_tape(DEVICE *dev);
-void clrerror_dev(DEVICE *dev, int func);
-int fsr_dev(DEVICE *dev, int num);
-
-extern int debug_level;
/*
* Allocate and initialize the DEVICE structure
Emsg0(M_FATAL, 0, dev->errmsg);
return 0;
}
- dev->state &= ~(ST_APPEND|ST_READ|ST_EOT | ST_EOF | ST_WEOT); /* remove EOF/EOT flags */
+ dev->state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */
dev->block_num = dev->file = 0;
dev->file_addr = 0;
if (dev->state & ST_TAPE) {
return 1;
}
+ dev->state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */
dev->block_num = dev->file = 0;
dev->file_addr = 0;
#ifdef MTUNLOCK
/* Forward referenced functions */
static int label_cmd(JCR *jcr);
static int relabel_cmd(JCR *jcr);
+static int release_cmd(JCR *jcr);
static int setdebug_cmd(JCR *jcr);
static int cancel_cmd(JCR *cjcr);
static int mount_cmd(JCR *jcr);
{"unmount", unmount_cmd},
{"status", status_cmd},
{"autochanger", autochanger_cmd},
+ {"release", release_cmd},
{NULL, NULL} /* list terminator */
};
return 1;
}
+/*
+ * Release command from Director. This rewinds the device and if
+ * configured does a offline and ensures that Bacula will
+ * re-read the label of the tape before continuing. This gives
+ * the operator the chance to change the tape anytime before the
+ * next job starts.
+ */
+static int release_cmd(JCR *jcr)
+{
+ POOLMEM *dname;
+ BSOCK *dir = jcr->dir_bsock;
+ DEVRES *device;
+ DEVICE *dev;
+ int found = 0;
+
+ dname = get_memory(dir->msglen+1);
+ if (sscanf(dir->msg, "release %s", dname) == 1) {
+ unbash_spaces(dname);
+ 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, dname) == 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)) {
+ Dmsg0(90, "Device already released\n");
+ bnet_fsend(dir, _("3911 Device %s already released.\n"), dev_name(dev));
+
+ } else if (dev->dev_blocked == BST_WAITING_FOR_SYSOP ||
+ dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP) {
+ Dmsg2(90, "%d waiter dev_block=%d. doing unmount\n", dev->num_waiting,
+ dev->dev_blocked);
+ bnet_fsend(dir, _("3912 Device %s waiting for mount.\n"), dev_name(dev));
+
+ } else if (dev->dev_blocked == BST_DOING_ACQUIRE) {
+ bnet_fsend(dir, _("3913 Device %s is busy in acquire.\n"),
+ dev_name(dev));
+
+ } else if (dev->dev_blocked == BST_WRITING_LABEL) {
+ bnet_fsend(dir, _("3914 Device %s is being labeled.\n"),
+ dev_name(dev));
+
+ } else if (dev->state & ST_READ || dev->num_writers) {
+ if (dev->state & ST_READ) {
+ Dmsg0(90, "Device in read mode\n");
+ bnet_fsend(dir, _("3915 Device %s is busy with 1 reader.\n"),
+ dev_name(dev));
+ } else {
+ Dmsg1(90, "Device busy with %d writers\n", dev->num_writers);
+ bnet_fsend(dir, _("3916 Device %s is busy with %d writer(s).\n"),
+ dev_name(dev), dev->num_writers);
+ }
+
+ } else { /* device not being used */
+ Dmsg0(90, "Device not in use, unmounting\n");
+ release_volume(jcr, dev);
+ bnet_fsend(dir, _("3012 Device %s released.\n"), dev_name(dev));
+ }
+ V(dev->mutex);
+ } else {
+ bnet_fsend(dir, _("3999 Device %s not found\n"), dname);
+ }
+ } else {
+ /* NB dir->msg gets clobbered in bnet_fsend, so save command */
+ pm_strcpy(&jcr->errmsg, dir->msg);
+ bnet_fsend(dir, _("3917 Error scanning release command: %s\n"), jcr->errmsg);
+ }
+ free_memory(dname);
+ bnet_sig(dir, BNET_EOD);
+ return 1;
+}
+
+
/*
* Autochanger command from Director
recycle = ask = autochanger = 0;
if (release) {
Dmsg0(100, "mount_next_volume release=1\n");
- /*
- * First erase all memory of the current volume
- */
- dev->block_num = dev->file = 0;
- dev->EndBlock = dev->EndFile = 0;
- memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo));
- memset(&jcr->VolCatInfo, 0, sizeof(jcr->VolCatInfo));
- memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
- dev->state &= ~ST_LABEL; /* label not yet read */
- jcr->VolumeName[0] = 0;
-
- if (!dev_is_tape(dev) || !dev_cap(dev, CAP_ALWAYSOPEN)) {
- offline_or_rewind_dev(dev);
- close_dev(dev);
- }
- /* If we have not closed the device, then at least rewind the tape */
- if (dev->state & ST_OPENED) {
- offline_or_rewind_dev(dev);
- }
+ release_volume(jcr, dev);
ask = 1; /* ask operator to mount tape */
}
Dmsg0(90, "End of Device reached.\n");
return 0;
}
+
+void release_volume(JCR *jcr, DEVICE *dev)
+{
+ /*
+ * First erase all memory of the current volume
+ */
+ dev->block_num = dev->file = 0;
+ dev->EndBlock = dev->EndFile = 0;
+ memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo));
+ memset(&jcr->VolCatInfo, 0, sizeof(jcr->VolCatInfo));
+ memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
+ dev->state &= ~ST_LABEL; /* label not yet read */
+ jcr->VolumeName[0] = 0;
+
+ if (!dev_is_tape(dev) || !dev_cap(dev, CAP_ALWAYSOPEN)) {
+ offline_or_rewind_dev(dev);
+ close_dev(dev);
+ }
+
+ /* If we have not closed the device, then at least rewind the tape */
+ if (dev->state & ST_OPENED) {
+ offline_or_rewind_dev(dev);
+ }
+}
/* 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);
+void release_volume(JCR *jcr, DEVICE *dev);
/* From autochanger.c */
int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir);
if (bpb <= 0) {
bpb = 1;
}
- bpb = dev->VolCatInfo.VolCatRBytes / bpb;
+ if (dev->VolCatInfo.VolCatRBytes > 0) {
+ bpb = dev->VolCatInfo.VolCatRBytes / bpb;
+ } else {
+ bpb = 0;
+ }
bnet_fsend(user, _(" Total Bytes Read=%s Blocks Read=%s Bytes/block=%s\n"),
edit_uint64_with_commas(dev->VolCatInfo.VolCatRBytes, b1),
edit_uint64_with_commas(dev->VolCatInfo.VolCatReads, b2),
job_type_to_str(jcr->JobType), jcr->Job);
}
if (jcr->device) {
- bnet_fsend(user, _("%s %s job %s is using device %s volume %s\n"),
+ bnet_fsend(user, _("%s %s job %s using Volume \"%s\" on device %s\n"),
job_level_to_str(jcr->JobLevel),
job_type_to_str(jcr->JobType),
- jcr->Job, jcr->device->device_name,
- jcr->VolumeName);
+ jcr->VolumeName,
+ jcr->Job, jcr->device->device_name);
sec = time(NULL) - jcr->run_time;
if (sec <= 0) {
sec = 1;
break;
default:
break;
-}
+ }
}