+2003-10-15 Version 1.32b 20Oct03 Beta
+22Oct03
+- Print block read error (checksum, I/O, BB01, ...) once then
+ the number found at the end of the reading.
+- Implement RunAfterFailedJob
+- Change db_find_job_start_time() to require a Full save before
+ running an Incremental or Differential job.
+- Remove has_volume_expired from code that updates vol info
+21Oct03
+- Implement "delete job"
+20Oct03
+- More documentation, add Marc Brueckner's tips to manual.
+- Tweak gnome2-console scroll window.
+- Turn off some debug info.
+18Oct03
+- Modify Verify to accept VerifyJob = xx, where the last backup job
+ of job xxx will be verified.
+- Add changing the Pool name for a Volume to "update volume"
+- Write most of the code for Verify Disk to Catalog.
+- Recreate the src/gnome2-console directory.
+- Change all the text handling code to the Gnome 2.0 way.
+- Correct the way verify filenames are returned to the Director so
+ that directories are in canonical form (i.e. trailing /).
+- Handle casting bug in glade-2 by sed'ing support.c in gnome2-console.
+
2003-10-15 Version 1.32b 14Oct03 Release
14Oct03
- Modify configure so that if threaded MySQL client library
- Release Notes for Bacula 1.32b
+ Release Notes for Bacula 1.32c
- Bacula code: Total files = 259 Total lines = 78,067 (*.h *.c *.in)
+ Bacula code: Total files = 259 Total lines = 78,317 (*.h *.c *.in)
+
+Most Significant Changes since 1.32b
+- Implemented a RunAfterFailedJob
+- Implemented "delete job"
+- Gnome 2.0 console compiles and works (but still has
+ certain scrolling problems).
+- Implemented VerifyJob record that tells Verify which
+ job to verify (JobId not required).
+- First cut Verify Disk to Catalog
Most Significant Changes since 1.32a:
- Improve forward space file/block during restore, many
- Document ln -sf /usr/lib/libncurses.so /usr/lib/libtermcap.so
and install the esound-devĀ package for compiling Console on
SuSE.
+- Document delete job command.
+- Document update volume pool and other command line keywords.
+- Document verify_disk_to_catalog
-For 1.33
-- Limit the number of block checksum/header BB01, ... errors printed.
+For 1.32c
+- If last Full back is purged and an Incremental or Differential remains,
+ Bacula does not promote the Incremental to a Full.
+- Finish implementation of Verify=DiskToCatalog
- Add VerifyJob to "run" summary (yes/mod/no) prompt.
+
+For 1.33
- Add device name to "Current Volume not acceptable because ..."
- Make sure that Bacula rechecks the tape after the 20 min wait.
- Set IO_NOWAIT on Bacula TCP/IP packets.
- Try doing a raw partition backup and restore by mounting a
Windows partition.
-- Implement Verify=DiskToCatalog
-- Implement a RunAfterFailedJob
- Report CVS problems to SourceForge.
- Implement .consolerc for Console
- Is it really important to make Job name the same to find the
- I want to restore by file to some date.
---- 1.32b released
- Figure out a way to move Volumes from one pool to another.
+- Implement a RunAfterFailedJob
+- Limit the number of block checksum/header BB01, ... errors printed.
+
*/
static int do_media_purge(B_DB *mdb, MEDIA_DBR *mr)
{
- char *query = (char *)get_pool_memory(PM_MESSAGE);
+ POOLMEM *query = get_pool_memory(PM_MESSAGE);
struct s_del_ctx del;
int i;
for (i=0; i < del.num_ids; i++) {
Dmsg1(400, "Delete JobId=%d\n", del.JobId[i]);
- Mmsg(&query, "DELETE FROM Job WHERE JobId=%d", del.JobId[i]);
+ Mmsg(&query, "DELETE FROM Job WHERE JobId=%u", del.JobId[i]);
db_sql_query(mdb, query, NULL, (void *)NULL);
- Mmsg(&query, "DELETE FROM File WHERE JobId=%d", del.JobId[i]);
+ Mmsg(&query, "DELETE FROM File WHERE JobId=%u", del.JobId[i]);
db_sql_query(mdb, query, NULL, (void *)NULL);
- Mmsg(&query, "DELETE FROM JobMedia WHERE JobId=%d", del.JobId[i]);
+ Mmsg(&query, "DELETE FROM JobMedia WHERE JobId=%u", del.JobId[i]);
db_sql_query(mdb, query, NULL, (void *)NULL);
}
free(del.JobId);
extern int QueryDB(char *file, int line, JCR *jcr, B_DB *db, char *select_cmd);
/*
- * Find job start time. Used to find last full save
- * for Incremental and Differential saves.
+ * Find job start time if JobId specified, otherwise
+ * find last full save for Incremental and Differential saves.
+ *
+ * StartTime is returned in stime
*
* Returns: 0 on failure
* 1 on success, jr is unchanged, but stime is set
db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime)
{
SQL_ROW row;
- uint32_t JobId;
db_lock(mdb);
/* If no Id given, we must find corresponding job */
if (jr->JobId == 0) {
/* Differential is since last Full backup */
- if (jr->Level == L_DIFFERENTIAL) {
- Mmsg(&mdb->cmd,
-"SELECT JobId FROM Job WHERE JobStatus='T' AND Type='%c' AND "
+ Mmsg(&mdb->cmd,
+"SELECT StartTime FROM Job WHERE JobStatus='T' AND Type='%c' AND "
"Level='%c' AND Name='%s' AND ClientId=%u AND FileSetId=%u "
"ORDER BY StartTime DESC LIMIT 1",
- jr->Type, L_FULL, jr->Name, jr->ClientId, jr->FileSetId);
+ jr->Type, L_FULL, jr->Name, jr->ClientId, jr->FileSetId);
+
+ if (jr->Level == L_DIFFERENTIAL) {
/* Incremental is since last Full, Incremental, or Differential */
+ /* SQL cmd already edited above */
+
} else if (jr->Level == L_INCREMENTAL) {
+ /*
+ * For an Incremental job, we must first ensure
+ * that a Full backup wase done (cmd edited above)
+ * then we do a second look to find the most recent
+ * backup
+ */
+ if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+ Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
+ sql_strerror(mdb), mdb->cmd);
+ db_unlock(mdb);
+ return 0;
+ }
+ if ((row = sql_fetch_row(mdb)) == NULL) {
+ sql_free_result(mdb);
+ Mmsg(&mdb->errmsg, _("No prior Full backup Job record found.\n"));
+ db_unlock(mdb);
+ return 0;
+ }
+ sql_free_result(mdb);
+ /* Now edit SQL command for Incremental Job */
Mmsg(&mdb->cmd,
-"SELECT JobId FROM Job WHERE JobStatus='T' AND Type='%c' AND "
+"SELECT StartTime FROM Job WHERE JobStatus='T' AND Type='%c' AND "
"Level IN ('%c','%c','%c') AND Name='%s' AND ClientId=%u "
"ORDER BY StartTime DESC LIMIT 1",
jr->Type, L_INCREMENTAL, L_DIFFERENTIAL, L_FULL, jr->Name,
db_unlock(mdb);
return 0;
}
- Dmsg1(100, "Submitting: %s\n", mdb->cmd);
- if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
- Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
- sql_strerror(mdb), mdb->cmd);
- db_unlock(mdb);
- return 0;
- }
- if ((row = sql_fetch_row(mdb)) == NULL) {
- sql_free_result(mdb);
- Mmsg(&mdb->errmsg, _("No prior Job record found.\n"));
- db_unlock(mdb);
- return 0;
- }
- JobId = atoi(row[0]);
- sql_free_result(mdb);
} else {
- JobId = jr->JobId; /* search for particular id */
+ Dmsg1(100, "Submitting: %s\n", mdb->cmd);
+ Mmsg(&mdb->cmd, "SELECT StartTime FROM Job WHERE Job.JobId=%u", jr->JobId);
}
- Dmsg1(100, "Submitting: %s\n", mdb->cmd);
- Mmsg(&mdb->cmd, "SELECT StartTime FROM Job WHERE Job.JobId=%u", JobId);
if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
pm_strcpy(stime, ""); /* set EOS */
}
if ((row = sql_fetch_row(mdb)) == NULL) {
- Mmsg2(&mdb->errmsg, _("No Job found for JobId=%u: ERR=%s\n"), JobId, sql_strerror(mdb));
+ Mmsg1(&mdb->errmsg, _("No Job record found: ERR=%s\n"), sql_strerror(mdb));
sql_free_result(mdb);
db_unlock(mdb);
return 0;
/*
* Now try recycling if necessary
+ * reason set non-NULL if we cannot use it
*/
- is_volume_valid_or_recyclable(jcr, &mr, &reason);
+ check_if_volume_valid_or_recyclable(jcr, &mr, &reason);
}
}
if (reason == NULL) {
bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus));
mr.Slot = sdmr.Slot;
- /*
- * Apply expiration periods and limits, if not a label request,
- * and ignore status because if !label we won't use it.
- */
- if (!label) {
- has_volume_expired(jcr, &mr);
- }
-
Dmsg2(200, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
/*
* Write the modified record to the DB
{"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},
+ {"runafterfailedjob", store_str, ITEM(res_job.RunAfterFailedJob), 0, 0, 0},
{"clientrunbeforejob", store_str, ITEM(res_job.ClientRunBeforeJob), 0, 0, 0},
{"clientrunafterjob", store_str, ITEM(res_job.ClientRunAfterJob), 0, 0, 0},
{"spoolattributes", store_yesno, ITEM(res_job.SpoolAttributes), 1, ITEM_DEFAULT, 0},
if (res->res_job.RunAfterJob) {
sendit(sock, " --> RunAfter=%s\n", NPRT(res->res_job.RunAfterJob));
}
+ if (res->res_job.RunAfterFailedJob) {
+ sendit(sock, " --> RunAfterFailed=%s\n", NPRT(res->res_job.RunAfterFailedJob));
+ }
if (res->res_job.WriteBootstrap) {
sendit(sock, " --> WriteBootstrap=%s\n", NPRT(res->res_job.WriteBootstrap));
}
if (res->res_job.RunAfterJob) {
free(res->res_job.RunAfterJob);
}
+ if (res->res_job.RunAfterFailedJob) {
+ free(res->res_job.RunAfterFailedJob);
+ }
if (res->res_job.ClientRunBeforeJob) {
free(res->res_job.ClientRunBeforeJob);
}
/*
* 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_CONSOLE 1012
-
-#define R_LAST R_CONSOLE
+#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_CONSOLE 1012
+
+#define R_LAST R_CONSOLE
/*
* 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 */
struct RUN;
/*
- * Director Resource
+ * Director Resource
*
*/
struct DIRRES {
- RES hdr;
- int DIRport; /* where we listen -- UA port server port */
- char *DIRaddr; /* bind address */
- char *password; /* Password for UA access */
- int enable_ssl; /* Use SSL for UA */
- char *query_file; /* SQL query file */
- char *working_directory; /* WorkingDirectory */
- char *pid_directory; /* PidDirectory */
- char *subsys_directory; /* SubsysDirectory */
- int require_ssl; /* Require SSL for all connections */
- MSGS *messages; /* Daemon message handler */
- uint32_t MaxConcurrentJobs; /* Max concurrent jobs for whole director */
- utime_t FDConnectTimeout; /* timeout for connect in seconds */
- utime_t SDConnectTimeout; /* timeout in seconds */
+ RES hdr;
+ int DIRport; /* where we listen -- UA port server port */
+ char *DIRaddr; /* bind address */
+ char *password; /* Password for UA access */
+ int enable_ssl; /* Use SSL for UA */
+ char *query_file; /* SQL query file */
+ char *working_directory; /* WorkingDirectory */
+ char *pid_directory; /* PidDirectory */
+ char *subsys_directory; /* SubsysDirectory */
+ int require_ssl; /* Require SSL for all connections */
+ MSGS *messages; /* Daemon message handler */
+ uint32_t MaxConcurrentJobs; /* Max concurrent jobs for whole director */
+ utime_t FDConnectTimeout; /* timeout for connect in seconds */
+ utime_t SDConnectTimeout; /* timeout in seconds */
};
/*
* Console Resource
*/
struct CONRES {
- RES hdr;
- char *password; /* UA server password */
- int enable_ssl; /* Use SSL */
+ RES hdr;
+ char *password; /* UA server password */
+ int enable_ssl; /* Use SSL */
};
*
*/
struct CAT {
- RES hdr;
+ RES hdr;
- int db_port; /* Port -- not yet implemented */
- char *db_address; /* host name for remote access */
- char *db_socket; /* Socket for local access */
+ int db_port; /* Port -- not yet implemented */
+ char *db_address; /* host name for remote access */
+ char *db_socket; /* Socket for local access */
char *db_password;
char *db_user;
char *db_name;
*
*/
struct CLIENT {
- RES hdr;
+ RES hdr;
- int FDport; /* Where File daemon listens */
- int AutoPrune; /* Do automatic pruning? */
- utime_t FileRetention; /* file retention period in seconds */
- utime_t JobRetention; /* job retention period in seconds */
+ int FDport; /* Where File daemon listens */
+ int AutoPrune; /* Do automatic pruning? */
+ utime_t FileRetention; /* file retention period in seconds */
+ utime_t JobRetention; /* job retention period in seconds */
char *address;
char *password;
- CAT *catalog; /* Catalog resource */
- uint32_t MaxConcurrentJobs; /* Maximume concurrent jobs */
- uint32_t NumConcurrentJobs; /* number of concurrent jobs running */
- int enable_ssl; /* Use SSL */
+ CAT *catalog; /* Catalog resource */
+ uint32_t MaxConcurrentJobs; /* Maximume concurrent jobs */
+ uint32_t NumConcurrentJobs; /* number of concurrent jobs running */
+ int enable_ssl; /* Use SSL */
};
/*
*
*/
struct 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;
char *dev_name;
- int autochanger; /* set if autochanger */
- uint32_t MaxConcurrentJobs; /* Maximume concurrent jobs */
- uint32_t NumConcurrentJobs; /* number of concurrent jobs running */
- int enable_ssl; /* Use SSL */
+ int autochanger; /* set if autochanger */
+ uint32_t MaxConcurrentJobs; /* Maximume concurrent jobs */
+ uint32_t NumConcurrentJobs; /* number of concurrent jobs running */
+ int enable_ssl; /* Use SSL */
};
*
*/
struct JOB {
- RES hdr;
-
- int JobType; /* job type (backup, verify, restore */
- int level; /* default backup/verify level */
- int Priority; /* Job priority */
- 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 */
- char *ClientRunBeforeJob; /* Run client program before Job */
- char *ClientRunAfterJob; /* Run client program after Job */
- char *WriteBootstrap; /* Where to write bootstrap Job updates */
- int replace; /* How (overwrite, ..) */
- utime_t MaxRunTime; /* max run time in seconds */
- utime_t MaxStartDelay; /* max start delay in seconds */
- int PrefixLinks; /* prefix soft links with Where path */
- 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 */
- uint32_t MaxConcurrentJobs; /* Maximume concurrent jobs */
- int RescheduleOnError; /* Set to reschedule on error */
- int RescheduleTimes; /* Number of times to reschedule job */
- utime_t RescheduleInterval; /* Reschedule interval */
+ RES hdr;
+
+ int JobType; /* job type (backup, verify, restore */
+ int level; /* default backup/verify level */
+ int Priority; /* Job priority */
+ 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 */
+ char *RunAfterFailedJob; /* Run program after Job that errs */
+ char *ClientRunBeforeJob; /* Run client program before Job */
+ char *ClientRunAfterJob; /* Run client program after Job */
+ char *WriteBootstrap; /* Where to write bootstrap Job updates */
+ int replace; /* How (overwrite, ..) */
+ utime_t MaxRunTime; /* max run time in seconds */
+ utime_t MaxStartDelay; /* max start delay in seconds */
+ int PrefixLinks; /* prefix soft links with Where path */
+ 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 */
+ uint32_t MaxConcurrentJobs; /* Maximume concurrent jobs */
+ int RescheduleOnError; /* Set to reschedule on error */
+ int RescheduleTimes; /* Number of times to reschedule job */
+ utime_t RescheduleInterval; /* Reschedule interval */
- MSGS *messages; /* How and where to send messages */
- SCHED *schedule; /* When -- Automatic schedule */
- CLIENT *client; /* Who to backup */
- FILESET *fileset; /* What to backup -- Fileset */
- STORE *storage; /* Where is device -- Storage daemon */
- POOL *pool; /* Where is media -- Media Pool */
- JOB *verify_job; /* Job name to verify */
- uint32_t NumConcurrentJobs; /* number of concurrent jobs running */
+ MSGS *messages; /* How and where to send messages */
+ SCHED *schedule; /* When -- Automatic schedule */
+ CLIENT *client; /* Who to backup */
+ FILESET *fileset; /* What to backup -- Fileset */
+ STORE *storage; /* Where is device -- Storage daemon */
+ POOL *pool; /* Where is media -- Media Pool */
+ JOB *verify_job; /* Job name to verify */
+ uint32_t NumConcurrentJobs; /* number of concurrent jobs running */
};
#define MAX_FOPTS 30
/* File options structure */
struct FOPTS {
- char opts[MAX_FOPTS]; /* options string */
- alist match; /* match string(s) */
- alist base_list; /* list of base names */
+ char opts[MAX_FOPTS]; /* options string */
+ alist match; /* match string(s) */
+ alist base_list; /* list of base names */
};
/* This is either an include item or an exclude item */
struct INCEXE {
- FOPTS *current_opts; /* points to current options structure */
- FOPTS **opts_list; /* options list */
- int num_opts; /* number of options items */
- alist name_list; /* filename list -- holds char * */
+ FOPTS *current_opts; /* points to current options structure */
+ FOPTS **opts_list; /* options list */
+ int num_opts; /* number of options items */
+ alist name_list; /* filename list -- holds char * */
};
/*
*
*/
struct FILESET {
- RES hdr;
+ RES hdr;
- int new_include; /* Set if new include used */
- INCEXE **include_items; /* array of incexe structures */
- int num_includes; /* number in array */
+ int new_include; /* Set if new include used */
+ INCEXE **include_items; /* array of incexe structures */
+ int num_includes; /* number in array */
INCEXE **exclude_items;
int num_excludes;
- int have_MD5; /* set if MD5 initialized */
- struct MD5Context md5c; /* MD5 of include/exclude */
- char MD5[30]; /* base 64 representation of MD5 */
+ int have_MD5; /* set if MD5 initialized */
+ struct MD5Context md5c; /* MD5 of include/exclude */
+ char MD5[30]; /* base 64 representation of MD5 */
};
*
*/
struct SCHED {
- RES hdr;
+ RES hdr;
RUN *run;
};
*
*/
struct GROUP {
- RES hdr;
+ RES hdr;
};
/*
* Counter Resource
*/
struct COUNTER {
- RES hdr;
-
- int32_t MinValue; /* Minimum value */
- int32_t MaxValue; /* Maximum value */
- int32_t CurrentValue; /* Current value */
- COUNTER *WrapCounter; /* Wrap counter name */
- CAT *Catalog; /* Where to store */
- bool created; /* Created in DB */
+ RES hdr;
+
+ int32_t MinValue; /* Minimum value */
+ int32_t MaxValue; /* Maximum value */
+ int32_t CurrentValue; /* Current value */
+ COUNTER *WrapCounter; /* Wrap counter name */
+ CAT *Catalog; /* Where to store */
+ bool created; /* Created in DB */
};
/*
*
*/
struct POOL {
- RES hdr;
-
- char *pool_type; /* Pool type */
- char *label_format; /* Label format string */
- char *cleaning_prefix; /* Cleaning label prefix */
- 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 purge_oldest_volume; /* purge oldest volume */
- int recycle_oldest_volume; /* attempt to recycle oldest volume */
- int recycle_current_volume; /* attempt recycle of current volume */
- uint32_t max_volumes; /* max number of volumes */
- utime_t VolRetention; /* volume retention period in seconds */
- utime_t VolUseDuration; /* duration volume can be used */
- uint32_t MaxVolJobs; /* Maximum jobs on the Volume */
- uint32_t MaxVolFiles; /* Maximum files on the Volume */
- uint64_t MaxVolBytes; /* Maximum bytes on the Volume */
- int AutoPrune; /* default for pool auto prune */
- int Recycle; /* default for media recycle yes/no */
+ RES hdr;
+
+ char *pool_type; /* Pool type */
+ char *label_format; /* Label format string */
+ char *cleaning_prefix; /* Cleaning label prefix */
+ 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 purge_oldest_volume; /* purge oldest volume */
+ int recycle_oldest_volume; /* attempt to recycle oldest volume */
+ int recycle_current_volume; /* attempt recycle of current volume */
+ uint32_t max_volumes; /* max number of volumes */
+ utime_t VolRetention; /* volume retention period in seconds */
+ utime_t VolUseDuration; /* duration volume can be used */
+ uint32_t MaxVolJobs; /* Maximum jobs on the Volume */
+ uint32_t MaxVolFiles; /* Maximum files on the Volume */
+ uint64_t MaxVolBytes; /* Maximum bytes on the Volume */
+ int AutoPrune; /* default for pool auto prune */
+ int Recycle; /* default for media recycle yes/no */
};
CONRES res_con;
CLIENT res_client;
STORE res_store;
- CAT res_cat;
- JOB res_job;
+ CAT res_cat;
+ JOB res_job;
FILESET res_fs;
SCHED res_sch;
GROUP res_group;
POOL res_pool;
MSGS res_msgs;
COUNTER res_counter;
- RES hdr;
+ RES hdr;
};
/* Run structure contained in Schedule Resource */
struct RUN {
- RUN *next; /* points to next run record */
- int level; /* level override */
- int Priority; /* priority override */
+ RUN *next; /* points to next run record */
+ int level; /* level override */
+ int Priority; /* priority 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 */
bstrncpy(since, ", since=", since_len);
bstrncat(since, jcr->stime, since_len);
}
- Dmsg1(115, "Last start time = %s\n", jcr->stime);
+ Dmsg1(100, "Last start time = %s\n", jcr->stime);
break;
}
}
Pmsg1(0, "Unimplemented job type: %d\n", jcr->JobType);
break;
}
- if (jcr->job->RunAfterJob) {
+ if ((jcr->job->RunAfterJob && jcr->JobStatus == JS_Terminated) ||
+ (jcr->job->RunAfterFailedJob && jcr->JobStatus != JS_Terminated)) {
POOLMEM *after = get_pool_memory(PM_FNAME);
int status;
BPIPE *bpipe;
char line[MAXSTRING];
- after = edit_job_codes(jcr, after, jcr->job->RunAfterJob, "");
+ if (jcr->JobStatus == JS_Terminated) {
+ after = edit_job_codes(jcr, after, jcr->job->RunAfterJob, "");
+ } else {
+ after = edit_job_codes(jcr, after, jcr->job->RunAfterFailedJob, "");
+ }
bpipe = open_bpipe(after, 0, "r");
free_pool_memory(after);
while (fgets(line, sizeof(line), bpipe->rfd)) {
}
status = close_bpipe(bpipe);
if (status != 0) {
- Jmsg(jcr, M_FATAL, 0, _("RunAfterJob returned non-zero status=%d\n"),
- status);
+ if (jcr->JobStatus == JS_Terminated) {
+ Jmsg(jcr, M_FATAL, 0, _("RunAfterJob returned non-zero status=%d\n"),
+ status);
+ } else {
+ Jmsg(jcr, M_FATAL, 0, _("RunAfterFailedJob returned non-zero status=%d\n"),
+ status);
+ }
set_jcr_job_status(jcr, JS_FatalError);
update_job_end_record(jcr);
}
if (ok) {
/* If we can use the volume, check if it is expired */
if (has_volume_expired(jcr, mr)) {
- /* Need to update media */
- if (!db_update_media_record(jcr, jcr->db, mr)) {
- Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
- mr->VolumeName, db_strerror(jcr->db));
- }
if (retry++ < 200) { /* sanity check */
continue; /* try again from the top */
} else {
}
}
}
+ if (expired) {
+ /* Need to update media */
+ if (!db_update_media_record(jcr, jcr->db, mr)) {
+ Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
+ mr->VolumeName, db_strerror(jcr->db));
+ }
+ }
return expired;
}
* Returns: on failure - reason = NULL
* on success - reason - pointer to reason
*/
-bool is_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason)
+void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason)
{
int ok;
*reason = NULL;
/* Check if a duration or limit has expired */
- has_volume_expired(jcr, mr);
+ if (has_volume_expired(jcr, mr)) {
+ *reason = "volume has expired";
+ /* Keep going because we may be able to recycle volume */
+ }
/*
* Now see if we can use the volume as is
*/
if (strcmp(mr->VolStatus, "Append") == 0 ||
strcmp(mr->VolStatus, "Recycle") == 0) {
- return true;
+ *reason = NULL;
+ return;
}
/*
- * Check if the Volume is alreay marked for recycling
+ * Check if the Volume is already marked for recycling
*/
if (strcmp(mr->VolStatus, "Purged") == 0) {
if (recycle_volume(jcr, mr)) {
Jmsg(jcr, M_INFO, 0, "Recycled current volume \"%s\"\n", mr->VolumeName);
- return true;
+ *reason = NULL;
+ return;
} else {
/* In principle this shouldn't happen */
*reason = "and recycling of current volume failed";
- return false;
+ return;
}
}
/* If fully purged, recycle current volume */
if (recycle_volume(jcr, mr)) {
Jmsg(jcr, M_INFO, 0, "Recycled current volume \"%s\"\n", mr->VolumeName);
- return true; /* Good volume */
+ *reason = NULL;
} else {
*reason = "but should be Append, Purged or Recycle (recycling of the "
"current volume failed)";
"recycle current volume, as it still contains unpruned data)";
}
}
- return *reason ? false : true;
}
/* next_vol.c */
int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int create);
bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr);
-bool is_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason);
+void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason);
/* newvol.c */
int newVolume(JCR *jcr, MEDIA_DBR *mr);
static int update_pool(UAContext *ua);
static int delete_volume(UAContext *ua);
static int delete_pool(UAContext *ua);
+static int delete_job(UAContext *ua);
static int mount_cmd(UAContext *ua, char *cmd);
static int release_cmd(UAContext *ua, char *cmd);
static int update_cmd(UAContext *ua, char *cmd);
static char *keywords[] = {
N_("volume"),
N_("pool"),
+ N_("job"),
NULL};
if (!open_db(ua)) {
return 1;
}
- bsendmsg(ua, _(
-"In general it is not a good idea to delete either a\n"
-"Pool or a Volume since they may contain data.\n\n"));
switch (find_arg_keyword(ua, keywords)) {
case 0:
case 1:
delete_pool(ua);
return 1;
+ case 2:
+ delete_job(ua);
+ return 1;
default:
break;
}
+
+ bsendmsg(ua, _(
+"In general it is not a good idea to delete either a\n"
+"Pool or a Volume since they may contain data.\n\n"));
+
switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
case 0:
delete_volume(ua);
case 1:
delete_pool(ua);
break;
+ case 2:
+ delete_job(ua);
+ return 1;
default:
bsendmsg(ua, _("Nothing done.\n"));
break;
return 1;
}
+static int delete_job(UAContext *ua)
+{
+ POOLMEM *query = get_pool_memory(PM_MESSAGE);
+ JobId_t JobId;
+
+ int i = find_arg_with_value(ua, "jobid");
+ if (i >= 0) {
+ JobId = str_to_int64(ua->argv[i]);
+ } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
+ return 0;
+ } else {
+ JobId = ua->pint32_val;
+ }
+ Mmsg(&query, "DELETE FROM Job WHERE JobId=%u", JobId);
+ db_sql_query(ua->db, query, NULL, (void *)NULL);
+ Mmsg(&query, "DELETE FROM File WHERE JobId=%u", JobId);
+ db_sql_query(ua->db, query, NULL, (void *)NULL);
+ Mmsg(&query, "DELETE FROM JobMedia WHERE JobId=%u", JobId);
+ db_sql_query(ua->db, query, NULL, (void *)NULL);
+ free_pool_memory(query);
+ bsendmsg(ua, _("Job %u and associated records deleted from the catalog.\n"), JobId);
+ return 1;
+}
+
/*
* Delete media records from database -- dangerous
*/
return 1;
case FT_DIRNOCHG:
case FT_NOCHG:
- Jmsg(jcr, M_INFO, -1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
+ Jmsg(jcr, M_SKIPPED, -1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
return 1;
case FT_ISARCH:
Jmsg(jcr, M_SKIPPED, -1, _(" Archive file skipped: %s\n"), ff_pkt->fname);
GtkWidget *label_dialog;
GdkFont *text_font = NULL;
PangoFontDescription *font_desc;
-GtkAdjustment *vadj;
pthread_mutex_t cmd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cmd_wait;
char cmd[1000];
static void terminate_console(int sig);
static gint message_handler(gpointer data);
static int initial_connect_to_director(gpointer data);
+static void set_scroll_bar_to_end(void);
/* Static variables */
static char *configfile = NULL;
static bool at_prompt = false;
static bool ready = false;
static bool quit = false;
+static bool adjusted = false;
static guint initial;
#define CONFIG_FILE "./gnome-console.conf" /* default configuration file */
buf_len = gtk_text_buffer_get_char_count(textbuf);
gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
gtk_text_iter_set_offset(&iter, buf_len);
- /*
- * Force the scroll bars to the bottom so that most
- * recent text is on screen.
- */
- if (ready || at_prompt) {
- vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroll1));
- gtk_adjustment_set_value(vadj, vadj->upper);
- }
+ adjusted = false;
}
void set_statusf(char *fmt, ...)
va_start(arg_ptr, fmt);
len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
gtk_label_set_text(GTK_LABEL(status1), buf);
+ set_scroll_bar_to_end();
ready = false;
}
void set_status_ready()
{
- GtkTextBuffer *textbuf;
gtk_label_set_text(GTK_LABEL(status1), " Ready");
- if (!ready) {
- textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
- vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroll1));
- gtk_adjustment_set_value(vadj, vadj->upper);
- }
ready = true;
+ set_scroll_bar_to_end();
}
void set_status(char *buf)
{
gtk_label_set_text(GTK_LABEL(status1), buf);
+ set_scroll_bar_to_end();
ready = false;
}
+
+static void set_scroll_bar_to_end(void)
+{
+ GtkAdjustment *vadj;
+ /*
+ * Force the scroll bars to the bottom so that most
+ * recent text is on screen.
+ */
+ if ((ready || at_prompt) && !adjusted) {
+ adjusted = true;
+ vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroll1));
+ gtk_adjustment_set_value(vadj, vadj->upper);
+ }
+ gtk_widget_show(text1);
+}
* Only the first block checksum error was reported.
* If there are more, report it now.
*/
-void print_block_errors(JCR *jcr, DEV_BLOCK *block)
+void print_block_read_errors(JCR *jcr, DEV_BLOCK *block)
{
- if (block->checksum_errors > 1) {
- Jmsg(jcr, M_ERROR, 0, _("%d block checksum errors ignored.\n"),
- block->checksum_errors);
+ if (block->read_errors > 1) {
+ Jmsg(jcr, M_ERROR, 0, _("%d block read errors ignored.\n"),
+ block->read_errors);
}
}
if (strncmp(Id, BLKHDR1_ID, BLKHDR_ID_LENGTH) != 0) {
Mmsg2(&dev->errmsg, _("Buffer ID error. Wanted: %s, got %s. Buffer discarded.\n"),
BLKHDR1_ID, Id);
- Emsg0(M_ERROR, 0, dev->errmsg);
+ if (block->read_errors == 0 || verbose >= 2) {
+ Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
+ }
+ block->read_errors++;
return 0;
}
} else if (Id[3] == '2') {
if (strncmp(Id, BLKHDR2_ID, BLKHDR_ID_LENGTH) != 0) {
Mmsg2(&dev->errmsg, _("Buffer ID error. Wanted: %s, got %s. Buffer discarded.\n"),
BLKHDR2_ID, Id);
- Emsg0(M_ERROR, 0, dev->errmsg);
+ if (block->read_errors == 0 || verbose >= 2) {
+ Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
+ }
+ block->read_errors++;
return 0;
}
} else {
Mmsg1(&dev->errmsg, _("Expected block-id BB01 or BB02, got %s. Buffer discarded.\n"), Id);
- Emsg0(M_ERROR, 0, dev->errmsg);
+ if (block->read_errors == 0 || verbose >= 2) {
+ Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
+ }
+ block->read_errors++;
return 0;
}
if (block_len > MAX_BLOCK_LENGTH) {
Mmsg1(&dev->errmsg, _("Block length %u is insane (too large), probably due to a bad archive.\n"),
block_len);
- Emsg0(M_ERROR, 0, dev->errmsg);
+ if (block->read_errors == 0 || verbose >= 2) {
+ Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
+ }
+ block->read_errors++;
return 0;
}
BlockCheckSum = bcrc32((uint8_t *)block->buf+BLKHDR_CS_LENGTH,
block_len-BLKHDR_CS_LENGTH);
if (BlockCheckSum != CheckSum) {
- Dmsg2(00, "Block checksum mismatch: calc=%x blk=%x\n", BlockCheckSum,
- CheckSum);
Mmsg3(&dev->errmsg, _("Block checksum mismatch in block %u: calc=%x blk=%x\n"),
(unsigned)BlockNumber, BlockCheckSum, CheckSum);
- if (block->checksum_errors == 0) {
+ if (block->read_errors == 0 || verbose >= 2) {
Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
}
- block->checksum_errors++;
+ block->read_errors++;
+ return 0;
}
}
return 1;
BlockNumber = block->BlockNumber + 1;
if (!unser_block_header(jcr, dev, block)) {
- Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
block->read_len = 0;
return 0;
}
uint32_t read_len; /* bytes read into buffer, if zero, block empty */
uint32_t VolSessionId; /* */
uint32_t VolSessionTime; /* */
+ uint32_t read_errors; /* block errors (checksum, header, ...) */
int BlockVer; /* block version 1 or 2 */
bool write_failed; /* set if write failed */
bool block_read; /* set when block read */
- int checksum_errors; /* count of block checksum errors */
int32_t FirstIndex; /* first index this block */
int32_t LastIndex; /* last index this block */
char *bufp; /* pointer into buffer */
* 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
+ * a 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.
void free_block(DEV_BLOCK *block);
int write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-void print_block_errors(JCR *jcr, DEV_BLOCK *block);
+void print_block_read_errors(JCR *jcr, DEV_BLOCK *block);
#define CHECK_BLOCK_NUMBERS true
#define NO_BLOCK_NUMBER_CHECK false
rec = nrec;
}
delete recs;
- print_block_errors(jcr, block);
+ print_block_read_errors(jcr, block);
free_block(block);
return ok;
}