* never have errors, or it is really fatal.
*/
B_DB *
-db_init_database(char *db_name, char *db_user, char *db_password)
+db_init_database(void *jcr, char *db_name, char *db_user, char *db_password)
{
B_DB *mdb;
P(mutex); /* lock DB queue */
qinsert(&db_list, &mdb->bq); /* put db in list */
Dmsg0(200, "Done db_open_database()\n");
mdb->cfd = -1;
+ mdb->jcr = jcr;
V(mutex);
return mdb;
}
uint32_t cached_path_id;
int transaction; /* transaction started */
int changes; /* changes during transaction */
+ void *jcr; /* JCR or NULL */
} B_DB;
POOLMEM *cached_path;
uint32_t cached_path_id;
int changes; /* changes made to db */
+ void *jcr; /* JCR or NULL */
} B_DB;
POOLMEM *cmd; /* Command string */
POOLMEM *cached_path;
uint32_t cached_path_id;
+ void *jcr; /* JCR or NULL */
} B_DB;
#endif /* HAVE_MYSQL */
* never have errors, or it is really fatal.
*/
B_DB *
-db_init_database(char *db_name, char *db_user, char *db_password)
+db_init_database(void *jcr, char *db_name, char *db_user, char *db_password)
{
B_DB *mdb;
mdb->cached_path_id = 0;
mdb->ref_count = 1;
qinsert(&db_list, &mdb->bq); /* put db in list */
+ mdb->jcr = jcr;
V(mutex);
return mdb;
}
/* Database prototypes */
/* sql.c */
-B_DB *db_init_database(char *db_name, char *db_user, char *db_password);
+B_DB *db_init_database(void *jcr, char *db_name, char *db_user, char *db_password);
int db_open_database(B_DB *db);
void db_close_database(B_DB *db);
void db_escape_string(char *snew, char *old, int len);
int db_update_job_end_record(B_DB *db, JOB_DBR *jr);
int db_update_pool_record(B_DB *db, POOL_DBR *pr);
int db_update_media_record(B_DB *db, MEDIA_DBR *mr);
-int db_add_MD5_to_file_record(B_DB *mdb, FileId_t FileId, char *MD5);
+int db_add_MD5_to_file_record(B_DB *mdb, FileId_t FileId, char *MD5);
int db_mark_file_record(B_DB *mdb, FileId_t FileId, JobId_t JobId);
#endif /* __SQL_PROTOS_H */
/* If more than one, report error, but return first row */
if (mdb->num_rows > 1) {
Mmsg1(&mdb->errmsg, _("More than one Client!: %d\n"), (int)(mdb->num_rows));
- Emsg0(M_ERROR, 0, mdb->errmsg);
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
}
if (mdb->num_rows >= 1) {
if ((row = sql_fetch_row(mdb)) == NULL) {
Mmsg1(&mdb->errmsg, _("error fetching Client row: %s\n"), sql_strerror(mdb));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
sql_free_result(mdb);
db_unlock(mdb);
return 0;
if (!INSERT_DB(mdb, mdb->cmd)) {
Mmsg2(&mdb->errmsg, _("Create DB Client record %s failed. ERR=%s\n"),
mdb->cmd, sql_strerror(mdb));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
cr->ClientId = 0;
stat = 0;
} else {
if (mdb->num_rows > 1) {
Mmsg1(&mdb->errmsg, _("More than one FileSet!: %d\n"), (int)(mdb->num_rows));
- Emsg0(M_ERROR, 0, mdb->errmsg);
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
}
if (mdb->num_rows >= 1) {
if ((row = sql_fetch_row(mdb)) == NULL) {
Mmsg1(&mdb->errmsg, _("error fetching FileSet row: ERR=%s\n"), sql_strerror(mdb));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
sql_free_result(mdb);
db_unlock(mdb);
return 0;
if (!INSERT_DB(mdb, mdb->cmd)) {
Mmsg2(&mdb->errmsg, _("Create DB FileSet record %s failed. ERR=%s\n"),
mdb->cmd, sql_strerror(mdb));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
fsr->FileSetId = 0;
stat = 0;
} else {
*/
if (ar->Stream != STREAM_UNIX_ATTRIBUTES) {
Mmsg0(&mdb->errmsg, _("Attempt to put non-attributes into catalog\n"));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
return 0;
}
*/
fnl = p - l;
if (fnl > 255) {
- Emsg1(M_WARNING, 0, _("Filename truncated to 255 chars: %s\n"), l);
+ Jmsg(mdb->jcr, M_WARNING, 0, _("Filename truncated to 255 chars: %s\n"), l);
fnl = 255;
}
if (fnl > 0) {
pnl = l - ar->fname;
if (pnl > 255) {
- Emsg1(M_WARNING, 0, _("Path name truncated to 255 chars: %s\n"), ar->fname);
+ Jmsg(mdb->jcr, M_WARNING, 0, _("Path name truncated to 255 chars: %s\n"), ar->fname);
pnl = 255;
}
strncpy(spath, ar->fname, pnl);
if (pnl == 0) {
Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), ar->fname);
- Emsg0(M_ERROR, 0, mdb->errmsg);
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
spath[0] = ' ';
spath[1] = 0;
pnl = 1;
if (!INSERT_DB(mdb, mdb->cmd)) {
Mmsg2(&mdb->errmsg, _("Create db File record %s failed. ERR=%s"),
mdb->cmd, sql_strerror(mdb));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
ar->FileId = 0;
stat = 0;
} else {
if (*path == 0) {
Mmsg0(&mdb->errmsg, _("Null path given to db_create_path_record\n"));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
ar->PathId = 0;
ASSERT(ar->PathId);
return 0;
char ed1[30];
Mmsg2(&mdb->errmsg, _("More than one Path!: %s for Path=%s\n"),
edit_uint64(mdb->num_rows, ed1), path);
- Emsg1(M_ERROR, 0, "%s", mdb->errmsg);
- Emsg1(M_ERROR, 0, "%s\n", mdb->cmd);
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
}
if (mdb->num_rows >= 1) {
if ((row = sql_fetch_row(mdb)) == NULL) {
db_unlock(mdb);
Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
sql_free_result(mdb);
ar->PathId = 0;
ASSERT(ar->PathId);
if (!INSERT_DB(mdb, mdb->cmd)) {
Mmsg2(&mdb->errmsg, _("Create db Path record %s failed. ERR=%s\n"),
mdb->cmd, sql_strerror(mdb));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
ar->PathId = 0;
stat = 0;
} else {
if (mdb->num_rows > 1) {
Mmsg2(&mdb->errmsg, _("More than one Filename!: %d File=%s\n"),
(int)(mdb->num_rows), fname);
- Emsg1(M_ERROR, 0, "%s", mdb->errmsg);
- Emsg1(M_ERROR, 0, "%s\n", mdb->cmd);
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
}
if (mdb->num_rows >= 1) {
if ((row = sql_fetch_row(mdb)) == NULL) {
Mmsg2(&mdb->errmsg, _("error fetching row for file=%s: ERR=%s\n"),
fname, sql_strerror(mdb));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
ar->FilenameId = 0;
} else {
ar->FilenameId = atoi(row[0]);
if (!INSERT_DB(mdb, mdb->cmd)) {
Mmsg2(&mdb->errmsg, _("Create db Filename record %s failed. ERR=%s\n"),
mdb->cmd, sql_strerror(mdb));
+ Jmsg(mdb->jcr, M_ERROR, 0, "%s", mdb->errmsg);
ar->FilenameId = 0;
} else {
ar->FilenameId = sql_insert_id(mdb);
* never have errors, or it is really fatal.
*/
B_DB *
-db_init_database(char *db_name, char *db_user, char *db_password)
+db_init_database(void *jcr, char *db_name, char *db_user, char *db_password)
{
B_DB *mdb;
mdb->cached_path_id = 0;
mdb->ref_count = 1;
qinsert(&db_list, &mdb->bq); /* put db in list */
+ mdb->jcr = jcr;
V(mutex);
return mdb;
}
memset(&jcr, 0, sizeof(jcr));
if (ndir > 1) {
- UA_sock = init_bsock(0, "", "", 0);
+ UA_sock = init_bsock(NULL, 0, "", "", 0);
try_again:
fprintf(output, "Available Directors:\n");
LockRes();
parse_config(configfile);
if (!check_resources()) {
- Emsg1(M_ERROR_TERM, 0, "Please correct configuration file: %s\n", configfile);
+ Jmsg(NULL, M_ERROR_TERM, 0, "Please correct configuration file: %s\n", configfile);
}
if (test_config) {
Dmsg0(200, "check_resources()\n");
if (!check_resources()) {
- Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
+ Jmsg(NULL, M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
}
/* Reset globals */
job = (JOB *)GetNextRes(R_JOB, NULL);
director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
if (!director) {
- Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n\
+ Jmsg(NULL, M_FATAL, 0, _("No Director resource defined in %s\n\
Without that I don't know who I am :-(\n"), configfile);
OK = FALSE;
} else {
if (!director->working_directory) {
- Emsg0(M_FATAL, 0, _("No working directory specified. Cannot continue.\n"));
+ Jmsg(NULL, M_FATAL, 0, _("No working directory specified. Cannot continue.\n"));
OK = FALSE;
}
working_directory = director->working_directory;
if (!director->messages) { /* If message resource not specified */
director->messages = (MSGS *)GetNextRes(R_MSGS, NULL);
if (!director->messages) {
- Emsg1(M_FATAL, 0, _("No Messages resource defined in %s\n"), configfile);
+ Jmsg(NULL, M_FATAL, 0, _("No Messages resource defined in %s\n"), configfile);
OK = FALSE;
}
}
if (GetNextRes(R_DIRECTOR, (RES *)director) != NULL) {
- Emsg1(M_FATAL, 0, _("Only one Director resource permitted in %s\n"),
+ Jmsg(NULL, M_FATAL, 0, _("Only one Director resource permitted in %s\n"),
configfile);
OK = FALSE;
}
}
if (!job) {
- Emsg1(M_FATAL, 0, _("No Job records defined in %s\n"), configfile);
+ Jmsg(NULL, M_FATAL, 0, _("No Job records defined in %s\n"), configfile);
OK = FALSE;
}
for (job=NULL; (job = (JOB *)GetNextRes(R_JOB, (RES *)job)); ) {
if (!job->client) {
- Emsg1(M_FATAL, 0, _("No Client record defined for job %s\n"), job->hdr.name);
+ Jmsg(NULL, M_FATAL, 0, _("No Client record defined for job %s\n"), job->hdr.name);
OK = FALSE;
}
if (!job->fileset) {
- Emsg1(M_FATAL, 0, _("No FileSet record defined for job %s\n"), job->hdr.name);
+ Jmsg(NULL, M_FATAL, 0, _("No FileSet record defined for job %s\n"), job->hdr.name);
OK = FALSE;
}
if (!job->storage && job->JobType != JT_VERIFY) {
- Emsg1(M_FATAL, 0, _("No Storage resource defined for job %s\n"), job->hdr.name);
+ Jmsg(NULL, M_FATAL, 0, _("No Storage resource defined for job %s\n"), job->hdr.name);
OK = FALSE;
}
if (!job->pool) {
- Emsg1(M_FATAL, 0, _("No Pool resource defined for job %s\n"), job->hdr.name);
+ Jmsg(NULL, M_FATAL, 0, _("No Pool resource defined for job %s\n"), job->hdr.name);
OK = FALSE;
}
if (job->client && job->client->catalog) {
* Make sure we can open catalog, otherwise print a warning
* message because the server is probably not running.
*/
- db = db_init_database(catalog->db_name, catalog->db_user,
+ db = db_init_database(NULL, catalog->db_name, catalog->db_user,
catalog->db_password);
if (!db_open_database(db)) {
- Emsg1(M_FATAL, 0, "%s", db_strerror(db));
+ Jmsg(NULL, M_FATAL, 0, "%s", db_strerror(db));
}
db_close_database(db);
} else {
if (job->client) {
- Emsg1(M_FATAL, 0, _("No Catalog resource defined for client %s\n"),
+ Jmsg(NULL, M_FATAL, 0, _("No Catalog resource defined for client %s\n"),
job->client->hdr.name);
OK = FALSE;
}
{"prunevolumes", store_yesno, ITEM(res_job.PruneVolumes), 1, ITEM_DEFAULT, 0},
{"runbeforejob", store_str, ITEM(res_job.RunBeforeJob), 0, 0, 0},
{"runafterjob", store_str, ITEM(res_job.RunAfterJob), 0, 0, 0},
+ {"spoolattributes", store_yesno, ITEM(res_job.SpoolAttributes), 1, ITEM_DEFAULT, 0},
{NULL, NULL, NULL, 0, 0, 0}
};
/*
* Resource codes -- they must be sequential for indexing
*/
-#define R_FIRST 1001
-
-#define R_DIRECTOR 1001
-#define R_CLIENT 1002
-#define R_JOB 1003
-#define R_STORAGE 1004
-#define R_CATALOG 1005
-#define R_SCHEDULE 1006
-#define R_FILESET 1007
-#define R_GROUP 1008
-#define R_POOL 1009
-#define R_MSGS 1010
-#define R_COUNTER 1011
-
-#define R_LAST R_COUNTER
+#define R_FIRST 1001
+
+#define R_DIRECTOR 1001
+#define R_CLIENT 1002
+#define R_JOB 1003
+#define R_STORAGE 1004
+#define R_CATALOG 1005
+#define R_SCHEDULE 1006
+#define R_FILESET 1007
+#define R_GROUP 1008
+#define R_POOL 1009
+#define R_MSGS 1010
+#define R_COUNTER 1011
+
+#define R_LAST R_COUNTER
/*
* Some resource attributes
*/
-#define R_NAME 1020
-#define R_ADDRESS 1021
-#define R_PASSWORD 1022
-#define R_TYPE 1023
-#define R_BACKUP 1024
+#define R_NAME 1020
+#define R_ADDRESS 1021
+#define R_PASSWORD 1022
+#define R_TYPE 1023
+#define R_BACKUP 1024
/* Used for certain KeyWord tables */
-struct s_kw {
+struct s_kw {
char *name;
- int token;
+ int token;
};
/* Job Level keyword structure */
struct s_jl {
- char *level_name; /* level keyword */
- int level; /* level */
- int job_type; /* JobType permitting this level */
+ char *level_name; /* level keyword */
+ int level; /* level */
+ int job_type; /* JobType permitting this level */
};
/* Job Type keyword structure */
/* Definition of the contents of each Resource */
/*
- * Director Resource
+ * Director Resource
*
*/
struct s_res_dir {
- RES hdr;
- int DIRport; /* where we listen -- UA port server port */
- char *password; /* Password for UA access */
- char *query_file; /* SQL query file */
- char *working_directory; /* WorkingDirectory */
- char *pid_directory; /* PidDirectory */
- char *subsys_directory; /* SubsysDirectory */
+ RES hdr;
+ int DIRport; /* where we listen -- UA port server port */
+ char *password; /* Password for UA access */
+ char *query_file; /* SQL query file */
+ char *working_directory; /* WorkingDirectory */
+ char *pid_directory; /* PidDirectory */
+ char *subsys_directory; /* SubsysDirectory */
struct s_res_msgs *messages; /* Daemon message handler */
- int MaxConcurrentJobs;
- btime_t FDConnectTimeout; /* timeout for connect in seconds */
- btime_t SDConnectTimeout; /* timeout in seconds */
+ int MaxConcurrentJobs;
+ btime_t FDConnectTimeout; /* timeout for connect in seconds */
+ btime_t SDConnectTimeout; /* timeout in seconds */
};
typedef struct s_res_dir DIRRES;
*
*/
struct s_res_client {
- RES hdr;
+ RES hdr;
- int FDport; /* Where File daemon listens */
- int AutoPrune; /* Do automatic pruning? */
- btime_t FileRetention; /* file retention period in seconds */
- btime_t JobRetention; /* job retention period in seconds */
+ int FDport; /* Where File daemon listens */
+ int AutoPrune; /* Do automatic pruning? */
+ btime_t FileRetention; /* file retention period in seconds */
+ btime_t JobRetention; /* job retention period in seconds */
char *address;
char *password;
struct s_res_cat *catalog; /* Catalog resource */
*
*/
struct s_res_store {
- RES hdr;
+ RES hdr;
- int SDport; /* port where Directors connect */
- int SDDport; /* data port for File daemon */
+ int SDport; /* port where Directors connect */
+ int SDDport; /* data port for File daemon */
char *address;
char *password;
char *media_type;
*
*/
struct s_res_cat {
- RES hdr;
+ RES hdr;
- int DBport; /* Port -- not yet implemented */
+ int DBport; /* Port -- not yet implemented */
char *address;
char *db_password;
char *db_user;
*
*/
struct s_res_job {
- RES hdr;
-
- int JobType; /* job type (backup, verify, restore */
- int level; /* default backup/verify level */
- int RestoreJobId; /* What -- JobId to restore */
- char *RestoreWhere; /* Where on disk to restore -- directory */
- char *RestoreBootstrap; /* Bootstrap file */
- char *RunBeforeJob; /* Run program before Job */
- char *RunAfterJob; /* Run program after Job */
- int RestoreOptions; /* How (overwrite, ..) */
- btime_t MaxRunTime; /* max run time in seconds */
- btime_t MaxStartDelay; /* max start delay in seconds */
- int PruneJobs; /* Force pruning of Jobs */
- int PruneFiles; /* Force pruning of Files */
- int PruneVolumes; /* Force pruning of Volumes */
+ RES hdr;
+
+ int JobType; /* job type (backup, verify, restore */
+ int level; /* default backup/verify level */
+ int RestoreJobId; /* What -- JobId to restore */
+ char *RestoreWhere; /* Where on disk to restore -- directory */
+ char *RestoreBootstrap; /* Bootstrap file */
+ char *RunBeforeJob; /* Run program before Job */
+ char *RunAfterJob; /* Run program after Job */
+ int RestoreOptions; /* How (overwrite, ..) */
+ btime_t MaxRunTime; /* max run time in seconds */
+ btime_t MaxStartDelay; /* max start delay in seconds */
+ int PruneJobs; /* Force pruning of Jobs */
+ int PruneFiles; /* Force pruning of Files */
+ int PruneVolumes; /* Force pruning of Volumes */
+ int SpoolAttributes; /* Set to spool attributes in SD */
struct s_res_msgs *messages; /* How and where to send messages */
struct s_res_sch *schedule; /* When -- Automatic schedule */
struct s_res_client *client; /* Who to backup */
struct s_res_fs *fileset; /* What to backup -- Fileset */
struct s_res_store *storage; /* Where is device -- Storage daemon */
- struct s_res_pool *pool; /* Where is media -- Media Pool */
+ struct s_res_pool *pool; /* Where is media -- Media Pool */
};
typedef struct s_res_job JOB;
*
*/
struct s_res_fs {
- RES hdr;
+ RES hdr;
char **include_array;
int num_includes;
char **exclude_array;
int num_excludes;
int exclude_size;
- int have_MD5; /* set if MD5 initialized */
- struct MD5Context md5c; /* MD5 of include/exclude */
+ int have_MD5; /* set if MD5 initialized */
+ struct MD5Context md5c; /* MD5 of include/exclude */
};
typedef struct s_res_fs FILESET;
*
*/
struct s_res_sch {
- RES hdr;
+ RES hdr;
struct s_run *run;
};
*
*/
struct s_res_group {
- RES hdr;
+ RES hdr;
};
typedef struct s_res_group GROUP;
* Counter Resource
*/
struct s_res_counter {
- RES hdr;
+ RES hdr;
- int32_t MinValue; /* Minimum value */
- int32_t MaxValue; /* Maximum value */
- int Global; /* global/local */
- char *WrapCounter; /* Wrap counter name */
+ int32_t MinValue; /* Minimum value */
+ int32_t MaxValue; /* Maximum value */
+ int Global; /* global/local */
+ char *WrapCounter; /* Wrap counter name */
};
typedef struct s_res_counter COUNTER;
*
*/
struct s_res_pool {
- RES hdr;
+ RES hdr;
struct s_res_counter counter; /* Counter resources */
- char *pool_type; /* Pool type */
- char *label_format; /* Label format string */
- int use_catalog; /* maintain catalog for media */
- int catalog_files; /* maintain file entries in catalog */
- int use_volume_once; /* write on volume only once */
- int accept_any_volume; /* accept any volume */
- int max_volumes; /* max number of volumes */
- btime_t VolRetention; /* volume retention period in seconds */
- int AutoPrune; /* default for pool auto prune */
- int Recycle; /* default for media recycle yes/no */
+ char *pool_type; /* Pool type */
+ char *label_format; /* Label format string */
+ int use_catalog; /* maintain catalog for media */
+ int catalog_files; /* maintain file entries in catalog */
+ int use_volume_once; /* write on volume only once */
+ int accept_any_volume; /* accept any volume */
+ int max_volumes; /* max number of volumes */
+ btime_t VolRetention; /* volume retention period in seconds */
+ int AutoPrune; /* default for pool auto prune */
+ int Recycle; /* default for media recycle yes/no */
};
typedef struct s_res_pool POOL;
* resource structure definitions.
*/
union u_res {
- struct s_res_dir res_dir;
- struct s_res_client res_client;
- struct s_res_store res_store;
- struct s_res_cat res_cat;
- struct s_res_job res_job;
- struct s_res_fs res_fs;
- struct s_res_sch res_sch;
- struct s_res_group res_group;
- struct s_res_pool res_pool;
- struct s_res_msgs res_msgs;
+ struct s_res_dir res_dir;
+ struct s_res_client res_client;
+ struct s_res_store res_store;
+ struct s_res_cat res_cat;
+ struct s_res_job res_job;
+ struct s_res_fs res_fs;
+ struct s_res_sch res_sch;
+ struct s_res_group res_group;
+ struct s_res_pool res_pool;
+ struct s_res_msgs res_msgs;
struct s_res_counter res_counter;
RES hdr;
};
/* Run structure contained in Schedule Resource */
struct s_run {
- struct s_run *next; /* points to next run record */
- int level; /* level override */
+ struct s_run *next; /* points to next run record */
+ int level; /* level override */
int job_type;
- POOL *pool; /* Pool override */
- STORE *storage; /* Storage override */
- MSGS *msgs; /* Messages override */
+ POOL *pool; /* Pool override */
+ STORE *storage; /* Storage override */
+ MSGS *msgs; /* Messages override */
char *since;
int level_no;
- int minute; /* minute to run job */
- time_t last_run; /* last time run */
- time_t next_run; /* next time to run */
+ int minute; /* minute to run job */
+ time_t last_run; /* last time run */
+ time_t next_run; /* next time to run */
char hour[nbytes_for_bits(24)]; /* bit set for each hour */
char mday[nbytes_for_bits(31)]; /* bit set for each day of month */
char month[nbytes_for_bits(12)]; /* bit set for each month */
* Open database
*/
Dmsg0(50, "Open database\n");
- jcr->db=db_init_database(jcr->catalog->db_name, jcr->catalog->db_user,
+ jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
jcr->catalog->db_password);
if (!db_open_database(jcr->db)) {
Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
/* Commands sent to Storage daemon */
static char jobcmd[] = "JobId=%d job=%s job_name=%s client_name=%s \
-type=%d level=%d FileSet=%s Allow=%s Session=%s\n";
+type=%d level=%d FileSet=%s NoAttr=%d SpoolAttr=%d\n";
static char use_device[] = "use device=%s media_type=%s pool_name=%s pool_type=%s\n";
/* Response from Storage daemon */
bash_spaces(jcr->client->hdr.name);
bnet_fsend(sd, jobcmd, jcr->JobId, jcr->Job, jcr->job->hdr.name,
jcr->client->hdr.name, jcr->JobType, jcr->JobLevel,
- jcr->fileset->hdr.name, "append", "*");
+ jcr->fileset->hdr.name, !jcr->pool->catalog_files,
+ jcr->job->SpoolAttributes);
unbash_spaces(jcr->job->hdr.name);
unbash_spaces(jcr->client->hdr.name);
unbash_spaces(jcr->fileset->hdr.name);
bash_spaces(media_type);
bash_spaces(pool_type);
bash_spaces(pool_name);
- sd->msg = (char *) check_pool_memory_size(sd->msg, sizeof(device_name) +
+ sd->msg = check_pool_memory_size(sd->msg, sizeof(device_name) +
device_name_len + media_type_len + pool_type_len + pool_name_len);
bnet_fsend(sd, use_device, device_name, media_type, pool_name, pool_type);
Dmsg1(110, ">stored: %s", sd->msg);
jcr->use_count++; /* mark in use by msg thread */
V(jcr->mutex);
if ((status=pthread_create(&thid, NULL, msg_thread, (void *)jcr)) != 0) {
- Emsg1(M_ABORT, 0, _("Cannot create message thread: %s\n"), strerror(status));
+ Jmsg1(jcr, M_ABORT, 0, _("Cannot create message thread: %s\n"), strerror(status));
}
jcr->SD_msg_chan = thid;
return 1;
}
Dmsg0(150, "Open database\n");
- ua->db = db_init_database(ua->catalog->db_name, ua->catalog->db_user,
+ ua->db = db_init_database(NULL, ua->catalog->db_name, ua->catalog->db_user,
ua->catalog->db_password);
if (!db_open_database(ua->db)) {
bnet_fsend(ua->UA_sock, _("Could not open DB %s: ERR=%s"),
jcr->last_fname = get_pool_memory(PM_FNAME);
jcr->client_name = get_memory(strlen(my_name) + 1);
strcpy(jcr->client_name, my_name);
+ dir->jcr = (void *)jcr;
/**********FIXME******* add command handler error code */
if (bsock->spool) {
nwritten = fwrite(ptr, 1, nbytes, bsock->spool_fd);
if (nwritten != nbytes) {
- Emsg1(M_ERROR, 0, _("Spool write error. ERR=%s\n"), strerror(errno));
+ Jmsg1(bsock->jcr, M_ERROR, 0, _("Spool write error. ERR=%s\n"), strerror(errno));
Dmsg2(400, "nwritten=%d nbytes=%d.\n", nwritten, nbytes);
return -1;
}
if (nbytes != sizeof(int32_t)) {
bsock->errors++;
bsock->b_errno = EIO;
- Emsg3(M_ERROR, 0, _("Read %d expected %d from %s\n"), nbytes, sizeof(int32_t),
+ Jmsg3(bsock->jcr, M_ERROR, 0, _("Read %d expected %d from %s\n"), nbytes, sizeof(int32_t),
bsock->who);
return -2;
}
bsock->b_errno = errno;
}
bsock->errors++;
- Emsg4(M_ERROR, 0, _("Read error from %s:%s:%d: ERR=%s\n"),
+ Jmsg4(bsock->jcr, M_ERROR, 0, _("Read error from %s:%s:%d: ERR=%s\n"),
bsock->who, bsock->host, bsock->port, bnet_strerror(bsock));
return -2;
}
if (nbytes != pktsiz) {
bsock->b_errno = EIO;
bsock->errors++;
- Emsg5(M_ERROR, 0, _("Read expected %d got %d from %s:%s:%d\n"), pktsiz, nbytes,
+ Jmsg5(bsock->jcr, M_ERROR, 0, _("Read expected %d got %d from %s:%s:%d\n"), pktsiz, nbytes,
bsock->who, bsock->host, bsock->port);
return -2;
}
nbytes = fread(bsock->msg, 1, bsock->msglen, bsock->spool_fd);
if (nbytes != (size_t)bsock->msglen) {
Dmsg2(400, "nbytes=%d msglen=%d\n", nbytes, bsock->msglen);
- Emsg1(M_ERROR, 0, _("fread error. ERR=%s\n"), strerror(errno));
+ Jmsg1(bsock->jcr, M_ERROR, 0, _("fread error. ERR=%s\n"), strerror(errno));
return 0;
}
}
bnet_send(bsock);
}
if (ferror(bsock->spool_fd)) {
- Emsg1(M_ERROR, 0, _("fread error. ERR=%s\n"), strerror(errno));
+ Jmsg1(bsock->jcr, M_ERROR, 0, _("fread error. ERR=%s\n"), strerror(errno));
return 0;
}
return 1;
bsock->b_errno = errno;
}
if (rc < 0) {
- /****FIXME***** use Mmsg */
- Emsg4(M_ERROR, 0, _("Write error sending to %s:%s:%d: ERR=%s\n"),
+ Jmsg4(bsock->jcr, M_ERROR, 0, _("Write error sending to %s:%s:%d: ERR=%s\n"),
bsock->who, bsock->host, bsock->port, bnet_strerror(bsock));
} else {
- Emsg5(M_ERROR, 0, _("Wrote %d bytes to %s:%s:%d, but only %d accepted.\n"),
+ Jmsg5(bsock->jcr, M_ERROR, 0, _("Wrote %d bytes to %s:%s:%d, but only %d accepted.\n"),
bsock->who, bsock->host, bsock->port, bsock->msglen, rc);
}
return 0;
}
if (rc < 0) {
/************FIXME********* use Pmsg() **/
- Emsg4(M_ERROR, 0, _("Write error sending to %s:%s:%d: ERR=%s\n"),
+ Jmsg4(bsock->jcr, M_ERROR, 0, _("Write error sending to %s:%s:%d: ERR=%s\n"),
bsock->who, bsock->host, bsock->port, bnet_strerror(bsock));
} else {
- Emsg5(M_ERROR, 0, _("Wrote %d bytes to %s:%s:%d, but only %d accepted.\n"),
+ Jmsg5(bsock->jcr, M_ERROR, 0, _("Wrote %d bytes to %s:%s:%d, but only %d accepted.\n"),
bsock->who, bsock->host, bsock->port, bsock->msglen, rc);
}
return 0;
* Convert a hostname or dotted IP address into
* a s_addr. We handle only IPv4.
*/
-static uint32_t *bget_host_ip(char *host)
+static uint32_t *bget_host_ip(void *jcr, char *host)
{
struct in_addr inaddr;
uint32_t *addr_list; /* this really should be struct in_addr */
return NULL;
}
if (hp->h_length != sizeof(inaddr.s_addr) || hp->h_addrtype != AF_INET) {
- Emsg2(M_WARNING, 0, _("gethostbyname() network address length error.\n\
+ Jmsg2(jcr, M_WARNING, 0, _("gethostbyname() network address length error.\n\
Wanted %d got %d bytes for s_addr.\n"), sizeof(inaddr.s_addr), hp->h_length);
return NULL;
}
* ***FIXME*** implement service from /etc/services
*/
static BSOCK *
-bnet_open(char *name, char *host, char *service, int port)
+bnet_open(void *jcr, char *name, char *host, char *service, int port)
{
int sockfd;
struct sockaddr_in tcp_serv_addr; /* socket information */
tcp_serv_addr.sin_family = AF_INET;
tcp_serv_addr.sin_port = htons(port);
- if ((addr_list=bget_host_ip(host)) == NULL) {
+ if ((addr_list=bget_host_ip(jcr, host)) == NULL) {
return NULL;
}
* Receive notification when connection dies.
*/
if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &turnon, sizeof(turnon)) < 0) {
- Emsg1(M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"), strerror(errno));
+ Jmsg(jcr, M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"), strerror(errno));
}
for (i = 0; addr_list[i] != ((uint32_t) -1); i++) {
close(sockfd);
return NULL;
}
- return init_bsock(sockfd, name, host, port);
+ return init_bsock(jcr, sockfd, name, host, port);
}
/*
int i;
BSOCK *bsock;
- for (i=0; (bsock = bnet_open(name, host, service, port)) == NULL; i -= retry_interval) {
+ for (i=0; (bsock = bnet_open(jcr, name, host, service, port)) == NULL; i -= retry_interval) {
Dmsg4(100, "Unable to connect to %s on %s:%d. ERR=%s\n",
name, host, port, strerror(errno));
if (i <= 0) {
dbuf_size = size;
if ((bs->msg = realloc_pool_memory(bs->msg, dbuf_size+100)) == NULL) {
- Emsg0(M_FATAL, 0, _("Could not malloc 32K BSOCK data buffer\n"));
+ Jmsg0(bs->jcr, M_FATAL, 0, _("Could not malloc 32K BSOCK data buffer\n"));
return 0;
}
if (rw & BNET_SETBUF_READ) {
while ((dbuf_size > TAPE_BSIZE) &&
(setsockopt(bs->fd, SOL_SOCKET, SO_RCVBUF, (char *)&dbuf_size, sizeof(dbuf_size)) < 0)) {
- Emsg1(M_ERROR, 0, _("sockopt error: %s\n"), strerror(errno));
+ Jmsg1(bs->jcr, M_ERROR, 0, _("sockopt error: %s\n"), strerror(errno));
dbuf_size -= TAPE_BSIZE;
}
Dmsg1(200, "set network buffer size=%d\n", dbuf_size);
if (dbuf_size != MAX_NETWORK_BUFFER_SIZE)
- Emsg1(M_WARNING, 0, _("Warning network buffer = %d bytes not max size.\n"), dbuf_size);
+ Jmsg1(bs->jcr, M_WARNING, 0, _("Warning network buffer = %d bytes not max size.\n"), dbuf_size);
if (dbuf_size % TAPE_BSIZE != 0) {
- Emsg1(M_ABORT, 0, _("Network buffer size %d not multiple of tape block size.\n"),
+ Jmsg1(bs->jcr, M_ABORT, 0, _("Network buffer size %d not multiple of tape block size.\n"),
dbuf_size);
}
}
if (rw & BNET_SETBUF_WRITE) {
while ((dbuf_size > TAPE_BSIZE) &&
(setsockopt(bs->fd, SOL_SOCKET, SO_SNDBUF, (char *)&dbuf_size, sizeof(dbuf_size)) < 0)) {
- Emsg1(M_ERROR, 0, _("sockopt error: %s\n"), strerror(errno));
+ Jmsg1(bs->jcr, M_ERROR, 0, _("sockopt error: %s\n"), strerror(errno));
dbuf_size -= TAPE_BSIZE;
}
Dmsg1(200, "set network buffer size=%d\n", dbuf_size);
if (dbuf_size != MAX_NETWORK_BUFFER_SIZE)
- Emsg1(M_WARNING, 0, _("Warning network buffer = %d bytes not max size.\n"), dbuf_size);
+ Jmsg1(bs->jcr, M_WARNING, 0, _("Warning network buffer = %d bytes not max size.\n"), dbuf_size);
if (dbuf_size % TAPE_BSIZE != 0) {
- Emsg1(M_ABORT, 0, _("Network buffer size %d not multiple of tape block size.\n"),
+ Jmsg1(bs->jcr, M_ABORT, 0, _("Network buffer size %d not multiple of tape block size.\n"),
dbuf_size);
}
}
* This probably should be done in net_open
*/
BSOCK *
-init_bsock(int sockfd, char *who, char *host, int port)
+init_bsock(void *jcr, int sockfd, char *who, char *host, int port)
{
BSOCK *bsock = (BSOCK *)malloc(sizeof(BSOCK));
memset(bsock, 0, sizeof(BSOCK));
* heartbeats are implemented
*/
bsock->timeout = 60 * 60 * 6 * 24; /* 6 days timeout */
+ bsock->jcr = jcr;
return bsock;
}
fromhost(&request);
if (!hosts_access(&request)) {
V(mutex);
- Emsg2(M_WARNING, 0, _("Connection from %s:%d refused by hosts.access"),
+ Jmsg2(NULL, M_WARNING, 0, _("Connection from %s:%d refused by hosts.access"),
inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
close(newsockfd);
continue;
/* Queue client to be served */
if ((stat = workq_add(client_wq,
- (void *)init_bsock(newsockfd, "client", caller, port))) != 0) {
- Emsg1(M_ABORT, 0, _("Could not add job to client queue: ERR=%s\n"), strerror(stat));
+ (void *)init_bsock(NULL, newsockfd, "client", caller, port))) != 0) {
+ Jmsg1(NULL, M_ABORT, 0, _("Could not add job to client queue: ERR=%s\n"), strerror(stat));
}
}
}
struct s_bsock *next; /* next BSOCK if duped */
int spool; /* set for spooling */
FILE *spool_fd; /* spooling file */
+ void *jcr; /* jcr or NULL for error msgs */
} BSOCK;
/* Signal definitions for use in bnet_sig() */
return; /* no destination */
switch (type) {
case M_ABORT:
- sprintf(buf, "%s ABORTING due to ERROR in %s:%d\n",
+ sprintf(buf, "%s: ABORTING due to ERROR in %s:%d\n",
my_name, file, line);
break;
case M_ERROR_TERM:
- sprintf(buf, "%s ERROR TERMINATION at %s:%d\n",
+ sprintf(buf, "%s: ERROR TERMINATION at %s:%d\n",
my_name, file, line);
break;
case M_FATAL:
msgs = daemon_msgs;
}
if (!job) {
- job = "*None*";
+ job = "";
}
buf = rbuf; /* we are the Director */
sprintf(buf, "%s ERROR TERMINATION\n", my_name);
break;
case M_FATAL:
- sprintf(buf, "%s: Job %s Fatal error: ", my_name, job);
+ sprintf(buf, "%s: %s Fatal error: ", my_name, job);
if (jcr) {
jcr->JobStatus = JS_FatalError;
}
break;
case M_ERROR:
- sprintf(buf, "%s: Job %s Error: ", my_name, job);
+ sprintf(buf, "%s: %s Error: ", my_name, job);
if (jcr) {
jcr->Errors++;
}
break;
case M_WARNING:
- sprintf(buf, "%s: Job %s Warning: ", my_name, job);
+ sprintf(buf, "%s: %s Warning: ", my_name, job);
break;
default:
sprintf(buf, "%s: ", my_name);
*/
/* base64.c */
-void base64_init (void);
-int to_base64 (intmax_t value, char *where);
-int from_base64 (intmax_t *value, char *where);
-void encode_stat (char *buf, struct stat *statp);
-void decode_stat (char *buf, struct stat *statp);
-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);
+void encode_stat (char *buf, struct stat *statp);
+void decode_stat (char *buf, struct stat *statp);
+int bin_to_base64 (char *buf, char *bin, int len);
/* bmisc.c */
-char *bstrncpy (char *dest, char *src, int maxlen);
-void *b_malloc (char *file, int line, size_t size);
+char *bstrncpy (char *dest, 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, size_t size, const char *format, ...);
-int bvsnprintf (char *str, size_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 *brealloc (void *buf, size_t size);
+void *bcalloc (size_t size1, size_t size2);
+int bsnprintf (char *str, size_t size, const char *format, ...);
+int bvsnprintf (char *str, size_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);
#ifndef HAVE_STRERROR_R
-int strerror_r (int errnum, char *buf, size_t bufsiz);
+int strerror_r (int errnum, char *buf, size_t bufsiz);
#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);
-BSOCK * bnet_connect (void *jcr, int retry_interval,
- int max_retry_time, char *name, char *host, char *service,
- int port, int verbose);
-int bnet_wait_data (BSOCK *bsock, int sec);
-void bnet_close (BSOCK *bsock);
-BSOCK * init_bsock (int sockfd, char *who, char *ip, int port);
-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_despool (BSOCK *bsock);
+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);
+BSOCK * bnet_connect (void *jcr, int retry_interval,
+ int max_retry_time, char *name, char *host, char *service,
+ int port, int verbose);
+int bnet_wait_data (BSOCK *bsock, int sec);
+void bnet_close (BSOCK *bsock);
+BSOCK * init_bsock (void *jcr, int sockfd, char *who, char *ip, int port);
+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_despool (BSOCK *bsock);
/* cram-md5.c */
int cram_md5_get_auth(BSOCK *bs, char *password);
int cram_md5_auth(BSOCK *bs, char *password);
void hmac_md5(uint8_t* text, int text_len, uint8_t* key,
- int key_len, uint8_t *hmac);
+ int key_len, uint8_t *hmac);
/* create_file.c */
int create_file(void *jcr, char *fname, char *ofile, char *lname,
- int type, struct stat *statp, int *ofd);
+ int type, struct stat *statp, int *ofd);
int set_statp(void *jcr, char *fname, char *ofile, char *lname, int type,
- struct stat *statp);
+ struct stat *statp);
/* crc32.c */
uint32_t bcrc32(uint8_t *buf, int len);
/* daemon.c */
-void daemon_start ();
+void daemon_start ();
/* 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);
/* makepath.c */
int make_path(
- void *jcr,
- const char *argpath,
- int mode,
- int parent_mode,
- uid_t owner,
- gid_t group,
- int preserve_existing,
- char *verbose_fmt_string);
+ void *jcr,
+ const char *argpath,
+ int mode,
+ int parent_mode,
+ uid_t owner,
+ gid_t group,
+ int preserve_existing,
+ char *verbose_fmt_string);
/* message.c */
-void my_name_is (int argc, char *argv[], char *name);
-void init_msg (void *jcr, MSGS *msg);
-void term_msg (void);
-void close_msg (void *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 (void *jcr, int type, int level, char *fmt, ...);
-void dispatch_message (void *jcr, int type, int level, char *buf);
-void init_console_msg (char *wd);
-void free_msgs_res (MSGS *msgs);
-int open_spool_file (void *jcr, BSOCK *bs);
-int close_spool_file (void *vjcr, BSOCK *bs);
+void my_name_is (int argc, char *argv[], char *name);
+void init_msg (void *jcr, MSGS *msg);
+void term_msg (void);
+void close_msg (void *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 (void *jcr, int type, int level, char *fmt, ...);
+void dispatch_message (void *jcr, int type, int level, char *buf);
+void init_console_msg (char *wd);
+void free_msgs_res (MSGS *msgs);
+int open_spool_file (void *jcr, BSOCK *bs);
+int close_spool_file (void *vjcr, BSOCK *bs);
/* bnet_server.c */
-void bnet_thread_server(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(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);
/* 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);
/* util.c */
-void lcase (char *str);
-void bash_spaces (char *str);
-void unbash_spaces (char *str);
-void strip_trailing_junk (char *str);
-void strip_trailing_slashes (char *dir);
-int skip_spaces (char **msg);
-int skip_nonspaces (char **msg);
-int fstrsch (char *a, char *b);
-char * encode_time (time_t time, char *buf);
-char * encode_mode (mode_t mode, char *buf);
-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 do_shell_expansion (char *name);
-int is_a_number (const char *num);
-int string_to_btime (char *str, btime_t *value);
-char *edit_btime (btime_t val, char *buf);
-void jobstatus_to_ascii (int JobStatus, char *msg, int maxlen);
-void pm_strcat (POOLMEM **pm, char *str);
-void 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 lcase (char *str);
+void bash_spaces (char *str);
+void unbash_spaces (char *str);
+void strip_trailing_junk (char *str);
+void strip_trailing_slashes (char *dir);
+int skip_spaces (char **msg);
+int skip_nonspaces (char **msg);
+int fstrsch (char *a, char *b);
+char * encode_time (time_t time, char *buf);
+char * encode_mode (mode_t mode, char *buf);
+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 do_shell_expansion (char *name);
+int is_a_number (const char *num);
+int string_to_btime (char *str, btime_t *value);
+char *edit_btime (btime_t val, char *buf);
+void jobstatus_to_ascii (int JobStatus, char *msg, int maxlen);
+void pm_strcat (POOLMEM **pm, char *str);
+void 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 print_ls_output (char *fname, char *lname, int type, struct stat *statp);
+ *void print_ls_output (char *fname, char *lname, int type, struct stat *statp);
*/
/* watchdog.c */
dummy:
#
-SVRSRCS = stored.c append.c askdir.c authenticate.c block.c dev.c \
+SVRSRCS = stored.c acquire.c append.c askdir.c authenticate.c \
+ block.c dev.c \
device.c dircmd.c fd_cmds.c fdmsg.c job.c \
label.c match_bsr.c parse_bsr.c \
- read.c record.c stored_conf.c
-SVROBJS = stored.o append.o askdir.o authenticate.o block.o dev.o \
+ read.c record.c stored_conf.c mount.c
+SVROBJS = stored.o acquire.o append.o askdir.o authenticate.o \
+ block.o dev.o \
device.o dircmd.o fd_cmds.o fdmsg.o job.o \
- label.o match_bsr.o parse_bsr.o \
- read.o record.o stored_conf.o
+ label.o match_bsr.o mount.o parse_bsr.o \
+ read.o record.o stored_conf.o
-# bpool is depricated
+# bpool is deprecated
#POOLSRCS = bpool.c block.c dev.c device.c askdir.c label.c \
# record.c stored_conf.c
#POOLOBJS = bpool.o block.o dev.o device.o askdir.o label.o \
#
TAPESRCS = btape.c block.c dev.c device.c askdir.c label.c \
- record.c stored_conf.c
+ acquire.c mount.c record.c stored_conf.c
TAPEOBJS = btape.o block.o dev.o device.o askdir.o label.o \
- record.o stored_conf.o
+ acquire.o mount.o record.o stored_conf.o
BLSOBJS = bls.o block.o device.o dev.o label.o match_bsr.o \
- parse_bsr.o record.o
+ acquire.o mount.o parse_bsr.o record.o
BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o \
- match_bsr.o parse_bsr.o
+ acquire.o mount.o match_bsr.o parse_bsr.o
-SCNOBJS = bscan.o block.o device.o dev.o askdir.o label.o record.o
+SCNOBJS = bscan.o block.o device.o dev.o askdir.o label.o \
+ acquire.o mount.o record.o match_bsr.o parse_bsr.o
--- /dev/null
+/*
+ * Routines to acquire and release a device for read/write
+ *
+ * Kern Sibbald, August MMII
+ *
+ * Version $Id$
+ */
+/*
+ Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ */
+
+#include "bacula.h" /* pull in global headers */
+#include "stored.h" /* pull in Storage Deamon headers */
+
+
+/*********************************************************************
+ * Acquire device for reading. We permit (for the moment)
+ * only one reader. We read the Volume label from the block and
+ * leave the block pointers just after the label.
+ *
+ * Returns: 0 if failed for any reason
+ * 1 if successful
+ */
+int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
+{
+ int stat;
+
+ lock_device(dev);
+ if (dev->state & ST_READ || dev->num_writers > 0) {
+ Jmsg(jcr, M_FATAL, 0, _("Device %s is busy.\n"), dev_name(dev));
+ unlock_device(dev);
+ return 0;
+ }
+ dev->state &= ~ST_LABEL; /* force reread of label */
+ block_device(dev, BST_DOING_ACQUIRE);
+ unlock_device(dev);
+ stat = ready_dev_for_read(jcr, dev, block);
+ P(dev->mutex);
+ unblock_device(dev);
+ V(dev->mutex);
+ return stat;
+}
+
+/*
+ * Acquire device for writing. We permit multiple writers.
+ * If this is the first one, we read the label.
+ *
+ * Returns: 0 if failed for any reason
+ * 1 if successful
+ */
+int acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
+{
+ int release = 0;
+ int do_mount = 0;
+
+ lock_device(dev);
+ Dmsg1(190, "acquire_append device is %s\n", dev_is_tape(dev)?"tape":"disk");
+
+
+ if (dev->state & ST_APPEND) {
+ /*
+ * Device already in append mode
+ *
+ * Check if we have the right Volume mounted
+ * OK if AnonVols and volume info OK
+ * OK if next volume matches current volume
+ * otherwise mount desired volume obtained from
+ * dir_find_next_appendable_volume
+ */
+ strcpy(jcr->VolumeName, dev->VolHdr.VolName);
+ if (((dev->capabilities & CAP_ANONVOLS) &&
+ !dir_get_volume_info(jcr)) ||
+ (!dir_find_next_appendable_volume(jcr) ||
+ strcmp(dev->VolHdr.VolName, jcr->VolumeName) != 0)) { /* wrong tape mounted */
+ if (dev->num_writers != 0) {
+ Jmsg(jcr, M_FATAL, 0, _("Device %s is busy writing with another Volume.\n"), dev_name(dev));
+ unlock_device(dev);
+ return 0;
+ }
+ /* Wrong tape mounted, release it, then fall through to get correct one */
+ release = 1;
+ do_mount = 1;
+ }
+ } else {
+ /* Not already in append mode, so mount the device */
+ if (dev->state & ST_READ) {
+ Jmsg(jcr, M_FATAL, 0, _("Device %s is busy reading.\n"), dev_name(dev));
+ unlock_device(dev);
+ return 0;
+ }
+ ASSERT(dev->num_writers == 0);
+ do_mount = 1;
+ }
+
+ if (do_mount) {
+ block_device(dev, BST_DOING_ACQUIRE);
+ unlock_device(dev);
+ if (!mount_next_write_volume(jcr, dev, block, release)) {
+ Jmsg(jcr, M_FATAL, 0, _("Could not ready device %s for append.\n"),
+ dev_name(dev));
+ P(dev->mutex);
+ unblock_device(dev);
+ unlock_device(dev);
+ return 0;
+ }
+ P(dev->mutex);
+ unblock_device(dev);
+ }
+
+ dev->num_writers++;
+ if (dev->num_writers > 1) {
+ Dmsg2(0, "Hey!!!! There are %d writers on device %s\n", dev->num_writers,
+ dev_name(dev));
+ }
+ if (jcr->NumVolumes == 0) {
+ jcr->NumVolumes = 1;
+ }
+ attach_jcr_to_device(dev, jcr); /* attach jcr to device */
+ unlock_device(dev);
+ return 1; /* got it */
+}
+
+/*
+ * This job is done, so release the device. From a Unix standpoint,
+ * the device remains open.
+ *
+ */
+int release_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
+{
+ P(dev->mutex);
+ Dmsg1(100, "release_device device is %s\n", dev_is_tape(dev)?"tape":"disk");
+ if (dev->state & ST_READ) {
+ dev->state &= ~ST_READ; /* clear read bit */
+ if (!dev_is_tape(dev) || !(dev->capabilities & CAP_ALWAYSOPEN)) {
+ if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
+ offline_dev(dev);
+ }
+ close_dev(dev);
+ }
+ /******FIXME**** send read volume usage statistics to director */
+
+ } else if (dev->num_writers > 0) {
+ dev->num_writers--;
+ Dmsg1(00, "There are %d writers in release_device\n", dev->num_writers);
+ if (dev->num_writers == 0) {
+ weof_dev(dev, 1);
+ Dmsg0(100, "dir_create_jobmedia_record. Release\n");
+ dir_create_jobmedia_record(jcr);
+ dev->VolCatInfo.VolCatFiles++; /* increment number of files */
+ /* Note! do volume update before close, which zaps VolCatInfo */
+ Dmsg0(100, "dir_update_vol_info. Release\n");
+ dir_update_volume_info(jcr, &dev->VolCatInfo, 0); /* send Volume info to Director */
+
+ if (!dev_is_tape(dev) || !(dev->capabilities & CAP_ALWAYSOPEN)) {
+ if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
+ offline_dev(dev);
+ }
+ close_dev(dev);
+ }
+ } else {
+ Dmsg0(100, "dir_create_jobmedia_record. Release\n");
+ dir_create_jobmedia_record(jcr);
+ Dmsg0(100, "dir_update_vol_info. Release\n");
+ dir_update_volume_info(jcr, &dev->VolCatInfo, 0); /* send Volume info to Director */
+ }
+ } else {
+ Jmsg1(jcr, M_ERROR, 0, _("BAD ERROR: release_device %s not in use.\n"), dev_name(dev));
+ }
+ detach_jcr_from_device(dev, jcr);
+ V(dev->mutex);
+ return 1;
+}
+
+
+
+/*
+ * This routine ensures that the device is ready for
+ * reading. If it is a file, it opens it.
+ * If it is a tape, it checks the volume name
+ *
+ * Returns 0 on failure
+ * Returns 1 on success
+ */
+int ready_dev_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
+{
+ if (!(dev->state & ST_OPENED)) {
+ Dmsg1(120, "bstored: open vol=%s\n", jcr->VolumeName);
+ if (open_dev(dev, jcr->VolumeName, READ_ONLY) < 0) {
+ Jmsg(jcr, M_FATAL, 0, _("Open device %s volume %s failed, ERR=%s\n"),
+ dev_name(dev), jcr->VolumeName, strerror_dev(dev));
+ return 0;
+ }
+ Dmsg1(129, "open_dev %s OK\n", dev_name(dev));
+ }
+
+ for (;;) {
+ if (job_cancelled(jcr)) {
+ Mmsg0(&dev->errmsg, _("Job cancelled.\n"));
+ return 0;
+ }
+ if (!rewind_dev(dev)) {
+ Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"),
+ dev_name(dev), strerror_dev(dev));
+ }
+ switch (read_dev_volume_label(jcr, dev, block)) {
+ case VOL_OK:
+ break; /* got it */
+ default:
+ /* Send error message generated by read_dev_volume_label() */
+ Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+ if (!rewind_dev(dev)) {
+ Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"),
+ dev_name(dev), strerror_dev(dev));
+ }
+ /* Mount a specific volume and no other */
+ if (!dir_ask_sysop_to_mount_volume(jcr, dev)) {
+ return 0; /* error return */
+ }
+ continue; /* try reading again */
+ }
+ break;
+ }
+
+ dev->state |= ST_READ;
+ return 1;
+}
// jcr->spool_attributes = 1;
jcr->no_attributes = 1;
#endif
- jcr->spool_attributes = 1;
if (!jcr->no_attributes && jcr->spool_attributes) {
open_spool_file(jcr, jcr->dir_bsock);
static BSR *bsr = NULL;
+static DEV_RECORD *rec;
+static DEV_BLOCK *block;
+
static void usage()
{
fprintf(stderr,
int type;
long record_file_index;
long total = 0;
- DEV_RECORD rec;
- DEV_BLOCK *block;
POOLMEM *fname; /* original file name */
POOLMEM *ofile; /* output name with prefix */
POOLMEM *lname; /* link name */
return;
}
- memset(&rec, 0, sizeof(rec));
- rec.data = get_memory(70000);
+ rec = new_record();
+ free_pool_memory(rec->data);
+ rec->data = get_memory(70000);
uint32_t compress_buf_size = 70000;
POOLMEM *compress_buf = get_memory(compress_buf_size);
for ( ;; ) {
- int ok;
- DEV_RECORD *record; /* for reading label of multi-volumes */
-
- if (!read_record(dev, block, &rec)) {
+ if (!read_block_from_device(dev, block)) {
uint32_t status;
- Dmsg1(500, "Main read record failed. rem=%d\n", rec.remainder);
+ Dmsg1(500, "Main read record failed. rem=%d\n", rec->remainder);
if (dev->state & ST_EOT) {
- if (rec.remainder) {
- Dmsg0(500, "Not end of record.\n");
- }
- Dmsg2(90, "NumVolumes=%d CurVolume=%d\n", jcr->NumVolumes, jcr->CurVolume);
- /*
- * End Of Tape -- mount next Volume (if another specified)
- */
- if (jcr->NumVolumes > 1 && jcr->CurVolume < jcr->NumVolumes) {
- VOL_LIST *vol = jcr->VolList;
- /* Find next Volume */
- jcr->CurVolume++;
- for (int i=1; i<jcr->CurVolume; i++) {
- vol = vol->next;
- }
- strcpy(jcr->VolumeName, vol->VolumeName);
- Dmsg1(400, "There is another volume %s.\n", jcr->VolumeName);
-
- close_dev(dev);
- dev->state &= ~ST_READ;
- if (!acquire_device_for_read(jcr, dev, block)) {
- Emsg2(M_FATAL, 0, "Cannot open Dev=%s, Vol=%s\n", dev_name(dev),
- jcr->VolumeName);
- ok = FALSE;
- break;
- }
- record = new_record();
- Dmsg1(500, "read record after new tape. rem=%d\n", record->remainder);
- read_record(dev, block, record); /* read vol label */
- dump_label_record(dev, record, 0);
- free_record(record);
- continue;
+ if (!mount_next_read_volume(jcr, dev, block)) {
+ break;
}
- Dmsg0(90, "End of Device reached.\n");
- break; /* End of Tape */
+ continue;
}
if (dev->state & ST_EOF) {
continue; /* try again */
}
+ if (dev->state & ST_SHORT) {
+ continue;
+ }
Pmsg0(0, "Read Record got a bad record\n");
status_dev(dev, &status);
Dmsg1(20, "Device status: %x\n", status);
status, dev_name(dev), strerror(errno));
}
-
- /* This is no longer used */
- if (rec.VolSessionId == 0 && rec.VolSessionTime == 0) {
- Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
- break; /* END OF FILE */
- }
-
- /*
- * Check for Start or End of Session Record
- *
- */
- if (rec.FileIndex < 0) {
- char *rtype;
- memset(&sessrec, 0, sizeof(sessrec));
- switch (rec.FileIndex) {
- case PRE_LABEL:
- rtype = "Fresh Volume Label";
- break;
- case VOL_LABEL:
- rtype = "Volume Label";
- unser_volume_label(dev, &rec);
- break;
- case SOS_LABEL:
- rtype = "Begin Session";
- unser_session_label(&sessrec, &rec);
- break;
- case EOS_LABEL:
- rtype = "End Session";
- break;
- case EOM_LABEL:
- rtype = "End of Media";
- break;
- default:
- rtype = "Unknown";
- break;
- }
- if (debug_level > 0) {
- printf("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
- rtype, rec.VolSessionId, rec.VolSessionTime, rec.Stream, rec.data_len);
+ for (rec->state=0; !is_block_empty(rec); ) {
+ if (!read_record_from_block(block, rec)) {
+ break;
}
- Dmsg1(40, "Got label = %d\n", rec.FileIndex);
- if (rec.FileIndex == EOM_LABEL) { /* end of tape? */
- Dmsg0(40, "Get EOM LABEL\n");
- break; /* yes, get out */
+ /* This is no longer used */
+ if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
+ Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
+ break; /* END OF FILE */
}
- continue; /* ignore other labels */
- } /* end if label record */
-
- /* Is this the file we want? */
- if (bsr && !match_bsr(bsr, &rec, &dev->VolHdr, &sessrec)) {
- continue;
- }
- /* File Attributes stream */
- if (rec.Stream == STREAM_UNIX_ATTRIBUTES) {
- char *ap, *lp, *fp;
-
- /* If extracting, it was from previous stream, so
- * close the output file.
+ /*
+ * Check for Start or End of Session Record
+ *
*/
- if (extract) {
- if (ofd < 0) {
- Emsg0(M_ERROR_TERM, 0, "Logic error output file should be open\n");
+ if (rec->FileIndex < 0) {
+ char *rtype;
+ memset(&sessrec, 0, sizeof(sessrec));
+ switch (rec->FileIndex) {
+ case PRE_LABEL:
+ rtype = "Fresh Volume Label";
+ break;
+ case VOL_LABEL:
+ rtype = "Volume Label";
+ unser_volume_label(dev, rec);
+ break;
+ case SOS_LABEL:
+ rtype = "Begin Session";
+ unser_session_label(&sessrec, rec);
+ break;
+ case EOS_LABEL:
+ rtype = "End Session";
+ break;
+ case EOM_LABEL:
+ rtype = "End of Media";
+ break;
+ default:
+ rtype = "Unknown";
+ break;
+ }
+ if (debug_level > 0) {
+ printf("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
+ rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
}
- close(ofd);
- ofd = -1;
- extract = FALSE;
- set_statp(jcr, fname, ofile, lname, type, &statp);
- }
- if (sizeof_pool_memory(fname) < rec.data_len) {
- fname = realloc_pool_memory(fname, rec.data_len + 1);
- }
- if (sizeof_pool_memory(ofile) < sizeof_pool_memory(fname) + wherelen + 1) {
- ofile = realloc_pool_memory(ofile, sizeof_pool_memory(fname) + wherelen + 1);
- }
- if (sizeof_pool_memory(lname) < rec.data_len) {
- ofile = realloc_pool_memory(ofile, rec.data_len + 1);
+ Dmsg1(40, "Got label = %d\n", rec->FileIndex);
+ if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
+ Dmsg0(40, "Get EOM LABEL\n");
+ break; /* yes, get out */
+ }
+ continue; /* ignore other labels */
+ } /* end if label record */
+
+ /* Is this the file we want? */
+ if (bsr && !match_bsr(bsr, rec, &dev->VolHdr, &sessrec)) {
+ rec->remainder = 0;
+ continue;
}
- *fname = 0;
- *lname = 0;
-
- /*
- * An Attributes record consists of:
- * File_index
- * Type (FT_types)
- * Filename
- * Attributes
- * Link name (if file linked i.e. FT_LNK)
- *
- */
- sscanf(rec.data, "%ld %d", &record_file_index, &type);
- if (record_file_index != rec.FileIndex)
- Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
- rec.FileIndex, record_file_index);
- ap = rec.data;
- while (*ap++ != ' ') /* skip record file index */
- ;
- while (*ap++ != ' ') /* skip type */
- ;
- /* Save filename and position to attributes */
- fp = fname;
- while (*ap != 0) {
- *fp++ = *ap++;
+ if (is_partial_record(rec)) {
+ break;
}
- *fp = *ap++; /* terminate filename & point to attribs */
- /* Skip to Link name */
- if (type == FT_LNK) {
- lp = ap;
- while (*lp++ != 0) {
- ;
+ /* File Attributes stream */
+ if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
+ char *ap, *lp, *fp;
+
+ /* If extracting, it was from previous stream, so
+ * close the output file.
+ */
+ if (extract) {
+ if (ofd < 0) {
+ Emsg0(M_ERROR_TERM, 0, "Logic error output file should be open\n");
+ }
+ close(ofd);
+ ofd = -1;
+ extract = FALSE;
+ set_statp(jcr, fname, ofile, lname, type, &statp);
}
- strcat(lname, lp); /* "save" link name */
- } else {
- *lname = 0;
- }
-
- if (file_is_included(ff, fname) && !file_is_excluded(ff, fname)) {
+ if (sizeof_pool_memory(fname) < rec->data_len) {
+ fname = realloc_pool_memory(fname, rec->data_len + 1);
+ }
+ if (sizeof_pool_memory(ofile) < sizeof_pool_memory(fname) + wherelen + 1) {
+ ofile = realloc_pool_memory(ofile, sizeof_pool_memory(fname) + wherelen + 1);
+ }
+ if (sizeof_pool_memory(lname) < rec->data_len) {
+ lname = realloc_pool_memory(lname, rec->data_len + 1);
+ }
+ *fname = 0;
+ *lname = 0;
- decode_stat(ap, &statp);
- /*
- * Prepend the where directory so that the
- * files are put where the user wants.
+ /*
+ * An Attributes record consists of:
+ * File_index
+ * Type (FT_types)
+ * Filename
+ * Attributes
+ * Link name (if file linked i.e. FT_LNK)
*
- * We do a little jig here to handle Win32 files with
- * a drive letter.
- * If where is null and we are running on a win32 client,
- * change nothing.
- * Otherwise, if the second character of the filename is a
- * colon (:), change it into a slash (/) -- this creates
- * a reasonable pathname on most systems.
*/
- strcpy(ofile, where);
- if (fname[1] == ':') {
- fname[1] = '/';
- strcat(ofile, fname);
- fname[1] = ':';
+ sscanf(rec->data, "%ld %d", &record_file_index, &type);
+ if (record_file_index != rec->FileIndex)
+ Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
+ rec->FileIndex, record_file_index);
+ ap = rec->data;
+ while (*ap++ != ' ') /* skip record file index */
+ ;
+ while (*ap++ != ' ') /* skip type */
+ ;
+ /* Save filename and position to attributes */
+ fp = fname;
+ while (*ap != 0) {
+ *fp++ = *ap++;
+ }
+ *fp = *ap++; /* terminate filename & point to attribs */
+
+ /* Skip to Link name */
+ if (type == FT_LNK) {
+ lp = ap;
+ while (*lp++ != 0) {
+ ;
+ }
+ strcat(lname, lp); /* "save" link name */
} else {
- strcat(ofile, fname);
+ *lname = 0;
}
-/* Pmsg1(000, "Restoring: %s\n", ofile); */
- extract = create_file(jcr, fname, ofile, lname, type, &statp, &ofd);
+
+ if (file_is_included(ff, fname) && !file_is_excluded(ff, fname)) {
+
+ decode_stat(ap, &statp);
+ /*
+ * Prepend the where directory so that the
+ * files are put where the user wants.
+ *
+ * We do a little jig here to handle Win32 files with
+ * a drive letter.
+ * If where is null and we are running on a win32 client,
+ * change nothing.
+ * Otherwise, if the second character of the filename is a
+ * colon (:), change it into a slash (/) -- this creates
+ * a reasonable pathname on most systems.
+ */
+ strcpy(ofile, where);
+ if (fname[1] == ':') {
+ fname[1] = '/';
+ strcat(ofile, fname);
+ fname[1] = ':';
+ } else {
+ strcat(ofile, fname);
+ }
+ /* Pmsg1(000, "Restoring: %s\n", ofile); */
- if (extract) {
- print_ls_output(ofile, lname, type, &statp);
+ extract = create_file(jcr, fname, ofile, lname, type, &statp, &ofd);
+
+ if (extract) {
+ print_ls_output(ofile, lname, type, &statp);
+ }
}
- }
- /* Data stream and extracting */
- } else if (rec.Stream == STREAM_FILE_DATA) {
- if (extract) {
- total += rec.data_len;
- Dmsg2(8, "Write %ld bytes, total=%ld\n", rec.data_len, total);
- if ((uint32_t)write(ofd, rec.data, rec.data_len) != rec.data_len) {
- Emsg1(M_ERROR_TERM, 0, "Write error: %s\n", strerror(errno));
+ /* Data stream and extracting */
+ } else if (rec->Stream == STREAM_FILE_DATA) {
+ if (extract) {
+ total += rec->data_len;
+ Dmsg2(8, "Write %ld bytes, total=%ld\n", rec->data_len, total);
+ if ((uint32_t)write(ofd, rec->data, rec->data_len) != rec->data_len) {
+ Emsg1(M_ERROR_TERM, 0, "Write error: %s\n", strerror(errno));
+ }
}
- }
-
- } else if (rec.Stream == STREAM_GZIP_DATA) {
+
+ } else if (rec->Stream == STREAM_GZIP_DATA) {
#ifdef HAVE_LIBZ
- if (extract) {
- uLongf compress_len;
+ if (extract) {
+ uLongf compress_len;
- compress_len = compress_buf_size;
- if (uncompress((Bytef *)compress_buf, &compress_len,
- (const Bytef *)rec.data, (uLong)rec.data_len) != Z_OK) {
- Emsg0(M_ERROR_TERM, 0, _("Uncompression error.\n"));
- }
+ compress_len = compress_buf_size;
+ if (uncompress((Bytef *)compress_buf, &compress_len,
+ (const Bytef *)rec->data, (uLong)rec->data_len) != Z_OK) {
+ Emsg0(M_ERROR_TERM, 0, _("Uncompression error.\n"));
+ }
- Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
- if ((uLongf)write(ofd, compress_buf, (size_t)compress_len) != compress_len) {
- Pmsg0(0, "===Write error===\n");
- Emsg2(M_ERROR_TERM, 0, "Write error on %s: %s\n", ofile, strerror(errno));
+ Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
+ if ((uLongf)write(ofd, compress_buf, (size_t)compress_len) != compress_len) {
+ Pmsg0(0, "===Write error===\n");
+ Emsg2(M_ERROR_TERM, 0, "Write error on %s: %s\n", ofile, strerror(errno));
+ }
+ total += compress_len;
+ Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
+ compress_len);
}
- total += compress_len;
- Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec.data_len,
- compress_len);
- }
#else
- if (extract) {
- Emsg0(M_ERROR_TERM, 0, "GZIP data stream found, but GZIP not configured!\n");
- }
+ if (extract) {
+ Emsg0(M_ERROR_TERM, 0, "GZIP data stream found, but GZIP not configured!\n");
+ }
#endif
- /* If extracting, wierd stream (not 1 or 2), close output file anyway */
- } else if (extract) {
- if (ofd < 0) {
- Emsg0(M_ERROR_TERM, 0, "Logic error output file should be open\n");
+ /* If extracting, wierd stream (not 1 or 2), close output file anyway */
+ } else if (extract) {
+ if (ofd < 0) {
+ Emsg0(M_ERROR_TERM, 0, "Logic error output file should be open\n");
+ }
+ close(ofd);
+ ofd = -1;
+ extract = FALSE;
+ set_statp(jcr, fname, ofile, lname, type, &statp);
+ } else if (rec->Stream != STREAM_MD5_SIGNATURE) {
+ Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec->Stream, rec->data);
}
- close(ofd);
- ofd = -1;
- extract = FALSE;
- set_statp(jcr, fname, ofile, lname, type, &statp);
- } else if (rec.Stream != STREAM_MD5_SIGNATURE) {
- Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec.Stream, rec.data);
}
}
+
/* If output file is still open, it was the last one in the
* archive since we just hit an end of file, so close the file.
*/
free_pool_memory(compress_buf);
term_dev(dev);
free_block(block);
+ free_record(rec);
return;
}
static void do_setup(char *infname)
{
jcr = new_jcr(sizeof(JCR), my_free_jcr);
+ jcr->VolSessionId = 1;
+ jcr->VolSessionTime = (uint32_t)time(NULL);
+ jcr->bsr = bsr;
+ strcpy(jcr->Job, "bls");
+ jcr->dev_name = get_pool_memory(PM_FNAME);
+ strcpy(jcr->dev_name, infname);
+
VolName = Vol;
VolName[0] = 0;
if (strncmp(infname, "/dev/", 5) != 0) {
NumVolumes++;
}
- jcr->VolumeName = (char *)check_pool_memory_size(jcr->VolumeName, strlen(VolName)+1);
- strcpy(jcr->VolumeName, VolName);
+ pm_strcpy(&jcr->VolumeName, VolName);
if (!acquire_device_for_read(jcr, dev, block)) {
Emsg0(M_ERROR, 0, dev->errmsg);
exit(1);
CurVolume++;
Dmsg1(20, "There is another volume %s.\n", p);
VolName = p;
- jcr->VolumeName = check_pool_memory_size(jcr->VolumeName,
- strlen(VolName)+1);
- strcpy(jcr->VolumeName, VolName);
+ pm_strcpy(&jcr->VolumeName, VolName);
close_dev(dev);
dev->state &= ~ST_READ;
record = 0;
for (rec->state=0; !is_block_empty(rec); ) {
- if (!new_read_record_from_block(block, rec)) {
+ if (!read_record_from_block(block, rec)) {
Dmsg2(30, "!read-break. stat=%s blk=%d\n", rec_state_to_str(rec),
block->BlockNumber);
break;
static DEVICE *dev = NULL;
static B_DB *db;
static JCR *jcr;
+static BSR *bsr;
static void usage()
{
init_msg(NULL, NULL);
- while ((ch = getopt(argc, argv, "d:?")) != -1) {
+ while ((ch = getopt(argc, argv, "b:d:?")) != -1) {
switch (ch) {
+ case 'b':
+ bsr = parse_bsr(NULL, optarg);
+ break;
case 'd': /* debug level */
debug_level = atoi(optarg);
if (debug_level <= 0)
jcr->VolSessionId = 1;
jcr->VolSessionTime = (uint32_t)time(NULL);
jcr->NumVolumes = 1;
+ jcr->bsr = bsr;
+ strcpy(jcr->Job, "bscan");
+ jcr->dev_name = get_pool_memory(PM_FNAME);
+ strcpy(jcr->dev_name, argv[0]);
/* *** FIXME **** need to put in corect db, user, and password */
- if ((db=db_init_database("bacula", "bacula", "")) == NULL) {
+ if ((db=db_init_database(NULL, "bacula", "bacula", "")) == NULL) {
Emsg0(M_ABORT, 0, "Could not init Bacula database\n");
}
if (!db_open_database(db)) {
struct stat statp;
int type;
long record_file_index;
- DEV_RECORD rec;
+ DEV_RECORD *rec;
DEV_BLOCK *block;
- char *fname; /* original file name */
- char *ofile; /* output name with prefix */
- char *lname; /* link name */
+ POOLMEM *fname; /* original file name */
+ POOLMEM *ofile; /* output name with prefix */
+ POOLMEM *lname; /* link name */
MEDIA_DBR mr;
POOL_DBR pr;
JOB_DBR jr;
*p = 0;
}
}
+ strcpy(jcr->VolumeName, VolName);
dev = init_dev(NULL, devname);
if (!dev || !open_device(dev)) {
}
Dmsg0(90, "Device opened for read.\n");
- fname = (char *)get_pool_memory(PM_FNAME);
- ofile = (char *)get_pool_memory(PM_FNAME);
- lname = (char *)get_pool_memory(PM_FNAME);
+ fname = get_pool_memory(PM_FNAME);
+ ofile = get_pool_memory(PM_FNAME);
+ lname = get_pool_memory(PM_FNAME);
block = new_block(dev);
-
- strcpy(jcr->VolumeName, VolName);
+
+ create_vol_list(jcr);
if (!acquire_device_for_read(jcr, dev, block)) {
Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
}
- memset(&rec, 0, sizeof(rec));
- rec.data = (char *)get_memory(70000);
+ rec = new_record();
+ free_pool_memory(rec->data);
+ rec->data = get_memory(70000);
memset(&mr, 0, sizeof(mr));
memset(&pr, 0, sizeof(pr));
for ( ;; ) {
- if (!read_record(dev, block, &rec)) {
+ if (!read_block_from_device(dev, block)) {
uint32_t status;
if (dev->state & ST_EOT) {
- break;
+ if (!mount_next_read_volume(jcr, dev, block)) {
+ break;
+ }
+ continue;
}
if (dev->state & ST_EOF) {
continue; /* try again */
}
+ if (dev->state & ST_SHORT) {
+ continue;
+ }
Pmsg0(0, "Read Record got a bad record\n");
status_dev(dev, &status);
Dmsg1(20, "Device status: %x\n", status);
status, dev_name(dev), strerror(errno));
}
+ for (rec->state=0; !is_block_empty(rec); ) {
+ SESSION_LABEL label, elabel;
+ if (!read_record_from_block(block, rec)) {
+ break;
+ }
- /* This is no longer used */
- if (rec.VolSessionId == 0 && rec.VolSessionTime == 0) {
- Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
- break; /* END OF FILE */
- }
-
- /*
- * Check for Start or End of Session Record
- *
- */
- if (rec.FileIndex < 0) {
- SESSION_LABEL label, elabel;
- if (debug_level > 1) {
- dump_label_record(dev, &rec, 1);
- }
- switch (rec.FileIndex) {
- case PRE_LABEL:
- Pmsg0(000, "Volume is prelabeled. This tape cannot be scanned.\n");
- return;
- break;
- case VOL_LABEL:
- unser_volume_label(dev, &rec);
- strcpy(mr.VolumeName, dev->VolHdr.VolName);
- if (!db_get_media_record(db, &mr)) {
- Pmsg1(000, "VOL_LABEL: Media record not found for Volume: %s\n",
- mr.VolumeName);
- continue;
- }
- if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
- Pmsg2(000, "VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n",
- mr.MediaType, dev->VolHdr.MediaType);
- continue;
- }
- strcpy(pr.Name, dev->VolHdr.PoolName);
- if (!db_get_pool_record(db, &pr)) {
- Pmsg1(000, "VOL_LABEL: Pool record not found for Pool: %s\n",
- pr.Name);
- continue;
- }
- if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
- Pmsg2(000, "VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n",
- pr.PoolType, dev->VolHdr.PoolType);
- continue;
- }
- Pmsg1(000, "VOL_LABEL: OK for Volume: %s\n", mr.VolumeName);
- break;
- case SOS_LABEL:
- unser_session_label(&label, &rec);
- memset(&jr, 0, sizeof(jr));
- jr.JobId = label.JobId;
- if (!db_get_job_record(db, &jr)) {
- Pmsg1(000, "SOS_LABEL: Job record not found for JobId: %d\n",
- jr.JobId);
- continue;
- }
- if (rec.VolSessionId != jr.VolSessionId) {
- Pmsg2(000, "SOS_LABEL: VolSessId mismatch. DB=%d Vol=%d\n",
- jr.VolSessionId, rec.VolSessionId);
- continue;
- }
- if (rec.VolSessionTime != jr.VolSessionTime) {
- Pmsg2(000, "SOS_LABEL: VolSessTime mismatch. DB=%d Vol=%d\n",
- jr.VolSessionTime, rec.VolSessionTime);
- continue;
- }
- if (jr.PoolId != pr.PoolId) {
- Pmsg2(000, "SOS_LABEL: PoolId mismatch. DB=%d Vol=%d\n",
- jr.PoolId, pr.PoolId);
- continue;
- }
- break;
- case EOS_LABEL:
- unser_session_label(&elabel, &rec);
- if (elabel.JobId != label.JobId) {
- Pmsg2(000, "EOS_LABEL: Start/End JobId mismatch. Start=%d End=%d\n",
- label.JobId, elabel.JobId);
- continue;
- }
- if (elabel.JobFiles != jr.JobFiles) {
- Pmsg2(000, "EOS_LABEL: JobFiles mismatch. DB=%d EOS=%d\n",
- jr.JobFiles, elabel.JobFiles);
- continue;
- }
- if (elabel.JobBytes != jr.JobBytes) {
- Pmsg2(000, "EOS_LABEL: JobBytes mismatch. DB=%d EOS=%d\n",
- jr.JobBytes, elabel.JobBytes);
- continue;
- }
- Pmsg1(000, "EOS_LABEL: OK for JobId=%d\n", elabel.JobId);
- break;
- case EOM_LABEL:
- break;
- default:
- break;
+ /* This is no longer used */
+ if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
+ Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
+ break; /* END OF FILE */
}
- continue;
- }
- /* File Attributes stream */
- if (rec.Stream == STREAM_UNIX_ATTRIBUTES) {
- char *ap, *lp;
+ /*
+ * Check for Start or End of Session Record
+ *
+ */
+ if (rec->FileIndex < 0) {
- if (sizeof_pool_memory(fname) < rec.data_len) {
- fname = (char *)realloc_pool_memory(fname, rec.data_len + 1);
+ if (debug_level > 1) {
+ dump_label_record(dev, rec, 1);
+ }
+ switch (rec->FileIndex) {
+ case PRE_LABEL:
+ Pmsg0(000, "Volume is prelabeled. This tape cannot be scanned.\n");
+ return;
+ break;
+ case VOL_LABEL:
+ unser_volume_label(dev, rec);
+ strcpy(mr.VolumeName, dev->VolHdr.VolName);
+ if (!db_get_media_record(db, &mr)) {
+ Pmsg1(000, "VOL_LABEL: Media record not found for Volume: %s\n",
+ mr.VolumeName);
+ continue;
+ }
+ if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
+ Pmsg2(000, "VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n",
+ mr.MediaType, dev->VolHdr.MediaType);
+ continue;
+ }
+ strcpy(pr.Name, dev->VolHdr.PoolName);
+ if (!db_get_pool_record(db, &pr)) {
+ Pmsg1(000, "VOL_LABEL: Pool record not found for Pool: %s\n",
+ pr.Name);
+ continue;
+ }
+ if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
+ Pmsg2(000, "VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n",
+ pr.PoolType, dev->VolHdr.PoolType);
+ continue;
+ }
+ Pmsg1(000, "VOL_LABEL: OK for Volume: %s\n", mr.VolumeName);
+ break;
+ case SOS_LABEL:
+ unser_session_label(&label, rec);
+ memset(&jr, 0, sizeof(jr));
+ jr.JobId = label.JobId;
+ if (!db_get_job_record(db, &jr)) {
+ Pmsg1(000, "SOS_LABEL: Job record not found for JobId: %d\n",
+ jr.JobId);
+ continue;
+ }
+ if (rec->VolSessionId != jr.VolSessionId) {
+ Pmsg2(000, "SOS_LABEL: VolSessId mismatch. DB=%d Vol=%d\n",
+ jr.VolSessionId, rec->VolSessionId);
+ continue;
+ }
+ if (rec->VolSessionTime != jr.VolSessionTime) {
+ Pmsg2(000, "SOS_LABEL: VolSessTime mismatch. DB=%d Vol=%d\n",
+ jr.VolSessionTime, rec->VolSessionTime);
+ continue;
+ }
+ if (jr.PoolId != pr.PoolId) {
+ Pmsg2(000, "SOS_LABEL: PoolId mismatch. DB=%d Vol=%d\n",
+ jr.PoolId, pr.PoolId);
+ continue;
+ }
+ break;
+ case EOS_LABEL:
+ unser_session_label(&elabel, rec);
+ if (elabel.JobId != label.JobId) {
+ Pmsg2(000, "EOS_LABEL: Start/End JobId mismatch. Start=%d End=%d\n",
+ label.JobId, elabel.JobId);
+ continue;
+ }
+ if (elabel.JobFiles != jr.JobFiles) {
+ Pmsg2(000, "EOS_LABEL: JobFiles mismatch. DB=%d EOS=%d\n",
+ jr.JobFiles, elabel.JobFiles);
+ continue;
+ }
+ if (elabel.JobBytes != jr.JobBytes) {
+ Pmsg2(000, "EOS_LABEL: JobBytes mismatch. DB=%d EOS=%d\n",
+ jr.JobBytes, elabel.JobBytes);
+ continue;
+ }
+ Pmsg1(000, "EOS_LABEL: OK for JobId=%d\n", elabel.JobId);
+ break;
+ case EOM_LABEL:
+ break;
+ default:
+ break;
+ }
+ continue;
}
- if (sizeof_pool_memory(lname) < rec.data_len) {
- ofile = (char *)realloc_pool_memory(ofile, rec.data_len + 1);
+
+ /* Is this the file we want? */
+ if (bsr && !match_bsr(bsr, rec, &dev->VolHdr, &label)) {
+ rec->remainder = 0;
+ continue;
}
- *fname = 0;
- *lname = 0;
-
- /*
- * An Attributes record consists of:
- * File_index
- * Type (FT_types)
- * Filename
- * Attributes
- * Link name (if file linked i.e. FT_LNK)
- *
- */
- sscanf(rec.data, "%ld %d %s", &record_file_index, &type, fname);
- if (record_file_index != rec.FileIndex)
- Emsg2(M_ABORT, 0, "Record header file index %ld not equal record index %ld\n",
- rec.FileIndex, record_file_index);
- ap = rec.data;
- /* Skip to attributes */
- while (*ap++ != 0)
- ;
- /* Skip to Link name */
- if (type == FT_LNK) {
- lp = ap;
- while (*lp++ != 0) {
- ;
+ if (is_partial_record(rec)) {
+ break;
+ }
+
+ /* File Attributes stream */
+ if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
+ char *ap, *lp, *fp;
+
+ if (sizeof_pool_memory(fname) < rec->data_len) {
+ fname = realloc_pool_memory(fname, rec->data_len + 1);
}
- strcat(lname, lp); /* "save" link name */
- } else {
+ if (sizeof_pool_memory(lname) < rec->data_len) {
+ lname = realloc_pool_memory(lname, rec->data_len + 1);
+ }
+ *fname = 0;
*lname = 0;
- }
+ /*
+ * An Attributes record consists of:
+ * File_index
+ * Type (FT_types)
+ * Filename
+ * Attributes
+ * Link name (if file linked i.e. FT_LNK)
+ *
+ */
+ sscanf(rec->data, "%ld %d", &record_file_index, &type);
+ if (record_file_index != rec->FileIndex)
+ Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
+ rec->FileIndex, record_file_index);
+ ap = rec->data;
+ while (*ap++ != ' ') /* skip record file index */
+ ;
+ while (*ap++ != ' ') /* skip type */
+ ;
+ /* Save filename and position to attributes */
+ fp = fname;
+ while (*ap != 0) {
+ *fp++ = *ap++;
+ }
+ *fp = *ap++; /* terminate filename & point to attribs */
- decode_stat(ap, &statp);
-/* Dmsg1(000, "Restoring: %s\n", ofile); */
+ /* Skip through attributes to link name */
+ if (type == FT_LNK) {
+ lp = ap;
+ while (*lp++ != 0) {
+ ;
+ }
+ strcat(lname, lp); /* "save" link name */
+ } else {
+ *lname = 0;
+ }
- if (debug_level > 1) {
- print_ls_output(fname, &statp);
- }
+ decode_stat(ap, &statp);
- /* Data stream and extracting */
- } else if (rec.Stream == STREAM_FILE_DATA) {
+ if (debug_level > 1) {
+ print_ls_output(fname, &statp);
+ }
+
+ /* Data stream and extracting */
+ } else if (rec->Stream == STREAM_FILE_DATA) {
- } else if (rec.Stream != STREAM_MD5_SIGNATURE) {
- Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec.Stream, rec.data);
+ } else if (rec->Stream != STREAM_MD5_SIGNATURE) {
+ Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec->Stream, rec->data);
+ }
}
}
free_pool_memory(lname);
term_dev(dev);
free_block(block);
+ free_record(rec);
return;
}
#include "stored.h" /* pull in Storage Deamon headers */
/* Forward referenced functions */
-static int mount_next_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *label_blk, int release);
-static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd);
extern char my_name[];
extern int debug_level;
-
-/*********************************************************************
- * Acquire device for reading. We permit (for the moment)
- * only one reader. We read the Volume label from the block and
- * leave the block pointers just after the label.
- *
- * Returns: 0 if failed for any reason
- * 1 if successful
- */
-int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
-{
- int stat;
-
- lock_device(dev);
- if (dev->state & ST_READ || dev->num_writers > 0) {
- Jmsg(jcr, M_FATAL, 0, _("Device %s is busy.\n"), dev_name(dev));
- unlock_device(dev);
- return 0;
- }
- dev->state &= ~ST_LABEL; /* force reread of label */
- block_device(dev, BST_DOING_ACQUIRE);
- unlock_device(dev);
- stat = ready_dev_for_read(jcr, dev, block);
- P(dev->mutex);
- unblock_device(dev);
- V(dev->mutex);
- return stat;
-}
-
-/*
- * Acquire device for writing. We permit multiple writers.
- * If this is the first one, we read the label.
- *
- * Returns: 0 if failed for any reason
- * 1 if successful
- */
-int acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
-{
- int release = 0;
- int do_mount = 0;
-
- lock_device(dev);
- Dmsg1(190, "acquire_append device is %s\n", dev_is_tape(dev)?"tape":"disk");
-
-
- if (dev->state & ST_APPEND) {
- /*
- * Device already in append mode
- *
- * Check if we have the right Volume mounted
- * OK if AnonVols and volume info OK
- * OK if next volume matches current volume
- * otherwise mount desired volume obtained from
- * dir_find_next_appendable_volume
- */
- strcpy(jcr->VolumeName, dev->VolHdr.VolName);
- if (((dev->capabilities & CAP_ANONVOLS) &&
- !dir_get_volume_info(jcr)) ||
- (!dir_find_next_appendable_volume(jcr) ||
- strcmp(dev->VolHdr.VolName, jcr->VolumeName) != 0)) { /* wrong tape mounted */
- if (dev->num_writers != 0) {
- Jmsg(jcr, M_FATAL, 0, _("Device %s is busy writing with another Volume.\n"), dev_name(dev));
- unlock_device(dev);
- return 0;
- }
- /* Wrong tape mounted, release it, then fall through to get correct one */
- release = 1;
- do_mount = 1;
- }
- } else {
- /* Not already in append mode, so mount the device */
- if (dev->state & ST_READ) {
- Jmsg(jcr, M_FATAL, 0, _("Device %s is busy reading.\n"), dev_name(dev));
- unlock_device(dev);
- return 0;
- }
- ASSERT(dev->num_writers == 0);
- do_mount = 1;
- }
-
- if (do_mount) {
- block_device(dev, BST_DOING_ACQUIRE);
- unlock_device(dev);
- if (!mount_next_volume(jcr, dev, block, release)) {
- Jmsg(jcr, M_FATAL, 0, _("Could not ready device %s for append.\n"),
- dev_name(dev));
- P(dev->mutex);
- unblock_device(dev);
- unlock_device(dev);
- return 0;
- }
- P(dev->mutex);
- unblock_device(dev);
- }
-
- dev->num_writers++;
- if (dev->num_writers > 1) {
- Dmsg2(0, "Hey!!!! There are %d writers on device %s\n", dev->num_writers,
- dev_name(dev));
- }
- if (jcr->NumVolumes == 0) {
- jcr->NumVolumes = 1;
- }
- attach_jcr_to_device(dev, jcr); /* attach jcr to device */
- unlock_device(dev);
- return 1; /* got it */
-}
-
-/*
- * This job is done, so release the device. From a Unix standpoint,
- * the device remains open.
- *
- */
-int release_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
-{
- P(dev->mutex);
- Dmsg1(100, "release_device device is %s\n", dev_is_tape(dev)?"tape":"disk");
- if (dev->state & ST_READ) {
- dev->state &= ~ST_READ; /* clear read bit */
- if (!dev_is_tape(dev) || !(dev->capabilities & CAP_ALWAYSOPEN)) {
- if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
- offline_dev(dev);
- }
- close_dev(dev);
- }
- /******FIXME**** send read volume usage statistics to director */
-
- } else if (dev->num_writers > 0) {
- dev->num_writers--;
- Dmsg1(00, "There are %d writers in release_device\n", dev->num_writers);
- if (dev->num_writers == 0) {
- weof_dev(dev, 1);
- Dmsg0(100, "dir_create_jobmedia_record. Release\n");
- dir_create_jobmedia_record(jcr);
- dev->VolCatInfo.VolCatFiles++; /* increment number of files */
- /* Note! do volume update before close, which zaps VolCatInfo */
- Dmsg0(100, "dir_update_vol_info. Release\n");
- dir_update_volume_info(jcr, &dev->VolCatInfo, 0); /* send Volume info to Director */
-
- if (!dev_is_tape(dev) || !(dev->capabilities & CAP_ALWAYSOPEN)) {
- if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
- offline_dev(dev);
- }
- close_dev(dev);
- }
- } else {
- Dmsg0(100, "dir_create_jobmedia_record. Release\n");
- dir_create_jobmedia_record(jcr);
- Dmsg0(100, "dir_update_vol_info. Release\n");
- dir_update_volume_info(jcr, &dev->VolCatInfo, 0); /* send Volume info to Director */
- }
- } else {
- Jmsg1(jcr, M_ERROR, 0, _("BAD ERROR: release_device %s not in use.\n"), dev_name(dev));
- }
- detach_jcr_from_device(dev, jcr);
- V(dev->mutex);
- return 1;
-}
-
-
-
-/*
- * If release is set, we rewind the current volume,
- * which we no longer want, and ask the user (console)
- * to mount the next volume.
- *
- * Continue trying until we get it, and then ensure
- * that we can write on it.
- *
- * This routine returns a 0 only if it is REALLY
- * impossible to get the requested Volume.
- */
-static int mount_next_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release)
-{
- int recycle, ask, retry = 0, autochanger;
-
- Dmsg0(190, "Enter mount_next_volume()\n");
-
-mount_next_vol:
- if (retry++ > 5) {
- Jmsg(jcr, M_FATAL, 0, _("Too many errors on device %s.\n"), dev_name(dev));
- return 0;
- }
- if (job_cancelled(jcr)) {
- Jmsg(jcr, M_FATAL, 0, _("Job cancelled.\n"));
- return 0;
- }
- recycle = ask = autochanger = 0;
- if (release) {
- Dmsg0(500, "mount_next_volume release=1\n");
- /*
- * First erase all memory of the current volume
- */
- dev->block_num = 0;
- dev->file = 0;
- dev->LastBlockNumWritten = 0;
- memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo));
- memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
- dev->state &= ~ST_LABEL; /* label not yet read */
-
- if (!dev_is_tape(dev) || !(dev->capabilities & CAP_ALWAYSOPEN)) {
- if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
- offline_dev(dev);
- }
- close_dev(dev);
- }
-
- /* If we have not closed the device, then at least rewind the tape */
- if (dev->state & ST_OPENED) {
- if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
- offline_dev(dev);
- }
- if (!rewind_dev(dev)) {
- Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"),
- dev_name(dev), strerror_dev(dev));
- }
- }
- ask = 1; /* ask operator to mount tape */
- } else {
- /*
- * Get Director's idea of what tape we should have mounted.
- */
- if (!dir_find_next_appendable_volume(jcr)) {
- ask = 1; /* we must ask */
- }
- }
- release = 1; /* release if we "recurse" */
-
- /*
- * Get next volume and ready it for append
- * This code ensures that the device is ready for
- * writing. We start from the assumption that there
- * may not be a tape mounted.
- *
- * If the device is a file, we create the output
- * file. If it is a tape, we check the volume name
- * and move the tape to the end of data.
- *
- * It assumes that the device is not already in use!
- *
- */
-
- Dmsg0(100, "Enter ready_dev_for_append\n");
-
- dev->state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF);
-
- jcr->VolFirstFile = jcr->JobFiles; /* first update of Vol FileIndex */
- for ( ;; ) {
- int slot = jcr->VolCatInfo.Slot;
-
- /*
- * Handle autoloaders here. If we cannot autoload it, we
- * will fall through to ask the sysop.
- */
- if (dev->capabilities && CAP_AUTOCHANGER && slot <= 0) {
- if (dir_find_next_appendable_volume(jcr)) {
- slot = jcr->VolCatInfo.Slot;
- }
- }
- Dmsg1(100, "Want changer slot=%d\n", slot);
-
- if (slot > 0 && jcr->device->changer_name && jcr->device->changer_command) {
- uint32_t timeout = jcr->device->max_changer_wait;
- POOLMEM *changer, *results;
- int status, loaded;
-
- results = get_pool_memory(PM_MESSAGE);
- changer = get_pool_memory(PM_FNAME);
- /* Find out what is loaded */
- changer = edit_device_codes(jcr, changer, jcr->device->changer_command,
- "loaded");
- status = run_program(changer, timeout, results);
- if (status == 0) {
- loaded = atoi(results);
- } else {
- loaded = -1; /* force unload */
- }
- Dmsg1(100, "loaded=%s\n", results);
-
- /* If bad status or tape we want is not loaded, load it. */
- if (status != 0 || loaded != slot) {
- if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
- offline_dev(dev);
- }
- /* We are going to load a new tape, so close the device */
- force_close_dev(dev);
- if (loaded != 0) { /* must unload drive */
- Dmsg0(100, "Doing changer unload.\n");
- changer = edit_device_codes(jcr, changer,
- jcr->device->changer_command, "unload");
- status = run_program(changer, timeout, NULL);
- Dmsg1(100, "unload status=%d\n", status);
- }
- /*
- * Load the desired cassette
- */
- Dmsg1(100, "Doing changer load slot %d\n", slot);
- changer = edit_device_codes(jcr, changer,
- jcr->device->changer_command, "load");
- status = run_program(changer, timeout, NULL);
- Dmsg2(100, "load slot %d status=%d\n", slot, status);
- }
- free_pool_memory(changer);
- free_pool_memory(results);
- Dmsg1(100, "After changer, status=%d\n", status);
- if (status == 0) { /* did we succeed? */
- ask = 0; /* yes, got vol, no need to ask sysop */
- autochanger = 1; /* tape loaded by changer */
- }
- }
-
-
- if (ask && !dir_ask_sysop_to_mount_next_volume(jcr, dev)) {
- return 0; /* error return */
- }
- Dmsg1(200, "want vol=%s\n", jcr->VolumeName);
-
- /* Open device */
- for ( ; !(dev->state & ST_OPENED); ) {
- if (open_dev(dev, jcr->VolCatInfo.VolCatName, READ_WRITE) < 0) {
- if (dev->dev_errno == EAGAIN || dev->dev_errno == EBUSY) {
- sleep(30);
- }
- Jmsg2(jcr, M_FATAL, 0, _("Unable to open device %s. ERR=%s\n"),
- dev_name(dev), strerror_dev(dev));
- return 0;
- }
- }
-
- /*
- * Now make sure we have the right tape mounted
- */
-read_volume:
- switch (read_dev_volume_label(jcr, dev, block)) {
- case VOL_OK:
- Dmsg1(500, "Vol OK name=%s\n", jcr->VolumeName);
- memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo));
- if (strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0) {
- recycle = 1;
- }
- break; /* got it */
- case VOL_NAME_ERROR:
- Dmsg1(500, "Vol NAME Error Name=%s\n", jcr->VolumeName);
- /* Check if we can accept this as an anonymous volume */
- strcpy(jcr->VolumeName, dev->VolHdr.VolName);
- if (!dev->capabilities & CAP_ANONVOLS || !dir_get_volume_info(jcr)) {
- goto mount_next_vol;
- }
- Dmsg1(200, "want new name=%s\n", jcr->VolumeName);
- memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo));
- break;
-
- case VOL_NO_LABEL:
- case VOL_IO_ERROR:
- Dmsg1(500, "Vol NO_LABEL or IO_ERROR name=%s\n", jcr->VolumeName);
- /* If permitted, create a label */
- if (dev->capabilities & CAP_LABEL) {
- Dmsg0(190, "Create volume label\n");
- if (!write_volume_label_to_dev(jcr, (DEVRES *)dev->device, jcr->VolumeName,
- jcr->pool_name)) {
- goto mount_next_vol;
- }
- Jmsg(jcr, M_INFO, 0, _("Created Volume label %s on device %s.\n"),
- jcr->VolumeName, dev_name(dev));
- goto read_volume; /* read label we just wrote */
- }
- /* NOTE! Fall-through wanted. */
- default:
- /* Send error message */
- Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
- if (autochanger) {
- Jmsg(jcr, M_ERROR, 0, _("Autochanger Volume %s not found in slot %d.\n\
- Setting slot to zero in catalog.\n"),
- jcr->VolumeName, jcr->VolCatInfo.Slot);
- jcr->VolCatInfo.Slot = 0; /* invalidate slot */
- dir_update_volume_info(jcr, &jcr->VolCatInfo, 1); /* set slot */
- }
- goto mount_next_vol;
- }
- break;
- }
-
- /*
- * See if we have a fresh tape or tape with data.
- *
- * Note, if the LabelType is PRE_LABEL, it was labeled
- * but never written. If so, rewrite the label but set as
- * VOL_LABEL. We rewind and return the label (reconstructed)
- * in the block so that in the case of a new tape, data can
- * be appended just after the block label. If we are writing
- * an second volume, the calling routine will write the label
- * before writing the overflow block.
- *
- * If the tape is marked as Recycle, we rewrite the label.
- */
- if (dev->VolHdr.LabelType == PRE_LABEL || recycle) {
- Dmsg1(190, "ready_for_append found freshly labeled volume. dev=%x\n", dev);
- dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */
- write_volume_label_to_block(jcr, dev, block);
- /*
- * Write the block now to ensure we have write permission.
- * It is better to find out now rather than later.
- */
- dev->VolCatInfo.VolCatBytes = 0;
- if (!rewind_dev(dev)) {
- Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"),
- dev_name(dev), strerror_dev(dev));
- }
- if (recycle) {
- if (!truncate_dev(dev)) {
- Jmsg2(jcr, M_WARNING, 0, _("Truncate error on device %s. ERR=%s\n"),
- dev_name(dev), strerror_dev(dev));
- }
- }
- if (!write_block_to_dev(dev, block)) {
- Jmsg2(jcr, M_ERROR, 0, _("Unable to write device %s. ERR=%s\n"),
- dev_name(dev), strerror_dev(dev));
- goto mount_next_vol;
- }
- if (!rewind_dev(dev)) {
- Jmsg2(jcr, M_ERROR, 0, _("Unable to rewind device %s. ERR=%s\n"),
- dev_name(dev), strerror_dev(dev));
- goto mount_next_vol;
- }
- /* Recreate a correct volume label and return it in the block */
- write_volume_label_to_block(jcr, dev, block);
- dev->VolCatInfo.VolCatJobs = 1;
- dev->VolCatInfo.VolCatFiles = 1;
- dev->VolCatInfo.VolCatErrors = 0;
- dev->VolCatInfo.VolCatBlocks = 1;
- if (recycle) {
- dev->VolCatInfo.VolCatMounts++;
- dev->VolCatInfo.VolCatRecycles++;
- } else {
- dev->VolCatInfo.VolCatMounts = 1;
- dev->VolCatInfo.VolCatRecycles = 0;
- dev->VolCatInfo.VolCatWrites = 1;
- dev->VolCatInfo.VolCatReads = 1;
- }
- strcpy(dev->VolCatInfo.VolCatStatus, "Append");
- Dmsg0(100, "dir_update_vol_info. Set Append\n");
- dir_update_volume_info(jcr, &dev->VolCatInfo, 1); /* indicate doing relabel */
- if (recycle) {
- Jmsg(jcr, M_INFO, 0, _("Recycled volume %s on device %s, all previous data lost.\n"),
- jcr->VolumeName, dev_name(dev));
- } else {
- Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume %s on device %s\n"),
- jcr->VolumeName, dev_name(dev));
- }
-
- } else {
- /*
- * OK, at this point, we have a valid Bacula label, but
- * we need to position to the end of the volume, since we are
- * just now putting it into append mode.
- */
- Dmsg0(200, "Device previously written, moving to end of data\n");
- Jmsg(jcr, M_INFO, 0, _("Volume %s previously written, moving to end of data.\n"),
- jcr->VolumeName);
- if (!eod_dev(dev)) {
- Jmsg(jcr, M_ERROR, 0, _("Unable to position to end of data %s. ERR=%s\n"),
- dev_name(dev), strerror_dev(dev));
- Jmsg(jcr, M_INFO, 0, _("Marking Volume %s in Error in Catalog.\n"),
- jcr->VolumeName);
- strcpy(dev->VolCatInfo.VolCatStatus, "Error");
- Dmsg0(100, "dir_update_vol_info. Set Error.\n");
- dir_update_volume_info(jcr, &dev->VolCatInfo, 0);
- goto mount_next_vol;
- }
- /* *****FIXME**** we should do some checking for files too */
- if (dev_is_tape(dev)) {
- Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume at file=%d.\n"), dev_file(dev));
- /*
- * Check if we are positioned on the tape at the same place
- * that the database says we should be.
- */
- if (dev->VolCatInfo.VolCatFiles != dev_file(dev) + 1) {
- /* ****FIXME**** this should refuse to write on tape */
- Jmsg(jcr, M_ERROR, 0, _("Hey! Num files mismatch! Volume=%d Catalog=%d\n"),
- dev_file(dev)+1, dev->VolCatInfo.VolCatFiles);
- }
- }
- /* Update Volume Info -- will be written at end of Job */
- dev->VolCatInfo.VolCatMounts++; /* Update mounts */
- dev->VolCatInfo.VolCatJobs++;
- /* Return an empty block */
- empty_block(block); /* we used it for reading so set for write */
- }
- dev->state |= ST_APPEND;
- Dmsg0(100, "Normal return from read_dev_for_append\n");
- return 1;
-}
-
-/*
- * This routine ensures that the device is ready for
- * reading. If it is a file, it opens it.
- * If it is a tape, it checks the volume name
- *
- * Returns 0 on failure
- * Returns 1 on success
- */
-int ready_dev_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
-{
- if (!(dev->state & ST_OPENED)) {
- Dmsg1(120, "bstored: open vol=%s\n", jcr->VolumeName);
- if (open_dev(dev, jcr->VolumeName, READ_ONLY) < 0) {
- Jmsg(jcr, M_FATAL, 0, _("Open device %s volume %s failed, ERR=%s\n"),
- dev_name(dev), jcr->VolumeName, strerror_dev(dev));
- return 0;
- }
- Dmsg1(129, "open_dev %s OK\n", dev_name(dev));
- }
-
- for (;;) {
- if (job_cancelled(jcr)) {
- Mmsg0(&dev->errmsg, _("Job cancelled.\n"));
- return 0;
- }
- if (!rewind_dev(dev)) {
- Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"),
- dev_name(dev), strerror_dev(dev));
- }
- switch (read_dev_volume_label(jcr, dev, block)) {
- case VOL_OK:
- break; /* got it */
- default:
- /* Send error message generated by read_dev_volume_label() */
- Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
- if (!rewind_dev(dev)) {
- Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"),
- dev_name(dev), strerror_dev(dev));
- }
- /* Mount a specific volume and no other */
- if (!dir_ask_sysop_to_mount_volume(jcr, dev)) {
- return 0; /* error return */
- }
- continue; /* try reading again */
- }
- break;
- }
-
- dev->state |= ST_READ;
- return 1;
-}
-
/*
* This is the dreaded moment. We either have an end of
* medium condition or worse, and error condition.
/* Unlock, but leave BLOCKED */
unlock_device(dev);
- if (!mount_next_volume(jcr, dev, label_blk, 1)) {
+ if (!mount_next_write_volume(jcr, dev, label_blk, 1)) {
P(dev->mutex);
unblock_device(dev);
return 0; /* device locked */
/*
* If this is a new tape, the label_blk will contain the
* label, so write it now. If this is a previously
- * used tape, mount_next_volume() will return an
+ * used tape, mount_next_write_volume() will return an
* empty label_blk, and nothing will be written.
*/
Dmsg0(190, "write label block to dev\n");
pthread_cond_broadcast(&dev->wait); /* wake them up */
}
}
-
-
-
-/*
- * Edit codes into ChangerCommand
- * %% = %
- * %a = archive device name
- * %c = changer device name
- * %f = Client's name
- * %j = Job name
- * %o = command
- * %s = Slot base 0
- * %S = Slot base 1
- * %v = Volume name
- *
- *
- * omsg = edited output message
- * imsg = input string containing edit codes (%x)
- * cmd = command string (load, unload, ...)
- *
- */
-static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd)
-{
- char *p;
- const char *str;
- char add[20];
-
- *omsg = 0;
- Dmsg1(200, "edit_device_codes: %s\n", imsg);
- for (p=imsg; *p; p++) {
- if (*p == '%') {
- switch (*++p) {
- case '%':
- str = "%";
- break;
- case 'a':
- str = jcr->device->dev->dev_name;
- break;
- case 'c':
- str = NPRT(jcr->device->changer_name);
- break;
- case 'o':
- str = NPRT(cmd);
- break;
- case 's':
- sprintf(add, "%d", jcr->VolCatInfo.Slot - 1);
- str = add;
- break;
- case 'S':
- sprintf(add, "%d", jcr->VolCatInfo.Slot);
- str = add;
- break;
- case 'j': /* Job name */
- str = jcr->Job;
- break;
- case 'v':
- str = NPRT(jcr->VolumeName);
- break;
- case 'f':
- str = NPRT(jcr->client_name);
- break;
-
- default:
- add[0] = '%';
- add[1] = *p;
- add[2] = 0;
- str = add;
- break;
- }
- } else {
- add[0] = *p;
- add[1] = 0;
- str = add;
- }
- Dmsg1(200, "add_str %s\n", str);
- pm_strcat(&omsg, (char *)str);
- Dmsg1(200, "omsg=%s\n", omsg);
- }
- return omsg;
-}
char ec1[30];
+ fd->jcr = (void *)jcr;
+ dir->jcr = (void *)jcr;
Dmsg1(120, "Start run Job=%s\n", jcr->Job);
bnet_fsend(dir, Job_start, jcr->Job);
time(&jcr->start_time);
/* Requests from the Director daemon */
static char jobcmd[] = "JobId=%d job=%127s job_name=%127s client_name=%127s \
-type=%d level=%d FileSet=%127s Allow=";
+type=%d level=%d FileSet=%127s NoAttr=%d SpoolAttr=%d\n";
static char use_device[] = "use device=%s media_type=%s pool_name=%s pool_type=%s\n";
/* Responses sent to Director daemon */
char auth_key[100];
BSOCK *dir = jcr->dir_bsock;
POOLMEM *job_name, *client_name, *job, *fileset_name;
- int JobType, level;
+ int JobType, level, spool_attributes, no_attributes;
struct timeval tv;
struct timezone tz;
struct timespec timeout;
client_name = get_memory(dir->msglen);
fileset_name = get_memory(dir->msglen);
if (sscanf(dir->msg, jobcmd, &JobId, job, job_name, client_name,
- &JobType, &level, fileset_name) != 7) {
+ &JobType, &level, fileset_name, &no_attributes,
+ &spool_attributes) != 9) {
bnet_fsend(dir, BAD_job, dir->msg);
Emsg1(M_FATAL, 0, _("Bad Job Command from Director: %s\n"), dir->msg);
free_memory(job);
strcpy(jcr->fileset_name, fileset_name);
jcr->JobType = JobType;
jcr->JobLevel = level;
+ jcr->no_attributes = no_attributes;
+ jcr->spool_attributes = spool_attributes;
free_memory(job);
free_memory(job_name);
free_memory(client_name);
JCR *jcr;
if (!(jcr=get_jcr_by_full_name(job_name))) {
- Emsg1(M_FATAL, 0, _("Job name not found: %s\n"), job_name);
+ Jmsg1(NULL, M_FATAL, 0, _("Job name not found: %s\n"), job_name);
return;
}
jcr->file_bsock = fd;
+ jcr->file_bsock->jcr = (void *)jcr;
Dmsg1(110, "Found Job %s\n", job_name);
create_session_label(jcr, rec, label);
rec->FileIndex = label;
- while (!write_record_to_block(block, rec)) {
- Dmsg2(190, "!write_record data_len=%d rem=%d\n", rec->data_len,
- rec->remainder);
+ /*
+ * We guarantee that the session record can totally fit
+ * into a block. If not, write the block, and put it in
+ * the next block. Having the sesssion record totally in
+ * one block makes reading them much easier (no need to
+ * read the next block).
+ */
+ if (!can_write_record_to_block(block, rec)) {
if (!write_block_to_device(jcr, dev, block)) {
Dmsg0(90, "Got session label write_block_to_dev error.\n");
Jmsg(jcr, M_FATAL, 0, _("Error writing Session label to %s: %s\n"),
return 0;
}
}
+ write_record_to_block(block, rec);
Dmsg6(20, "Write sesson_label record JobId=%d FI=%s SessId=%d Strm=%s len=%d\n\
remainder=%d\n", jcr->JobId,
--- /dev/null
+/*
+ *
+ * Routines for handling mounting tapes for reading and for
+ * writing.
+ *
+ * Kern Sibbald, August MMII
+ *
+ * Version $Id$
+ */
+/*
+ Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ */
+
+#include "bacula.h" /* pull in global headers */
+#include "stored.h" /* pull in Storage Deamon headers */
+
+/* Forward referenced functions */
+static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd);
+
+
+/*
+ * If release is set, we rewind the current volume,
+ * which we no longer want, and ask the user (console)
+ * to mount the next volume.
+ *
+ * Continue trying until we get it, and then ensure
+ * that we can write on it.
+ *
+ * This routine returns a 0 only if it is REALLY
+ * impossible to get the requested Volume.
+ */
+int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release)
+{
+ int recycle, ask, retry = 0, autochanger;
+
+ Dmsg0(190, "Enter mount_next_volume()\n");
+
+mount_next_vol:
+ if (retry++ > 5) {
+ Jmsg(jcr, M_FATAL, 0, _("Too many errors on device %s.\n"), dev_name(dev));
+ return 0;
+ }
+ if (job_cancelled(jcr)) {
+ Jmsg(jcr, M_FATAL, 0, _("Job cancelled.\n"));
+ return 0;
+ }
+ recycle = ask = autochanger = 0;
+ if (release) {
+ Dmsg0(500, "mount_next_volume release=1\n");
+ /*
+ * First erase all memory of the current volume
+ */
+ dev->block_num = 0;
+ dev->file = 0;
+ dev->LastBlockNumWritten = 0;
+ memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo));
+ memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
+ dev->state &= ~ST_LABEL; /* label not yet read */
+
+ if (!dev_is_tape(dev) || !(dev->capabilities & CAP_ALWAYSOPEN)) {
+ if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
+ offline_dev(dev);
+ }
+ close_dev(dev);
+ }
+
+ /* If we have not closed the device, then at least rewind the tape */
+ if (dev->state & ST_OPENED) {
+ if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
+ offline_dev(dev);
+ }
+ if (!rewind_dev(dev)) {
+ Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"),
+ dev_name(dev), strerror_dev(dev));
+ }
+ }
+ ask = 1; /* ask operator to mount tape */
+ } else {
+ /*
+ * Get Director's idea of what tape we should have mounted.
+ */
+ if (!dir_find_next_appendable_volume(jcr)) {
+ ask = 1; /* we must ask */
+ }
+ }
+ release = 1; /* release if we "recurse" */
+
+ /*
+ * Get next volume and ready it for append
+ * This code ensures that the device is ready for
+ * writing. We start from the assumption that there
+ * may not be a tape mounted.
+ *
+ * If the device is a file, we create the output
+ * file. If it is a tape, we check the volume name
+ * and move the tape to the end of data.
+ *
+ * It assumes that the device is not already in use!
+ *
+ */
+
+ Dmsg0(100, "Enter ready_dev_for_append\n");
+
+ dev->state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF);
+
+ jcr->VolFirstFile = jcr->JobFiles; /* first update of Vol FileIndex */
+ for ( ;; ) {
+ int slot = jcr->VolCatInfo.Slot;
+
+ /*
+ * Handle autoloaders here. If we cannot autoload it, we
+ * will fall through to ask the sysop.
+ */
+ if (dev->capabilities && CAP_AUTOCHANGER && slot <= 0) {
+ if (dir_find_next_appendable_volume(jcr)) {
+ slot = jcr->VolCatInfo.Slot;
+ }
+ }
+ Dmsg1(100, "Want changer slot=%d\n", slot);
+
+ if (slot > 0 && jcr->device->changer_name && jcr->device->changer_command) {
+ uint32_t timeout = jcr->device->max_changer_wait;
+ POOLMEM *changer, *results;
+ int status, loaded;
+
+ results = get_pool_memory(PM_MESSAGE);
+ changer = get_pool_memory(PM_FNAME);
+ /* Find out what is loaded */
+ changer = edit_device_codes(jcr, changer, jcr->device->changer_command,
+ "loaded");
+ status = run_program(changer, timeout, results);
+ if (status == 0) {
+ loaded = atoi(results);
+ } else {
+ loaded = -1; /* force unload */
+ }
+ Dmsg1(100, "loaded=%s\n", results);
+
+ /* If bad status or tape we want is not loaded, load it. */
+ if (status != 0 || loaded != slot) {
+ if (dev->capabilities & CAP_OFFLINEUNMOUNT) {
+ offline_dev(dev);
+ }
+ /* We are going to load a new tape, so close the device */
+ force_close_dev(dev);
+ if (loaded != 0) { /* must unload drive */
+ Dmsg0(100, "Doing changer unload.\n");
+ changer = edit_device_codes(jcr, changer,
+ jcr->device->changer_command, "unload");
+ status = run_program(changer, timeout, NULL);
+ Dmsg1(100, "unload status=%d\n", status);
+ }
+ /*
+ * Load the desired cassette
+ */
+ Dmsg1(100, "Doing changer load slot %d\n", slot);
+ changer = edit_device_codes(jcr, changer,
+ jcr->device->changer_command, "load");
+ status = run_program(changer, timeout, NULL);
+ Dmsg2(100, "load slot %d status=%d\n", slot, status);
+ }
+ free_pool_memory(changer);
+ free_pool_memory(results);
+ Dmsg1(100, "After changer, status=%d\n", status);
+ if (status == 0) { /* did we succeed? */
+ ask = 0; /* yes, got vol, no need to ask sysop */
+ autochanger = 1; /* tape loaded by changer */
+ }
+ }
+
+
+ if (ask && !dir_ask_sysop_to_mount_next_volume(jcr, dev)) {
+ return 0; /* error return */
+ }
+ Dmsg1(200, "want vol=%s\n", jcr->VolumeName);
+
+ /* Open device */
+ for ( ; !(dev->state & ST_OPENED); ) {
+ if (open_dev(dev, jcr->VolCatInfo.VolCatName, READ_WRITE) < 0) {
+ if (dev->dev_errno == EAGAIN || dev->dev_errno == EBUSY) {
+ sleep(30);
+ }
+ Jmsg2(jcr, M_FATAL, 0, _("Unable to open device %s. ERR=%s\n"),
+ dev_name(dev), strerror_dev(dev));
+ return 0;
+ }
+ }
+
+ /*
+ * Now make sure we have the right tape mounted
+ */
+read_volume:
+ switch (read_dev_volume_label(jcr, dev, block)) {
+ case VOL_OK:
+ Dmsg1(500, "Vol OK name=%s\n", jcr->VolumeName);
+ memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo));
+ if (strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0) {
+ recycle = 1;
+ }
+ break; /* got it */
+ case VOL_NAME_ERROR:
+ Dmsg1(500, "Vol NAME Error Name=%s\n", jcr->VolumeName);
+ /* Check if we can accept this as an anonymous volume */
+ strcpy(jcr->VolumeName, dev->VolHdr.VolName);
+ if (!dev->capabilities & CAP_ANONVOLS || !dir_get_volume_info(jcr)) {
+ goto mount_next_vol;
+ }
+ Dmsg1(200, "want new name=%s\n", jcr->VolumeName);
+ memcpy(&dev->VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo));
+ break;
+
+ case VOL_NO_LABEL:
+ case VOL_IO_ERROR:
+ Dmsg1(500, "Vol NO_LABEL or IO_ERROR name=%s\n", jcr->VolumeName);
+ /* If permitted, create a label */
+ if (dev->capabilities & CAP_LABEL) {
+ Dmsg0(190, "Create volume label\n");
+ if (!write_volume_label_to_dev(jcr, (DEVRES *)dev->device, jcr->VolumeName,
+ jcr->pool_name)) {
+ goto mount_next_vol;
+ }
+ Jmsg(jcr, M_INFO, 0, _("Created Volume label %s on device %s.\n"),
+ jcr->VolumeName, dev_name(dev));
+ goto read_volume; /* read label we just wrote */
+ }
+ /* NOTE! Fall-through wanted. */
+ default:
+ /* Send error message */
+ Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
+ if (autochanger) {
+ Jmsg(jcr, M_ERROR, 0, _("Autochanger Volume %s not found in slot %d.\n\
+ Setting slot to zero in catalog.\n"),
+ jcr->VolumeName, jcr->VolCatInfo.Slot);
+ jcr->VolCatInfo.Slot = 0; /* invalidate slot */
+ dir_update_volume_info(jcr, &jcr->VolCatInfo, 1); /* set slot */
+ }
+ goto mount_next_vol;
+ }
+ break;
+ }
+
+ /*
+ * See if we have a fresh tape or tape with data.
+ *
+ * Note, if the LabelType is PRE_LABEL, it was labeled
+ * but never written. If so, rewrite the label but set as
+ * VOL_LABEL. We rewind and return the label (reconstructed)
+ * in the block so that in the case of a new tape, data can
+ * be appended just after the block label. If we are writing
+ * an second volume, the calling routine will write the label
+ * before writing the overflow block.
+ *
+ * If the tape is marked as Recycle, we rewrite the label.
+ */
+ if (dev->VolHdr.LabelType == PRE_LABEL || recycle) {
+ Dmsg1(190, "ready_for_append found freshly labeled volume. dev=%x\n", dev);
+ dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */
+ write_volume_label_to_block(jcr, dev, block);
+ /*
+ * Write the block now to ensure we have write permission.
+ * It is better to find out now rather than later.
+ */
+ dev->VolCatInfo.VolCatBytes = 0;
+ if (!rewind_dev(dev)) {
+ Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s. ERR=%s\n"),
+ dev_name(dev), strerror_dev(dev));
+ }
+ if (recycle) {
+ if (!truncate_dev(dev)) {
+ Jmsg2(jcr, M_WARNING, 0, _("Truncate error on device %s. ERR=%s\n"),
+ dev_name(dev), strerror_dev(dev));
+ }
+ }
+ if (!write_block_to_dev(dev, block)) {
+ Jmsg2(jcr, M_ERROR, 0, _("Unable to write device %s. ERR=%s\n"),
+ dev_name(dev), strerror_dev(dev));
+ goto mount_next_vol;
+ }
+ if (!rewind_dev(dev)) {
+ Jmsg2(jcr, M_ERROR, 0, _("Unable to rewind device %s. ERR=%s\n"),
+ dev_name(dev), strerror_dev(dev));
+ goto mount_next_vol;
+ }
+ /* Recreate a correct volume label and return it in the block */
+ write_volume_label_to_block(jcr, dev, block);
+ dev->VolCatInfo.VolCatJobs = 1;
+ dev->VolCatInfo.VolCatFiles = 1;
+ dev->VolCatInfo.VolCatErrors = 0;
+ dev->VolCatInfo.VolCatBlocks = 1;
+ if (recycle) {
+ dev->VolCatInfo.VolCatMounts++;
+ dev->VolCatInfo.VolCatRecycles++;
+ } else {
+ dev->VolCatInfo.VolCatMounts = 1;
+ dev->VolCatInfo.VolCatRecycles = 0;
+ dev->VolCatInfo.VolCatWrites = 1;
+ dev->VolCatInfo.VolCatReads = 1;
+ }
+ strcpy(dev->VolCatInfo.VolCatStatus, "Append");
+ Dmsg0(100, "dir_update_vol_info. Set Append\n");
+ dir_update_volume_info(jcr, &dev->VolCatInfo, 1); /* indicate doing relabel */
+ if (recycle) {
+ Jmsg(jcr, M_INFO, 0, _("Recycled volume %s on device %s, all previous data lost.\n"),
+ jcr->VolumeName, dev_name(dev));
+ } else {
+ Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume %s on device %s\n"),
+ jcr->VolumeName, dev_name(dev));
+ }
+
+ } else {
+ /*
+ * OK, at this point, we have a valid Bacula label, but
+ * we need to position to the end of the volume, since we are
+ * just now putting it into append mode.
+ */
+ Dmsg0(200, "Device previously written, moving to end of data\n");
+ Jmsg(jcr, M_INFO, 0, _("Volume %s previously written, moving to end of data.\n"),
+ jcr->VolumeName);
+ if (!eod_dev(dev)) {
+ Jmsg(jcr, M_ERROR, 0, _("Unable to position to end of data %s. ERR=%s\n"),
+ dev_name(dev), strerror_dev(dev));
+ Jmsg(jcr, M_INFO, 0, _("Marking Volume %s in Error in Catalog.\n"),
+ jcr->VolumeName);
+ strcpy(dev->VolCatInfo.VolCatStatus, "Error");
+ Dmsg0(100, "dir_update_vol_info. Set Error.\n");
+ dir_update_volume_info(jcr, &dev->VolCatInfo, 0);
+ goto mount_next_vol;
+ }
+ /* *****FIXME**** we should do some checking for files too */
+ if (dev_is_tape(dev)) {
+ Jmsg(jcr, M_INFO, 0, _("Ready to append to end of Volume at file=%d.\n"), dev_file(dev));
+ /*
+ * Check if we are positioned on the tape at the same place
+ * that the database says we should be.
+ */
+ if (dev->VolCatInfo.VolCatFiles != dev_file(dev) + 1) {
+ /* ****FIXME**** this should refuse to write on tape */
+ Jmsg(jcr, M_ERROR, 0, _("Hey! Num files mismatch! Volume=%d Catalog=%d\n"),
+ dev_file(dev)+1, dev->VolCatInfo.VolCatFiles);
+ }
+ }
+ /* Update Volume Info -- will be written at end of Job */
+ dev->VolCatInfo.VolCatMounts++; /* Update mounts */
+ dev->VolCatInfo.VolCatJobs++;
+ /* Return an empty block */
+ empty_block(block); /* we used it for reading so set for write */
+ }
+ dev->state |= ST_APPEND;
+ Dmsg0(100, "Normal return from read_dev_for_append\n");
+ return 1;
+}
+
+int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
+{
+ Dmsg2(90, "NumVolumes=%d CurVolume=%d\n", jcr->NumVolumes, jcr->CurVolume);
+ /*
+ * End Of Tape -- mount next Volume (if another specified)
+ */
+ if (jcr->NumVolumes > 1 && jcr->CurVolume < jcr->NumVolumes) {
+ VOL_LIST *vol = jcr->VolList;
+ /* Find next Volume */
+ jcr->CurVolume++;
+ for (int i=1; i<jcr->CurVolume; i++) {
+ vol = vol->next;
+ }
+ strcpy(jcr->VolumeName, vol->VolumeName);
+ Dmsg1(400, "There is another volume %s.\n", jcr->VolumeName);
+
+ close_dev(dev);
+ dev->state &= ~ST_READ;
+ if (!acquire_device_for_read(jcr, dev, block)) {
+ Emsg2(M_FATAL, 0, "Cannot open Dev=%s, Vol=%s\n", dev_name(dev),
+ jcr->VolumeName);
+ return 0;
+ }
+ return 1; /* next volume mounted */
+ }
+ Dmsg0(90, "End of Device reached.\n");
+ return 0;
+}
+
+
+/*
+ * Edit codes into ChangerCommand
+ * %% = %
+ * %a = archive device name
+ * %c = changer device name
+ * %f = Client's name
+ * %j = Job name
+ * %o = command
+ * %s = Slot base 0
+ * %S = Slot base 1
+ * %v = Volume name
+ *
+ *
+ * omsg = edited output message
+ * imsg = input string containing edit codes (%x)
+ * cmd = command string (load, unload, ...)
+ *
+ */
+static char *edit_device_codes(JCR *jcr, char *omsg, char *imsg, char *cmd)
+{
+ char *p;
+ const char *str;
+ char add[20];
+
+ *omsg = 0;
+ Dmsg1(200, "edit_device_codes: %s\n", imsg);
+ for (p=imsg; *p; p++) {
+ if (*p == '%') {
+ switch (*++p) {
+ case '%':
+ str = "%";
+ break;
+ case 'a':
+ str = jcr->device->dev->dev_name;
+ break;
+ case 'c':
+ str = NPRT(jcr->device->changer_name);
+ break;
+ case 'o':
+ str = NPRT(cmd);
+ break;
+ case 's':
+ sprintf(add, "%d", jcr->VolCatInfo.Slot - 1);
+ str = add;
+ break;
+ case 'S':
+ sprintf(add, "%d", jcr->VolCatInfo.Slot);
+ str = add;
+ break;
+ case 'j': /* Job name */
+ str = jcr->Job;
+ break;
+ case 'v':
+ str = NPRT(jcr->VolumeName);
+ break;
+ case 'f':
+ str = NPRT(jcr->client_name);
+ break;
+
+ default:
+ add[0] = '%';
+ add[1] = *p;
+ add[2] = 0;
+ str = add;
+ break;
+ }
+ } else {
+ add[0] = *p;
+ add[1] = 0;
+ str = add;
+ }
+ Dmsg1(200, "add_str %s\n", str);
+ pm_strcat(&omsg, (char *)str);
+ Dmsg1(200, "omsg=%s\n", omsg);
+ }
+ return omsg;
+}
/* From stored.c */
uint32_t new_VolSessionId();
+/* From acquire.c */
+int acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int ready_dev_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int release_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+
/* From askdir.c */
int dir_get_volume_info(JCR *jcr);
int dir_find_next_appendable_volume(JCR *jcr);
/* From device.c */
int open_device(DEVICE *dev);
-int acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int ready_dev_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int release_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
void block_device(DEVICE *dev, int state);
void unblock_device(DEVICE *dev);
void lock_device(DEVICE *dev);
int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
SESSION_LABEL *sesrec);
+/* From mount.c */
+int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release);
+int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+
+
/* From parse_bsr.c */
extern BSR *parse_bsr(JCR *jcr, char *lf);
extern void dump_bsr(BSR *bsr);
char *FI_to_ascii(int fi);
char *stream_to_ascii(int stream);
int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec);
-int new_read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec);
DEV_RECORD *new_record();
void free_record(DEV_RECORD *rec);
-int read_record(DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record);
BSOCK *fd_sock = jcr->file_bsock;
int ok = TRUE;
DEVICE *dev;
- DEV_RECORD rec;
+ DEV_RECORD *rec;
DEV_BLOCK *block;
POOLMEM *hdr;
SESSION_LABEL sessrec; /* session record */
block = new_block(dev);
+
create_vol_list(jcr);
Dmsg1(20, "Found %d volumes names to restore.\n", jcr->NumVolumes);
return 0;
}
- memset(&rec, 0, sizeof(rec));
- rec.data = ds->msg; /* use socket message buffer */
+ rec = new_record();
+ rec->data = ds->msg; /* use socket message buffer */
hdr = get_pool_memory(PM_MESSAGE);
/*
* matched.
*/
for ( ;ok; ) {
- DEV_RECORD *record; /* for reading label of multi-volumes */
-
if (job_cancelled(jcr)) {
ok = FALSE;
break;
}
/* Read Record */
- Dmsg1(500, "Main read_record. rem=%d\n", rec.remainder);
- if (!read_record(dev, block, &rec)) {
- Dmsg1(500, "Main read record failed. rem=%d\n", rec.remainder);
+ Dmsg1(500, "Main read_record. rem=%d\n", rec->remainder);
+ if (!read_block_from_device(dev, block)) {
+ Dmsg1(500, "Main read record failed. rem=%d\n", rec->remainder);
if (dev->state & ST_EOT) {
- if (rec.remainder) {
+ if (rec->remainder) {
Dmsg0(500, "Not end of record.\n");
}
- Dmsg2(90, "NumVolumes=%d CurVolume=%d\n", jcr->NumVolumes, jcr->CurVolume);
- /*
- * End Of Tape -- mount next Volume (if another specified)
- */
- if (jcr->NumVolumes > 1 && jcr->CurVolume < jcr->NumVolumes) {
- VOL_LIST *vol = jcr->VolList;
- close_dev(dev);
- jcr->CurVolume++;
- for (int i=1; i<jcr->CurVolume; i++) {
- vol = vol->next;
- }
- strcpy(jcr->VolumeName, vol->VolumeName);
- Dmsg1(100, "There is another volume %s.\n", jcr->VolumeName);
- dev->state &= ~ST_READ;
- if (!acquire_device_for_read(jcr, dev, block)) {
- Jmsg(jcr, M_FATAL, 0, _("Cannot open Dev=%s, Vol=%s\n"), dev_name(dev), jcr->VolumeName);
- ok = FALSE;
- break;
- }
- record = new_record();
- Dmsg1(500, "read record after new tape. rem=%d\n", record->remainder);
- read_record(dev, block, record); /* read vol label */
- dump_label_record(dev, record, 0);
- free_record(record);
- continue;
+ if (!mount_next_read_volume(jcr, dev, block)) {
+ break;
}
- Dmsg0(90, "End of Device reached.\n");
- break; /* End of Tape */
+ continue;
}
if (dev->state & ST_EOF) {
Dmsg0(90, "Got End of File. Trying again ...\n");
continue; /* End of File */
}
-
- Jmsg2(jcr, M_FATAL, 0, _("Read error on Record Header %s ERR=%s\n"), dev_name(dev), strerror(errno));
- ok = FALSE;
- break;
+ if (dev->state & ST_SHORT) {
+ continue;
+ }
}
- /* Some sort of label? */
- if (rec.FileIndex < 0) {
- char *rtype;
- memset(&sessrec, 0, sizeof(sessrec));
- switch (rec.FileIndex) {
- case PRE_LABEL:
- rtype = "Fresh Volume Label";
- break;
- case VOL_LABEL:
- rtype = "Volume Label";
- unser_volume_label(dev, &rec);
- break;
- case SOS_LABEL:
- rtype = "Begin Session";
- unser_session_label(&sessrec, &rec);
- break;
- case EOS_LABEL:
- rtype = "End Session";
- break;
- case EOM_LABEL:
- rtype = "End of Media";
- break;
- default:
- rtype = "Unknown";
- break;
- }
- if (debug_level > 0) {
- printf("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
- rtype, rec.VolSessionId, rec.VolSessionTime, rec.Stream, rec.data_len);
- }
+ for (rec->state=0; !is_block_empty(rec); ) {
- Dmsg1(40, "Got label = %d\n", rec.FileIndex);
- if (rec.FileIndex == EOM_LABEL) { /* end of tape? */
- Dmsg0(40, "Get EOM LABEL\n");
- break; /* yes, get out */
- }
- continue; /* ignore other labels */
- } /* end if label record */
+ /* Some sort of label? */
+ if (rec->FileIndex < 0) {
+ char *rtype;
+ memset(&sessrec, 0, sizeof(sessrec));
+ switch (rec->FileIndex) {
+ case PRE_LABEL:
+ rtype = "Fresh Volume Label";
+ break;
+ case VOL_LABEL:
+ rtype = "Volume Label";
+ unser_volume_label(dev, rec);
+ break;
+ case SOS_LABEL:
+ rtype = "Begin Session";
+ unser_session_label(&sessrec, rec);
+ break;
+ case EOS_LABEL:
+ rtype = "End Session";
+ break;
+ case EOM_LABEL:
+ rtype = "End of Media";
+ break;
+ default:
+ rtype = "Unknown";
+ break;
+ }
+ Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
+ rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
- if (jcr->bsr) {
- /* Match BSR against current record */
- if (!match_bsr(jcr->bsr, &rec, &dev->VolHdr, &sessrec)) {
- Dmsg0(50, "BSR rejected record\n");
- continue;
+ Dmsg1(40, "Got label = %d\n", rec->FileIndex);
+ if (rec->FileIndex == EOM_LABEL) { /* end of tape? */
+ Dmsg0(40, "Get EOM LABEL\n");
+ break; /* yes, get out */
+ }
+ continue; /* ignore other labels */
+ } /* end if label record */
+
+ if (jcr->bsr) {
+ /* Match BSR against current record */
+ if (!match_bsr(jcr->bsr, rec, &dev->VolHdr, &sessrec)) {
+ Dmsg0(50, "BSR rejected record\n");
+ rec->remainder = 0;
+ continue;
+ }
+ } else {
+ /* Old way, deprecated */
+ if (rec->VolSessionId != jcr->read_VolSessionId ||
+ rec->VolSessionTime != jcr->read_VolSessionTime) {
+ Dmsg0(50, "Ignore record ids not equal\n");
+ rec->remainder = 0;
+ continue; /* ignore */
+ }
}
- } else {
- /* Old way, deprecated */
- if (rec.VolSessionId != jcr->read_VolSessionId ||
- rec.VolSessionTime != jcr->read_VolSessionTime) {
- Dmsg0(50, "Ignore record ids not equal\n");
- continue; /* ignore */
+ if (is_partial_record(rec)) {
+ break;
+ }
+
+ /* Generate Header parameters and send to File daemon
+ * Note, we build header in hdr buffer to avoid wiping
+ * out the data record
+ */
+ ds->msg = hdr;
+ if (!bnet_fsend(ds, rec_header, rec->VolSessionId, rec->VolSessionTime,
+ rec->FileIndex, rec->Stream, rec->data_len)) {
+ Dmsg1(30, ">filed: Error Hdr=%s\n", ds->msg);
+ ds->msg = rec->data;
+ ok = FALSE;
+ break;
+ } else {
+ Dmsg1(30, ">filed: Hdr=%s\n", ds->msg);
}
- }
-
- /* Generate Header parameters and send to File daemon
- * Note, we build header in hdr buffer to avoid wiping
- * out the data record
- */
- ds->msg = hdr;
- if (!bnet_fsend(ds, rec_header, rec.VolSessionId, rec.VolSessionTime,
- rec.FileIndex, rec.Stream, rec.data_len)) {
- Dmsg1(30, ">filed: Error Hdr=%s\n", ds->msg);
- ds->msg = rec.data;
- ok = FALSE;
- break;
- } else {
- Dmsg1(30, ">filed: Hdr=%s\n", ds->msg);
- }
- ds->msg = rec.data; /* restore data record address */
+ ds->msg = rec->data; /* restore data record address */
- /* Send data record to File daemon */
- ds->msglen = rec.data_len;
- Dmsg1(40, ">filed: send %d bytes data.\n", ds->msglen);
- if (!bnet_send(ds)) {
- Dmsg0(0, "Error sending to FD\n");
- Jmsg1(jcr, M_FATAL, 0, _("Error sending to File daemon. ERR=%s\n"),
- bnet_strerror(ds));
- ok = FALSE;
+ /* Send data record to File daemon */
+ ds->msglen = rec->data_len;
+ Dmsg1(40, ">filed: send %d bytes data.\n", ds->msglen);
+ if (!bnet_send(ds)) {
+ Dmsg0(0, "Error sending to FD\n");
+ Jmsg1(jcr, M_FATAL, 0, _("Error sending to File daemon. ERR=%s\n"),
+ bnet_strerror(ds));
+ ok = FALSE;
+ }
}
}
/* Send end of data to FD */
}
free_pool_memory(hdr);
free_block(block);
+ free_record(rec);
free_vol_list(jcr);
Dmsg0(30, "Done reading.\n");
return ok ? 1 : 0;
Dmsg0(150, "Leave free_record.\n");
}
-/*
- * Read a record from a block
- * if necessary, read the block from the device without locking
- * if necessary, handle getting a new Volume
- *
- * Returns: 0 on failure
- * 1 on success
- */
-int read_record(DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
-{
- Dmsg2(90, "read_record() dev=%x state=%x\n", dev, dev->state);
-
- while (!read_record_from_block(block, record)) {
- Dmsg2(90, "!read_record_from_block data_len=%d rem=%d\n", record->data_len,
- record->remainder);
- if (!read_block_from_dev(dev, block)) {
- Dmsg0(200, "===== Got read block I/O error ======\n");
- return 0;
- }
- }
- Dmsg4(90, "read_record FI=%s SessId=%d Strm=%s len=%d\n",
- FI_to_ascii(record->FileIndex), record->VolSessionId,
- stream_to_ascii(record->Stream), record->data_len);
- record->File = dev->file;
- record->Block = dev->block_num;
- return 1;
-}
-
/*
* Write a Record to the block
/*
- * Read a Record from the block
- * Returns: 0 on failure
- * 1 on success
+ * Test if we can write whole record to the block
+ *
+ * Returns: 0 on failure
+ * 1 on success (all bytes can be written)
*/
-int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
+int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
{
- ser_declare;
uint32_t remlen;
- uint32_t VolSessionId;
- uint32_t VolSessionTime;
- int32_t FileIndex;
- int32_t Stream;
- uint32_t data_bytes;
-
- remlen = block->binbuf;
-
- /* Clear state flags */
- rec->state = 0;
-
- /*
- * Get the header. There is always a full header,
- * otherwise we find it in the next block.
- */
- if (remlen >= RECHDR_LENGTH) {
- Dmsg3(90, "read_record_block: remlen=%d data_len=%d rem=%d\n",
- remlen, rec->data_len, rec->remainder);
- unser_begin(block->bufp, RECHDR_LENGTH);
- unser_uint32(VolSessionId);
- unser_uint32(VolSessionTime);
- unser_int32(FileIndex);
- unser_int32(Stream);
- unser_uint32(data_bytes);
-
- ASSERT(unser_length(block->bufp) == RECHDR_LENGTH);
- block->bufp += RECHDR_LENGTH;
- block->binbuf -= RECHDR_LENGTH;
- remlen -= RECHDR_LENGTH;
-
- /*
- * if Stream is negative, it means that this is a continuation
- * of a previous partially written record.
- */
- if (Stream < 0) { /* continuation record? */
- Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
- rec->remainder);
- rec->state |= REC_CONTINUATION;
- if (!rec->remainder) { /* if we didn't read previously */
- rec->data_len = 0; /* return data as if no continuation */
- } else if (rec->VolSessionId != VolSessionId ||
- rec->VolSessionTime != VolSessionTime ||
- rec->Stream != -Stream) {
- rec->state |= REC_NO_MATCH;
- return 0; /* This is from some other Session */
- }
- rec->Stream = -Stream; /* set correct Stream */
- } else { /* Regular record */
- rec->Stream = Stream;
- rec->data_len = 0; /* transfer to beginning of data */
+ remlen = block->buf_len - block->binbuf;
+ if (rec->remainder == 0) {
+ if (remlen >= RECHDR_LENGTH) {
+ remlen -= RECHDR_LENGTH;
+ rec->remainder = rec->data_len;
+ } else {
+ return 0;
}
- rec->VolSessionId = VolSessionId;
- rec->VolSessionTime = VolSessionTime;
- rec->FileIndex = FileIndex;
-
- Dmsg6(90, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%d\n\
-remlen=%d data_len=%d\n",
- FI_to_ascii(rec->FileIndex), rec->VolSessionId,
- stream_to_ascii(rec->Stream), data_bytes, remlen, rec->data_len);
} else {
- /*
- * No more records in this block because the number
- * of remaining bytes are less than a record header
- * length, so return empty handed, but indicate that
- * he must read again. By returning, we allow the
- * higher level routine to fetch the next block and
- * then reread.
- */
- Dmsg0(90, "read_record_block: nothing\n");
- if (!rec->remainder) {
- rec->remainder = 1; /* set to expect continuation */
- rec->data_len = 0; /* no data transferred */
- }
- rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
return 0;
}
-
- ASSERT(data_bytes < MAX_BLOCK_LENGTH); /* temp sanity check */
-
- rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
-
- /*
- * At this point, we have read the header, now we
- * must transfer as much of the data record as
- * possible taking into account: 1. A partial
- * data record may have previously been transferred,
- * 2. The current block may not contain the whole data
- * record.
- */
- if (remlen >= data_bytes) {
- /* Got whole record */
- memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
- block->bufp += data_bytes;
- block->binbuf -= data_bytes;
- rec->data_len += data_bytes;
- } else {
- /* Partial record */
- memcpy(rec->data+rec->data_len, block->bufp, remlen);
- block->bufp += remlen;
- block->binbuf -= remlen;
- rec->data_len += remlen;
- rec->remainder = 1; /* partial record transferred */
- Dmsg1(90, "read_record_block: partial xfered=%d\n", rec->data_len);
- rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
- /********FIXME********* this should return 1 */
+ if (rec->remainder > 0 && remlen < rec->remainder) {
return 0;
}
- rec->remainder = 0;
- Dmsg4(90, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
- FI_to_ascii(rec->FileIndex), rec->VolSessionId,
- stream_to_ascii(rec->Stream), rec->data_len);
- return 1; /* transferred full record */
+ return 1;
}
+
/*
* Read a Record from the block
* Returns: 0 if nothing read or if the continuation record does not match.
* routine may have to be called again with a new
* block if the entire record was not read.
*/
-int new_read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
+int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
{
ser_declare;
uint32_t remlen;
}
/* Open database */
- db = db_init_database(db_name, user, password);
+ db = db_init_database(NULL, db_name, user, password);
if (!db_open_database(db)) {
Emsg1(M_FATAL, 0, "%s", db_strerror(db));
}