From: Kern Sibbald Date: Wed, 22 Oct 2003 16:36:41 +0000 (+0000) Subject: Print block errors once + add RunAfterFailedJob + improve db_find_job_start_time... X-Git-Tag: Release-7.0.0~9951 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=f839473bf1146fde5cf0a058e8c5baec879e301a;p=bacula%2Fbacula Print block errors once + add RunAfterFailedJob + improve db_find_job_start_time + tweak recycling git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@756 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/ChangeLog b/bacula/ChangeLog index c06d96429a..563d42267b 100644 --- a/bacula/ChangeLog +++ b/bacula/ChangeLog @@ -1,4 +1,29 @@ +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 diff --git a/bacula/ReleaseNotes b/bacula/ReleaseNotes index 4831d591b5..f9ef8d92fb 100644 --- a/bacula/ReleaseNotes +++ b/bacula/ReleaseNotes @@ -1,7 +1,16 @@ - 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 diff --git a/bacula/kernstodo b/bacula/kernstodo index b273b52d87..bada35994b 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -35,17 +35,22 @@ For 1.33 Testing/Documentation: - 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 @@ -1054,3 +1059,6 @@ Done: (see kernsdone for more) - 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. + diff --git a/bacula/src/cats/sql_delete.c b/bacula/src/cats/sql_delete.c index 45de5dc65c..5a0790804e 100644 --- a/bacula/src/cats/sql_delete.c +++ b/bacula/src/cats/sql_delete.c @@ -153,7 +153,7 @@ static int delete_handler(void *ctx, int num_fields, char **row) */ 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; @@ -173,11 +173,11 @@ static int do_media_purge(B_DB *mdb, MEDIA_DBR *mr) 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); diff --git a/bacula/src/cats/sql_find.c b/bacula/src/cats/sql_find.c index 6279e8a11a..948a984062 100644 --- a/bacula/src/cats/sql_find.c +++ b/bacula/src/cats/sql_find.c @@ -53,8 +53,10 @@ extern void print_result(B_DB *mdb); 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 @@ -63,7 +65,6 @@ int db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime) { SQL_ROW row; - uint32_t JobId; db_lock(mdb); @@ -71,16 +72,39 @@ db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime) /* 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, @@ -90,27 +114,11 @@ db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime) 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 */ @@ -121,7 +129,7 @@ db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime) } 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; diff --git a/bacula/src/dird/catreq.c b/bacula/src/dird/catreq.c index 457cc402b1..44bb598019 100644 --- a/bacula/src/dird/catreq.c +++ b/bacula/src/dird/catreq.c @@ -145,8 +145,9 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) /* * 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) { @@ -217,14 +218,6 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) 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 diff --git a/bacula/src/dird/dird_conf.c b/bacula/src/dird/dird_conf.c index a5b3ae2646..e29e192fa6 100644 --- a/bacula/src/dird/dird_conf.c +++ b/bacula/src/dird/dird_conf.c @@ -215,6 +215,7 @@ static struct res_items job_items[] = { {"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}, @@ -516,6 +517,9 @@ void dump_resource(int type, RES *reshdr, void sendit(void *sock, char *fmt, ... 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)); } @@ -837,6 +841,9 @@ void free_resource(int type) 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); } diff --git a/bacula/src/dird/dird_conf.h b/bacula/src/dird/dird_conf.h index bf3ee96271..5eee0c5da3 100644 --- a/bacula/src/dird/dird_conf.h +++ b/bacula/src/dird/dird_conf.h @@ -30,44 +30,44 @@ /* * 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 */ @@ -85,33 +85,33 @@ struct POOL; 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 */ }; @@ -120,11 +120,11 @@ struct CONRES { * */ 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; @@ -136,18 +136,18 @@ struct CAT { * */ 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 */ }; /* @@ -155,18 +155,18 @@ struct CLIENT { * */ 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 */ }; @@ -175,58 +175,59 @@ struct STORE { * */ 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 * */ }; /* @@ -234,16 +235,16 @@ struct INCEXE { * */ 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 */ }; @@ -252,7 +253,7 @@ struct FILESET { * */ struct SCHED { - RES hdr; + RES hdr; RUN *run; }; @@ -262,21 +263,21 @@ struct SCHED { * */ 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 */ }; /* @@ -284,26 +285,26 @@ struct COUNTER { * */ 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 */ }; @@ -315,33 +316,33 @@ union URES { 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 */ diff --git a/bacula/src/dird/fd_cmds.c b/bacula/src/dird/fd_cmds.c index ce37b34777..a3341644c7 100644 --- a/bacula/src/dird/fd_cmds.c +++ b/bacula/src/dird/fd_cmds.c @@ -155,7 +155,7 @@ void get_level_since_time(JCR *jcr, char *since, int since_len) 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; } } diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index 597edcfafb..db7064e716 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -206,13 +206,18 @@ static void *job_thread(void *arg) 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)) { @@ -220,8 +225,13 @@ static void *job_thread(void *arg) } 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); } diff --git a/bacula/src/dird/next_vol.c b/bacula/src/dird/next_vol.c index ddb4e125b7..65a9a3bfa8 100644 --- a/bacula/src/dird/next_vol.c +++ b/bacula/src/dird/next_vol.c @@ -100,11 +100,6 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int create) 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 { @@ -172,6 +167,13 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) } } } + 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; } @@ -181,34 +183,39 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) * 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; } } @@ -239,7 +246,7 @@ bool is_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason) /* 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)"; @@ -249,5 +256,4 @@ bool is_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason) "recycle current volume, as it still contains unpruned data)"; } } - return *reason ? false : true; } diff --git a/bacula/src/dird/protos.h b/bacula/src/dird/protos.h index 0d77157ddc..911bab1a10 100644 --- a/bacula/src/dird/protos.h +++ b/bacula/src/dird/protos.h @@ -102,7 +102,7 @@ extern void wait_for_storage_daemon_termination(JCR *jcr); /* 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); diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index 48e1d49a82..678e569d2b 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -75,6 +75,7 @@ static int update_volume(UAContext *ua); static int update_pool(UAContext *ua); static int delete_volume(UAContext *ua); static int delete_pool(UAContext *ua); +static int 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); @@ -1451,15 +1452,13 @@ static int delete_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: @@ -1468,9 +1467,17 @@ static int delete_cmd(UAContext *ua, char *cmd) 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); @@ -1478,6 +1485,9 @@ static int delete_cmd(UAContext *ua, char *cmd) case 1: delete_pool(ua); break; + case 2: + delete_job(ua); + return 1; default: bsendmsg(ua, _("Nothing done.\n")); break; @@ -1485,6 +1495,30 @@ static int delete_cmd(UAContext *ua, char *cmd) 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 */ diff --git a/bacula/src/filed/verify.c b/bacula/src/filed/verify.c index 30dddf2cd2..cf414d6320 100644 --- a/bacula/src/filed/verify.c +++ b/bacula/src/filed/verify.c @@ -119,7 +119,7 @@ static int verify_file(FF_PKT *ff_pkt, void *pkt) 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); diff --git a/bacula/src/stored/block.c b/bacula/src/stored/block.c index 358ff6ec99..e815e3cd8b 100644 --- a/bacula/src/stored/block.c +++ b/bacula/src/stored/block.c @@ -131,11 +131,11 @@ DEV_BLOCK *new_block(DEVICE *dev) * 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); } } @@ -223,7 +223,10 @@ static int unser_block_header(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) 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') { @@ -235,12 +238,18 @@ static int unser_block_header(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) 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; } @@ -248,7 +257,10 @@ static int unser_block_header(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) 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; } @@ -268,14 +280,13 @@ static int unser_block_header(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) 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; @@ -650,7 +661,6 @@ reread: 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; } diff --git a/bacula/src/stored/block.h b/bacula/src/stored/block.h index 6827457935..1c3c304bb0 100644 --- a/bacula/src/stored/block.h +++ b/bacula/src/stored/block.h @@ -96,10 +96,10 @@ struct DEV_BLOCK { 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 */ diff --git a/bacula/src/stored/mount.c b/bacula/src/stored/mount.c index c3ac6728af..3b419860dd 100644 --- a/bacula/src/stored/mount.c +++ b/bacula/src/stored/mount.c @@ -240,7 +240,7 @@ mount_error: * 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. diff --git a/bacula/src/stored/protos.h b/bacula/src/stored/protos.h index 228cc20151..e84cfc1f15 100644 --- a/bacula/src/stored/protos.h +++ b/bacula/src/stored/protos.h @@ -58,7 +58,7 @@ void empty_block(DEV_BLOCK *block); void free_block(DEV_BLOCK *block); int write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); -void print_block_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 diff --git a/bacula/src/stored/read_record.c b/bacula/src/stored/read_record.c index 6b9a177d87..ea8b513f99 100644 --- a/bacula/src/stored/read_record.c +++ b/bacula/src/stored/read_record.c @@ -244,7 +244,7 @@ int read_records(JCR *jcr, DEVICE *dev, rec = nrec; } delete recs; - print_block_errors(jcr, block); + print_block_read_errors(jcr, block); free_block(block); return ok; }