/* * $Id: journal.c,v 1.11 2002/10/17 20:02:30 ljb Exp $ * originally Id: journal.c,v 1.13 1998/06/23 23:30:44 gerald Exp */ #include #include #include "mrt.h" #include "trace.h" #include #include #include "config_file.h" #include #include #include "irrd.h" extern trace_t *default_trace; void make_journal_name (char * dbname, int journal_ext, char * journal_name); /* journal_irr_update * Record what we're doing to the database in the .journal file * if mode == IRR_UPDATE then an add */ /* JW this routine can be speeded up. We know the offset and length of the object, lets read it and write it with two sys calls */ void journal_irr_update (irr_database_t *db, irr_object_t *object, int mode, int skip_obj) { char buffer[BUFSIZE+1]; char *sadd = "ADD\n\n"; char *sdelete = "DEL\n\n"; db->serial_number++; /* until we have our syntax checker, just copy the serial as is */ #ifdef notdef /* serial numbers will get messed up if we actually skip */ if (!skip_obj) /* don't put bogus/filtered objects in journal file */ #endif { journal_log_serial_number (db); if (mode == IRR_UPDATE) write (db->journal_fd, sadd, strlen (sadd)); else if (mode == IRR_DELETE) write (db->journal_fd, sdelete, strlen (sdelete)); else { trace (ERROR, default_trace,"ERROR journal.c: journal_write_serial(): unrecognized mode (%d) db-(%s)\n", mode, db->name); return; /* write (db->journal_fd, snomode, strlen (snomode)); */ } fseek (object->fp, object->offset, SEEK_SET); while (fgets (buffer, BUFSIZE, object->fp) != NULL) { if (strlen (buffer) < 2) break; write (db->journal_fd, buffer, strlen (buffer)); } strcpy (buffer, "\n"); write (db->journal_fd, buffer, strlen (buffer)); } /* whew! The change is on disk, so we can save the new serial number */ write_irr_serial (db); return; } /* journal_log_serial_number * For crash recovery and when we act as a mirror server, * stamp the .journal file with the current serial * number for the database */ void journal_log_serial_number (irr_database_t *database) { char buffer[512]; sprintf (buffer, "%s SERIAL %ld\n", "%", database->serial_number); write (database->journal_fd, buffer, strlen (buffer)); return; } /* if the journal file is too big, roll it over and create a .JOURNAL.old * or something like that */ void journal_maybe_rollover (irr_database_t *database) { struct stat buf; char file_old[BUFSIZE], file_new[BUFSIZE]; fstat(database->journal_fd, &buf); if (buf.st_size > IRR_MAX_JOURNAL_SIZE) { trace (NORM, default_trace, "Rolling Journal file (> %d bytes) for %s\n", IRR_MAX_JOURNAL_SIZE, database->name); make_journal_name (database->name, JOURNAL_NEW, file_new); make_journal_name (database->name, JOURNAL_OLD, file_old); close (database->journal_fd); rename(file_new, file_old); if ((database->journal_fd = open (file_new, O_RDWR | O_CREAT, 0664)) < 0) trace (NORM, default_trace, "**** ERROR **** Could not open %s (%s)!\n", file_new, strerror (errno)); } return; } /* * return 1 if *.JOURNAL file exists and a valid serial # header is found * otherwise return 0 * return value of first good serial found if possible */ int find_oldest_serial (char *dbname, int journal_ext, u_long *oldestserial) { char file[BUFSIZE], buf[BUFSIZE]; FILE *fp; make_journal_name (dbname, journal_ext, file); if ((fp = fopen (file, "r")) != NULL) { while (fgets (buf, sizeof (buf) - 1, fp) != NULL) { if (sscanf (buf, "%% SERIAL %s", file) == 1) { fclose (fp); if (convert_to_lu (file, oldestserial) < 0) return (0); return (1); } } } if (fp != NULL) fclose (fp); return (0); } /* * return 1 if *.CURRENTSERIAL is read without error * else return -1 */ int get_current_serial (char *dbname, u_long *currserial) { char tmp[BUFSIZE], file[BUFSIZE]; int ret_val = -1; FILE *fp; strcpy (tmp, dbname); convert_toupper(tmp); sprintf (file, "%s/%s.CURRENTSERIAL", IRR.database_dir, tmp); if ((fp = fopen (file, "r")) != NULL) { memset (tmp, 0, sizeof (tmp)); if (fgets (tmp, sizeof (tmp) - 1, fp) != NULL && convert_to_lu (tmp, currserial) > 0) ret_val = 1; fclose (fp); } return (ret_val); } void make_journal_name (char * dbname, int journal_ext, char * journal_name) { if (journal_ext == JOURNAL_NEW) sprintf (journal_name, "%s/%s.%s", IRR.database_dir, dbname, SJOURNAL_NEW); else sprintf (journal_name, "%s/%s.%s", IRR.database_dir, dbname, SJOURNAL_OLD); } #define FLS_BUFSIZE 2048 #define FLS_OVERFLOW 12 /* 99 billion + NL */ /* * return 0 if unable to find a serial number, or there are problems opening * or reading JOURNAL file. * return 1 if found */ int find_last_serial (char *dbname, int journal_ext, u_long *last_serial) { char file[BUFSIZE], sz_serialno[BUFSIZE]; char buf [FLS_BUFSIZE + FLS_OVERFLOW + 1]; off_t offset; ssize_t bytes_read, bytes_to_read = FLS_BUFSIZE; int off_by_one = 0; int i, fd = -1; FILE *fp; make_journal_name (dbname, journal_ext, file); if ((fp = fopen (file, "r")) != NULL) { fd = fileno (fp); memset(buf, 0, FLS_BUFSIZE + FLS_OVERFLOW + 1); /* Find end of file */ offset = lseek (fd, 0L, SEEK_END); if (offset == -1) { goto FAIL; } while (offset > 0) { offset = MAX((offset - FLS_BUFSIZE), 0); /* We should really check return here */ lseek(fd, offset, SEEK_SET); bytes_read = read (fd, buf, bytes_to_read); if (bytes_read <= 0) { /* We probably should throw a trace here */ goto FAIL; } /* Start at end of buffer, work towards front looking for % SERIAL */ for (i = bytes_read + off_by_one; (i >= 0) && (buf[i] != '%'); i--); off_by_one = 0; if ((i > 0) || ((i == 0) && (offset == 0))) { /* We've found the % */ /* It either at start of file or has a preceding NL */ if ((i == 0) || (buf[i-1] == '\n')) { sscanf(buf+i, "%% SERIAL %s\n", sz_serialno); if (convert_to_lu (sz_serialno, last_serial) < 0) goto FAIL; else { fclose (fp); return (1); } break; } } else if (i == 0) { /* And offset != 0 */ off_by_one = 1; } if (offset <= bytes_to_read) { bytes_to_read = offset; } memcpy(buf + bytes_to_read, buf, ((FLS_OVERFLOW+FLS_BUFSIZE)-bytes_to_read)); } } FAIL: if (fd >= 0) { fclose (fp); } return (0); }