+16Feb04
+- Added first cut of message queuing to prevent recursion in
+ low level routines. This code is not yet working.
+- Spent a *huge* amount of time looking at the tape driver
+ code in the wake of several reports of tape labels getting
+ trashed. The code looks fine.
+- Created a tape with an error (by writing a block at the
+ beginning of a tape that had valid data), and found that
+ Under Linux there is no way to read past an I/O error.
+ I tried mt; I tried my own program (btape); and I tried
+ using scsitape, which talks directly to the scsi driver.
+ This is *VERY BAD* news.
+- I spent an *enormous* amount of time enhancing the btape
+ test program as well as making the fill command work with
+ both one and two tapes and the autochanger if configured.
+ The multiple tape fill test is now quite comprehensive.
+ It checks the last block on the first tape, the first block
+ on the second tape, and the last block (11) on the second
+ tape.
+- Teaked the tape driver for several functions to use the
+ OS driver's notion of mt_file if there is an error.
+13Feb04
+- Add DB update scripts retrieved by Scott to a new updatedb
+ directory at the top level.
+- Add -p option to bcopy to allow ignoring errors on input.
+- COALESCE(xx,0) does not work on PostgreSQL because the 0 is not a
+ correct time. Replaced by adding LastWritten IS NULL to the sort
+ line as was first suggested by JML.
+12Feb04
+- Use COALESCE(LastWritten, 0) in sql_find.c to get NULLs to sort last.
+ See note above.
+- Add write/read and positioning test to btape "test" program.
+10Feb04
+- Fix bad printf of InChanger flag reported by Pascal Pederiva.
+- Fix bad indexing off stack in authenticate.c reported by
+ Pascal Pederiva.
09Feb04
- Add \n to hosts.access reject message.
- Implement security message class and make hosts.access message use
that class.
08Feb04
- Fix check_memory bug in ua_query.c, which gives a bus error on Solaris.
+ This affected only the query command.
07Feb04
- Added backup to cdwriter script to examples provided by Johan Decock.
- Fixed a bug where ls really did lsmark (just invert command table
- "quit" command in restore tree.
Most Significant Changes since 1.32d
+- Improved btape "test" and "fill" commands.
- 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.
/* Bacula common configuration defines */
-#undef TRUE
-#undef FALSE
+#undef TRUE
+#undef FALSE
#define TRUE 1
#define FALSE 0
#endif
#ifdef PROTOTYPES
-# define __PROTO(p) p
+# define __PROTO(p) p
#else
-# define __PROTO(p) ()
+# define __PROTO(p) ()
#endif
#ifdef DEBUG
#define DEFAULT_NETWORK_BUFFER_SIZE (32 * 1024)
/*
- * Stream definitions. Once defined these must NEVER
+ * Stream definitions. Once defined these must NEVER
* change as they go on the storage media.
* Note, the following streams are passed from the SD to the DIR
* so that they may be put into the catalog (actually only the
* STREAM_MD5_SIGNATURE
* STREAM_SHA1_SIGNATURE
*/
-#define STREAM_UNIX_ATTRIBUTES 1 /* Generic Unix attributes */
-#define STREAM_FILE_DATA 2 /* Standard uncompressed data */
-#define STREAM_MD5_SIGNATURE 3 /* MD5 signature for the file */
-#define STREAM_GZIP_DATA 4 /* GZip compressed file data */
+#define STREAM_UNIX_ATTRIBUTES 1 /* Generic Unix attributes */
+#define STREAM_FILE_DATA 2 /* Standard uncompressed data */
+#define STREAM_MD5_SIGNATURE 3 /* MD5 signature for the file */
+#define STREAM_GZIP_DATA 4 /* GZip compressed file data */
/* Extended Unix attributes with Win32 Extended data. Deprecated. */
#define STREAM_UNIX_ATTRIBUTES_EX 5 /* Extended Unix attr for Win32 EX */
-#define STREAM_SPARSE_DATA 6 /* Sparse data stream */
+#define STREAM_SPARSE_DATA 6 /* Sparse data stream */
#define STREAM_SPARSE_GZIP_DATA 7
-#define STREAM_PROGRAM_NAMES 8 /* program names for program data */
-#define STREAM_PROGRAM_DATA 9 /* Data needing program */
-#define STREAM_SHA1_SIGNATURE 10 /* SHA1 signature for the file */
-#define STREAM_WIN32_DATA 11 /* Win32 BackupRead data */
-#define STREAM_WIN32_GZIP_DATA 12 /* Gzipped Win32 BackupRead data */
+#define STREAM_PROGRAM_NAMES 8 /* program names for program data */
+#define STREAM_PROGRAM_DATA 9 /* Data needing program */
+#define STREAM_SHA1_SIGNATURE 10 /* SHA1 signature for the file */
+#define STREAM_WIN32_DATA 11 /* Win32 BackupRead data */
+#define STREAM_WIN32_GZIP_DATA 12 /* Gzipped Win32 BackupRead data */
/*
- * File type (Bacula defined).
+ * File type (Bacula defined).
* NOTE!!! These are saved in the Attributes record on the tape, so
- * do not change them. If need be, add to them.
+ * do not change them. If need be, add to them.
*
* This is stored as 32 bits on tape, but only FT_MASK bits are
* used for the file type. The upper bits are used to indicate
* additional optional fields in the attribute record.
*/
-#define FT_MASK 0xFFFF /* Bits used by FT (type) */
-#define FT_LNKSAVED 1 /* hard link to file already saved */
-#define FT_REGE 2 /* Regular file but empty */
-#define FT_REG 3 /* Regular file */
-#define FT_LNK 4 /* Soft Link */
-#define FT_DIR 5 /* Directory */
-#define FT_SPEC 6 /* Special file -- chr, blk, fifo, sock */
-#define FT_NOACCESS 7 /* Not able to access */
-#define FT_NOFOLLOW 8 /* Could not follow link */
-#define FT_NOSTAT 9 /* Could not stat file */
-#define FT_NOCHG 10 /* Incremental option, file not changed */
-#define FT_DIRNOCHG 11 /* Incremental option, directory not changed */
-#define FT_ISARCH 12 /* Trying to save archive file */
-#define FT_NORECURSE 13 /* No recursion into directory */
-#define FT_NOFSCHG 14 /* Different file system, prohibited */
-#define FT_NOOPEN 15 /* Could not open directory */
-#define FT_RAW 16 /* Raw block device */
-#define FT_FIFO 17 /* Raw fifo device */
+#define FT_MASK 0xFFFF /* Bits used by FT (type) */
+#define FT_LNKSAVED 1 /* hard link to file already saved */
+#define FT_REGE 2 /* Regular file but empty */
+#define FT_REG 3 /* Regular file */
+#define FT_LNK 4 /* Soft Link */
+#define FT_DIR 5 /* Directory */
+#define FT_SPEC 6 /* Special file -- chr, blk, fifo, sock */
+#define FT_NOACCESS 7 /* Not able to access */
+#define FT_NOFOLLOW 8 /* Could not follow link */
+#define FT_NOSTAT 9 /* Could not stat file */
+#define FT_NOCHG 10 /* Incremental option, file not changed */
+#define FT_DIRNOCHG 11 /* Incremental option, directory not changed */
+#define FT_ISARCH 12 /* Trying to save archive file */
+#define FT_NORECURSE 13 /* No recursion into directory */
+#define FT_NOFSCHG 14 /* Different file system, prohibited */
+#define FT_NOOPEN 15 /* Could not open directory */
+#define FT_RAW 16 /* Raw block device */
+#define FT_FIFO 17 /* Raw fifo device */
/* Definitions for upper part of type word (see above). */
-#define AR_DATA_STREAM (1<<16) /* Data stream id present */
+#define AR_DATA_STREAM (1<<16) /* Data stream id present */
/*
* Internal code for Signature types
*/
-#define NO_SIG 0
+#define NO_SIG 0
#define MD5_SIG 1
#define SHA1_SIG 2
*/
/* Debug Messages that are printed */
#ifdef DEBUG
-#define Dmsg0(lvl, msg) d_msg(__FILE__, __LINE__, lvl, msg)
-#define Dmsg1(lvl, msg, a1) d_msg(__FILE__, __LINE__, lvl, msg, a1)
+#define Dmsg0(lvl, msg) d_msg(__FILE__, __LINE__, lvl, msg)
+#define Dmsg1(lvl, msg, a1) d_msg(__FILE__, __LINE__, lvl, msg, a1)
#define Dmsg2(lvl, msg, a1, a2) d_msg(__FILE__, __LINE__, lvl, msg, a1, a2)
#define Dmsg3(lvl, msg, a1, a2, a3) d_msg(__FILE__, __LINE__, lvl, msg, a1, a2, a3)
#define Dmsg4(lvl, msg, arg1, arg2, arg3, arg4) d_msg(__FILE__, __LINE__, lvl, msg, arg1, arg2, arg3, arg4)
#endif /* DEBUG */
#ifdef TRACE_FILE
-#define Tmsg0(lvl, msg) t_msg(__FILE__, __LINE__, lvl, msg)
-#define Tmsg1(lvl, msg, a1) t_msg(__FILE__, __LINE__, lvl, msg, a1)
+#define Tmsg0(lvl, msg) t_msg(__FILE__, __LINE__, lvl, msg)
+#define Tmsg1(lvl, msg, a1) t_msg(__FILE__, __LINE__, lvl, msg, a1)
#define Tmsg2(lvl, msg, a1, a2) t_msg(__FILE__, __LINE__, lvl, msg, a1, a2)
#define Tmsg3(lvl, msg, a1, a2, a3) t_msg(__FILE__, __LINE__, lvl, msg, a1, a2, a3)
#define Tmsg4(lvl, msg, arg1, arg2, arg3, arg4) t_msg(__FILE__, __LINE__, lvl, msg, arg1, arg2, arg3, arg4)
/* Messages that are printed (uses d_msg) */
-#define Pmsg0(lvl, msg) p_msg(__FILE__, __LINE__, lvl, msg)
-#define Pmsg1(lvl, msg, a1) p_msg(__FILE__, __LINE__, lvl, msg, a1)
+#define Pmsg0(lvl, msg) p_msg(__FILE__, __LINE__, lvl, msg)
+#define Pmsg1(lvl, msg, a1) p_msg(__FILE__, __LINE__, lvl, msg, a1)
#define Pmsg2(lvl, msg, a1, a2) p_msg(__FILE__, __LINE__, lvl, msg, a1, a2)
#define Pmsg3(lvl, msg, a1, a2, a3) p_msg(__FILE__, __LINE__, lvl, msg, a1, a2, a3)
#define Pmsg4(lvl, msg, arg1, arg2, arg3, arg4) p_msg(__FILE__, __LINE__, lvl, msg, arg1, arg2, arg3, arg4)
/* Daemon Error Messages that are delivered according to the message resource */
-#define Emsg0(typ, lvl, msg) e_msg(__FILE__, __LINE__, typ, lvl, msg)
-#define Emsg1(typ, lvl, msg, a1) e_msg(__FILE__, __LINE__, typ, lvl, msg, a1)
-#define Emsg2(typ, lvl, msg, a1, a2) e_msg(__FILE__, __LINE__, typ, lvl, msg, a1, a2)
+#define Emsg0(typ, lvl, msg) e_msg(__FILE__, __LINE__, typ, lvl, msg)
+#define Emsg1(typ, lvl, msg, a1) e_msg(__FILE__, __LINE__, typ, lvl, msg, a1)
+#define Emsg2(typ, lvl, msg, a1, a2) e_msg(__FILE__, __LINE__, typ, lvl, msg, a1, a2)
#define Emsg3(typ, lvl, msg, a1, a2, a3) e_msg(__FILE__, __LINE__, typ, lvl, msg, a1, a2, a3)
#define Emsg4(typ, lvl, msg, a1, a2, a3, a4) e_msg(__FILE__, __LINE__, typ, lvl, msg, a1, a2, a3, a4)
#define Emsg5(typ, lvl, msg, a1, a2, a3, a4, a5) e_msg(__FILE__, __LINE__, typ, lvl, msg, a1, a2, a3, a4, a5)
#define Emsg6(typ, lvl, msg, a1, a2, a3, a4, a5, a6) e_msg(__FILE__, __LINE__, typ, lvl, msg, a1, a2, a3, a4, a5, a6)
/* Job Error Messages that are delivered according to the message resource */
-#define Jmsg0(jcr, typ, lvl, msg) j_msg(__FILE__, __LINE__, jcr, typ, lvl, msg)
-#define Jmsg1(jcr, typ, lvl, msg, a1) j_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1)
+#define Jmsg0(jcr, typ, lvl, msg) j_msg(__FILE__, __LINE__, jcr, typ, lvl, msg)
+#define Jmsg1(jcr, typ, lvl, msg, a1) j_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1)
#define Jmsg2(jcr, typ, lvl, msg, a1, a2) j_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1, a2)
#define Jmsg3(jcr, typ, lvl, msg, a1, a2, a3) j_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1, a2, a3)
#define Jmsg4(jcr, typ, lvl, msg, a1, a2, a3, a4) j_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1, a2, a3, a4)
#define Jmsg5(jcr, typ, lvl, msg, a1, a2, a3, a4, a5) j_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1, a2, a3, a4, a5)
#define Jmsg6(jcr, typ, lvl, msg, a1, a2, a3, a4, a5, a6) j_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1, a2, a3, a4, a5, a6)
+/* Queued Job Error Messages that are delivered according to the message resource */
+#define Qmsg0(jcr, typ, lvl, msg) q_msg(__FILE__, __LINE__, jcr, typ, lvl, msg)
+#define Qmsg1(jcr, typ, lvl, msg, a1) q_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1)
+#define Qmsg2(jcr, typ, lvl, msg, a1, a2) q_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1, a2)
+#define Qmsg3(jcr, typ, lvl, msg, a1, a2, a3) q_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1, a2, a3)
+#define Qmsg4(jcr, typ, lvl, msg, a1, a2, a3, a4) q_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1, a2, a3, a4)
+#define Qmsg5(jcr, typ, lvl, msg, a1, a2, a3, a4, a5) q_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1, a2, a3, a4, a5)
+#define Qmsg6(jcr, typ, lvl, msg, a1, a2, a3, a4, a5, a6) q_msg(__FILE__, __LINE__, jcr, typ, lvl, msg, a1, a2, a3, a4, a5, a6)
+
/* Memory Messages that are edited into a Pool Memory buffer */
-#define Mmsg0(buf, msg) m_msg(__FILE__, __LINE__, buf, msg)
-#define Mmsg1(buf, msg, a1) m_msg(__FILE__, __LINE__, buf, msg, a1)
+#define Mmsg0(buf, msg) m_msg(__FILE__, __LINE__, buf, msg)
+#define Mmsg1(buf, msg, a1) m_msg(__FILE__, __LINE__, buf, msg, a1)
#define Mmsg2(buf, msg, a1, a2) m_msg(__FILE__, __LINE__, buf, msg, a1, a2)
#define Mmsg3(buf, msg, a1, a2, a3) m_msg(__FILE__, __LINE__, buf, msg, a1, a2, a3)
#define Mmsg4(buf, msg, a1, a2, a3, a4) m_msg(__FILE__, __LINE__, buf, msg, a1, a2, a3, a4)
void p_msg(char *file, int line, int level, char *fmt,...);
void e_msg(char *file, int line, int type, int level, char *fmt,...);
void j_msg(char *file, int line, JCR *jcr, int type, int level, char *fmt,...);
+void q_msg(char *file, int line, JCR *jcr, int type, int level, char *fmt,...);
int m_msg(char *file, int line, POOLMEM **msgbuf, char *fmt,...);
return;
}
-static void job_monitor_destructor(watchdog_t *self)
-{
- JCR *control_jcr = (JCR *) self->data;
-
- free_jcr(control_jcr);
-}
-
-static void job_monitor_watchdog(watchdog_t *self)
-{
- JCR *control_jcr, *jcr;
-
- control_jcr = (JCR *)self->data;
-
- Dmsg1(400, "job_monitor_watchdog %p called\n", self);
-
- lock_jcr_chain();
-
- foreach_jcr(jcr) {
- bool cancel;
-
- if (jcr->JobId == 0) {
- Dmsg2(400, "Skipping JCR %p (%s) with JobId 0\n",
- jcr, jcr->Job);
- /* Keep reference counts correct */
- free_locked_jcr(jcr);
- continue;
- }
-
- /* check MaxWaitTime */
- cancel = job_check_maxwaittime(control_jcr, jcr);
-
- /* check MaxRunTime */
- cancel |= job_check_maxruntime(control_jcr, jcr);
-
- if (cancel) {
- Dmsg3(200, "Cancelling JCR %p jobid %d (%s)\n",
- jcr, jcr->JobId, jcr->Job);
-
- UAContext *ua = new_ua_context(jcr);
- ua->jcr = control_jcr;
- cancel_job(ua, jcr);
- free_ua_context(ua);
-
- Dmsg1(200, "Have cancelled JCR %p\n", jcr);
- }
-
- /* Keep reference counts correct */
- free_locked_jcr(jcr);
- }
- unlock_jcr_chain();
-}
-
-/*
- * Check if the maxwaittime has expired and it is possible
- * to cancel the job.
- */
-static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr)
-{
- bool cancel = false;
-
- if (jcr->job->MaxWaitTime == 0) {
- return false;
- }
- if ((watchdog_time - jcr->start_time) < jcr->job->MaxWaitTime) {
- Dmsg3(200, "Job %p (%s) with MaxWaitTime %d not expired\n",
- jcr, jcr->Job, jcr->job->MaxWaitTime);
- return false;
- }
- Dmsg3(200, "Job %d (%s): MaxWaitTime of %d seconds exceeded, "
- "checking status\n",
- jcr->JobId, jcr->Job, jcr->job->MaxWaitTime);
- switch (jcr->JobStatus) {
- case JS_Created:
- case JS_Blocked:
- case JS_WaitFD:
- case JS_WaitSD:
- case JS_WaitStoreRes:
- case JS_WaitClientRes:
- case JS_WaitJobRes:
- case JS_WaitPriority:
- case JS_WaitMaxJobs:
- case JS_WaitStartTime:
- cancel = true;
- Dmsg0(200, "JCR blocked in #1\n");
- break;
- case JS_Running:
- Dmsg0(200, "JCR running, checking SD status\n");
- switch (jcr->SDJobStatus) {
- case JS_WaitMount:
- case JS_WaitMedia:
- case JS_WaitFD:
- cancel = true;
- Dmsg0(200, "JCR blocked in #2\n");
- break;
- default:
- Dmsg0(200, "JCR not blocked in #2\n");
- break;
- }
- break;
- case JS_Terminated:
- case JS_ErrorTerminated:
- case JS_Canceled:
- case JS_FatalError:
- Dmsg0(200, "JCR already dead in #3\n");
- break;
- default:
- Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
- jcr->JobStatus);
- }
- Dmsg3(200, "MaxWaitTime result: %scancel JCR %p (%s)\n",
- cancel ? "" : "do not ", jcr, jcr->job);
-
- return cancel;
-}
-
-/*
- * Check if maxruntime has expired and if the job can be
- * canceled.
- */
-static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr)
-{
- bool cancel = false;
-
- if (jcr->job->MaxRunTime == 0) {
- return false;
- }
- if ((watchdog_time - jcr->start_time) < jcr->job->MaxRunTime) {
- Dmsg3(200, "Job %p (%s) with MaxRunTime %d not expired\n",
- jcr, jcr->Job, jcr->job->MaxRunTime);
- return false;
- }
-
- switch (jcr->JobStatus) {
- case JS_Created:
- case JS_Running:
- case JS_Blocked:
- case JS_WaitFD:
- case JS_WaitSD:
- case JS_WaitStoreRes:
- case JS_WaitClientRes:
- case JS_WaitJobRes:
- case JS_WaitPriority:
- case JS_WaitMaxJobs:
- case JS_WaitStartTime:
- case JS_Differences:
- cancel = true;
- break;
- case JS_Terminated:
- case JS_ErrorTerminated:
- case JS_Canceled:
- case JS_FatalError:
- cancel = false;
- break;
- default:
- Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
- jcr->JobStatus);
- }
-
- Dmsg3(200, "MaxRunTime result: %scancel JCR %p (%s)\n",
- cancel ? "" : "do not ", jcr, jcr->job);
-
- return cancel;
-}
/*
* Run a job -- typically called by the scheduler, but may also
}
-/*
- * Cancel a job -- typically called by the UA (Console program), but may also
- * be called by the job watchdog.
- *
- * Returns: 1 if cancel appears to be successful
- * 0 on failure. Message sent to ua->jcr.
- */
-int cancel_job(UAContext *ua, JCR *jcr)
-{
- BSOCK *sd, *fd;
-
- switch (jcr->JobStatus) {
- case JS_Created:
- case JS_WaitJobRes:
- case JS_WaitClientRes:
- case JS_WaitStoreRes:
- case JS_WaitPriority:
- case JS_WaitMaxJobs:
- case JS_WaitStartTime:
- set_jcr_job_status(jcr, JS_Canceled);
- bsendmsg(ua, _("JobId %d, Job %s marked to be canceled.\n"),
- jcr->JobId, jcr->Job);
- jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
- return 1;
-
- default:
- set_jcr_job_status(jcr, JS_Canceled);
-
- /* Cancel File daemon */
- if (jcr->file_bsock) {
- ua->jcr->client = jcr->client;
- if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
- bsendmsg(ua, _("Failed to connect to File daemon.\n"));
- return 0;
- }
- Dmsg0(200, "Connected to file daemon\n");
- fd = ua->jcr->file_bsock;
- bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
- while (bnet_recv(fd) >= 0) {
- bsendmsg(ua, "%s", fd->msg);
- }
- bnet_sig(fd, BNET_TERMINATE);
- bnet_close(fd);
- ua->jcr->file_bsock = NULL;
- }
-
- /* Cancel Storage daemon */
- if (jcr->store_bsock) {
- ua->jcr->store = jcr->store;
- if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
- bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
- return 0;
- }
- Dmsg0(200, "Connected to storage daemon\n");
- sd = ua->jcr->store_bsock;
- bnet_fsend(sd, "cancel Job=%s\n", jcr->Job);
- while (bnet_recv(sd) >= 0) {
- bsendmsg(ua, "%s", sd->msg);
- }
- bnet_sig(sd, BNET_TERMINATE);
- bnet_close(sd);
- ua->jcr->store_bsock = NULL;
- }
- }
-
- return 1;
-}
/*
* This is the engine called by jobq.c:jobq_add() when we were pulled
}
}
}
+ /* Send off any queued messages */
+ if (jcr->msg_queue->size() > 0) {
+ dequeue_messages(jcr);
+ }
}
bail_out:
break;
}
+/*
+ * Cancel a job -- typically called by the UA (Console program), but may also
+ * be called by the job watchdog.
+ *
+ * Returns: 1 if cancel appears to be successful
+ * 0 on failure. Message sent to ua->jcr.
+ */
+int cancel_job(UAContext *ua, JCR *jcr)
+{
+ BSOCK *sd, *fd;
+
+ switch (jcr->JobStatus) {
+ case JS_Created:
+ case JS_WaitJobRes:
+ case JS_WaitClientRes:
+ case JS_WaitStoreRes:
+ case JS_WaitPriority:
+ case JS_WaitMaxJobs:
+ case JS_WaitStartTime:
+ set_jcr_job_status(jcr, JS_Canceled);
+ bsendmsg(ua, _("JobId %d, Job %s marked to be canceled.\n"),
+ jcr->JobId, jcr->Job);
+ jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
+ return 1;
+
+ default:
+ set_jcr_job_status(jcr, JS_Canceled);
+
+ /* Cancel File daemon */
+ if (jcr->file_bsock) {
+ ua->jcr->client = jcr->client;
+ if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
+ bsendmsg(ua, _("Failed to connect to File daemon.\n"));
+ return 0;
+ }
+ Dmsg0(200, "Connected to file daemon\n");
+ fd = ua->jcr->file_bsock;
+ bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
+ while (bnet_recv(fd) >= 0) {
+ bsendmsg(ua, "%s", fd->msg);
+ }
+ bnet_sig(fd, BNET_TERMINATE);
+ bnet_close(fd);
+ ua->jcr->file_bsock = NULL;
+ }
+
+ /* Cancel Storage daemon */
+ if (jcr->store_bsock) {
+ ua->jcr->store = jcr->store;
+ if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
+ bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
+ return 0;
+ }
+ Dmsg0(200, "Connected to storage daemon\n");
+ sd = ua->jcr->store_bsock;
+ bnet_fsend(sd, "cancel Job=%s\n", jcr->Job);
+ while (bnet_recv(sd) >= 0) {
+ bsendmsg(ua, "%s", sd->msg);
+ }
+ bnet_sig(sd, BNET_TERMINATE);
+ bnet_close(sd);
+ ua->jcr->store_bsock = NULL;
+ }
+ }
+
+ return 1;
+}
+
+
+static void job_monitor_destructor(watchdog_t *self)
+{
+ JCR *control_jcr = (JCR *) self->data;
+
+ free_jcr(control_jcr);
+}
+
+static void job_monitor_watchdog(watchdog_t *self)
+{
+ JCR *control_jcr, *jcr;
+
+ control_jcr = (JCR *)self->data;
+
+ Dmsg1(400, "job_monitor_watchdog %p called\n", self);
+
+ lock_jcr_chain();
+
+ foreach_jcr(jcr) {
+ bool cancel;
+
+ if (jcr->JobId == 0) {
+ Dmsg2(400, "Skipping JCR %p (%s) with JobId 0\n",
+ jcr, jcr->Job);
+ /* Keep reference counts correct */
+ free_locked_jcr(jcr);
+ continue;
+ }
+
+ /* check MaxWaitTime */
+ cancel = job_check_maxwaittime(control_jcr, jcr);
+
+ /* check MaxRunTime */
+ cancel |= job_check_maxruntime(control_jcr, jcr);
+
+ if (cancel) {
+ Dmsg3(200, "Cancelling JCR %p jobid %d (%s)\n",
+ jcr, jcr->JobId, jcr->Job);
+
+ UAContext *ua = new_ua_context(jcr);
+ ua->jcr = control_jcr;
+ cancel_job(ua, jcr);
+ free_ua_context(ua);
+
+ Dmsg1(200, "Have cancelled JCR %p\n", jcr);
+ }
+
+ /* Keep reference counts correct */
+ free_locked_jcr(jcr);
+ }
+ unlock_jcr_chain();
+}
+
+/*
+ * Check if the maxwaittime has expired and it is possible
+ * to cancel the job.
+ */
+static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr)
+{
+ bool cancel = false;
+
+ if (jcr->job->MaxWaitTime == 0) {
+ return false;
+ }
+ if ((watchdog_time - jcr->start_time) < jcr->job->MaxWaitTime) {
+ Dmsg3(200, "Job %p (%s) with MaxWaitTime %d not expired\n",
+ jcr, jcr->Job, jcr->job->MaxWaitTime);
+ return false;
+ }
+ Dmsg3(200, "Job %d (%s): MaxWaitTime of %d seconds exceeded, "
+ "checking status\n",
+ jcr->JobId, jcr->Job, jcr->job->MaxWaitTime);
+ switch (jcr->JobStatus) {
+ case JS_Created:
+ case JS_Blocked:
+ case JS_WaitFD:
+ case JS_WaitSD:
+ case JS_WaitStoreRes:
+ case JS_WaitClientRes:
+ case JS_WaitJobRes:
+ case JS_WaitPriority:
+ case JS_WaitMaxJobs:
+ case JS_WaitStartTime:
+ cancel = true;
+ Dmsg0(200, "JCR blocked in #1\n");
+ break;
+ case JS_Running:
+ Dmsg0(200, "JCR running, checking SD status\n");
+ switch (jcr->SDJobStatus) {
+ case JS_WaitMount:
+ case JS_WaitMedia:
+ case JS_WaitFD:
+ cancel = true;
+ Dmsg0(200, "JCR blocked in #2\n");
+ break;
+ default:
+ Dmsg0(200, "JCR not blocked in #2\n");
+ break;
+ }
+ break;
+ case JS_Terminated:
+ case JS_ErrorTerminated:
+ case JS_Canceled:
+ case JS_FatalError:
+ Dmsg0(200, "JCR already dead in #3\n");
+ break;
+ default:
+ Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
+ jcr->JobStatus);
+ }
+ Dmsg3(200, "MaxWaitTime result: %scancel JCR %p (%s)\n",
+ cancel ? "" : "do not ", jcr, jcr->job);
+
+ return cancel;
+}
+
+/*
+ * Check if maxruntime has expired and if the job can be
+ * canceled.
+ */
+static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr)
+{
+ bool cancel = false;
+
+ if (jcr->job->MaxRunTime == 0) {
+ return false;
+ }
+ if ((watchdog_time - jcr->start_time) < jcr->job->MaxRunTime) {
+ Dmsg3(200, "Job %p (%s) with MaxRunTime %d not expired\n",
+ jcr, jcr->Job, jcr->job->MaxRunTime);
+ return false;
+ }
+
+ switch (jcr->JobStatus) {
+ case JS_Created:
+ case JS_Running:
+ case JS_Blocked:
+ case JS_WaitFD:
+ case JS_WaitSD:
+ case JS_WaitStoreRes:
+ case JS_WaitClientRes:
+ case JS_WaitJobRes:
+ case JS_WaitPriority:
+ case JS_WaitMaxJobs:
+ case JS_WaitStartTime:
+ case JS_Differences:
+ cancel = true;
+ break;
+ case JS_Terminated:
+ case JS_ErrorTerminated:
+ case JS_Canceled:
+ case JS_FatalError:
+ cancel = false;
+ break;
+ default:
+ Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
+ jcr->JobStatus);
+ }
+
+ Dmsg3(200, "MaxRunTime result: %scancel JCR %p (%s)\n",
+ cancel ? "" : "do not ", jcr, jcr->job);
+
+ return cancel;
+}
+
+
/*
* Get or create a Client record for this Job
*/
BSOCK *store_bsock; /* Storage connection socket */
BSOCK *file_bsock; /* File daemon connection socket */
JCR_free_HANDLER *daemon_free_jcr; /* Local free routine */
+ dlist *msg_queue; /* Queued messages */
+ bool dequeuing; /* dequeuing messages */
POOLMEM *errmsg; /* edited error message */
char Job[MAX_NAME_LENGTH]; /* Unique name of this Job */
uint32_t JobId; /* Director's JobId */
MSGS *jcr_msgs; /* Copy of message resource -- actually used */
uint32_t ClientId; /* Client associated with Job */
char *where; /* prefix to restore files to */
- int prefix_links; /* Prefix links with Where path */
+ bool prefix_links; /* Prefix links with Where path */
int cached_pnl; /* cached path length */
POOLMEM *cached_path; /* cached path */
/*
- * Bacula doubly linked list routines.
+ * Bacula doubly linked list routines.
*
* dlist is a doubly linked list with the links being in the
- * list data item.
- *
+ * list data item.
+ *
* Kern Sibbald, July MMIII
*
* Version $Id$
((dlink *)((char *)tail+loffset))->next = item;
}
tail = item;
- if (head == NULL) { /* if empty list, */
- head = item; /* item is head as well */
+ if (head == NULL) { /* if empty list, */
+ head = item; /* item is head as well */
}
num_items++;
}
((dlink *)((char *)head+loffset))->prev = item;
}
head = item;
- if (tail == NULL) { /* if empty list, */
- tail = item; /* item is tail too */
+ if (tail == NULL) { /* if empty list, */
+ tail = item; /* item is tail too */
}
num_items++;
}
-void dlist::insert_before(void *item, void *where)
+void dlist::insert_before(void *item, void *where)
{
dlink *where_link = (dlink *)((char *)where+loffset);
num_items++;
}
-void dlist::insert_after(void *item, void *where)
+void dlist::insert_after(void *item, void *where)
{
dlink *where_link = (dlink *)((char *)where+loffset);
if (item == head) {
head = ilink->next;
if (head) {
- ((dlink *)((char *)head+loffset))->prev = NULL;
+ ((dlink *)((char *)head+loffset))->prev = NULL;
}
if (item == tail) {
- tail = ilink->prev;
+ tail = ilink->prev;
}
} else if (item == tail) {
tail = ilink->prev;
if (tail) {
- ((dlink *)((char *)tail+loffset))->next = NULL;
+ ((dlink *)((char *)tail+loffset))->next = NULL;
}
} else {
xitem = ilink->next;
jcr->buf = bstrdup(buf);
jcr_chain->prepend(jcr);
if (i == 10) {
- save_jcr = jcr;
+ save_jcr = jcr;
}
}
jcr->buf = bstrdup(buf);
jcr_chain->append(jcr);
if (i == 10) {
- save_jcr = jcr;
+ save_jcr = jcr;
}
}
JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr)
{
JCR *jcr;
+ MQUEUE_ITEM *item = NULL;
struct sigaction sigtimer;
Dmsg0(200, "Enter new_jcr\n");
jcr = (JCR *)malloc(size);
memset(jcr, 0, size);
+ jcr->msg_queue = new dlist(item, &item->link);
jcr->my_thread_id = pthread_self();
jcr->sched_time = time(NULL);
jcr->daemon_free_jcr = daemon_free_jcr; /* plug daemon free routine */
pthread_mutex_destroy(&jcr->mutex);
close_msg(jcr); /* close messages for this job */
+ delete jcr->msg_queue;
/* do this after closing messages */
if (jcr->client_name) {
}
}
+/*
+ * If we come here, prefix the message with the file:line-number,
+ * then pass it on to the normal Jmsg routine.
+ */
+void j_msg(char *file, int line, JCR *jcr, int type, int level, char *fmt,...)
+{
+ va_list arg_ptr;
+ int i, len, maxlen;
+ POOLMEM *pool_buf;
+
+ pool_buf = get_pool_memory(PM_EMSG);
+ i = Mmsg(&pool_buf, "%s:%d ", file, line);
+
+again:
+ maxlen = sizeof_pool_memory(pool_buf) - i - 1;
+ va_start(arg_ptr, fmt);
+ len = bvsnprintf(pool_buf+i, maxlen, fmt, arg_ptr);
+ va_end(arg_ptr);
+ if (len < 0 || len >= maxlen) {
+ pool_buf = realloc_pool_memory(pool_buf, maxlen + i + maxlen/2);
+ goto again;
+ }
+
+ Jmsg(jcr, type, level, "%s", pool_buf);
+ free_memory(pool_buf);
+}
+
+
/*
* Edit a message into a Pool memory buffer, with file:lineno
*/
return len;
}
+static pthread_mutex_t msg_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * We queue messages rather than print them directly. This
+ * is generally used in low level routines (msg handler, bnet)
+ * to prevent recursion.
+ */
+void Qmsg(JCR *jcr, int type, int level, char *fmt,...)
+{
+ va_list arg_ptr;
+ int len, maxlen;
+ POOLMEM *pool_buf;
+ MQUEUE_ITEM *item;
+
+ if (jcr->dequeuing) { /* do not allow recursion */
+ return;
+ }
+ pool_buf = get_pool_memory(PM_EMSG);
+
+again:
+ maxlen = sizeof_pool_memory(pool_buf) - 1;
+ va_start(arg_ptr, fmt);
+ len = bvsnprintf(pool_buf, maxlen, fmt, arg_ptr);
+ va_end(arg_ptr);
+ if (len < 0 || len >= maxlen) {
+ pool_buf = realloc_pool_memory(pool_buf, maxlen + maxlen/2);
+ goto again;
+ }
+ P(msg_queue_mutex);
+ item = (MQUEUE_ITEM *)malloc(sizeof(MQUEUE_ITEM) + strlen(pool_buf) + 1);
+ item->type = type;
+ item->level = level;
+ strcpy(item->msg, pool_buf);
+ jcr->msg_queue->append(item);
+ V(msg_queue_mutex);
+ free_memory(pool_buf);
+}
+
+/*
+ * Dequeue messages
+ */
+void dequeue_messages(JCR *jcr)
+{
+ MQUEUE_ITEM *item;
+ P(msg_queue_mutex);
+ jcr->dequeuing = true;
+ foreach_dlist(item, jcr->msg_queue) {
+ Jmsg(jcr, item->type, item->level, "%s", item->msg);
+ }
+ jcr->msg_queue->destroy();
+ jcr->dequeuing = false;
+ V(msg_queue_mutex);
+}
+
/*
* If we come here, prefix the message with the file:line-number,
- * then pass it on to the normal Jmsg routine.
+ * then pass it on to the normal Qmsg routine.
*/
-void j_msg(char *file, int line, JCR *jcr, int type, int level, char *fmt,...)
+void q_msg(char *file, int line, JCR *jcr, int type, int level, char *fmt,...)
{
va_list arg_ptr;
int i, len, maxlen;
goto again;
}
- Jmsg(jcr, type, level, "%s", pool_buf);
+ Qmsg(jcr, type, level, "%s", pool_buf);
free_memory(pool_buf);
}
#define MD_CONSOLE 9 /* send msg to UserAgent or console */
#define MD_MAIL_ON_ERROR 10 /* email messages if job errors */
+/* Queued message item */
+struct MQUEUE_ITEM {
+ dlink link;
+ int type;
+ int level;
+ char msg[1];
+};
+
void d_msg(char *file, int line, int level, char *fmt,...);
void e_msg(char *file, int line, int type, int level, char *fmt,...);
struct JCR;
/* attr.c */
-ATTR *new_attr();
-void free_attr(ATTR *attr);
-int unpack_attributes_record(JCR *jcr, int32_t stream, char *rec, ATTR *attr);
-void build_attr_output_fnames(JCR *jcr, ATTR *attr);
-void print_ls_output(JCR *jcr, ATTR *attr);
+ATTR *new_attr();
+void free_attr(ATTR *attr);
+int unpack_attributes_record(JCR *jcr, int32_t stream, char *rec, ATTR *attr);
+void build_attr_output_fnames(JCR *jcr, ATTR *attr);
+void print_ls_output(JCR *jcr, ATTR *attr);
/* base64.c */
-void base64_init (void);
-int to_base64 (intmax_t value, char *where);
-int from_base64 (intmax_t *value, char *where);
-int bin_to_base64 (char *buf, char *bin, int len);
+void base64_init (void);
+int to_base64 (intmax_t value, char *where);
+int from_base64 (intmax_t *value, char *where);
+int bin_to_base64 (char *buf, char *bin, int len);
/* bsys.c */
-char *bstrncpy (char *dest, const char *src, int maxlen);
-char *bstrncat (char *dest, const char *src, int maxlen);
-void *b_malloc (char *file, int line, size_t size);
+char *bstrncpy (char *dest, const char *src, int maxlen);
+char *bstrncat (char *dest, const char *src, int maxlen);
+void *b_malloc (char *file, int line, size_t size);
#ifndef DEBUG
-void *bmalloc (size_t size);
+void *bmalloc (size_t size);
#endif
-void *brealloc (void *buf, size_t size);
-void *bcalloc (size_t size1, size_t size2);
-int bsnprintf (char *str, int32_t size, const char *format, ...);
-int bvsnprintf (char *str, int32_t size, const char *format, va_list ap);
-int pool_sprintf (char *pool_buf, char *fmt, ...);
-void create_pid_file (char *dir, char *progname, int port);
-int delete_pid_file (char *dir, char *progname, int port);
-void drop (char *uid, char *gid);
-int bmicrosleep (time_t sec, long usec);
-char *bfgets (char *s, int size, FILE *fd);
-void make_unique_filename (POOLMEM **name, int Id, char *what);
+void *brealloc (void *buf, size_t size);
+void *bcalloc (size_t size1, size_t size2);
+int bsnprintf (char *str, int32_t size, const char *format, ...);
+int bvsnprintf (char *str, int32_t size, const char *format, va_list ap);
+int pool_sprintf (char *pool_buf, char *fmt, ...);
+void create_pid_file (char *dir, char *progname, int port);
+int delete_pid_file (char *dir, char *progname, int port);
+void drop (char *uid, char *gid);
+int bmicrosleep (time_t sec, long usec);
+char *bfgets (char *s, int size, FILE *fd);
+void make_unique_filename (POOLMEM **name, int Id, char *what);
#ifndef HAVE_STRTOLL
-long long int strtoll (const char *ptr, char **endptr, int base);
+long long int strtoll (const char *ptr, char **endptr, int base);
#endif
/* bnet.c */
-int32_t bnet_recv (BSOCK *bsock);
-int bnet_send (BSOCK *bsock);
-int bnet_fsend (BSOCK *bs, char *fmt, ...);
-int bnet_set_buffer_size (BSOCK *bs, uint32_t size, int rw);
-int bnet_sig (BSOCK *bs, int sig);
-int bnet_ssl_server (BSOCK *bsock, char *password, int ssl_need, int ssl_has);
-int bnet_ssl_client (BSOCK *bsock, char *password, int ssl_need);
-BSOCK * bnet_connect (JCR *jcr, int retry_interval,
- int max_retry_time, char *name, char *host, char *service,
- int port, int verbose);
-void bnet_close (BSOCK *bsock);
-BSOCK * init_bsock (JCR *jcr, int sockfd, char *who, char *ip,
- int port, struct sockaddr_in *client_addr);
-BSOCK * dup_bsock (BSOCK *bsock);
-void term_bsock (BSOCK *bsock);
-char * bnet_strerror (BSOCK *bsock);
-char * bnet_sig_to_ascii (BSOCK *bsock);
-int bnet_wait_data (BSOCK *bsock, int sec);
-int bnet_wait_data_intr (BSOCK *bsock, int sec);
-int bnet_despool_to_bsock (BSOCK *bsock);
-int is_bnet_stop (BSOCK *bsock);
-int is_bnet_error (BSOCK *bsock);
-void bnet_suppress_error_messages(BSOCK *bsock, int flag);
+int32_t bnet_recv (BSOCK *bsock);
+int bnet_send (BSOCK *bsock);
+int bnet_fsend (BSOCK *bs, char *fmt, ...);
+int bnet_set_buffer_size (BSOCK *bs, uint32_t size, int rw);
+int bnet_sig (BSOCK *bs, int sig);
+int bnet_ssl_server (BSOCK *bsock, char *password, int ssl_need, int ssl_has);
+int bnet_ssl_client (BSOCK *bsock, char *password, int ssl_need);
+BSOCK * bnet_connect (JCR *jcr, int retry_interval,
+ int max_retry_time, char *name, char *host, char *service,
+ int port, int verbose);
+void bnet_close (BSOCK *bsock);
+BSOCK * init_bsock (JCR *jcr, int sockfd, char *who, char *ip,
+ int port, struct sockaddr_in *client_addr);
+BSOCK * dup_bsock (BSOCK *bsock);
+void term_bsock (BSOCK *bsock);
+char * bnet_strerror (BSOCK *bsock);
+char * bnet_sig_to_ascii (BSOCK *bsock);
+int bnet_wait_data (BSOCK *bsock, int sec);
+int bnet_wait_data_intr (BSOCK *bsock, int sec);
+int bnet_despool_to_bsock (BSOCK *bsock);
+int is_bnet_stop (BSOCK *bsock);
+int is_bnet_error (BSOCK *bsock);
+void bnet_suppress_error_messages(BSOCK *bsock, int flag);
/* bget_msg.c */
-int bget_msg(BSOCK *sock);
+int bget_msg(BSOCK *sock);
/* bpipe.c */
-BPIPE * open_bpipe(char *prog, int wait, char *mode);
-int close_wpipe(BPIPE *bpipe);
-int close_bpipe(BPIPE *bpipe);
+BPIPE * open_bpipe(char *prog, int wait, char *mode);
+int close_wpipe(BPIPE *bpipe);
+int close_bpipe(BPIPE *bpipe);
/* cram-md5.c */
int cram_md5_get_auth(BSOCK *bs, char *password, int ssl_need);
int cram_md5_auth(BSOCK *bs, char *password, int ssl_need);
void hmac_md5(uint8_t* text, int text_len, uint8_t* key,
- int key_len, uint8_t *hmac);
+ int key_len, uint8_t *hmac);
/* crc32.c */
uint32_t bcrc32(uint8_t *buf, int len);
/* daemon.c */
-void daemon_start ();
+void daemon_start ();
/* edit.c */
-uint64_t str_to_uint64(char *str);
-int64_t str_to_int64(char *str);
-char * edit_uint64_with_commas (uint64_t val, char *buf);
-char * add_commas (char *val, char *buf);
-char * edit_uint64 (uint64_t val, char *buf);
-int duration_to_utime (char *str, utime_t *value);
-int size_to_uint64(char *str, int str_len, uint64_t *rtn_value);
-char *edit_utime (utime_t val, char *buf);
-int is_a_number (const char *num);
-int is_an_integer (const char *n);
-bool is_name_valid (char *name, POOLMEM **msg);
+uint64_t str_to_uint64(char *str);
+int64_t str_to_int64(char *str);
+char * edit_uint64_with_commas (uint64_t val, char *buf);
+char * add_commas (char *val, char *buf);
+char * edit_uint64 (uint64_t val, char *buf);
+int duration_to_utime (char *str, utime_t *value);
+int size_to_uint64(char *str, int str_len, uint64_t *rtn_value);
+char *edit_utime (utime_t val, char *buf);
+int is_a_number (const char *num);
+int is_an_integer (const char *n);
+bool is_name_valid (char *name, POOLMEM **msg);
/* jcr.c (most definitions are in src/jcr.h) */
void init_last_jobs_list();
/* lex.c */
-LEX * lex_close_file (LEX *lf);
-LEX * lex_open_file (LEX *lf, char *fname, LEX_ERROR_HANDLER *scan_error);
-int lex_get_char (LEX *lf);
-void lex_unget_char (LEX *lf);
-char * lex_tok_to_str (int token);
-int lex_get_token (LEX *lf, int expect);
+LEX * lex_close_file (LEX *lf);
+LEX * lex_open_file (LEX *lf, char *fname, LEX_ERROR_HANDLER *scan_error);
+int lex_get_char (LEX *lf);
+void lex_unget_char (LEX *lf);
+char * lex_tok_to_str (int token);
+int lex_get_token (LEX *lf, int expect);
/* message.c */
-void my_name_is (int argc, char *argv[], char *name);
-void init_msg (JCR *jcr, MSGS *msg);
-void term_msg (void);
-void close_msg (JCR *jcr);
-void add_msg_dest (MSGS *msg, int dest, int type, char *where, char *dest_code);
-void rem_msg_dest (MSGS *msg, int dest, int type, char *where);
-void Jmsg (JCR *jcr, int type, int level, char *fmt, ...);
-void dispatch_message (JCR *jcr, int type, int level, char *buf);
-void init_console_msg (char *wd);
-void free_msgs_res (MSGS *msgs);
-int open_spool_file (JCR *jcr, BSOCK *bs);
-int close_spool_file (JCR *jcr, BSOCK *bs);
+void my_name_is (int argc, char *argv[], char *name);
+void init_msg (JCR *jcr, MSGS *msg);
+void term_msg (void);
+void close_msg (JCR *jcr);
+void add_msg_dest (MSGS *msg, int dest, int type, char *where, char *dest_code);
+void rem_msg_dest (MSGS *msg, int dest, int type, char *where);
+void Jmsg (JCR *jcr, int type, int level, char *fmt, ...);
+void dispatch_message (JCR *jcr, int type, int level, char *buf);
+void init_console_msg (char *wd);
+void free_msgs_res (MSGS *msgs);
+int open_spool_file (JCR *jcr, BSOCK *bs);
+int close_spool_file (JCR *jcr, BSOCK *bs);
+void dequeue_messages (JCR *jcr);
/* bnet_server.c */
-void bnet_thread_server(char *bind_addr, int port, int max_clients, workq_t *client_wq,
- void *handle_client_request(void *bsock));
-void bnet_server (int port, void handle_client_request(BSOCK *bsock));
-int net_connect (int port);
-BSOCK * bnet_bind (int port);
-BSOCK * bnet_accept (BSOCK *bsock, char *who);
+void bnet_thread_server(char *bind_addr, int port, int max_clients, workq_t *client_wq,
+ void *handle_client_request(void *bsock));
+void bnet_server (int port, void handle_client_request(BSOCK *bsock));
+int net_connect (int port);
+BSOCK * bnet_bind (int port);
+BSOCK * bnet_accept (BSOCK *bsock, char *who);
/* idcache.c */
char *getuser(uid_t uid);
/* signal.c */
-void init_signals (void terminate(int sig));
-void init_stack_dump (void);
+void init_signals (void terminate(int sig));
+void init_stack_dump (void);
/* scan.c */
-void strip_trailing_junk (char *str);
-void strip_trailing_slashes (char *dir);
-bool skip_spaces (char **msg);
-bool skip_nonspaces (char **msg);
-int fstrsch (char *a, char *b);
-int parse_args(POOLMEM *cmd, POOLMEM **args, int *argc,
- char **argk, char **argv, int max_args);
-char *next_arg(char **s);
+void strip_trailing_junk (char *str);
+void strip_trailing_slashes (char *dir);
+bool skip_spaces (char **msg);
+bool skip_nonspaces (char **msg);
+int fstrsch (char *a, char *b);
+int parse_args(POOLMEM *cmd, POOLMEM **args, int *argc,
+ char **argk, char **argv, int max_args);
+char *next_arg(char **s);
/* util.c */
-int is_buf_zero (char *buf, int len);
-void lcase (char *str);
-void bash_spaces (char *str);
-void unbash_spaces (char *str);
-char * encode_time (time_t time, char *buf);
-char * encode_mode (mode_t mode, char *buf);
-int do_shell_expansion (char *name, int name_len);
-void jobstatus_to_ascii (int JobStatus, char *msg, int maxlen);
-int pm_strcat (POOLMEM **pm, char *str);
-int pm_strcpy (POOLMEM **pm, char *str);
-int run_program (char *prog, int wait, POOLMEM *results);
-char * job_type_to_str (int type);
-char * job_status_to_str (int stat);
-char * job_level_to_str (int level);
-void make_session_key (char *key, char *seed, int mode);
-POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, char *to);
-void set_working_directory(char *wd);
+int is_buf_zero (char *buf, int len);
+void lcase (char *str);
+void bash_spaces (char *str);
+void unbash_spaces (char *str);
+char * encode_time (time_t time, char *buf);
+char * encode_mode (mode_t mode, char *buf);
+int do_shell_expansion (char *name, int name_len);
+void jobstatus_to_ascii (int JobStatus, char *msg, int maxlen);
+int pm_strcat (POOLMEM **pm, char *str);
+int pm_strcpy (POOLMEM **pm, char *str);
+int run_program (char *prog, int wait, POOLMEM *results);
+char * job_type_to_str (int type);
+char * job_status_to_str (int stat);
+char * job_level_to_str (int level);
+void make_session_key (char *key, char *seed, int mode);
+POOLMEM *edit_job_codes(JCR *jcr, char *omsg, char *imsg, char *to);
+void set_working_directory(char *wd);
/* watchdog.c */
}
dev->state &= ~ST_APPEND; /* clear any previous append mode */
- dev->state |= ST_READ; /* set reed mode */
+ dev->state |= ST_READ; /* set read mode */
attach_jcr_to_device(dev, jcr); /* attach jcr to device */
Jmsg(jcr, M_INFO, 0, _("Ready to read from volume \"%s\" on device %s.\n"),
jcr->VolumeName, dev_name(dev));
Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results);
if (status == 0) {
loaded = atoi(results);
+ if (loaded > 0) {
+ Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded\", result is Slot %d.\n"),
+ loaded);
+ } else {
+ Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded\", result is not loaded.\n"));
+ }
} else {
Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded\" command, status=%d.\n"), status);
loaded = -1; /* force unload */
force_close_dev(dev);
if (loaded != 0) { /* must unload drive */
Dmsg0(400, "Doing changer unload.\n");
- Jmsg(jcr, M_INFO, 0, _("3302 Issuing autochanger \"unload\" command.\n"));
+ Jmsg(jcr, M_INFO, 0, _("3303 Issuing autochanger \"unload\" command.\n"));
changer = edit_device_codes(jcr, changer,
jcr->device->changer_command, "unload");
status = run_program(changer, timeout, NULL);
* Load the desired cassette
*/
Dmsg1(400, "Doing changer load slot %d\n", slot);
- Jmsg(jcr, M_INFO, 0, _("3303 Issuing autochanger \"load slot %d\" command.\n"),
+ Jmsg(jcr, M_INFO, 0, _("3304 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, _("3304 Autochanger \"load slot %d\", status is OK.\n"),
+ Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d\", status is OK.\n"),
slot);
} else {
Jmsg(jcr, M_INFO, 0, _("3992 Bad autochanger \"load slot %d\", status=%d.\n"),
return block;
}
+
+/*
+ * Duplicate an existing block (eblock)
+ */
+DEV_BLOCK *dup_block(DEV_BLOCK *eblock)
+{
+ DEV_BLOCK *block = (DEV_BLOCK *)get_memory(sizeof(DEV_BLOCK));
+ int buf_len = sizeof_pool_memory(eblock->buf);
+
+ memcpy(block, eblock, sizeof(DEV_BLOCK));
+ block->buf = get_memory(buf_len);
+ memcpy(block->buf, eblock->buf, buf_len);
+ return block;
+}
+
+
/*
* Only the first block checksum error was reported.
* If there are more, report it now.
max_cap = dev->VolCatInfo.VolCatMaxBytes;
}
Jmsg(jcr, M_INFO, 0, _("User defined maximum volume capacity %s exceeded on device %s.\n"),
- edit_uint64(max_cap, ed1), dev->dev_name);
+ edit_uint64_with_commas(max_cap, ed1), dev->dev_name);
block->write_failed = true;
if (weof_dev(dev, 1) != 0) { /* end tape */
Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
}
/*
- * We are only looking for labels or in particula Job Session records
+ * We are only looking for labels or in particular Job Session records
*/
static int jobs_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
{
* If you change the format of the state file,
* increment this value
*/
-static uint32_t btape_state_level = 1;
+static uint32_t btape_state_level = 2;
DEVICE *dev = NULL;
DEVRES *device = NULL;
static void statcmd();
static void unfillcmd();
static int flush_block(DEV_BLOCK *block, int dump);
+#ifdef xxx_needed
static int record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec);
+#endif
+static int quickie_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec);
+static bool compare_blocks(DEV_BLOCK *last_block, DEV_BLOCK *block);
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 char *argv[MAX_CMD_ARGS];
static int argc;
+static int quickie_count = 0;
static BSR *bsr = NULL;
static int signals = TRUE;
-static int ok;
-static int stop;
+static bool ok;
+static int stop = 0;
static uint64_t vol_size;
static uint64_t VolBytes;
static time_t now;
static uint32_t eot_block_len;
static uint32_t eot_FileIndex;
static int dumped = 0;
+static DEV_BLOCK *last_block1 = NULL;
+static DEV_BLOCK *last_block2 = NULL;
static DEV_BLOCK *last_block = NULL;
static DEV_BLOCK *this_block = NULL;
+static DEV_BLOCK *first_block = NULL;
+static uint32_t last_file1 = 0;
+static uint32_t last_file2 = 0;
static uint32_t last_file = 0;
+static uint32_t last_block_num1 = 0;
+static uint32_t last_block_num2 = 0;
static uint32_t last_block_num = 0;
static uint32_t BlockNumber = 0;
static bool simple = true;
static char *VolumeName = NULL;
-static int vol_num;
+static int vol_num = 0;
static JCR *jcr = NULL;
if (!dev) {
exit(1);
}
- dev->max_volume_size = 0;
if (!open_the_device()) {
goto terminate;
}
free_bsr(bsr);
}
- if (last_block) {
- free_block(last_block);
- }
if (this_block) {
free_block(this_block);
}
}
if (!(dev->state & ST_OPENED)) {
- if (!open_device(dev)) {
+ if (!first_open_device(dev)) {
Pmsg1(0, "Device open failed. ERR=%s\n", strerror_dev(dev));
}
}
+ rewind_dev(dev);
write_volume_label_to_dev(jcr, jcr->device, cmd, "Default");
+ Pmsg1(-1, "Wrote Volume label for volume \"%s\".\n", cmd);
}
/*
DEV_RECORD *rec;
int stat = 0;
int len, i, j;
+ int *p;
Pmsg0(-1, _("\n=== Write, rewind, and re-read test ===\n\n"
- "I'm going to write 10 records and an EOF\n"
- "then write 10 records and an EOF, then rewind,\n"
+ "I'm going to write 1000 records and an EOF\n"
+ "then write 1000 records and an EOF, then rewind,\n"
"and re-read the data to verify that it is correct.\n\n"
"This is an *essential* feature ...\n\n"));
block = new_block(dev);
goto bail_out;
}
rec->data = check_pool_memory_size(rec->data, block->buf_len);
- len = rec->data_len = block->buf_len-100;
- for (i=1; i<=10; i++) {
- memset(rec->data, i, rec->data_len);
+ rec->data_len = block->buf_len-100;
+ len = rec->data_len/sizeof(i);
+ for (i=1; i<=1000; i++) {
+ p = (int *)rec->data;
+ for (j=0; j<len; j++) {
+ *p++ = i;
+ }
if (!write_record_to_block(block, rec)) {
Pmsg0(0, _("Error writing record to block.\n"));
goto bail_out;
goto bail_out;
}
}
- Pmsg1(0, _("Wrote 10 blocks of %d bytes.\n"), rec->data_len);
+ Pmsg1(0, _("Wrote 1000 blocks of %d bytes.\n"), rec->data_len);
weofcmd();
- for (i=11; i<=20; i++) {
- memset(rec->data, i, rec->data_len);
+ for (i=1001; i<=2000; i++) {
+ p = (int *)rec->data;
+ for (j=0; j<len; j++) {
+ *p++ = i;
+ }
if (!write_record_to_block(block, rec)) {
Pmsg0(0, _("Error writing record to block.\n"));
goto bail_out;
goto bail_out;
}
}
- Pmsg1(0, _("Wrote 10 blocks of %d bytes.\n"), rec->data_len);
+ Pmsg1(0, _("Wrote 1000 blocks of %d bytes.\n"), rec->data_len);
weofcmd();
if (dev_cap(dev, CAP_TWOEOF)) {
weofcmd();
} else {
Pmsg0(0, "Rewind OK.\n");
}
- for (i=1; i<=20; i++) {
+ for (i=1; i<=2000; i++) {
read_again:
if (!read_block_from_dev(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) {
if (dev_state(dev, ST_EOF)) {
Pmsg1(0, _("Read record failed! ERR=%s\n"), strerror(dev->dev_errno));
goto bail_out;
}
+ p = (int *)rec->data;
for (j=0; j<len; j++) {
- if (rec->data[j] != i) {
+ if (*p != i) {
Pmsg3(0, _("Bad data in record. Expected %d, got %d at byte %d. Test failed!\n"),
- i, rec->data[j], j);
+ i, *p, j);
goto bail_out;
}
+ p++;
}
- if (i == 10 || i == 20) {
- Pmsg0(-1, _("10 blocks re-read correctly.\n"));
+ if (i == 1000 || i == 2000) {
+ Pmsg0(-1, _("1000 blocks re-read correctly.\n"));
}
}
Pmsg0(-1, _("=== Test Succeeded. End Write, rewind, and re-read test ===\n\n"));
DEV_RECORD *rec;
int stat = 0;
int len, i, j;
+ bool ok = true;
+ int recno = 0;
+ int file = 0, blk = 0;
+ int *p;
Pmsg0(-1, _("\n=== Write, rewind, and position test ===\n\n"
- "I'm going to write 10 records and an EOF\n"
- "then write 10 records and an EOF, then rewind,\n"
+ "I'm going to write 1000 records and an EOF\n"
+ "then write 1000 records and an EOF, then rewind,\n"
"and position to a few blocks and verify that it is correct.\n\n"
"This is an *essential* feature ...\n\n"));
block = new_block(dev);
goto bail_out;
}
rec->data = check_pool_memory_size(rec->data, block->buf_len);
- len = rec->data_len = block->buf_len-100;
- for (i=1; i<=10; i++) {
- memset(rec->data, i, rec->data_len);
+ rec->data_len = block->buf_len-100;
+ len = rec->data_len/sizeof(i);
+ for (i=1; i<=1000; i++) {
+ p = (int *)rec->data;
+ for (j=0; j<len; j++) {
+ *p++ = i;
+ }
if (!write_record_to_block(block, rec)) {
Pmsg0(0, _("Error writing record to block.\n"));
goto bail_out;
goto bail_out;
}
}
- Pmsg1(0, _("Wrote 10 blocks of %d bytes.\n"), rec->data_len);
+ Pmsg1(0, _("Wrote 1000 blocks of %d bytes.\n"), rec->data_len);
weofcmd();
- for (i=11; i<=20; i++) {
- memset(rec->data, i, rec->data_len);
+ for (i=1001; i<=2000; i++) {
+ p = (int *)rec->data;
+ for (j=0; j<len; j++) {
+ *p++ = i;
+ }
if (!write_record_to_block(block, rec)) {
Pmsg0(0, _("Error writing record to block.\n"));
goto bail_out;
goto bail_out;
}
}
- Pmsg1(0, _("Wrote 10 blocks of %d bytes.\n"), rec->data_len);
+ Pmsg1(0, _("Wrote 1000 blocks of %d bytes.\n"), rec->data_len);
weofcmd();
if (dev_cap(dev, CAP_TWOEOF)) {
weofcmd();
Pmsg0(0, "Rewind OK.\n");
}
- for (i=1; i<=20; i++) {
- if (i != 5 && i != 17) {
+ while(ok) {
+ /* Set up next item to read based on where we are */
+ switch (recno) {
+ case 0:
+ recno = 5;
+ file = 0;
+ blk = 4;
+ break;
+ case 5:
+ recno = 201;
+ file = 0;
+ blk = 200;
+ break;
+ case 201:
+ recno = 1000;
+ file = 0;
+ blk = 999;
+ break;
+ case 1000:
+ recno = 1001;
+ file = 1;
+ blk = 0;
+ break;
+ case 1001:
+ recno = 1601;
+ file = 1;
+ blk = 600;
+ break;
+ case 1601:
+ recno = 2000;
+ file = 1;
+ blk = 999;
+ break;
+ case 2000:
+ ok = false;
continue;
}
- if (i == 5) {
- Pmsg0(-1, "Reposition to file:block 0:4\n");
- if (!reposition_dev(dev, 0, 4)) {
- Pmsg0(0, "Reposition error.\n");
- goto bail_out;
- }
- } else {
- Pmsg0(-1, "Reposition to file:block 1:6\n");
- if (!reposition_dev(dev, 1, 6)) {
- Pmsg0(0, "Reposition error.\n");
- goto bail_out;
- }
+ Pmsg2(-1, "Reposition to file:block %d:%d\n", file, blk);
+ if (!reposition_dev(dev, file, blk)) {
+ Pmsg0(0, "Reposition error.\n");
+ goto bail_out;
}
read_again:
if (!read_block_from_dev(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) {
Pmsg1(0, _("Read record failed! ERR=%s\n"), strerror(dev->dev_errno));
goto bail_out;
}
+ p = (int *)rec->data;
for (j=0; j<len; j++) {
- if (rec->data[j] != i) {
+ if (p[j] != recno) {
Pmsg3(0, _("Bad data in record. Expected %d, got %d at byte %d. Test failed!\n"),
- i, rec->data[j], j);
+ recno, p[j], j);
goto bail_out;
}
}
fsf_test(); /* do fast forward space file test */
autochanger_test(); /* do autochanger test */
-
- Pmsg0(-1, _("\n=== End Append files test ===\n"));
}
uint32_t i;
uint32_t min_block_size;
- ok = TRUE;
+ ok = true;
stop = 0;
vol_num = 0;
last_file = 0;
Pmsg0(-1, "\n\
This command simulates Bacula writing to a tape.\n\
It requires either one or two blank tapes, which it\n\
-will label and write. It will print a status approximately\n\
+will label and write.\n\n\
+If you have an autochanger configured, it will use\n\
+the tapes that are in slots 1 and 2, otherwise, you will\n\
+be prompted to insert the tapes when necessary.\n\n\
+It will print a status approximately\n\
every 322 MB, and write an EOF every 3.2 GB. If you have\n\
selected the simple test option, after writing the first tape\n\
-it will rewind it and re-read the last block written.\n\
+it will rewind it and re-read the last block written.\n\n\
If you have selected the multiple tape test, when the first tape\n\
fills, it will ask for a second, and after writing a few more \n\
blocks, it will stop. Then it will begin re-reading the\n\
two tapes.\n\n\
This may take a long time -- hours! ...\n\n");
-/*
- get_cmd("Insert a blank tape then indicate if you want\n"
- "to run the simplified test (s) with one tape or\n"
- "the complete multiple tape (m) test: (s/m) ");
+ get_cmd("Do you want to run the simplified test (s) with one tape\n"
+ "or the complete multiple tape (m) test: (s/m) ");
if (cmd[0] == 's') {
Pmsg0(-1, "Simple test (single tape) selected.\n");
simple = true;
} else if (cmd[0] == 'm') {
- Pmsg0(-1, "Complete multiple tape test selected.\n");
+ Pmsg0(-1, "Multiple tape test selected.\n");
simple = false;
} else {
Pmsg0(000, "Command aborted.\n");
return;
}
-*/
- get_cmd("Insert a blank tape then indicate when you are ready ...\n");
- simple = true;
- set_volume_name("TestVolume1", 1);
- labelcmd();
- VolumeName = NULL;
-
-
Dmsg1(20, "Begin append device=%s\n", dev_name(dev));
-
+ Dmsg1(20, "MaxVolSize=%s\n", edit_uint64(dev->max_volume_size, ec1));
/* Use fixed block size to simplify read back */
min_block_size = dev->min_block_size;
set_jcr_job_status(jcr, JS_ErrorTerminated);
Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"),
strerror_dev(dev));
- ok = FALSE;
+ ok = false;
}
Pmsg0(-1, "Wrote Start Of Session label.\n");
break;
}
}
- if (stop > 0) {
+ if (vol_num > 1) {
Dmsg0(100, "Write_end_session_label()\n");
/* Create Job status for end of session label */
if (!job_canceled(jcr) && ok) {
ok = FALSE;
}
Pmsg0(-1, _("Wrote End Of Session label.\n"));
+
+ /* Save last block info for second tape */
+ last_block_num2 = last_block_num;
+ last_file2 = last_file;
+ if (last_block2) {
+ free_block(last_block2);
+ }
+ last_block2 = dup_block(last_block);
}
sprintf(buf, "%s/btape.state", working_directory);
fd = open(buf, O_CREAT|O_TRUNC|O_WRONLY, 0640);
if (fd >= 0) {
write(fd, &btape_state_level, sizeof(btape_state_level));
- write(fd, &last_block_num, sizeof(last_block_num));
- write(fd, &last_file, sizeof(last_file));
- write(fd, last_block->buf, last_block->buf_len);
+ write(fd, &simple, sizeof(simple));
+ write(fd, &last_block_num1, sizeof(last_block_num1));
+ write(fd, &last_block_num2, sizeof(last_block_num2));
+ write(fd, &last_file1, sizeof(last_file1));
+ write(fd, &last_file2, sizeof(last_file2));
+ write(fd, last_block1->buf, last_block1->buf_len);
+ write(fd, last_block2->buf, last_block2->buf_len);
+ write(fd, first_block->buf, first_block->buf_len);
close(fd);
- Pmsg0(-1, "Wrote state file.\n");
+ Pmsg2(-1, "Wrote state file last_block_num1=%d last_block_num2=%d\n",
+ last_block_num1, last_block_num2);
} else {
Pmsg2(-1, _("Could not create state file: %s ERR=%s\n"), buf,
strerror(errno));
ok = FALSE;
}
- if (verbose) {
- Pmsg0(-1, "\n");
- dump_block(last_block, _("Last block written to tape.\n"));
- }
- Pmsg0(-1, _("\n\nDone filling tape. Now beginning re-read of tape ...\n"));
-
- if (simple) {
- do_unfill();
- } else {
- /* Multiple Volume tape */
- dumped = 0;
- VolBytes = 0;
- LastBlock = 0;
- block = new_block(dev);
-
- dev->capabilities |= CAP_ANONVOLS; /* allow reading any volume */
- dev->capabilities &= ~CAP_LABEL; /* don't label anything here */
-
- end_of_tape = 0;
+ Pmsg2(-1, _("\n\nDone filling tape%s. Now beginning re-read of %stape ...\n"),
+ simple?"":"s", simple?"":"first ");
+ do_unfill();
- time(&jcr->run_time); /* start counting time for rates */
- stop = 0;
- file_index = 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: "));
-
- 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);
- goto bail_out;
- }
- /* Read all records and then second tape */
- read_records(jcr, dev, record_cb, my_mount_next_read_volume);
- }
-bail_out:
dev->min_block_size = min_block_size;
free_block(block);
free_memory(rec.data);
{
int fd;
- if (!last_block) {
- last_block = new_block(dev);
- }
+ last_block1 = new_block(dev);
+ last_block2 = new_block(dev);
+ first_block = new_block(dev);
sprintf(buf, "%s/btape.state", working_directory);
fd = open(buf, O_RDONLY);
if (fd >= 0) {
uint32_t state_level;
read(fd, &state_level, sizeof(btape_state_level));
- read(fd, &last_block_num, sizeof(last_block_num));
- read(fd, &last_file, sizeof(last_file));
- read(fd, last_block->buf, last_block->buf_len);
+ read(fd, &simple, sizeof(simple));
+ read(fd, &last_block_num1, sizeof(last_block_num1));
+ read(fd, &last_block_num2, sizeof(last_block_num2));
+ read(fd, &last_file1, sizeof(last_file1));
+ read(fd, &last_file2, sizeof(last_file2));
+ read(fd, last_block1->buf, last_block1->buf_len);
+ read(fd, last_block2->buf, last_block2->buf_len);
+ read(fd, first_block->buf, first_block->buf_len);
close(fd);
if (state_level != btape_state_level) {
Pmsg0(-1, "\nThe state file level has changed. You must redo\n"
"You must redo the fill command.\n", buf, strerror(errno));
return;
}
-
do_unfill();
+ this_block = NULL;
}
static void do_unfill()
{
DEV_BLOCK *block;
+ bool autochanger;
dumped = 0;
VolBytes = 0;
LastBlock = 0;
block = new_block(dev);
+ Dmsg0(20, "Enter do_unfill\n");
dev->capabilities |= CAP_ANONVOLS; /* allow reading any volume */
dev->capabilities &= ~CAP_LABEL; /* don't label anything here */
end_of_tape = 0;
-
time(&jcr->run_time); /* start counting time for rates */
stop = 0;
file_index = 0;
+ if (last_block) {
+ free_block(last_block);
+ }
+ last_block_num = last_block_num1;
+ last_file = last_file1;
+ last_block = last_block1;
+ if (!simple) {
+ /* Multiple Volume tape */
+ /* Close device so user can use autochanger if desired */
+ if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
+ offline_dev(dev);
+ }
+ autochanger = autoload_device(jcr, dev, 1, NULL);
+ if (!autochanger) {
+ 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);
+ goto bail_out;
+ }
+ }
/*
+ * We now have the first tape mounted.
* Note, re-reading last block may have caused us to
- * lose track of where we are (block number unknown).
+ * loose track of where we are (block number unknown).
*/
rewind_dev(dev); /* get to a known place on tape */
+ /* Read the first 1000 records */
+ Pmsg0(-1, _("Reading the first 1000 records.\n"));
+ read_records(jcr, dev, quickie_cb, my_mount_next_read_volume);
Pmsg4(-1, _("Reposition from %u:%u to %u:%u\n"), dev->file, dev->block_num,
last_file, last_block_num);
if (!reposition_dev(dev, last_file, last_block_num)) {
Pmsg1(-1, _("Error reading block: ERR=%s\n"), strerror_dev(dev));
goto bail_out;
}
- if (last_block) {
- char *p, *q;
- uint32_t CheckSum, block_len;
- ser_declare;
- p = last_block->buf;
- q = block->buf;
- unser_begin(q, BLKHDR2_LENGTH);
- unser_uint32(CheckSum);
- unser_uint32(block_len);
- while (q < (block->buf+block_len)) {
- if (*p == *q) {
- p++;
- q++;
- continue;
- }
- Pmsg0(-1, "\n");
- dump_block(last_block, _("Last block written"));
- Pmsg0(-1, "\n");
- dump_block(block, _("Block read back"));
- Pmsg1(-1, "\n\nThe blocks differ at byte %u\n", p - last_block->buf);
- Pmsg0(-1, "\n\n!!!! The last block written and the block\n"
- "that was read back differ. The test FAILED !!!!\n"
- "This must be corrected before you use Bacula\n"
- "to write multi-tape Volumes.!!!!\n");
- goto bail_out;
- }
- Pmsg0(-1, _("\nThe blocks are identical. Test succeeded.\n\n"));
- if (verbose) {
- dump_block(last_block, _("Last block written"));
- dump_block(block, _("Block read back"));
+ if (compare_blocks(last_block, block)) {
+ if (simple) {
+ Pmsg0(-1, _("\nThe last block on the tape matches. Test succeeded.\n\n"));
+ } else {
+ Pmsg0(-1, _("\nThe last block of the first tape matches.\n\n"));
}
}
+ if (simple) {
+ goto bail_out;
+ }
-bail_out:
- free_block(block);
-}
-
-/*
- * We are called here from "unfill" for each record on the tape.
- */
-static int record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
-{
- SESSION_LABEL label;
+ /* restore info for last block on second Volume */
+ last_block_num = last_block_num2;
+ last_file = last_file2;
+ last_block = last_block2;
- if (stop > 1 && !dumped) { /* on second tape */
- dumped = 1;
- if (verbose) {
- dump_block(block, "First block on second tape");
- }
- Pmsg4(-1, "Blk: FileIndex=%d: block=%u size=%d vol=%s\n",
- rec->FileIndex, block->BlockNumber, block->block_len, dev->VolHdr.VolName);
- Pmsg6(-1, " Rec: VId=%d VT=%d FI=%s Strm=%s len=%d state=%x\n",
- rec->VolSessionId, rec->VolSessionTime,
- FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream, rec->FileIndex),
- rec->data_len, rec->state);
+ /* Multiple Volume tape */
+ /* Close device so user can use autochanger if desired */
+ if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
+ offline_dev(dev);
}
- if (rec->FileIndex < 0) {
- if (verbose > 1) {
- dump_label_record(dev, rec, 1);
- }
- switch (rec->FileIndex) {
- case PRE_LABEL:
- Pmsg0(-1, "Volume is prelabeled. This tape cannot be scanned.\n");
- return 1;;
- case VOL_LABEL:
- unser_volume_label(dev, rec);
- Pmsg3(-1, "VOL_LABEL: block=%u size=%d vol=%s\n", block->BlockNumber,
- block->block_len, dev->VolHdr.VolName);
- stop++;
- break;
- case SOS_LABEL:
- unser_session_label(&label, rec);
- Pmsg1(-1, "SOS_LABEL: JobId=%u\n", label.JobId);
- break;
- case EOS_LABEL:
- unser_session_label(&label, rec);
- Pmsg2(-1, "EOS_LABEL: block=%u JobId=%u\n", block->BlockNumber,
- label.JobId);
- break;
- case EOM_LABEL:
- Pmsg0(-1, "EOM_LABEL:\n");
- break;
- case EOT_LABEL: /* end of all tapes */
- char ec1[50];
- if (LastBlock != block->BlockNumber) {
- VolBytes += block->block_len;
- }
- LastBlock = block->BlockNumber;
- now = time(NULL);
- now -= jcr->run_time;
- if (now <= 0) {
- now = 1;
- }
- kbs = (double)VolBytes / (1000 * now);
- Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber,
- edit_uint64_with_commas(VolBytes, ec1), (float)kbs);
+ free_vol_list(jcr);
+ set_volume_name("TestVolume2", 2);
+ jcr->bsr = NULL;
+ create_vol_list(jcr);
+ autochanger = autoload_device(jcr, dev, 1, NULL);
+ if (!autochanger) {
+ force_close_dev(dev);
+ get_cmd(_("Mount second tape. Press enter when ready: "));
+ }
- Pmsg0(000, "End of all tapes.\n");
+ dev->state &= ~ST_READ;
+ if (!acquire_device_for_read(jcr, dev, block)) {
+ Pmsg1(-1, "%s", dev->errmsg);
+ goto bail_out;
+ }
- break;
- default:
- break;
- }
- return 1;
+ /* Space to "first" block which is last block not written
+ * on the previous tape.
+ */
+ Pmsg2(-1, _("Reposition from %u:%u to 0:1\n"), dev->file, dev->block_num);
+ if (!reposition_dev(dev, 0, 1)) {
+ Pmsg1(-1, "Reposition error. ERR=%s\n", strerror_dev(dev));
+ goto bail_out;
}
- if (++file_index != rec->FileIndex) {
- Pmsg3(000, "Incorrect FileIndex in Block %u. Got %d, expected %d.\n",
- block->BlockNumber, rec->FileIndex, file_index);
+ Pmsg1(-1, _("Reading block %d.\n"), dev->block_num);
+ if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) {
+ Pmsg1(-1, _("Error reading block: ERR=%s\n"), strerror_dev(dev));
+ goto bail_out;
}
- /*
- * Now check that the right data is in the record.
- */
- uint64_t *lp = (uint64_t *)rec->data;
- uint64_t val = ~file_index;
- for (uint32_t i=0; i < (REC_SIZE-sizeof(uint64_t))/sizeof(uint64_t); i++) {
- if (*lp++ != val) {
- Pmsg2(000, "Record %d contains bad data in Block %u.\n",
- file_index, block->BlockNumber);
- break;
- }
+ if (compare_blocks(first_block, block)) {
+ Pmsg0(-1, _("\nThe first block on the second tape matches.\n\n"));
}
- if (LastBlock != block->BlockNumber) {
- VolBytes += block->block_len;
+ /* Now find and compare the last block */
+ Pmsg4(-1, _("Reposition from %u:%u to %u:%u\n"), dev->file, dev->block_num,
+ last_file, last_block_num);
+ if (!reposition_dev(dev, last_file, last_block_num)) {
+ Pmsg1(-1, "Reposition error. ERR=%s\n", strerror_dev(dev));
+ goto bail_out;
}
- if ((block->BlockNumber != LastBlock) && (block->BlockNumber % 50000) == 0) {
- char ec1[50];
- now = time(NULL);
- now -= jcr->run_time;
- if (now <= 0) {
- now = 1;
+ Pmsg1(-1, _("Reading block %d.\n"), dev->block_num);
+ if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) {
+ Pmsg1(-1, _("Error reading block: ERR=%s\n"), strerror_dev(dev));
+ goto bail_out;
+ }
+ if (compare_blocks(last_block, block)) {
+ Pmsg0(-1, _("\nThe last block on the second tape matches. Test succeeded.\n\n"));
+ }
+
+bail_out:
+ free_block(block);
+ free_block(last_block1);
+ free_block(last_block2);
+ free_block(first_block);
+}
+
+/* Read 1000 records then stop */
+static int quickie_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
+{
+ quickie_count++;
+ return quickie_count <= 1000;
+}
+
+static bool compare_blocks(DEV_BLOCK *last_block, DEV_BLOCK *block)
+{
+ char *p, *q;
+ uint32_t CheckSum, block_len;
+ ser_declare;
+
+ p = last_block->buf;
+ q = block->buf;
+ unser_begin(q, BLKHDR2_LENGTH);
+ unser_uint32(CheckSum);
+ unser_uint32(block_len);
+ while (q < (block->buf+block_len)) {
+ if (*p == *q) {
+ p++;
+ q++;
+ continue;
}
- kbs = (double)VolBytes / (1000 * now);
- Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber,
- edit_uint64_with_commas(VolBytes, ec1), (float)kbs);
+ Pmsg0(-1, "\n");
+ dump_block(last_block, _("Last block written"));
+ Pmsg0(-1, "\n");
+ dump_block(block, _("Block read back"));
+ Pmsg1(-1, "\n\nThe blocks differ at byte %u\n", p - last_block->buf);
+ Pmsg0(-1, "\n\n!!!! The last block written and the block\n"
+ "that was read back differ. The test FAILED !!!!\n"
+ "This must be corrected before you use Bacula\n"
+ "to write multi-tape Volumes.!!!!\n");
+ return false;
}
- LastBlock = block->BlockNumber;
- if (end_of_tape) {
- Pmsg1(000, "End of all blocks. Block=%u\n", block->BlockNumber);
+ if (verbose) {
+ dump_block(last_block, _("Last block written"));
+ dump_block(block, _("Block read back"));
}
- return 1;
+ return true;
}
+
+
/*
* Write current block to tape regardless of whether or
* not it is full. If the tape fills, attempt to
if (!this_block) {
this_block = new_block(dev);
}
+ if (!last_block) {
+ last_block = new_block(dev);
+ }
/* Copy block */
- free_memory(this_block->buf);
- memcpy(this_block, block, sizeof(DEV_BLOCK));
- this_block->buf = get_memory(block->buf_len);
this_file = dev->file;
this_block_num = dev->block_num;
if (!write_block_to_dev(jcr, dev, block)) {
Pmsg3(000, "Last block at: %u:%u this_dev_block_num=%d\n",
last_file, last_block_num, this_block_num);
+ if (vol_num == 1) {
+ /*
+ * This is 1st tape, so save first tape info separate
+ * from second tape info
+ */
+ last_block_num1 = last_block_num;
+ last_file1 = last_file;
+ last_block1 = dup_block(last_block);
+ last_block2 = dup_block(last_block);
+ first_block = dup_block(block); /* first block second tape */
+ }
if (verbose) {
Pmsg3(000, "Block not written: FileIndex=%u blk_block=%u Size=%u\n",
(unsigned)file_index, block->BlockNumber, block->block_len);
- if (dump) {
- dump_block(block, "Block not written");
- }
+ dump_block(last_block, "Last block written");
+ Pmsg0(-1, "\n");
+ dump_block(block, "Block not written");
}
if (stop == 0) {
eot_block = block->BlockNumber;
eot_block_len = block->block_len;
eot_FileIndex = file_index;
+ stop = 1;
}
now = time(NULL);
now -= jcr->run_time;
unlock_device(dev);
return 0;
}
- stop = 1;
BlockNumber = 0; /* start counting for second tape */
}
unlock_device(dev);
return 1; /* end of tape reached */
}
+
/* Save contents after write so that the header is serialized */
memcpy(this_block->buf, block->buf, this_block->buf_len);
}
/* Dummies to replace askdir.c */
-int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw writing) { return 1;}
-int dir_update_volume_info(JCR *jcr, DEVICE *dev, int relabel) { return 1; }
-int dir_create_jobmedia_record(JCR *jcr) { return 1; }
int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
int dir_send_job_status(JCR *jcr) {return 1;}
+int dir_update_volume_info(JCR *jcr, DEVICE *dev, int relabel)
+{
+ return 1;
+}
+
+int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw writing)
+{
+ Dmsg0(20, "Enter dir_get_volume_info\n");
+ bstrncpy(jcr->VolCatInfo.VolCatName, jcr->VolumeName, sizeof(jcr->VolCatInfo.VolCatName));
+ return 1;
+}
-int dir_find_next_appendable_volume(JCR *jcr)
+int dir_create_jobmedia_record(JCR *jcr)
+{
+ jcr->WroteVol = false;
+ return 1;
+}
+
+
+int dir_find_next_appendable_volume(JCR *jcr)
{
- return 1;
+ Dmsg1(20, "Enter dir_find_next_appendable_volume. stop=%d\n", stop);
+ return jcr->VolumeName[0] != 0;
}
int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
{
+ Dmsg0(20, "Enter dir_ask_sysop_to_mount_volume\n");
+ if (jcr->VolumeName[0] == 0) {
+ return dir_ask_sysop_to_create_appendable_volume(jcr, dev);
+ }
/* Close device so user can use autochanger if desired */
if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
offline_dev(dev);
}
force_close_dev(dev);
Pmsg1(-1, "%s", dev->errmsg); /* print reason */
- fprintf(stderr, "Mount Volume \"%s\" on device %s and press return when ready: ",
- jcr->VolumeName, dev_name(dev));
+ if (jcr->VolumeName[0] == 0 || strcmp(jcr->VolumeName, "TestVolume2") == 0) {
+ fprintf(stderr, "Mount second Volume on device %s and press return when ready: ",
+ dev_name(dev));
+ } else {
+ fprintf(stderr, "Mount Volume \"%s\" on device %s and press return when ready: ",
+ jcr->VolumeName, dev_name(dev));
+ }
getchar();
return 1;
}
int dir_ask_sysop_to_create_appendable_volume(JCR *jcr, DEVICE *dev)
{
+ bool autochanger;
+ Dmsg0(20, "Enter dir_ask_sysop_to_create_appendable_volume\n");
+ if (stop == 0) {
+ set_volume_name("TestVolume1", 1);
+ } else {
+ set_volume_name("TestVolume2", 2);
+ }
/* Close device so user can use autochanger if desired */
if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
offline_dev(dev);
}
- force_close_dev(dev);
- fprintf(stderr, "Mount next Volume on device %s and press return when ready: ",
- dev_name(dev));
- getchar();
- set_volume_name("TestVolume2", 2);
+ autochanger = autoload_device(jcr, dev, 1, NULL);
+ if (!autochanger) {
+ force_close_dev(dev);
+ fprintf(stderr, "Mount blank Volume on device %s and press return when ready: ",
+ dev_name(dev));
+ getchar();
+ }
+ open_device(jcr, dev);
labelcmd();
VolumeName = NULL;
BlockNumber = 0;
- stop = 1;
return 1;
}
{
char ec1[50];
+ Dmsg0(20, "Enter my_mount_next_read_volume\n");
Pmsg1(000, "End of Volume \"%s\"\n", jcr->VolumeName);
if (LastBlock != block->BlockNumber) {
vol_num = volnum;
pm_strcpy(&jcr->VolumeName, VolName);
bstrncpy(dev->VolCatInfo.VolCatName, VolName, sizeof(dev->VolCatInfo.VolCatName));
+ bstrncpy(jcr->VolCatInfo.VolCatName, VolName, sizeof(jcr->VolCatInfo.VolCatName));
+ jcr->VolCatInfo.Slot = volnum;
}
/*
}
return omsg;
}
+
+#ifdef xxxx_needed
+/*
+ * We are called here from "unfill" for each record on the tape.
+ */
+static int record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
+{
+ SESSION_LABEL label;
+
+ if (stop > 1 && !dumped) { /* on second tape */
+ dumped = 1;
+ if (verbose) {
+ dump_block(block, "First block on second tape");
+ }
+ Pmsg4(-1, "Blk: FileIndex=%d: block=%u size=%d vol=%s\n",
+ rec->FileIndex, block->BlockNumber, block->block_len, dev->VolHdr.VolName);
+ Pmsg6(-1, " Rec: VId=%d VT=%d FI=%s Strm=%s len=%d state=%x\n",
+ rec->VolSessionId, rec->VolSessionTime,
+ FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream, rec->FileIndex),
+ rec->data_len, rec->state);
+ }
+ if (rec->FileIndex < 0) {
+ if (verbose > 1) {
+ dump_label_record(dev, rec, 1);
+ }
+ switch (rec->FileIndex) {
+ case PRE_LABEL:
+ Pmsg0(-1, "Volume is prelabeled. This tape cannot be scanned.\n");
+ return 1;;
+ case VOL_LABEL:
+ unser_volume_label(dev, rec);
+ Pmsg3(-1, "VOL_LABEL: block=%u size=%d vol=%s\n", block->BlockNumber,
+ block->block_len, dev->VolHdr.VolName);
+ stop++;
+ break;
+ case SOS_LABEL:
+ unser_session_label(&label, rec);
+ Pmsg1(-1, "SOS_LABEL: JobId=%u\n", label.JobId);
+ break;
+ case EOS_LABEL:
+ unser_session_label(&label, rec);
+ Pmsg2(-1, "EOS_LABEL: block=%u JobId=%u\n", block->BlockNumber,
+ label.JobId);
+ break;
+ case EOM_LABEL:
+ Pmsg0(-1, "EOM_LABEL:\n");
+ break;
+ case EOT_LABEL: /* end of all tapes */
+ char ec1[50];
+
+ if (LastBlock != block->BlockNumber) {
+ VolBytes += block->block_len;
+ }
+ LastBlock = block->BlockNumber;
+ now = time(NULL);
+ now -= jcr->run_time;
+ if (now <= 0) {
+ now = 1;
+ }
+ kbs = (double)VolBytes / (1000 * now);
+ Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber,
+ edit_uint64_with_commas(VolBytes, ec1), (float)kbs);
+
+ Pmsg0(000, "End of all tapes.\n");
+
+ break;
+ default:
+ break;
+ }
+ return 1;
+ }
+ if (++file_index != rec->FileIndex) {
+ Pmsg3(000, "Incorrect FileIndex in Block %u. Got %d, expected %d.\n",
+ block->BlockNumber, rec->FileIndex, file_index);
+ }
+ if (LastBlock != block->BlockNumber) {
+ VolBytes += block->block_len;
+ }
+ if ((block->BlockNumber != LastBlock) && (block->BlockNumber % 50000) == 0) {
+ char ec1[50];
+ now = time(NULL);
+ now -= jcr->run_time;
+ if (now <= 0) {
+ now = 1;
+ }
+ kbs = (double)VolBytes / (1000 * now);
+ Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber,
+ edit_uint64_with_commas(VolBytes, ec1), (float)kbs);
+ }
+ LastBlock = block->BlockNumber;
+ if (end_of_tape) {
+ Pmsg1(000, "End of all blocks. Block=%u\n", block->BlockNumber);
+ }
+ return 1;
+}
+#endif
dev = init_dev(NULL, device);
jcr->device->dev = dev;
- if (!dev || !open_device(dev)) {
+ if (!dev || !first_open_device(dev)) {
Jmsg1(jcr, M_FATAL, 0, _("Cannot open %s\n"), jcr->dev_name);
return NULL;
}
if (dev_cap(dev, CAP_FASTFSF) && !dev_cap(dev, CAP_EOM)) {
struct mtget mt_stat;
- Dmsg0(000,"Using FAST FSF for EOM\n");
+ Dmsg0(100,"Using FAST FSF for EOM\n");
if (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && mt_stat.mt_fileno <= 0) {
if (!rewind_dev(dev)) {
return 0;
}
if (dev_cap(dev, CAP_EOM)) {
- Dmsg0(000,"Using EOM for EOM\n");
+ Dmsg0(100,"Using EOM for EOM\n");
mt_com.mt_op = MTEOM;
mt_com.mt_count = 1;
}
dev->dev_name, strerror(dev->dev_errno));
return 0;
}
- Dmsg2(000, "EOD file=%d block=%d\n", mt_stat.mt_fileno, mt_stat.mt_blkno);
+ Dmsg2(100, "EOD file=%d block=%d\n", mt_stat.mt_fileno, mt_stat.mt_blkno);
dev->file = mt_stat.mt_fileno;
/*
*/
if (file_num == (int)dev->file) {
struct mtget mt_stat;
- Dmsg1(000, "fsf_dev did not advance from file %d\n", file_num);
+ Dmsg1(100, "fsf_dev did not advance from file %d\n", file_num);
if (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 &&
mt_stat.mt_fileno >= 0) {
Dmsg2(000, "Adjust file from %d to %d\n", dev->file , mt_stat.mt_fileno);
stat = bsf_dev(dev, 1);
/* If BSF worked and fileno is known (not -1), set file */
if (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && mt_stat.mt_fileno >= 0) {
- Dmsg2(000, "Adjust file from %d to %d\n", dev->file , mt_stat.mt_fileno);
+ Dmsg2(100, "Adjust file from %d to %d\n", dev->file , mt_stat.mt_fileno);
dev->file = mt_stat.mt_fileno;
} else {
dev->file++; /* wing it -- not correct on all OSes */
return 0;
}
Dmsg0(29, "fsr_dev\n");
- dev->block_num += num;
mt_com.mt_op = MTFSR;
mt_com.mt_count = num;
stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com);
dev->state &= ~ST_EOF;
dev->block_num += num;
} else {
- if (dev->state & ST_EOF) {
- dev->state |= ST_EOT;
+ struct mtget mt_stat;
+ if (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && mt_stat.mt_fileno >= 0) {
+ Dmsg4(100, "Adjust from %d:%d to %d:%d\n", dev->file,
+ dev->block_num, mt_stat.mt_fileno, mt_stat.mt_blkno);
+ dev->file = mt_stat.mt_fileno;
+ dev->block_num = mt_stat.mt_blkno;
} else {
-// dev->state |= ST_EOF; /* assume EOF */
-// dev->file++;
-// dev->file_addr = 0;
+ if (dev->state & ST_EOF) {
+ dev->state |= ST_EOT;
+ } else {
+ dev->state |= ST_EOF; /* assume EOF */
+ dev->file++;
+ dev->block_num = 0;
+ dev->file_addr = 0;
+ }
}
clrerror_dev(dev, MTFSR);
Mmsg2(&dev->errmsg, _("ioctl MTFSR error on %s. ERR=%s.\n"),
/*
* Reposition the device to file, block
- * Currently only works for tapes.
* Returns: 0 on failure
* 1 on success
*/
}
if (!(dev_state(dev, ST_TAPE))) {
- return 0;
+ off_t pos = (((off_t)file)<<32) + block;
+ if (lseek(dev->fd, pos, SEEK_SET) == (off_t)-1) {
+ dev->dev_errno = errno;
+ Mmsg2(&dev->errmsg, _("lseek error on %s. ERR=%s.\n"),
+ dev->dev_name, strerror(dev->dev_errno));
+ return 0;
+ }
+ return 1;
}
Dmsg4(100, "reposition_dev from %u:%u to %u:%u\n",
dev->file, dev->block_num, file, block);
/*
- * Open the device. Expect dev to already be initialized.
+ * First Open of the device. Expect dev to already be initialized.
*
* This routine is used only when the Storage daemon starts
* and always_open is set, and in the stand-alone utility
* Retuns: 0 on failure
* 1 on success
*/
-int open_device(DEVICE *dev)
+int first_open_device(DEVICE *dev)
{
Dmsg0(120, "start open_output_device()\n");
if (!dev) {
}
if (!(dev->state & ST_OPENED)) {
+ int mode;
+ if (dev_cap(dev, CAP_STREAM)) {
+ mode = OPEN_WRITE_ONLY;
+ } else {
+ mode = OPEN_READ_WRITE;
+ }
Dmsg0(129, "Opening device.\n");
- if (open_dev(dev, NULL, READ_WRITE) < 0) {
+ if (open_dev(dev, NULL, mode) < 0) {
Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), dev->errmsg);
unlock_device(dev);
return 0;
return 1;
}
+/*
+ * Make sure device is open, if not do so
+ */
+int open_device(JCR *jcr, DEVICE *dev)
+{
+ /* Open device */
+ if (!(dev_state(dev, ST_OPENED))) {
+ int mode;
+ if (dev_cap(dev, CAP_STREAM)) {
+ mode = OPEN_WRITE_ONLY;
+ } else {
+ mode = OPEN_READ_WRITE;
+ }
+ if (open_dev(dev, jcr->VolCatInfo.VolCatName, mode) < 0) {
+ Jmsg2(jcr, M_FATAL, 0, _("Unable to open device %s. ERR=%s\n"),
+ dev_name(dev), strerror_dev(dev));
+ return 0;
+ }
+ }
+ return 1;
+}
+
/*
* When dev_blocked is set, all threads EXCEPT thread with id no_wait_id
* must wait. The no_wait_id thread is out obtaining a new volume
force_close_dev(dev);
}
- /* Open device */
- if (!(dev_state(dev, ST_OPENED))) {
- int mode;
- if (dev_cap(dev, CAP_STREAM)) {
- mode = OPEN_WRITE_ONLY;
- } else {
- mode = OPEN_READ_WRITE;
- }
- if (open_dev(dev, jcr->VolCatInfo.VolCatName, mode) < 0) {
- Jmsg2(jcr, M_FATAL, 0, _("Unable to open device %s. ERR=%s\n"),
- dev_name(dev), strerror_dev(dev));
- return 0;
- }
+ /* Ensure the device is open */
+ if (!open_device(jcr, dev)) {
+ return 0;
}
/*
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 */
enum get_vol_info_rw {
GET_VOL_INFO_FOR_WRITE,
GET_VOL_INFO_FOR_READ
};
-int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw);
-int dir_find_next_appendable_volume(JCR *jcr);
-int dir_update_volume_info(JCR *jcr, DEVICE *dev, int label);
-int dir_ask_sysop_to_create_appendable_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, enum get_vol_info_rw);
+int dir_find_next_appendable_volume(JCR *jcr);
+int dir_update_volume_info(JCR *jcr, DEVICE *dev, int label);
+int dir_ask_sysop_to_create_appendable_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);
-void print_block_read_errors(JCR *jcr, DEV_BLOCK *block);
+DEV_BLOCK *dup_block(DEV_BLOCK *eblock);
+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);
+void print_block_read_errors(JCR *jcr, DEV_BLOCK *block);
#define CHECK_BLOCK_NUMBERS true
#define NO_BLOCK_NUMBER_CHECK false
-int read_block_from_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
-int read_block_from_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
+int read_block_from_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
+int read_block_from_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
/* 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_tape_error_status(JCR *jcr, DEVICE *dev);
+void display_tape_error_status(JCR *jcr, 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);
+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);
uint32_t status_dev(DEVICE *dev);
-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);
-int dev_can_write(DEVICE *dev);
-int offline_or_rewind_dev(DEVICE *dev);
-int reposition_dev(DEVICE *dev, uint32_t file, uint32_t block);
-void init_dev_wait_timers(DEVICE *dev);
-bool double_dev_wait_time(DEVICE *dev);
+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);
+int dev_can_write(DEVICE *dev);
+int offline_or_rewind_dev(DEVICE *dev);
+int reposition_dev(DEVICE *dev, uint32_t file, uint32_t block);
+void init_dev_wait_timers(DEVICE *dev);
+bool double_dev_wait_time(DEVICE *dev);
/* 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);
-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 _unblock_device(char *file, int line, DEVICE *dev);
-void _steal_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state);
-void _give_back_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold);
-void set_new_volume_parameters(JCR *jcr, DEVICE *dev);
-void set_new_file_parameters(JCR *jcr, DEVICE *dev);
-int device_is_unmounted(DEVICE *dev);
+int open_device(JCR *jcr, DEVICE *dev);
+int first_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 _unblock_device(char *file, int line, DEVICE *dev);
+void _steal_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state);
+void _give_back_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold);
+void set_new_volume_parameters(JCR *jcr, DEVICE *dev);
+void set_new_file_parameters(JCR *jcr, DEVICE *dev);
+int device_is_unmounted(DEVICE *dev);
/* 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 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, char *PoolName);
-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, char *PoolName);
+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);
-int match_bsr_block(BSR *bsr, DEV_BLOCK *block);
-void position_bsr_block(BSR *bsr, DEV_BLOCK *block);
-BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev);
-bool match_set_eof(BSR *bsr, DEV_RECORD *rec);
+int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
+ SESSION_LABEL *sesrec);
+int match_bsr_block(BSR *bsr, DEV_BLOCK *block);
+void position_bsr_block(BSR *bsr, DEV_BLOCK *block);
+BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev);
+bool match_set_eof(BSR *bsr, DEV_RECORD *rec);
/* 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);
-void mark_volume_in_error(JCR *jcr, DEVICE *dev);
+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);
+void mark_volume_in_error(JCR *jcr, DEVICE *dev);
/* From autochanger.c */
-int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir);
-int autochanger_list(JCR *jcr, DEVICE *dev, BSOCK *dir);
-void invalidate_slot_in_catalog(JCR *jcr, DEVICE *dev);
+int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir);
+int autochanger_list(JCR *jcr, DEVICE *dev, BSOCK *dir);
+void invalidate_slot_in_catalog(JCR *jcr, DEVICE *dev);
/* From parse_bsr.c */
-BSR *parse_bsr(JCR *jcr, char *lf);
-void dump_bsr(BSR *bsr, bool recurse);
-void free_bsr(BSR *bsr);
+BSR *parse_bsr(JCR *jcr, char *lf);
+void dump_bsr(BSR *bsr, bool recurse);
+void free_bsr(BSR *bsr);
VOL_LIST *new_vol();
-int add_vol(JCR *jcr, VOL_LIST *vol);
-void free_vol_list(JCR *jcr);
-void create_vol_list(JCR *jcr);
+int add_vol(JCR *jcr, VOL_LIST *vol);
+void free_vol_list(JCR *jcr);
+void create_vol_list(JCR *jcr);
/* 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);
+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);
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,
continue;
}
if (device->cap_bits & CAP_ALWAYSOPEN) {
- Dmsg1(20, "calling open_device %s\n", device->device_name);
- if (!open_device(device->dev)) {
+ Dmsg1(20, "calling first_open_device %s\n", device->device_name);
+ if (!first_open_device(device->dev)) {
Emsg1(M_ERROR, 0, _("Could not open device %s\n"), device->device_name);
}
}
#undef VERSION
#define VERSION "1.33.3"
#define VSTRING "1"
-#define BDATE "12 Feb 2004"
-#define LSMDATE "12Feb04"
+#define BDATE "16 Feb 2004"
+#define LSMDATE "16Feb04"
/* Debug flags */
#undef DEBUG