/* Copyright (c) 1998-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file tape.c The tape program. */ $* tape sn tape sc [] tape sl tape tn tape tc [comments] tape tl $* /** Backup set data. */ typedef struct { char line[2048]; /**< Text line. */ char *setNames[1024]; /**< All the set names. */ int used; /**< Currently used set. */ int confirmed; /**< Last confirmed set. */ } SetInfo; /** Backup tape data. */ typedef struct { unsigned used; /**< Number of uses. */ char comment[32]; /**< Comment for last backup to this tape. */ } TapeInfo; /** Backup tapes data. */ typedef struct { TapeInfo tapeInfo[10]; /**< Information for all ten tapes. */ int tapeDay; /**< Number of current backup. */ int confirmed; /**< Number of last confirmed backup. */ } TapeSetInfo; #include #include #include #include #include #if DK_HAVE_SYS_TYPES_H #include #endif #include #include $(trace-include) /** Initialize tape set information. @param info Tape set information to initialize. */ static void tapeSetInit DK_P1(TapeSetInfo *,info) { TapeInfo *ti; int i; info->tapeDay = info->confirmed = 0; ti = &(info->tapeInfo[0]); for(i = 0; i < 10; i++) { strcpy(ti->comment, "-------------------"); ti->used = 0; ti++; } } /** Read tape set information from file. @param info Tape set information. @param filename File name. @return 1 on success, 0 on error. */ static int tapeSetRead DK_P2(TapeSetInfo *,info, char *,filename) { int back = 0; FILE *f; char line[1024]; int i; TapeInfo *ti; tapeSetInit(info); if((f = dksf_fopen(filename, "r")) != NULL) { if(fgets(line, 1024, f)) { switch(sscanf(line, "%d %d", &(info->tapeDay), &(info->confirmed))) { case 0: { info->tapeDay = 0; } case 1: { info->confirmed = 0; } break; } i = 0; ti = &(info->tapeInfo[0]); back = 1; while(i < 10) { if(fgets(line, 1024, f)) { sscanf(line, "%s %u", &(ti->comment[0]), &(ti->used)); i++; ti++; } else { i = 10; back = 0; } } } else { } } else { back = 1; } return back; } /** Write tape set information to file. @param info Tape set to write. @param filename File name. @return 1 on success, 0 on error. */ static int tapeSetWrite DK_P2(TapeSetInfo *,info, char *,filename) { int back = 0, i = 0; FILE *f; TapeInfo *ti; if((f = dksf_fopen(filename, "w")) != NULL) { back = 1; fprintf(f, "%d %d\n", info->tapeDay, info->confirmed); ti = &(info->tapeInfo[0]); for(i = 0; i < 10; i++) { fprintf(f, "%s %u\n", &(ti->comment[0]), ti->used); ti++; } fclose(f); } else { fprintf(stderr, "ERROR: tape - Failed to write tape information file %s", filename ); fflush(stderr); } return back; } /** Read tape set information from file. @param set Tape set information record. @param filename Name of file to read. @return 1 on success, 0 on error. */ static int setRead DK_P2(SetInfo *,set, char *,filename) { int back = 0; FILE *f; char myline[1024]; char **ptr, *xptr; int lastWasText = 0, i = 0; $? " + setRead %s", filename f = dksf_fopen(filename, "r"); if(f) { $? " datei geoeffnet" if(fgets(&(set->line[0]), 2047, f)) { $? "erste Zeile gelesen" back = 1; set->used = set->confirmed = 0; if(fgets(myline, 1024, f)) { $? "zweite Zeile gelesen" switch(sscanf(myline, "%d %d", &(set->used), &(set->confirmed))) { case 0: { set->confirmed = set->used = 0; } break; case 1: { set->confirmed = 0; } break; } $? "zweite Zeile ausgewertet" } else { $? "keine zweite Zeile" set->confirmed = 0; set->used = 0; } $? "Namen initialisieren" ptr = &(set->setNames[0]); for(i = 0; i < 1024; i++) ptr[i] = NULL; lastWasText = 0; xptr = &(set->line[0]); $? "vor Setnamen setzen" while(*xptr) { if((*xptr == ' ') || (*xptr == '\t') || (*xptr == '\n')) { if(lastWasText) { *xptr = '\0'; } lastWasText = 0; } else { if(lastWasText == 0) { *(ptr++) = xptr; } lastWasText = 1; } xptr++; } $? "nach setzen der Setnamen" } fclose(f); } $? "- setRead" return back; } /** Write new tape set info to file. @param set Tape set information. @param filename File name to write. @return 1 on success, 0 on error. */ static int setWrite DK_P2(SetInfo *,set, char *,filename) { int back = 0; FILE *f; char **ptr; $? "+ setWrite" if((f = dksf_fopen(filename, "w")) != NULL) { ptr = &(set->setNames[0]); while(*ptr) fprintf(f, "%s ", *(ptr++)); fprintf(f, "\n%d %d\n", set->used, set->confirmed); fflush(f); fclose(f); back = 1; } $? "- setWrite %d", back return back; } /** Get name of current tape set. @param filename File name containing tape set information. @return 1 on success, 0 on error. */ static int tapeSetName DK_P1(char *,filename) { int back = 0, newtape = 0, rewrite = 0; SetInfo set; int setaz; char **ptr; $? "+ tapeSetName %s", filename if(setRead(&set, filename)) { ptr = &(set.setNames[0]); setaz = 0; while(*ptr) { setaz++; $? "Set %s", *ptr ptr++; } if(setaz > 0) { $? "setaz > 0" newtape = set.used; if(set.confirmed) { $? "bestaetigt" newtape++; rewrite = 1; set.confirmed = 0; } newtape = newtape % setaz; if(rewrite) set.used = newtape; ptr = &(set.setNames[0]); if(rewrite) { $? "Set muss geschrieben werden" if(setWrite(&set, filename)) { printf("%s\n", ptr[newtape]); fflush(stdout); } else { $? "Set muss nicht geschrieben werden" fprintf(stderr, "ERROR: tape - Failed to write to %s!\n", filename ); fflush(stdout); } } else { $? "kein neuschreiben" printf("%s\n", ptr[newtape]); fflush(stdout); } } else { fprintf(stderr, "ERROR: tape - No set names defined!\n"); fflush(stderr); } } else { fprintf(stderr, "ERROR: tape - Failed to read tape set file %s!\n", filename ); fflush(stdout); } $? "- tapeSetName %d", back return back; } /** Confirm backup to current tape set. @param filename Name of file containing the tape information. @return 1 on success, 0 on error. */ static int tapeSetConfirm DK_P1(char *,filename) { int back = 0; SetInfo set; $? "+ tapeSetConfirm %s", filename if(setRead(&set, filename)) { set.confirmed = 1; if(setWrite(&set, filename) == 0) { fprintf(stderr, "ERROR: tape - Failed to write tape set file %s!\n", filename ); fflush(stderr); } } else { fprintf(stderr, "ERROR: tape - Failed to read tape set file %s!\n", filename ); fflush(stderr); } return back; } /** List the tape sets. @param filename File name containing information about the tape sets. @return 1 on success, 0 on error. */ static int tapeSetList DK_P1(char *,filename) { int back = 0, i = 0; char **ptr; int setaz; SetInfo set; if(setRead(&set, filename)) { ptr = &(set.setNames[0]); setaz = 0; while(*ptr++) setaz++; ptr = &(set.setNames[0]); for(i = 0; i < setaz; i++) { if(i == set.used) { if(set.confirmed) { printf("+ "); } else { printf("- "); } } else { printf(" "); } printf("%s\n", ptr[i]); } } else { fprintf(stderr, "ERROR: tape - Failed to read file %s!\n", filename ); fflush(stderr); } return back; } /** Tape numbers in order of usage. This implements the 10 tape rotation scheme. At every time we will have backups of different ages. During 200 backups each of the 10 tapes is used 20 times. */ static int tapeNumbers[] = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 6, 1, 2, 3, 4, 7, 1, 2, 3, 4, 8, 2, 3, 4, 5, 6, 2, 3, 4, 5, 7, 2, 3, 4, 5, 8, 2, 3, 4, 5, 9, 3, 4, 5, 6, 7, 3, 4, 5, 6, 8, 3, 4, 5, 6, 9, 3, 4, 5, 6, 10, 4, 5, 6, 7, 8, 4, 5, 6, 7, 9, 4, 5, 6, 7, 10, 4, 5, 6, 7, 1, 5, 6, 7, 8, 9, 5, 6, 7, 8, 10, 5, 6, 7, 8, 1, 5, 6, 7, 8, 2, 6, 7, 8, 9, 10, 6, 7, 8, 9, 1, 6, 7, 8, 9, 2, 6, 7, 8, 9, 3, 7, 8, 9, 10, 1, 7, 8, 9, 10, 2, 7, 8, 9, 10, 3, 7, 8, 9, 10, 4, 8, 9, 10, 1, 2, 8, 9, 10, 1, 3, 8, 9, 10, 1, 4, 8, 9, 10, 1, 5, 9, 10, 1, 2, 3, 9, 10, 1, 2, 4, 9, 10, 1, 2, 5, 9, 10, 1, 2, 6, 10, 1, 2, 3, 4, 10, 1, 2, 3, 5, 10, 1, 2, 3, 6, 10, 1, 2, 3, 7 } ; /** Number of elements in \a tapeNumbers array. */ static int tapeNoAz = (int)(sizeof(tapeNumbers) / sizeof(int)); /** Get tape number for current backup. @param filename File name for tape information. @return 1 on success, 0 on error. */ static int tapeNumber DK_P1(char *,filename) { int back = 0; TapeSetInfo tsi; if(tapeSetRead(&tsi, filename)) { if(tsi.confirmed) { tsi.tapeDay += 1; tsi.tapeDay = tsi.tapeDay % tapeNoAz; tsi.confirmed = 0; if(tapeSetWrite(&tsi, filename)) { printf("%d\n", tapeNumbers[(tsi.tapeDay % tapeNoAz)]); back = 1; } else { fprintf(stderr, "ERROR: tape - Failed to write tape information file %s!\n", filename); fflush(stderr); } } else { printf("%d\n", tapeNumbers[(tsi.tapeDay % tapeNoAz)]); back = 1; } } else { fprintf(stderr, "ERROR: tape - Failed to read tape information file %s!\n", filename ); fflush(stderr); } return back; } /** Confirm one tape. @param filename File name with tape information. @return 1 on success, 0 on error. */ static int tapeConfirm DK_P1(char *,filename) { int back = 0; int nr = 0; TapeSetInfo tsi; TapeInfo *ti; time_t timer; struct tm *tmx; if(tapeSetRead(&tsi, filename)) { nr = tapeNumbers[(tsi.tapeDay % tapeNoAz)] - 1; ti = &(tsi.tapeInfo[nr]); tsi.confirmed = 1; time(&timer); tmx = localtime(&timer); sprintf( &(ti->comment[0]), "%04d-%02d-%02d_%02d:%02d:%02d", (tmx->tm_year + 1900), (1 + tmx->tm_mon), tmx->tm_mday, tmx->tm_hour, tmx->tm_min, tmx->tm_sec ); ti->used += 1; /* tsi.confirmed = 1; time(&timer); tmx = localtime(&timer); nr = tapeNumbers[(tsi.tapeDay % tapeNoAz)]; if(tmx) { sprintf(&((tsi.tapeInfo[nr]).comment[0]), "%04d-%02d-%02d %02d:%02d:%02d", (tmx->tm_year + 1900), (1 + tmx->tm_mon), tmx->tm_mday, tmx->tm_hour, tmx->tm_min, tmx->tm_sec ); } (tsi.tapeInfo[nr]).used += 1; */ if(tapeSetWrite(&tsi, filename)) { back = 1; } else { fprintf(stderr, "ERROR: tape - Failed to write tape information file %s!\n", filename ); fflush(stderr); } } else { fprintf(stderr, "ERROR: tape - Failed to read tape information file %s!\n", filename); fflush(stderr); } return back; } /** List tapes. @param filename File name containing tape information. @return 1 on success, 0 on error. */ static int tapeList DK_P1(char *,filename) { int back = 0; int i = 0; TapeInfo *ti; TapeSetInfo tsi; ti = &(tsi.tapeInfo[0]); if(tapeSetRead(&tsi, filename)) { back = 1; for(i = 0; i < 10; i++) { if(i == (tapeNumbers[(tsi.tapeDay % tapeNoAz)] - 1)) { if(tsi.confirmed) { printf("+ "); } else { printf("- "); } } else { printf(" "); } printf("%s (%d) %5u\n", &(ti->comment[0]), (i+1), ti->used); ti++; } } else { fprintf(stderr, "ERROR: tape - Failed to read tape information file %s!\n", filename); fflush(stderr); } return back; } /** Help text. */ static char *help_text[] = { "", "tape []", "==================================", "tape sn retrieves the name of the next tape set.", "tape sc [] confirms the current tape set.", "tape sl lists the tape sets.", "tape tn retrieves the number of the next tape.", "tape tc [] confirms writing to the current tape.", "type tl lists the tapes.", "", NULL }; /** Print short help text. */ static void usage DK_P0() { char **ptr; ptr = help_text; while(*ptr) { fputs(*(ptr++), stdout); fputc('\n', stdout); } } /** The main() function of the tape program. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ #if DK_HAVE_PROTOTYPES int main(int argc, char *argv[]) #else int main(argc, argv) int argc; char *argv[]; #endif { int setOrTape = 0; int action = 0; int exval = 0; char *ptr; $(trace-init tape.deb) if(argc >= 3) { ptr = argv[1]; while(*ptr != '\0') { switch(*ptr) { case 't': case 'T': { setOrTape = 1; } break; case 's': case 'S': { setOrTape = 0; } break; case 'n': case 'N': { action = 0; } break; case 'c': case 'C': { action = 1; } break; case 'l': case 'L': { action = 2; } break; } ptr++; } switch((3 * setOrTape) + action) { case 0: { exval = tapeSetName(argv[2]); } break; case 1: { exval = tapeSetConfirm(argv[2]); } break; case 2: { exval = tapeSetList(argv[2]); } break; case 3: { exval = tapeNumber(argv[2]); } break; case 4: { exval = tapeConfirm(argv[2]); } break; case 5: { exval = tapeList(argv[2]); } break; } } else { usage(); } exval = (exval ? 0 : 1); $(trace-end) exit(exval); return exval; }