/*
* $Id: scan.c,v 1.26 2002/10/17 20:02:31 ljb Exp $
* originally Id: scan.c,v 1.87 1998/07/29 21:15:17 gerald Exp
*/
/* The routines in this file scan/parse the datase.db files */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "mrt.h"
#include "trace.h"
#include <time.h>
#include <signal.h>
#include "config_file.h"
#include <fcntl.h>
#include "irrd.h"
extern trace_t *default_trace;
/* local functions */
static int populate_keyhash (irr_database_t *database);
static void pick_off_secondary_fields (char *buffer, int curr_f,
irr_object_t *irr_object,
enum DB_SYNTAX db_syntax);
void mark_deleted_irr_object (irr_database_t *database, u_long offset);
static void add_field_items (char *buf,
enum DB_SYNTAX db_syntax, LINKED_LIST **ll);
static char *build_indexes (FILE *fp, irr_database_t *db, irr_object_t *object,
u_long fp_pos, int update_flag, char *first_attr);
int find_blank_line (FILE *fp, char *buf, int buf_size,
enum STATES state, enum STATES *p_save_state,
u_long *position, u_long *offset, enum DB_SYNTAX db_syntax);
int dump_object_check (irr_object_t *object, enum STATES state, u_long mode,
int update_flag, irr_database_t *db, FILE *fp);
/* Note: it is not necessary to define every field, only
* those fields which irrd needs to recognize.
* col 1 is RIPE181 and col 2 is RPSL
*
* Also note that the data struct 'enum IRR_OBJECTS' defined in
* scan.h is the first dimension index into this array. get_curr_f ()
* returns the current field which acts as the index into this data struct.
* Any changes to key_info [] or to the 'enum IRR_OBJECTS' data struct
* need to be coordinated. m_info [] and obj_template [] also depend
* on 'enum IRR_OBJECTS'. See scan.h for more information. */
key_label_t key_info [] [2] = {
{ { "*an:", 4, NAME_F, AUTNUM_F}, { "aut-num:", 8, NAME_F, AUTNUM_F} },
{ { "*rt:", 4, NAME_F, ROUTE_F}, { "route:", 6, NAME_F, ROUTE_F} },
{ { "*am:", 4, NAME_F, ASMACRO_F}, { "", 1, NAME_F, ASMACRO_F} },
{ { "*cm:", 4, NAME_F, COMMUNITY_F},{ "", 1, NAME_F, COMMUNITY_F} },
{ { "*mt:", 4, NAME_F, MNTNER_F}, { "mntner:", 7, NAME_F, MNTNER_F} },
{ { "*ir:", 4, NAME_F, INET_RTR_F}, { "inet-rtr:", 9, NAME_F, INET_RTR_F} },
{ { "*pn:", 4, NAME_F, PERSON_F}, { "person:", 7, NAME_F, PERSON_F} },
{ { "*ro:", 4, NAME_F, ROLE_F}, { "role:", 5, NAME_F, ROLE_F} },
{ { "*is:", 4, NAME_F, IPV6_SITE_F},{ "ipv6-site:", 10, NAME_F, IPV6_SITE_F} },
{ { "*al:", 4, NON_NAME_F, XXX_F}, { "", 1, NON_NAME_F, XXX_F} },
{ { "*in:", 4, NAME_F, INETNUM_F}, { "inetnum:", 8, NAME_F, INETNUM_F} },
{ { "*or:", 4, NON_NAME_F, XXX_F}, { "origin:", 7, NON_NAME_F, XXX_F} },
{ { "*cl:", 4, NON_NAME_F, XXX_F}, { "", 1, NON_NAME_F, XXX_F } },
{ { "*wd:", 4, NON_NAME_F, XXX_F}, { "withdrawn:", 10, NON_NAME_F, XXX_F } },
{ { "*mb:", 4, NON_NAME_F, XXX_F}, { "mnt-by:", 7, NON_NAME_F, XXX_F } },
{ { "*ac:", 4, NON_NAME_F, XXX_F}, { "admin-c:", 8, NON_NAME_F, XXX_F } },
{ { "*tc:", 4, NON_NAME_F, XXX_F}, { "tech-c:", 7, NON_NAME_F, XXX_F } },
{ { "*nh:", 4, NON_NAME_F, XXX_F}, { "nic-hdl:", 8, NON_NAME_F, XXX_F} },
{ { "*dn:", 4, NAME_F, DOMAIN_F}, { "domain:", 7, NAME_F, DOMAIN_F } },
{ { "*ue:", 4, NON_NAME_F, XXX_F}, { "*error*:", 8, NON_NAME_F, XXX_F } },
{ { "*uw:", 4, NON_NAME_F, XXX_F}, { "warning:", 8, NON_NAME_F , XXX_F} },
/* rpsl specific fields */
{ { "", 1, NON_NAME_F, XXX_F}, { "mbrs-by-ref:", 12, NON_NAME_F, XXX_F } },
{ { "", 1, NON_NAME_F, XXX_F}, { "member-of:", 10, NON_NAME_F, XXX_F } },
{ { "", 1, NON_NAME_F, XXX_F}, { "members:", 8, NON_NAME_F, XXX_F } },
{ { "", 1, NON_NAME_F, XXX_F}, { "prefix:", 7, NON_NAME_F, XXX_F } },
{ { "", 1, NON_NAME_F, XXX_F}, { "as-set:", 7, NAME_F, AS_SET_F } },
{ { "", 1, NON_NAME_F , XXX_F},{ "route-set:", 10, NAME_F, ROUTE_SET_F } },
{ { "", 1, NON_NAME_F, XXX_F}, { "contact:", 8, NON_NAME_F, XXX_F } },
{ { "", 1, NON_NAME_F , XXX_F},{ "filter-set:", 11, NAME_F, FILTER_SET_F } },
{ { "", 1, NON_NAME_F , XXX_F},{ "rtr-set:", 8, NAME_F, RTR_SET_F } },
{ { "", 1, NON_NAME_F , XXX_F},{ "peering-set:", 12, NAME_F, PEERING_SET_F } },
{ { "", 1, NON_NAME_F , XXX_F},{ "key-cert:", 9, NAME_F, KEY_CERT_F } },
{ { "", 1, NON_NAME_F , XXX_F},{ "dictionary:", 11, NAME_F, DICTIONARY_F } },
{ { "", 1, NON_NAME_F , XXX_F},{ "repository:", 11, NAME_F, REPOSITORY_F } },
{ { "", 1, NON_NAME_F , XXX_F},{ "inet6num:", 9, NAME_F, INET6NUM_F } },
{ { "", 1, NON_NAME_F , XXX_F},{ "dom-prefix:", 11, NAME_F, DOMAIN_PREFIX_F } },
/* this should not change, add others before (ie, NO_FIELD row) */
{ { "",1, NON_NAME_F, XXX_F }, { "", 1, NON_NAME_F, XXX_F } },
};
static char *str_syntax[] = {
"RIPE181",
"RPSL"
};
#define MAX_RPSLNAME_LEN 32
/* scan_irr_serial
* read the <DB>.serial file and store the number for later use
*/
/* JMH - The serial number file is important only for mirrors
* and for authoritative databases. If its not there, starting
* from zero is fine. This function could happily be void,
* except that we can use it as a existance test for the file
* and that it contains a valid u_long
*/
int scan_irr_serial (irr_database_t *database) {
char tmp[BUFSIZE], file[BUFSIZE];
FILE *fp;
u_long serial = 0;
int ret_code = 0;
strcpy (tmp, database->name);
convert_toupper (tmp);
sprintf (file, "%s/%s.CURRENTSERIAL", IRR.database_dir, tmp);
fp = fopen (file, "r");
if (fp != NULL) {
memset (tmp, 0, sizeof (tmp));
if (fgets (tmp, sizeof (tmp), fp) != NULL) {
if (convert_to_lu (tmp, &serial) == 1) {
database->serial_number = serial;
ret_code = 1;
}
else
database->serial_number = 0;
}
fclose (fp);
}
return ret_code;
}
/* Write to disk the current serial value for DB (db->name).
*
* Function was rewritten to return a value to indicate if
* there were any errors in writing the serial value and also
* to avoid buffering issues. This is part of our support for
* atomic transactions.
*
* Note: Later we should write the current serial value as the
* first line in the DB in a fixed field. The original choice to
* have a seperate current serial file was (IMHO) a poor choice.
*
* Input:
* -pointer to the DB struct so we know the name of the DB
* and the current serial value to write to disk (db)
*
* Return:
* -1 if the current serial value was written to disk without error.
* -0 otherwise.
*/
int write_irr_serial (irr_database_t *db) {
int ret_code = 0;
char dbname[BUFSIZE], file[BUFSIZE], serial[20];
int fd;
/* make the current serial file name */
strcpy (dbname, db->name);
convert_toupper (dbname);
sprintf (file, "%s/%s.CURRENTSERIAL", IRR.database_dir, dbname);
/* now write the current serial to file */
if ((fd = open (file, O_WRONLY|O_TRUNC|O_CREAT, 0644)) >= 0) {
sprintf (serial, "%ld", db->serial_number);
if (write (fd, serial, strlen (serial)) > 0) {
SetStatusString (IRR.statusfile, dbname, "currentserial", serial);
ret_code = 1;
} else
trace (ERROR, default_trace, "write_irr_serial (): file write error: (%s)\n",
strerror (errno));
close (fd);
}
else
trace (ERROR, default_trace, "write_irr_serial (): file open error: (%s)\n",
strerror (errno));
return ret_code;
}
void write_irr_serial_export (u_long serial, irr_database_t *database) {
char db[BUFSIZE], file[BUFSIZE], serial_out[20];
FILE *fp;
if (database->export_filename != NULL)
strcpy (db, database->export_filename);
else
strcpy (db, database->name);
convert_toupper(db);
sprintf (file, "%s/%s.CURRENTSERIAL", IRR.ftp_dir, db);
if ((fp = fopen (file, "w")) != NULL) {
sprintf (serial_out, "%ld", serial);
fwrite (serial_out, 1, strlen (serial_out), fp);
fclose (fp);
}
SetStatusString (IRR.statusfile, db, "lastexport", serial_out);
}
/* scan_irr_file
* Open the db, update, or mirror log file for parsing
* The update_flag (if == 1 || 2) indicates this is a update/mirror file
* Determine the syntax (RIPE or RPSL)
*/
char *scan_irr_file (irr_database_t *database, char *extension,
int update_flag, FILE *update_fp) {
enum DB_SYNTAX syntax;
FILE *fp = update_fp;
char file[BUFSIZE], *p;
/* either an update or reading for the first time (a normal .db file) */
if (update_flag)
sprintf (file, "%s/.%s.%s", IRR.database_dir, database->name, extension);
else {
sprintf (file, "%s/%s.db", IRR.database_dir, database->name);
fp = database->db_fp;
}
/* CL: open file */
if (fp == NULL) {
if ((fp = fopen (file, "r+")) == NULL) {
/* try creating it for the first time */
trace (NORM, default_trace, "scan.c (%s) does not exist, creating it\n", file);
fp = fopen (file, "w+");
if (fp == NULL) {
trace (ERROR, default_trace, "scan_irr_file () "
"Could not open %s (%s)!\n",
file, strerror (errno));
return "IRRd error: Disk error in scan_irr_file (). Could not open DB";
}
}
/* the following setvbuf consumes alot of memory, performance does not seem
to be particularly improved by it. comment out for now -LJB */
/* setvbuf (fp, NULL, _IOFBF, IRRD_FAST_BUF_SIZE+2);*/ /* big buffer */
}
if (!update_flag)
database->db_fp = fp;
if ((syntax = get_database_syntax (fp)) == UNRECOGNIZED) {
trace (ERROR, default_trace, "Unrecognized DB syntax, reload aborted!\n");
trace (ERROR, default_trace, "**** DB-(%s)\n", database->name);
return "Unrecognized DB syntax!";
}
else if (syntax == EMPTY_DB) {
trace (NORM, default_trace, "WARNING: No DB objects found, "
"scan complete DB-(%s)!\n", database->name);
return "No DB objects found!";
}
if (database->db_syntax != syntax) {
if (database->db_syntax == EMPTY_DB)
database->db_syntax = syntax;
else {
trace (ERROR, default_trace, "DB syntax mismatch, reload aborted!\n");
trace (NORM, default_trace, "**** (%s) is type (%s), update file type is"
" (%s).\n", database->name, str_syntax[database->db_syntax],
str_syntax[syntax]);
return "DB syntax mismatch!";
}
}
if (IRR.database_syntax == EMPTY_DB)
IRR.database_syntax = syntax;
else if (IRR.database_syntax != MIXED &&
IRR.database_syntax != syntax)
IRR.database_syntax = MIXED;
if (!update_flag) {
/* rewind */
if (fseek (database->db_fp, 0L, SEEK_SET) < 0) {
trace (ERROR, default_trace, "scan_irr_file () rewind DB (%s) "
"error: %s\n", database->name, strerror (errno));
return "scan_irr_file () rewind DB error. Abort reload!";
}
database->time_loaded = time (NULL);
}
trace (NORM, default_trace, "Begin loading %s\n", file);
/* open transaction journal */
if (database->journal_fd < 0) {
char jfile[BUFSIZE];
make_journal_name (database->name, JOURNAL_NEW, jfile);
if ((database->journal_fd = open (jfile, O_RDWR | O_APPEND| O_CREAT, 0664)) < 0)
trace (ERROR, default_trace, "scan_irr_file () Could not open "
"journal file %s: (%s)!\n", jfile, strerror (errno));
}
/* if updating, log the serial number in the Journal file */
if (update_flag)
journal_maybe_rollover (database);
if (IRR.key_string_hash == NULL)
populate_keyhash (database);
p = (char *) scan_irr_file_main (fp, database, update_flag, SCAN_FILE);
fflush (database->db_fp);
trace (NORM, default_trace, "Finished loading %s\n", file);
return p;
}
/* scan_irr_file_main
* Parse file looking for objects.
* update_flag tell's us if we are parsing a mirror file (2)
* or !us...!ue update file (1), in contrast to a reload on bootstrap
* This tells us to look for mirror file format errors
* and save's us 'check for mirror header' cycles on reloads.
*/
void *scan_irr_file_main (FILE *fp, irr_database_t *database,
int update_flag, enum SCAN_T scan_scope) {
char buffer[4096], *cp, *p = NULL;
char first_attr[128];
u_long save_offset, offset, position, mode;
irr_object_t *irr_object;
enum IRR_OBJECTS curr_f;
enum STATES prev_state, save_state, state;
hash_spec_t hash_spec_item;
long lineno = 0;
/* init everything */
if (update_flag)
position = save_offset = offset = (u_long) ftell (fp);
else
position = save_offset = offset = 0;
mode = IRR_NOMODE;
state = BLANK_LINE;
curr_f = NO_FIELD;
irr_object = NULL;
if (scan_scope == SCAN_FILE) {
int hashsize;
if (update_flag)
hashsize = SMALL_HASH_SIZE; /* only need a smallish hash for updates */
else
hashsize = DEF_HASH_SIZE;
database->hash_spec_tmp =
HASH_Create (hashsize, HASH_KeyOffset,
HASH_Offset (&hash_spec_item, &hash_spec_item.key),
HASH_DestroyFunction, Delete_hash_spec, 0);
}
/* okay, here we go scanning the file */
while (state != DB_EOF) { /* scan to end of file */
if ((cp = fgets (buffer, sizeof (buffer), fp)) != NULL) {
lineno++;
position = offset;
offset += strlen (buffer);
}
prev_state = state;
state = get_state (buffer, cp, state, &save_state);
/*JW This doesn'tallow #'s, +'s at the end of an object
if (state == COMMENT &&
prev_state != COMMENT)
save_offset = position;
JW*/
/* dump comment lines and lines that exceed the buffer size */
if (state & (OVRFLW | OVRFLW_END | COMMENT ))
continue;
if (update_flag &&
state == START_F &&
irr_object == NULL) {
if (!strncmp ("%END", buffer, 4))
break; /* normal exit from successful mirror */
else {
state = pick_off_mirror_hdr (fp, buffer, sizeof(buffer), state, &save_state,
&mode, &position, &offset, database);
/* something wrong with update, abort scan */
if (state == DB_EOF) {
p = "IRRd error: scan_irr_file_main (): Missing 'ADD' or 'DEL' "
"and/or malformed update!";
break;
}
}
}
if (state & (DB_EOF | OVRFLW | OVRFLW_END | BLANK_LINE))
curr_f = NO_FIELD;
else if (state != LINE_CONT)
curr_f = get_curr_f (buffer);
/* we have entered into a new object -- create the initial structure */
if (irr_object == NULL && state == START_F) {
irr_object = New_IRR_Object (buffer, position, mode);
/* save a copy of the first attribute for logging purposes */
strncpy(first_attr, buffer, sizeof(first_attr));
first_attr[sizeof(first_attr) - 1] = '\0';
}
/* Ignore these fields if they come at the end of the object.
* The trick is to treat the fields like a comment at the end of the
* object.
* dump_object_check() will set state = DB_EOF if other error's found
*/
if (curr_f == SYNTAX_ERR || curr_f == WARNING) {
/* this junk should not be in a well-formed transaction */
if (atomic_trans) {
p = "IRRd error: scan_irr_file_main (): 'WARNING' or 'ERROR' "
"attribute found in update. Abort transaction!";
if (irr_object != NULL)
Delete_IRR_Object (irr_object);
break;
}
trace (ERROR, default_trace,"In (db,serial)=(%s,%lu) found 'WARNING' "
"or '*ERROR*' line, attempting to remove extraneous lines starting with:\n%s", database->name, database->serial_number + 1, buffer);
/* mark end of object, read past err's and warn's */
save_offset = position;
state = find_blank_line (fp, buffer, sizeof(buffer), state, &save_state,
&position, &offset, database->db_syntax);
state = dump_object_check (irr_object, state, mode, update_flag,
database, fp);
if (state == DB_EOF) { /* something went wrong, dump object and abort */
trace (ERROR, default_trace,"Attempt to remove RIPE server extraneous line failed! Abort!\n");
Delete_IRR_Object (irr_object);
irr_object = NULL;
mode = IRR_NOMODE;
continue;
} else
trace (NORM, default_trace, "Attempt to remove extraneous line succeeded!\n");
}
if (curr_f != NO_FIELD && (state & (START_F | LINE_CONT)) ) {
/* if continuation line, attribute value starts at beginning + 1 */
if (state == LINE_CONT)
cp = buffer + 1; /* Ignore initial whitespace or '+' */
else /* skip over attribute name label */
cp = buffer + key_info[curr_f][database->db_syntax].len;
/* NAME_F indicates object class name attribute */
if (key_info[curr_f][database->db_syntax].f_type == NAME_F) {
whitespace_remove(cp);
if (*cp != '\0') { /* class name value may be on a continuation line */
if (irr_object->name != NULL) {
/* Shouldn't have more than one class name attribute */
trace (NORM, default_trace, "Warning! Multiple class name attributes: Previous - %s %s, New - %s %s\n", key_info[irr_object->type][database->db_syntax].name, irr_object->name, key_info[curr_f][database->db_syntax].name, cp );
} else {
irr_object->name = strdup (cp);
irr_object->type = curr_f;
irr_object->filter_val = key_info[curr_f][database->db_syntax].filter_val;
}
}
} else
/* add secondary keys, and store things like origin and mnt-by */
pick_off_secondary_fields (cp, curr_f, irr_object, database->db_syntax);
continue;
}
/* Process OBJECT (we just read a '\n' on a line by itself or eof) */
if ( (state & (BLANK_LINE | DB_EOF)) && irr_object != NULL) {
if (scan_scope == SCAN_OBJECT)
return (void *) irr_object;
if (curr_f == SYNTAX_ERR || curr_f == WARNING)
position = save_offset;
else if (state == DB_EOF)
position = offset;
if ((p = build_indexes (fp, database, irr_object, position, update_flag, first_attr)) != NULL) {
if (!update_flag || (update_flag == 1 && atomic_trans))
state = DB_EOF; /* abort scan, something wrong found in input file */
else
p = NULL; /* ignore errors if mirroring or non-atomic update */
}
Delete_IRR_Object (irr_object);
irr_object = NULL;
mode = IRR_NOMODE;
}
} /* while (state != DB_EOF) */
/* only do on reload's and mirror updates */
if (scan_scope == SCAN_FILE) {
if (p == NULL)
commit_spec_hash (database); /* commit if no critical errors */
HASH_Destroy (database->hash_spec_tmp);
}
return (void *) p; /* return error string (if any) */
}
/* pick_off_secondary_fields
* store some information like as_origin, communities,
* and secondary indicie keys
*/
void pick_off_secondary_fields (char *buffer, int curr_f,
irr_object_t *irr_object,
enum DB_SYNTAX db_syntax) {
char *cp = buffer;
/* rt object origin */
if (curr_f == ORIGIN) {
whitespace_newline_remove(cp);
cp += 2;
irr_object->origin = (u_short) atoi (cp);
} else if (curr_f == NIC_HDL) {
whitespace_newline_remove(cp);
irr_object->nic_hdl = strdup (cp);
} else if (curr_f == WITHDRAWN) { /* withdrawn */
irr_object->withdrawn = 1;
} else if (db_syntax == RPSL && curr_f == PREFIX) { /* prefix -- IPV6 Site object */
whitespace_newline_remove(cp);
LL_Add (irr_object->ll_prefix, ascii2prefix (AF_INET6, cp));
}
/* ROUTE and AUT-NUM mnt-by: scanning */
else if (db_syntax == RPSL && curr_f == MNT_BY)
add_field_items (cp, db_syntax, &irr_object->ll_mnt_by);
/* ROUTE and AUT-NUM member-of: scanning */
else if (db_syntax == RPSL && curr_f == MEMBER_OF)
add_field_items (cp, db_syntax, &irr_object->ll_mbr_of);
/* AS-SET, RS-SET members: scanning */
else if (db_syntax == RPSL && curr_f == MEMBERS)
add_field_items (cp, db_syntax, &irr_object->ll_as);
/* AS-SET, RS-SET mbrs-by-ref: scanning */
else if (db_syntax == RPSL && curr_f == MBRS_BY_REF)
add_field_items (cp, db_syntax, &irr_object->ll_mbr_by_ref);
/* ASMACRO *al: scanning, RIPE181 only */
else if (db_syntax == RIPE181 && curr_f == AS_LIST)
add_field_items (cp, db_syntax, &irr_object->ll_as);
}
void add_field_items (char *buf, enum DB_SYNTAX db_syntax, LINKED_LIST **ll) {
char *cp = buf, *last;
whitespace_newline_remove (cp);
if (db_syntax == RPSL)
strtok_r (cp, ",", &last);
else
strtok_r (cp, " ", &last);
while (cp != NULL && *cp != '\0') {
whitespace_remove (cp);
if (*ll == NULL) /* this way we know if *ll != NULL list is non-empty */
*ll = LL_Create (LL_DestroyFunction, free, 0);
/* sanity check */
if (strlen (cp) > 0)
LL_Add ((*ll), strdup (cp));
if (db_syntax == RPSL)
cp = strtok_r (NULL, ",", &last);
else
cp = strtok_r (NULL, " ", &last);
}
}
/* JW: note state OVRFLW needs to be fixed so that if it
* occurs the object is dumped completely. This is
* consistent with transaction processing which will
* come next. The final step will be to roll-back
* any objects that were added before the buffer
* overflow and to report 'transaction abort' as
* the return code.
*/
/* Given the previous state and the current line to be
* processed return the next state which corresponds
* to the current input line. This state machine is
* needed by the scan routine to know when it is processing
* a new attribute so that it can build indexes, where the
* end of the object is and to detect line continuation.
* Several other routines also use this function for various
* sub-tasks. See scan.h for an explanation of the states.
*
* Return:
* state associated with the current input line
*
*/
int get_state (char *buf, char *cp,
enum STATES state, enum STATES *p_save_state) {
char *p;
if (cp == NULL) return DB_EOF;
if (buf[strlen (buf) - 1] != '\n') return OVRFLW;
/*JW put in later to support buffer overflow processing
if (state == OVRFLW) {
if (buf[0] == '\n') return BLANK_LINE;
if (buf[0] == '\r' &&
buf[1] == '\n') return BLANK_LINE;
return OVRFLW;
}*/
/* can have comments embedded in fields, ignore input after '#' */
if ((p = strchr (buf, '#')) != NULL) {
if (p == buf) { /* line starts with '#', we have a comment line */
if (state != COMMENT)
*p_save_state = state; /* save state for when we come out of comment */
return COMMENT;
}
*p = '\0'; /* effectively ignore input at '#' and beyond */
}
if (state == COMMENT)
state = *p_save_state; /* comment is over, restore old state */
if (state & (BLANK_LINE | OVRFLW_END)) {
if (buf[0] == '\n') return BLANK_LINE;
if (buf[0] == '\r' &&
buf[1] == '\n') return BLANK_LINE;
/* JMH - these are not really valid in the BLANK_LINE state
Will the parser ever pass this in? */
if (buf[0] == ' ' ||
buf[0] == '\t' ||
buf[0] == '+') return OVRFLW_END;
return START_F;
}
if (state & (START_F | LINE_CONT)) {
if (buf[0] == '\n') return BLANK_LINE;
if (buf[0] == '\r' &&
buf[1] == '\n') return BLANK_LINE;
if (buf[0] == ' ' ||
buf[0] == '\t' ||
buf[0] == '+') return LINE_CONT;
return START_F;
}
return OVRFLW_END;
}
/* get the current field and return it's index (eg, 'mnt-by:' or 'source:') */
int get_curr_f (char *buf) {
keystring_hash_t *keystring_item;
char *src, *dst;
char tmp[MAX_RPSLNAME_LEN + 1];
int i;
src = buf;
dst = tmp;
i = 0;
while (i < MAX_RPSLNAME_LEN && *src != '\0') {
if ( (*dst++ = tolower(*src++)) == ':') /* convert to lowercase and check for colon */
break;
i++;
}
*dst = '\0'; /* write a null after the colon */
keystring_item = HASH_Lookup (IRR.key_string_hash, tmp);
if (keystring_item == NULL) {
/* trace (NORM, default_trace,"Warning! Unrecognized attr: %s", buf); */
return (NO_FIELD);
}
return (keystring_item->num);
}
/* read past an object to the first blank line */
int find_blank_line (FILE *fp, char *buf, int buf_size,
enum STATES state, enum STATES *p_save_state,
u_long *position, u_long *offset, enum DB_SYNTAX db_syntax) {
char *cp;
do {
if ((cp = fgets (buf, buf_size, fp)) != NULL) {
*position = *offset;
*offset += strlen (buf);
}
/* } BOGUS for % in vi */
state = get_state (buf, cp, state, p_save_state);
/* only dump lines if the ERROR or WARN line come
* at the end of the object.
*/
if (state == START_F) {
if (strncasecmp (key_info[WARNING][db_syntax].name, buf,
key_info[WARNING][db_syntax].len) &&
strncasecmp (key_info[SYNTAX_ERR][db_syntax].name, buf,
key_info[SYNTAX_ERR][db_syntax].len)) {
trace (ERROR, default_trace,"find_blank_line(): Encountered ERROR or WARN line embedded within an object. Abort scan at following line: %s", buf);
return (DB_EOF);
}
}
} while (state != BLANK_LINE && state != DB_EOF);
return (state);
}
/* Checks for exactly one blank line past "ADD" or "DEL"
* and then start of object. Else something wrong, abort update, eg,
* ADD
*
* route: 198.108.60.0/24
*
*/
int read_blank_line_input (FILE *fp, char *buf, int buf_size,
enum STATES state, enum STATES *p_save_state,
u_long *position, u_long *offset,
irr_database_t *database) {
char *cp = NULL;
int lineno = 0;
do {
if ((cp = fgets (buf, buf_size, fp)) != NULL) {
*position = *offset;
*offset += strlen (buf);
}
/* } BOGUS to make vi % happy */
lineno++;
} while ((state = get_state (buf, cp, state, p_save_state)) == BLANK_LINE && lineno < 2);
if (state == START_F && lineno == 2)
return (state);
else
return (DB_EOF); /* something wrong with input, abort scan */
}
/* Check for an 'ADD' or 'DEL' at the begining of the
* mirror or update. Also calls read_blank_line_input ()
* to check for exactly one blank line and then start
* of object.
*
* Return:
* START_F if no errors.
* DB_EOF otherwise.
*/
int pick_off_mirror_hdr (FILE *fp, char *buf, int buf_size,
enum STATES state, enum STATES *p_save_state,
u_long *mode, u_long *position,
u_long *offset, irr_database_t *db) {
if (!strncasecmp ("ADD", buf, 3)) {
*mode = IRR_UPDATE;
state = read_blank_line_input (fp, buf, buf_size, state, p_save_state, position, offset, db);
}
else if (!strncasecmp ("DEL", buf, 3)) {
*mode = IRR_DELETE;
state = read_blank_line_input (fp, buf, buf_size, state, p_save_state, position, offset, db);
}
else
state = DB_EOF; /* no "ADD" or "DEL" so abort scan */
if (state == DB_EOF) {
trace (ERROR, default_trace,"scan.c: pick_off_mirror_hdr(): abort scan\n");
trace (ERROR, default_trace,"line (%s)\n", buf);
}
return (state);
}
char *build_indexes (FILE *fp, irr_database_t *db, irr_object_t *object,
u_long fp_pos, int update_flag, char *first_attr) {
u_long db_offset = 0;
int del_obj = -1;
int skip_obj = 0;
char *p = NULL;
/* good to have these checks so our scanner
can deal with junk objects */
if (update_flag && object->mode == IRR_NOMODE) {
trace (ERROR, default_trace, "got object with no "
"ADD or DEL during udpate, obj key-(%s), db-(%s)\n",
object->name, db->name);
return "Corrupted update file. Missing 'ADD' or 'DEL'";
}
if ( object->name == NULL || /* can get *XX: objects on db loads, */
object->type == NO_FIELD) { /* these obj's have NULL names */
if (update_flag) { /* log unrecognized object classes on updates */
trace (NORM, default_trace, "Unknown object class - %s", first_attr);
p = "Unrecognized object class";
}
skip_obj = 1;
}
if (db->obj_filter & object->filter_val) {
skip_obj = 1; /* skip object if it should be filtered */
/* trace (NORM, default_trace, "Filtered object class - %s", first_attr); */
}
object->len = fp_pos - object->offset;
object->fp = fp;
if (!skip_obj) {
switch (object->mode) {
case IRR_NOMODE: /* reading .db file for first time */
add_irr_object (db, object);
break;
case IRR_DELETE:
if (!update_flag) { /* db file has a "DEL" in it, abort scan */
trace (ERROR, default_trace, "DEL found in DB file, obj key-(%s)\n", object->name);
return "DB file has a 'DEL'";
}
if ((del_obj = delete_irr_object (db, object, &db_offset)) > 0) {
db->num_objects_deleted[object->type]++;
db->num_changes++;
}
else if (update_flag == 1) { /* !us...!ue udpate = 1, mirror update = 2 */
p = "Disk or indexing error! IRR_DELETE";
trace (ERROR, default_trace,
"DELETE disk or indexing error: key (%s)\n", object->name);
return (p);
}
break;
case IRR_UPDATE: /* implicit replace from mirror or update */
if (!update_flag) { /* db file has an "ADD" in it, abort scan */
trace (ERROR, default_trace,"ADD found in DB file, obj key-(%s)\n", object->name);
return "DB file has a 'ADD'. Abort!";
}
del_obj = delete_irr_object (db, object, &db_offset);
if (add_irr_object (db, object) > 0) {
db->num_objects_changed[object->type]++;
db->num_changes++;
}
else {
p = "Disk or indexing error! IRR_UPDATE: add_irr_object ()";
trace (ERROR, default_trace,
"UPDATE disk or indexing error: key (%s)\n", object->name);
}
break;
default:
trace (ERROR, default_trace,
"unrecognized mode (%d) obj key-(%s)\n",
object->mode, object->name);
return "Something wrong with file, no mode specified!";
}
}
if (update_flag) {
if (del_obj > 0)
mark_deleted_irr_object (db, db_offset);
if (!atomic_trans || update_flag != 1)
journal_irr_update (db, object, object->mode, skip_obj);
}
return p;
}
/* An *ERROR* or WARNING line has been found, now see if the scan should
* continue. Also journal the update if scan continues. We dump objects
* with said fields and continue scanning if all these checks pass.
*/
int dump_object_check (irr_object_t *object, enum STATES state, u_long mode,
int update_flag, irr_database_t *database, FILE *fp) {
if (mode == IRR_NOMODE) {
if (update_flag)
trace (ERROR, default_trace,"Missing 'ADD' or 'DEL' in mirror file "
"(%s), exit from dump object!\n", database->name);
else
trace (ERROR, default_trace,"'WARNING' or '*ERROR*' found in DB file "
"(%s), exit from dump object!\n", database->name);
state = DB_EOF;
}
else if (!update_flag) {
trace (ERROR, default_trace,"Serial update and 'WARNING' or '*ERROR*' "
"found in file (%s), exit from dump object!\n", database->name);
state = DB_EOF;
}
else if (state == DB_EOF) { /* causes us to skip over bad obj, next mirror
* will be able to run */
object->fp = fp;
journal_irr_update (database, object, mode, 1);
}
return (state);
}
static int populate_keyhash (irr_database_t *database) {
keystring_hash_t keystring_item;
int i;
IRR.key_string_hash =
HASH_Create (100, HASH_KeyOffset, HASH_Offset (&keystring_item, &keystring_item.key), 0);
/* populate */
for (i = 0; i < IRR_MAX_KEYS; i++) {
keystring_hash_t *keystring_item;
if (strlen (key_info[i][database->db_syntax].name) <= 0) continue;
keystring_item = New (keystring_hash_t);
keystring_item->key = strdup (key_info[i][database->db_syntax].name);
keystring_item->num = i;
HASH_Insert (IRR.key_string_hash, keystring_item);
}
return (1);
}
syntax highlighted by Code2HTML, v. 0.9.1