From: Kern Sibbald Date: Thu, 25 Apr 2002 20:35:41 +0000 (+0000) Subject: Start of Recycle Code X-Git-Tag: Release-1.20~23 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=48d6e0f64731f3e81f84a687e4cc4320c71e901e;p=bacula%2Fbacula Start of Recycle Code git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@6 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/Makefile.in b/bacula/Makefile.in index 2796af378c..4c7c500dd6 100755 --- a/bacula/Makefile.in +++ b/bacula/Makefile.in @@ -32,7 +32,10 @@ MKDIR = $(srcdir)/autoconf/mkinstalldirs all: Makefile @for I in ${subdirs}; \ - do (cd $$I; echo "==>Entering directory `pwd`"; $(MAKE) $@ || exit 1); done + do (cd $$I; echo "==>Entering directory `pwd`"; \ + $(MAKE) $@ || (echo ""; echo ""; echo " ====== Error in `pwd` ======"; \ + echo ""; echo "";)); \ + done depend: @for I in ${subdirs}; \ @@ -40,7 +43,10 @@ depend: bacula-fd: Makefile @for I in ${FDsubdirs}; \ - do (cd $$I; echo "==>Entering directory `pwd`"; $(MAKE) all || exit 1); done + do (cd $$I; echo "==>Entering directory `pwd`"; \ + $(MAKE) all || (echo ""; echo ""; echo " ====== Error in `pwd` ======"; \ + echo ""; echo "";)); \ + done #------------------------------------------------------------------------- configure: autoconf/configure.in autoconf/aclocal.m4 autoconf/acconfig.h autoconf/config.h.in diff --git a/bacula/src/cats/make_mysql_tables.in b/bacula/src/cats/make_mysql_tables.in index 0ffb178ac5..0cf996e15f 100644 --- a/bacula/src/cats/make_mysql_tables.in +++ b/bacula/src/cats/make_mysql_tables.in @@ -102,7 +102,7 @@ CREATE TABLE Media ( VolWrites INTEGER UNSIGNED NOT NULL, VolMaxBytes BIGINT UNSIGNED NOT NULL, VolCapacityBytes BIGINT UNSIGNED NOT NULL, - VolStatus ENUM('Full', 'Archive', 'Append', 'Recycle', + VolStatus ENUM('Full', 'Archive', 'Append', 'Recycle', 'Purged', 'Read-Only', 'Disabled', 'Error', 'Busy') NOT NULL, Recycle ENUM('No', 'Yes') NOT NULL, PRIMARY KEY(MediaId), diff --git a/bacula/src/cats/sql_delete.c b/bacula/src/cats/sql_delete.c index 5fd896af9f..d8c7fdfbd7 100644 --- a/bacula/src/cats/sql_delete.c +++ b/bacula/src/cats/sql_delete.c @@ -109,24 +109,117 @@ db_delete_pool_record(B_DB *mdb, POOL_DBR *pr) return 1; } +#define MAX_DEL_LIST_LEN 1000000 -/* Delete Media record */ -int db_delete_media_record(B_DB *mdb, MEDIA_DBR *mr) +struct s_del_ctx { + uint32_t *JobId; + int num_ids; /* ids stored */ + int max_ids; /* size of array */ + int num_del; /* number deleted */ + int tot_ids; /* total to process */ +}; + +/* + * Called here to make in memory list of JobIds to be + * deleted. The in memory list will then be transversed + * to issue the SQL DELETE commands. Note, the list + * is allowed to get to MAX_DEL_LIST_LEN to limit the + * maximum malloc'ed memory. + */ +static int delete_handler(void *ctx, int num_fields, char **row) { + struct s_del_ctx *del = (struct s_del_ctx *)ctx; - P(mdb->mutex); - if (mr->MediaId == 0) { - Mmsg(&mdb->cmd, "DELETE FROM Media WHERE VolumeName=\"%s\"", - mr->VolumeName); - } else { - Mmsg(&mdb->cmd, "DELETE FROM Media WHERE MediaId=%d", - mr->MediaId); + if (del->num_ids == MAX_DEL_LIST_LEN) { + return 1; + } + if (del->num_ids == del->max_ids) { + del->max_ids = (del->max_ids * 3) / 2; + del->JobId = (uint32_t *)brealloc(del->JobId, sizeof(uint32_t) * + del->max_ids); } + del->JobId[del->num_ids++] = (uint32_t)strtod(row[0], NULL); + return 0; +} - mr->MediaId = DELETE_DB(mdb, mdb->cmd); - V(mdb->mutex); +/* + * This routine will purge (delete) all records + * associated with a particular Volume. It will + * not delete the media record itself. + */ +static int do_media_purge(B_DB *mdb, MEDIA_DBR *mr) +{ + char *query = (char *)get_pool_memory(PM_MESSAGE); + struct s_del_ctx del; + int i; + + del.num_ids = 0; + del.tot_ids = 0; + del.num_del = 0; + del.max_ids = 0; + Mmsg(&mdb->cmd, "SELECT JobId from JobMedia WHERE MediaId=%d", mr->MediaId); + del.max_ids = mr->VolJobs; + if (del.max_ids < 100) { + del.max_ids = 100; + } else if (del.max_ids > MAX_DEL_LIST_LEN) { + del.max_ids = MAX_DEL_LIST_LEN; + } + del.JobId = (uint32_t *)malloc(sizeof(uint32_t) * del.max_ids); + db_sql_query(mdb, mdb->cmd, delete_handler, (void *)&del); + + 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]); + db_sql_query(mdb, query, NULL, (void *)NULL); + Mmsg(&query, "DELETE FROM File WHERE JobId=%d", del.JobId[i]); + db_sql_query(mdb, query, NULL, (void *)NULL); + Mmsg(&query, "DELETE FROM JobMedia WHERE JobId=%d", del.JobId[i]); + db_sql_query(mdb, query, NULL, (void *)NULL); + } + free(del.JobId); + free_pool_memory(query); return 1; } +/* Delete Media record and all records that + * are associated with it. + */ +int db_delete_media_record(B_DB *mdb, MEDIA_DBR *mr) +{ + if (mr->MediaId == 0 && !db_get_media_record(mdb, mr)) { + return 0; + } + /* Delete associated records */ + do_media_purge(mdb, mr); + + Mmsg(&mdb->cmd, "DELETE FROM Media WHERE MediaId=%d", mr->MediaId); + db_sql_query(mdb, mdb->cmd, NULL, (void *)NULL); + return 1; +} + +/* + * Purge all records associated with a + * media record. This does not delete the + * media record itself. But the media status + * is changed to "Purged". + */ +int db_purge_media_record(B_DB *mdb, MEDIA_DBR *mr) +{ + if (mr->MediaId == 0 && !db_get_media_record(mdb, mr)) { + return 0; + } + /* Delete associated records */ + do_media_purge(mdb, mr); + + /* Mark Volume as purged */ + strcpy(mr->VolStatus, "Purged"); + if (!db_update_media_record(mdb, mr)) { + return 0; + } + + return 1; +} + + #endif /* HAVE_MYSQL || HAVE_SQLITE */ diff --git a/bacula/src/console/console.c b/bacula/src/console/console.c index 8929df66bd..0fac356507 100644 --- a/bacula/src/console/console.c +++ b/bacula/src/console/console.c @@ -49,6 +49,8 @@ int get_cmd(char *prompt, BSOCK *sock, int sec); static char *configfile = NULL; static BSOCK *UA_sock = NULL; static DIRRES *dir; +static FILE *output = stdout; + #define CONFIG_FILE "./console.conf" /* default configuration file */ @@ -147,7 +149,7 @@ int main(int argc, char *argv[]) } UnlockRes(); if (ndir == 0) { - Emsg1(M_ABORT, 0, "No director resource defined in %s\n\ + Emsg1(M_ABORT, 0, "No Director resource defined in %s\n\ Without that I don't how to speak to the Director :-(\n", configfile); } @@ -161,11 +163,11 @@ Without that I don't how to speak to the Director :-(\n", configfile); if (ndir > 1) { UA_sock = init_bsock(0, "", "", 0); try_again: - printf("Available Directors:\n"); + fprintf(output, "Available Directors:\n"); LockRes(); ndir = 0; for (dir = NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) { - printf("%d %s at %s:%d\n", 1+ndir++, dir->hdr.name, dir->address, + fprintf(output, "%d %s at %s:%d\n", 1+ndir++, dir->hdr.name, dir->address, dir->DIRport); } UnlockRes(); @@ -174,7 +176,7 @@ try_again: } item = atoi(UA_sock->msg); if (item < 0 || item > ndir) { - printf("You must enter a number between 1 and %d\n", ndir); + fprintf(output, "You must enter a number between 1 and %d\n", ndir); goto try_again; } LockRes(); @@ -200,7 +202,7 @@ try_again: } jcr.dir_bsock = UA_sock; if (!authenticate_director(&jcr, dir)) { - printf("ERR: %s", UA_sock->msg); + fprintf(stderr, "ERR: %s", UA_sock->msg); terminate_console(0); return 1; } @@ -334,8 +336,8 @@ wait_for_data(int fd, int sec) int get_cmd(char *prompt, BSOCK *sock, int sec) { - fprintf(stdout, prompt); - fflush(stdout); + fprintf(output, prompt); + fflush(output); switch (wait_for_data(fileno(stdin), sec)) { case 0: return 0; /* timeout */ diff --git a/bacula/src/console/console_conf.c b/bacula/src/console/console_conf.c index fc5284ce70..555a08bfbb 100644 --- a/bacula/src/console/console_conf.c +++ b/bacula/src/console/console_conf.c @@ -64,6 +64,18 @@ int res_all_size = sizeof(res_all); * resource with the routine to process the record * information. */ + +/* Console "globals" */ +static struct res_items cons_items[] = { + {"name", store_name, ITEM(res_cons.hdr.name), 0, ITEM_REQUIRED, 0}, + {"description", store_str, ITEM(res_cons.hdr.desc), 0, 0, 0}, + {"rcfile", store_dir, ITEM(res_cons.rc_file), 0, 0, 0}, + {"historyfile", store_dir, ITEM(res_cons.hist_file), 0, 0, 0}, + {NULL, NULL, NULL, 0, 0, 0} +}; + + +/* Director's that we can contact */ static struct res_items dir_items[] = { {"name", store_name, ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0}, {"description", store_str, ITEM(res_dir.hdr.desc), 0, 0, 0}, @@ -78,6 +90,7 @@ static struct res_items dir_items[] = { * It must have one item for each of the resources. */ struct s_res resources[] = { + {"console", cons_items, R_CONSOLE, NULL}, {"director", dir_items, R_DIRECTOR, NULL}, {NULL, NULL, 0, NULL} }; @@ -98,6 +111,10 @@ void dump_resource(int type, RES *reshdr, void sendit(void *sock, char *fmt, ... recurse = 0; } switch (type) { + case R_CONSOLE: + printf("Console: name=%s rcfile=%s histfile=%s\n", reshdr->name, + res->res_cons.rc_file, res->res_cons.hist_file); + break; case R_DIRECTOR: printf("Director: name=%s address=%s DIRport=%d\n", reshdr->name, res->res_dir.address, res->res_dir.DIRport); @@ -136,6 +153,13 @@ void free_resource(int type) free(res->res_dir.hdr.desc); switch (type) { + case R_CONSOLE: + if (res->res_cons.rc_file) { + free(res->res_cons.rc_file); + } + if (res->res_cons.hist_file) { + free(res->res_cons.hist_file); + } case R_DIRECTOR: if (res->res_dir.address) free(res->res_dir.address); @@ -181,6 +205,7 @@ void save_resource(int type, struct res_items *items, int pass) if (pass == 2) { switch (type) { /* Resources not containing a resource */ + case R_CONSOLE: case R_DIRECTOR: break; @@ -204,6 +229,9 @@ void save_resource(int type, struct res_items *items, int pass) } switch (type) { + case R_CONSOLE: + size = sizeof(CONSRES); + break; case R_DIRECTOR: size = sizeof(DIRRES); break; diff --git a/bacula/src/console/console_conf.h b/bacula/src/console/console_conf.h index 04fdc7b85f..89b345b806 100644 --- a/bacula/src/console/console_conf.h +++ b/bacula/src/console/console_conf.h @@ -7,28 +7,39 @@ /* * Resource codes -- they must be sequential for indexing */ -#define R_FIRST 1001 +#define R_FIRST 1001 -#define R_DIRECTOR 1001 +#define R_CONSOLE 1001 +#define R_DIRECTOR 1002 -#define R_LAST R_DIRECTOR +#define R_LAST R_DIRECTOR /* * 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 /* Definition of the contents of each Resource */ + +/* Console "globals" */ +struct s_res_cons { + RES hdr; + char *rc_file; /* startup file */ + char *hist_file; /* command history file */ +}; +typedef struct s_res_cons CONSRES; + +/* Director */ struct s_res_dir { - RES hdr; - int DIRport; /* UA server port */ - char *address; /* UA server address */ - char *password; /* UA server password */ + RES hdr; + int DIRport; /* UA server port */ + char *address; /* UA server address */ + char *password; /* UA server password */ }; typedef struct s_res_dir DIRRES; @@ -37,7 +48,8 @@ typedef struct s_res_dir DIRRES; * resource structure definitions. */ union u_res { - struct s_res_dir res_dir; + struct s_res_dir res_dir; + struct s_res_cons res_cons; RES hdr; }; diff --git a/bacula/src/dird/catreq.c b/bacula/src/dird/catreq.c index f4e82ce0f5..d13d23c446 100644 --- a/bacula/src/dird/catreq.c +++ b/bacula/src/dird/catreq.c @@ -41,7 +41,7 @@ /* Requests from the Storage daemon */ static char Find_media[] = "CatReq Job=%127s FindMedia=%d\n"; -static char Find_Vol_Info[] = "CatReq Job=%127s GetVolInfo VolName=%127s\n"; +static char Get_Vol_Info[] = "CatReq Job=%127s GetVolInfo VolName=%127s\n"; static char Update_media[] = "CatReq Job=%127s UpdateMedia VolName=%s\ VolJobs=%d VolFiles=%d VolBlocks=%d VolBytes=%" lld " VolMounts=%d\ @@ -52,7 +52,7 @@ static char Update_media[] = "CatReq Job=%127s UpdateMedia VolName=%s\ /* Responses sent to Storage daemon */ static char OK_media[] = "1000 OK VolName=%s VolJobs=%d VolFiles=%d\ VolBlocks=%d VolBytes=%" lld " VolMounts=%d VolErrors=%d VolWrites=%d\ - VolMaxBytes=%" lld " VolCapacityBytes=%" lld "\n"; + VolMaxBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%s\n"; static char OK_update[] = "1000 OK UpdateMedia\n"; @@ -88,11 +88,19 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) jcr->MediaId = mr.MediaId; Dmsg1(20, "Find_next_vol MediaId=%d\n", jcr->MediaId); strcpy(jcr->VolumeName, mr.VolumeName); - ok = TRUE; } else { - /* See if we can create a new Volume */ - ok = newVolume(jcr); + /* Well, try finding recycled tapes */ + strcpy(mr.VolStatus, "Recycle"); + if (db_find_next_volume(jcr->db, index, &mr)) { + jcr->MediaId = mr.MediaId; + Dmsg1(20, "Find_next_vol MediaId=%d\n", jcr->MediaId); + strcpy(jcr->VolumeName, mr.VolumeName); + ok = TRUE; + } else { + /* See if we can create a new Volume */ + ok = newVolume(jcr); + } } /* * Send Find Media response to Storage daemon @@ -101,7 +109,8 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) bash_spaces(mr.VolumeName); bnet_fsend(bs, OK_media, mr.VolumeName, mr.VolJobs, mr.VolFiles, mr.VolBlocks, mr.VolBytes, mr.VolMounts, mr.VolErrors, - mr.VolWrites, mr.VolMaxBytes, mr.VolCapacityBytes); + mr.VolWrites, mr.VolMaxBytes, mr.VolCapacityBytes, + mr.VolStatus); } else { bnet_fsend(bs, "1999 No Media\n"); } @@ -109,7 +118,7 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) /* * Request to find specific volume information */ - } else if (sscanf(bs->msg, Find_Vol_Info, &Job, &mr.VolumeName) == 2) { + } else if (sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName) == 2) { Dmsg1(120, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName); /* * Find the Volume @@ -120,10 +129,12 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) Dmsg1(20, "VolumeInfo MediaId=%d\n", jcr->MediaId); strcpy(jcr->VolumeName, mr.VolumeName); /* - * Make sure this volume is suitable for this job + * Make sure this volume is suitable for this job, i.e. + * it is either Append or Recycle and Media Type matches. */ if (mr.PoolId == jcr->PoolId && - strcmp(mr.VolStatus, "Append") == 0 && + (strcmp(mr.VolStatus, "Append") == 0 || + strcmp(mr.VolStatus, "Recycle") == 0) && strcmp(mr.MediaType, jcr->store->media_type) == 0) { /* * Send Find Media response to Storage daemon @@ -131,7 +142,8 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) bash_spaces(mr.VolumeName); bnet_fsend(bs, OK_media, mr.VolumeName, mr.VolJobs, mr.VolFiles, mr.VolBlocks, mr.VolBytes, mr.VolMounts, mr.VolErrors, - mr.VolWrites, mr.VolMaxBytes, mr.VolCapacityBytes); + mr.VolWrites, mr.VolMaxBytes, mr.VolCapacityBytes, + mr.VolStatus); } else { Dmsg4(000, "get_media_record PoolId=%d wanted %d, Status=%s, \ MediaType=%s\n", mr.PoolId, jcr->PoolId, mr.VolStatus, mr.MediaType); diff --git a/bacula/src/dird/run_conf.c b/bacula/src/dird/run_conf.c index c58a713f5d..21b69f8944 100644 --- a/bacula/src/dird/run_conf.c +++ b/bacula/src/dird/run_conf.c @@ -176,7 +176,7 @@ void store_run(LEX *lc, struct res_items *item, int index, int pass) } else { lcase(lc->str); for (i=0; joblevels[i].level_name; i++) { - if (strcmp(lc->str, joblevels[i].level_name) == 0) { + if (strcasecmp(lc->str, joblevels[i].level_name) == 0) { lrun.level = joblevels[i].level; lrun.job_class = joblevels[i].job_class; i = 0; diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index d14de4a0fe..4ecc17a4bc 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -954,6 +954,9 @@ static int delete_media(UAContext *ua) } mr.MediaId = 0; strcpy(mr.VolumeName, ua->cmd); + bsendmsg(ua, _("\nThis command will delete volume %s\n" + "and all Jobs saved on that volume from the Catalog\n")); + if (!get_cmd(ua, _("If you want to continue enter pretty please: "))) { return 1; } @@ -1065,7 +1068,7 @@ gotVol: bnet_fsend(sd, _("label %s VolumeName=%s PoolName=%s MediaType=%s"), dev_name, mr.VolumeName, pr.Name, mr.MediaType); bsendmsg(ua, "Sending label command ...\n"); - while (bnet_recv(sd) > 0) { + while (bget_msg(sd, 0) > 0) { bsendmsg(ua, "%s", sd->msg); if (strncmp(sd->msg, "3000 OK label.", 14) == 0) { ok = TRUE; @@ -1253,6 +1256,7 @@ int open_db(UAContext *ua) close_db(ua); return 0; } + ua->jcr->db = ua->db; Dmsg1(50, "DB %s opened\n", ua->catalog->db_name); return 1; } @@ -1263,4 +1267,5 @@ void close_db(UAContext *ua) db_close_database(ua->db); } ua->db = NULL; + ua->jcr->db = NULL; } diff --git a/bacula/src/dird/ua_server.c b/bacula/src/dird/ua_server.c index 1ce7260392..606ea60ec4 100644 --- a/bacula/src/dird/ua_server.c +++ b/bacula/src/dird/ua_server.c @@ -33,7 +33,6 @@ extern void run_job(JCR *jcr); /* Imported variables */ -extern struct s_jl joblevels[]; extern int r_first; extern int r_last; extern struct s_res resources[]; @@ -155,11 +154,12 @@ getout: ua.UA_sock = NULL; } + close_db(&ua); /* do this before freeing JCR */ + if (ua.jcr) { free_jcr(ua.jcr); ua.jcr = NULL; } - close_db(&ua); if (ua.prompt) { free(ua.prompt); } diff --git a/bacula/src/dird/verify.c b/bacula/src/dird/verify.c index 4f216a3299..aa6e1a351f 100644 --- a/bacula/src/dird/verify.c +++ b/bacula/src/dird/verify.c @@ -325,6 +325,7 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, int last_full_id) struct stat statc; /* catalog stat */ int stat = JS_Terminated; char buf[MAXSTRING]; + char *fname = (char *)get_pool_memory(PM_MESSAGE); memset(&fdbr, 0, sizeof(FILE_DBR)); fd = jcr->file_bsock; @@ -341,9 +342,11 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, int last_full_id) char Opts_MD5[MAXSTRING]; /* Verify Opts or MD5 signature */ int do_MD5; + fname = (char *)check_pool_memory_size(fname, fd->msglen); + jcr->fname = (char *)check_pool_memory_size(fname, fd->msglen); Dmsg1(50, "Atts+MD5=%s\n", fd->msg); - if ((len = sscanf(fd->msg, "%ld %d %s %s", &file_index, &stream, - Opts_MD5, jcr->fname)) != 4) { + if ((len = sscanf(fd->msg, "%ld %d %100s %s", &file_index, &stream, + Opts_MD5, fname)) != 4) { Jmsg3(jcr, M_FATAL, 0, _("birdmsglen, fd->msg); jcr->JobStatus = JS_ErrorTerminated; @@ -359,6 +362,7 @@ msglen=%d msg=%s\n"), len, fd->msglen, fd->msg); decode_stat(attr, &statf); /* decode file stat packet */ do_MD5 = FALSE; jcr->fn_printed = FALSE; + strcpy(jcr->fname, fname); /* move filename into JCR */ Dmsg2(11, "dirdfname); Dmsg1(20, "dirdmsglen, fd->msg); } else if (do_MD5) { db_escape_string(buf, Opts_MD5, strlen(Opts_MD5)); if (strcmp(buf, fdbr.MD5) != 0) { - /***FIXME**** fname may not be valid */ prt_fname(jcr); if (debug_level >= 10) { Jmsg(jcr, M_INFO, 0, _(" MD5 not same. File=%s Cat=%s\n"), buf, fdbr.MD5); @@ -521,6 +524,7 @@ msglen=%d msg=%s\n"), len, fd->msglen, fd->msg); "AND File.FileIndex!=%d AND File.PathId=Path.PathId " "AND File.FilenameId=Filename.FilenameId", last_full_id, jcr->JobId); + /* missing_handler is called for each file found */ db_sql_query(jcr->db, buf, missing_handler, (void *)jcr); if (jcr->fn_printed) { stat = JS_Differences; diff --git a/bacula/src/lib/parse_conf.c b/bacula/src/lib/parse_conf.c index 5017929f47..009ab8cd81 100755 --- a/bacula/src/lib/parse_conf.c +++ b/bacula/src/lib/parse_conf.c @@ -457,11 +457,11 @@ void store_int(LEX *lc, struct res_items *item, int index, int pass) int token; token = lex_get_token(lc); - if (token != T_NUMBER) { + if (token != T_NUMBER || !is_a_number(lc->str)) { scan_err1(lc, "expected an integer number, got: %s", lc->str); } else { errno = 0; - *(int *)(item->value) = strtol(lc->str, NULL, 0); + *(int *)(item->value) = (int)strtod(lc->str, NULL); if (errno != 0) { scan_err1(lc, "expected an integer number, got: %s", lc->str); } @@ -476,11 +476,11 @@ void store_pint(LEX *lc, struct res_items *item, int index, int pass) int token; token = lex_get_token(lc); - if (token != T_NUMBER) { - scan_err1(lc, "expected an integer number, got: %s", lc->str); + if (token != T_NUMBER || !is_a_number(lc->str)) { + scan_err1(lc, "expected a positive integer number, got: %s", lc->str); } else { errno = 0; - token = strtol(lc->str, NULL, 0); + token = (int)strtod(lc->str, NULL); if (errno != 0 || token < 0) { scan_err1(lc, "expected a postive integer number, got: %s", lc->str); } @@ -497,7 +497,8 @@ void store_int64(LEX *lc, struct res_items *item, int index, int pass) int token; token = lex_get_token(lc); - if (token != T_NUMBER) { + Dmsg2(400, "int64=:%s: %f\n", lc->str, strtod(lc->str, NULL)); + if (token != T_NUMBER || !is_a_number(lc->str)) { scan_err1(lc, "expected an integer number, got: %s", lc->str); } else { errno = 0; @@ -514,14 +515,16 @@ void store_size(LEX *lc, struct res_items *item, int index, int pass) { int token, i, ch; uint64_t value; - int mod[] = {'k', 'm', 'g'}; - uint64_t mult[] = {1024, /* kilobyte */ + int mod[] = {'*', 'k', 'm', 'g', 0}; /* first item * not used */ + uint64_t mult[] = {1, /* byte */ + 1024, /* kilobyte */ 1048576, /* megabyte */ 1073741824}; /* gigabyte */ #ifdef we_have_a_compiler_that_works - int mod[] = {'k', 'm', 'g', 't'}; - uint64_t mult[] = {1024, /* kilobyte */ + int mod[] = {'*', 'k', 'm', 'g', 't', 0}; + uint64_t mult[] = {1, /* byte */ + 1024, /* kilobyte */ 1048576, /* megabyte */ 1073741824, /* gigabyte */ 1099511627776};/* terabyte */ @@ -532,9 +535,10 @@ void store_size(LEX *lc, struct res_items *item, int index, int pass) errno = 0; switch (token) { case T_NUMBER: + Dmsg2(400, "size num=:%s: %f\n", lc->str, strtod(lc->str, NULL)); value = (uint64_t)strtod(lc->str, NULL); if (errno != 0 || token < 0) { - scan_err1(lc, "expected a size, got: %s", lc->str); + scan_err1(lc, "expected a size number, got: %s", lc->str); } *(uint64_t *)(item->value) = value; break; @@ -547,25 +551,27 @@ void store_size(LEX *lc, struct res_items *item, int index, int pass) if (ISUPPER(ch)) { ch = tolower(ch); } - while (i < (int)sizeof(mod)) { + while (mod[++i] != 0) { if (ch == mod[i]) { lc->str_len--; lc->str[lc->str_len] = 0; /* strip modifier */ break; } - i++; } } - if (i >= (int)sizeof(mod)) { - scan_err1(lc, "expected a size, got: %s", lc->str); + if (mod[i] == 0 || !is_a_number(lc->str)) { + scan_err1(lc, "expected a size number, got: %s", lc->str); } + Dmsg3(400, "size str=:%s: %f i=%d\n", lc->str, strtod(lc->str, NULL), i); + value = (uint64_t)strtod(lc->str, NULL); Dmsg1(400, "Int value = %d\n", (int)value); if (errno != 0 || value < 0) { - scan_err1(lc, "expected a size, got: %s", lc->str); + scan_err1(lc, "expected a size number, got: %s", lc->str); } - *(uint64_t *)(item->value) = (uint64_t)(strtod(lc->str, NULL) * mult[i]); - Dmsg1(400, "Full value = %f\n", strtod(lc->str, NULL) * mult[i]); + *(uint64_t *)(item->value) = value * mult[i]; + Dmsg2(400, "Full value = %f %" lld "\n", strtod(lc->str, NULL) * mult[i], + value *mult[i]); break; default: scan_err1(lc, "expected a size, got: %s", lc->str); @@ -581,7 +587,7 @@ void store_size(LEX *lc, struct res_items *item, int index, int pass) void store_time(LEX *lc, struct res_items *item, int index, int pass) { int token, i, ch, value; - int mod[] = {'s', 'm', 'h', 'd', 'w', 'o', 'q', 'y'}; + int mod[] = {'*', 's', 'm', 'h', 'd', 'w', 'o', 'q', 'y', 0}; int mult[] = {1, 60, 60*60, 60*60*24, 60*60*24*7, 60*60*24*30, 60*60*24*91, 60*60*24*365}; @@ -589,7 +595,7 @@ void store_time(LEX *lc, struct res_items *item, int index, int pass) errno = 0; switch (token) { case T_NUMBER: - token = strtol(lc->str, NULL, 0); + token = (int)strtod(lc->str, NULL); if (errno != 0 || token < 0) { scan_err1(lc, "expected a time period, got: %s", lc->str); } @@ -604,17 +610,18 @@ void store_time(LEX *lc, struct res_items *item, int index, int pass) if (ISUPPER(ch)) { ch = tolower(ch); } - while (i < (int)sizeof(mod)) { + while (mod[++i] != 0) { if (ch == mod[i]) { + lc->str_len--; + lc->str[lc->str_len] = 0; /* strip modifier */ break; } - i++; } } - if (i >= (int)sizeof(mod)) { + if (mod[i] == 0 || !is_a_number(lc->str)) { scan_err1(lc, "expected a time period, got: %s", lc->str); } - value = strtol(lc->str, NULL, 0); + value = (int)strtod(lc->str, NULL); if (errno != 0 || value < 0) { scan_err1(lc, "expected a time period, got: %s", lc->str); } diff --git a/bacula/src/lib/protos.h b/bacula/src/lib/protos.h index fb1091ca4e..36b1d17149 100644 --- a/bacula/src/lib/protos.h +++ b/bacula/src/lib/protos.h @@ -138,6 +138,9 @@ char * encode_mode __PROTO((mode_t mode, char *buf)); char * edit_uint_with_commas __PROTO((uint64_t val, char *buf)); char * add_commas __PROTO((char *val, char *buf)); int do_shell_expansion(char *name); +int is_a_number(const char *num); + + /* *void print_ls_output __PROTO((char *fname, char *lname, int type, struct stat *statp)); */ diff --git a/bacula/src/lib/util.c b/bacula/src/lib/util.c index ab82295e92..742f373fe7 100644 --- a/bacula/src/lib/util.c +++ b/bacula/src/lib/util.c @@ -33,8 +33,37 @@ */ /* - * Edit a number with commas, the supplied buffer - * must be at least 27 bytes long. + * Check if specified string is a number or not. + * Taken from SQLite, cool, thanks. + */ +int is_a_number(const char *n) +{ + int digit_seen = 0; + + if( *n == '-' || *n == '+' ) { + n++; + } + while (ISDIGIT(*n)) { + digit_seen = 1; + n++; + } + if (digit_seen && *n == '.') { + n++; + while (ISDIGIT(*n)) { n++; } + } + if (digit_seen && (*n == 'e' || *n == 'E') + && (ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && ISDIGIT(n[2])))) { + n += 2; /* skip e- or e+ */ + while (ISDIGIT(*n)) { n++; } + } + return digit_seen && *n==0; +} + + +/* + * Edit an integer number with commas, the supplied buffer + * must be at least 27 bytes long. The incoming number + * is always widened to 64 bits. */ char *edit_uint_with_commas(uint64_t val, char *buf) { @@ -42,6 +71,10 @@ char *edit_uint_with_commas(uint64_t val, char *buf) return add_commas(buf, buf); } +/* + * Add commas to a string, which is presumably + * a number. + */ char *add_commas(char *val, char *buf) { int len, nc; @@ -70,8 +103,7 @@ char *add_commas(char *val, char *buf) /* Convert a string in place to lower case */ -void -lcase(char *str) +void lcase(char *str) { while (*str) { if (ISUPPER(*str)) diff --git a/bacula/src/stored/askdir.c b/bacula/src/stored/askdir.c index a7d3c19c08..b064dd9070 100644 --- a/bacula/src/stored/askdir.c +++ b/bacula/src/stored/askdir.c @@ -30,7 +30,7 @@ /* Requests sent to the Director */ static char Find_media[] = "CatReq Job=%s FindMedia=%d\n"; -static char Find_Vol_Info[] = "CatReq Job=%s GetVolInfo VolName=%s\n"; +static char Get_Vol_Info[] = "CatReq Job=%s GetVolInfo VolName=%s\n"; static char Update_media[] = "CatReq Job=%s UpdateMedia VolName=%s\ VolJobs=%d VolFiles=%d VolBlocks=%d VolBytes=%" lld " VolMounts=%d\ @@ -46,7 +46,7 @@ static char Job_status[] = "3012 Job %s jobstatus %d\n"; /* Responses received from the Director */ static char OK_media[] = "1000 OK VolName=%127s VolJobs=%d VolFiles=%d\ VolBlocks=%d VolBytes=%" lld " VolMounts=%d VolErrors=%d VolWrites=%d\ - VolMaxBytes=%" lld " VolCapacityBytes=%" lld "\n"; + VolMaxBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s\n"; static char OK_update[] = "1000 OK UpdateMedia\n"; @@ -80,7 +80,7 @@ static int do_request_volume_info(JCR *jcr) &vol->VolCatBlocks, &vol->VolCatBytes, &vol->VolCatMounts, &vol->VolCatErrors, &vol->VolCatWrites, &vol->VolCatMaxBytes, - &vol->VolCatCapacityBytes) != 10) { + &vol->VolCatCapacityBytes, vol->VolCatStatus) != 11) { Dmsg1(30, "Bad response from Dir: %s\n", dir->msg); return 0; } @@ -88,7 +88,6 @@ static int do_request_volume_info(JCR *jcr) strcpy(jcr->VolumeName, vol->VolCatName); /* set desired VolumeName */ Dmsg1(200, "Got Volume=%s\n", vol->VolCatName); - strcpy(vol->VolCatStatus, "Append"); return 1; } @@ -97,7 +96,8 @@ static int do_request_volume_info(JCR *jcr) * Get Volume info for a specific volume from the Director's Database * * Returns: 1 on success (not Director guarantees that Pool and MediaType - * are correct and VolStatus==Append) + * are correct and VolStatus==Append or + * VolStatus==Recycle) * 0 on failure * * Volume information returned in jcr @@ -109,7 +109,7 @@ int dir_get_volume_info(JCR *jcr) strcpy(jcr->VolCatInfo.VolCatName, jcr->VolumeName); Dmsg1(200, "dir_get_volume_info=%s\n", jcr->VolCatInfo.VolCatName); bash_spaces(jcr->VolCatInfo.VolCatName); - bnet_fsend(dir, Find_Vol_Info, jcr->Job, jcr->VolCatInfo.VolCatName); + bnet_fsend(dir, Get_Vol_Info, jcr->Job, jcr->VolCatInfo.VolCatName); return do_request_volume_info(jcr); } diff --git a/bacula/src/stored/dev.h b/bacula/src/stored/dev.h index ead1ddf4d4..f71b77f83f 100644 --- a/bacula/src/stored/dev.h +++ b/bacula/src/stored/dev.h @@ -91,6 +91,7 @@ typedef struct s_volume_catalog_info { uint32_t VolCatErrors; /* Number of errors this volume */ uint32_t VolCatWrites; /* Number of writes this volume */ uint32_t VolCatReads; /* Number of reads this volume */ + uint32_t VolCatRecycles; /* Number of recycles this volume */ uint64_t VolCatMaxBytes; /* max bytes to write */ uint64_t VolCatCapacityBytes; /* capacity estimate */ char VolCatStatus[20]; /* Volume status */ diff --git a/bacula/src/stored/device.c b/bacula/src/stored/device.c index f92081f16d..c577e6dfab 100644 --- a/bacula/src/stored/device.c +++ b/bacula/src/stored/device.c @@ -288,6 +288,7 @@ static int mount_next_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *label_blk) static int ready_dev_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) { int mounted = 0; + int recycle = 0; Dmsg0(100, "Enter ready_dev_for_append\n"); @@ -331,6 +332,9 @@ static int ready_dev_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) case VOL_OK: Dmsg1(200, "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: /* Check if we can accept this as an anonymous volume */ @@ -387,8 +391,10 @@ mount_next_vol: * 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) { /* fresh tape */ + if (dev->VolHdr.LabelType == PRE_LABEL || recycle) { Dmsg1(90, "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); @@ -415,13 +421,26 @@ mount_next_vol: write_volume_label_to_block(jcr, dev, block); dev->VolCatInfo.VolCatJobs = 1; dev->VolCatInfo.VolCatFiles = 1; - dev->VolCatInfo.VolCatMounts = 1; dev->VolCatInfo.VolCatErrors = 0; - dev->VolCatInfo.VolCatWrites = 1; 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"); dir_update_volume_info(jcr, &dev->VolCatInfo); - Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume %s on device %s\n"), - jcr->VolumeName, dev_name(dev)); + 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 diff --git a/bacula/src/stored/dircmd.c b/bacula/src/stored/dircmd.c index a2d0b62f3b..7713d9626f 100644 --- a/bacula/src/stored/dircmd.c +++ b/bacula/src/stored/dircmd.c @@ -60,7 +60,7 @@ static int cancel_cmd(JCR *cjcr); static int mount_cmd(JCR *jcr); static int unmount_cmd(JCR *jcr); static int status_cmd(JCR *sjcr); -static void label_device_if_ok(JCR *jcr, DEVICE *dev, char *vname, char *poolname); +static void label_volume_if_ok(JCR *jcr, DEVICE *dev, char *vname, char *poolname); struct s_cmds { char *cmd; @@ -260,12 +260,12 @@ static int label_cmd(JCR *jcr) if (open_dev(dev, volname, READ_WRITE) < 0) { bnet_fsend(dir, _("3994 Connot open device: %s\n"), strerror_dev(dev)); } else { - label_device_if_ok(jcr, dev, volname, poolname); + label_volume_if_ok(jcr, dev, volname, poolname); force_close_dev(dev); } } else if (dev->dev_blocked && dev->dev_blocked != BST_DOING_ACQUIRE) { /* device blocked? */ - label_device_if_ok(jcr, dev, volname, poolname); + label_volume_if_ok(jcr, dev, volname, poolname); } else if (dev->state & ST_READ || dev->num_writers) { if (dev->state & ST_READ) { bnet_fsend(dir, _("3901 Device %s is busy with 1 reader.\n"), @@ -275,7 +275,7 @@ static int label_cmd(JCR *jcr) dev_name(dev), dev->num_writers); } } else { /* device not being used */ - label_device_if_ok(jcr, dev, volname, poolname); + label_volume_if_ok(jcr, dev, volname, poolname); } V(dev->mutex); } else { @@ -300,7 +300,7 @@ static int label_cmd(JCR *jcr) * * Enter with the mutex set */ -static void label_device_if_ok(JCR *jcr, DEVICE *dev, char *vname, char *poolname) +static void label_volume_if_ok(JCR *jcr, DEVICE *dev, char *vname, char *poolname) { BSOCK *dir = jcr->dir_bsock; DEV_BLOCK *block; diff --git a/bacula/src/version.h b/bacula/src/version.h index 33f8cc8597..df061e161c 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -1,8 +1,8 @@ /* */ #define VERSION "1.19" #define VSTRING "1" -#define DATE "24 April 2002" -#define LSMDATE "24Apr02" +#define DATE "25 April 2002" +#define LSMDATE "25Apr02" /* Debug flags */ #define DEBUG 1