/* * $Id: simple-cache.c,v 0.25 1992/08/12 04:11:14 ceder Exp $ * Copyright (C) 1991 Lysator Academic Computer Association. * * This file is part of the LysKOM server. * * LysKOM is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * LysKOM is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with LysKOM; see the file COPYING. If not, write to * Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN, * or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, * MA 02139, USA. * * Please mail bug reports to bug-lyskom@lysator.liu.se. */ /* * This module contains some simple simulations of the routines in * cache.c. * * Extracted from ram-cache.c and rewritten by ceder. * * New database format with texts in their own file by Inge Wallin. * * New save algorithm by ceder. */ static char *rcsid = "$Id: simple-cache.c,v 0.25 1992/08/12 04:11:14 ceder Exp $"; /* * Possible improvements: +++ * When there are consecutive items in file A that shall be copied * to file B, copy them in one transfer (up to a certain limit). * * In pre_sync: compute size of, and allocate disk space for file B. */ /* * All functions that can fail sets kom_errno to a suitable value * if they fail. */ #include #include #include #include #include #include #include #include #include "s-collat-tabs.h" #include #include #include "cache.h" #include #include "lyskomd.h" #include "ram-parse.h" #include "log.h" #include "com.h" #include "isc-interface.h" #include "ram-output.h" #include "connections.h" #include "prot-a-output.h" #include "send-async.h" #include "memory.h" #include "exp.h" #include "tmp-limits.h" #include "cache-node.h" #include "disk-cache.h" static Small_conf * small_conf_arr[ MAX_CONF ]; static Cache_node_mcb * pers_mcb; static Cache_node_mcb * conf_mcb; static int next_free_num = 1; static Cache_node_mcb * text_mcb; static int next_text_num = 1; /* Defined in ramkomd.c */ extern char datafilename[BUFSIZ]; extern char backupfilename[BUFSIZ]; extern char textfilename[BUFSIZ]; /* * The elements in the following lists with same index refers to the same * conference. */ static int no_of_match_info; EXPORT Matching_info *match_table = NULL; EXPORT Conf_no *conf_table = NULL; /* Used in conference.c. */ static FILE *text_file= NULL; static FILE *file_a = NULL; /* Current file. */ static FILE *file_b = NULL; /* File under construction. */ /* * Four state variables for the background save. */ static enum { sync_save_conf, sync_save_pers, sync_save_text, sync_error, sync_wait, sync_ready } sync_state; static long sync_next; static Conf_no highest_conf_no; static Text_no highest_text_no; BUGDECL; /* Macros */ #define TRACE2(format, arg) if ( buglevel > 2 ) printf(format, arg) #define TRACE1(format) if ( buglevel > 2 ) printf(format) #define TRACESTR(str) if ( buglevel > 2 ) s_puts(str) static Person * read_person(FILE *fp, long pos, long size) { Person *p; p = alloc_person(); fseek(fp, pos+1, SEEK_SET); /* Skip '+' */ if ( fparse_person(fp, p) != OK ) { free_person(p); return NULL; } else return p; } static Conference * read_conference(FILE *fp, long pos, long size) { Conference *c; c = alloc_conference(); fseek(fp, pos+1, SEEK_SET); /* Skip '+' */ if ( fparse_conference(fp, c) != OK ) { free_conference(c); return NULL; } else return c; } static Text_stat * read_text_stat(FILE *fp, long pos, long size) { Text_stat *t; t = alloc_text_stat(); fseek(fp, pos+1, SEEK_SET); /* Skip '+' */ if ( fparse_text_stat(fp, t) != OK ) { free_text_stat(t); return NULL; } else return t; } static void pers_set_mru(Pers_no pers_no) { set_mru(pers_mcb, pers_no); } static void text_set_mru(Text_no text_no) { set_mru(text_mcb, text_no); } static void conf_set_mru(Conf_no conf_no) { set_mru(conf_mcb, conf_no); } static Cache_node * get_pers_node(Pers_no pers_no) { if ( pers_no >= next_free_num ) return NULL; return get_cache_node(pers_mcb, pers_no); } static void unlink_text_lru (Cache_node *node) { unlink_lru (node, &text_mcb->lru, &text_mcb->mru); } static void unlink_conf_lru (Cache_node *node) { unlink_lru (node, &conf_mcb->lru, &conf_mcb->mru); } static void unlink_pers_lru (Cache_node *node) { unlink_lru (node, &pers_mcb->lru, &pers_mcb->mru); } static Cache_node * get_conf_node(Conf_no conf_no) { if ( conf_no >= next_free_num ) return NULL; return get_cache_node(conf_mcb, conf_no); } static Cache_node * get_text_node(Text_no text_no) { if ( text_no >= next_text_num ) return NULL; return get_cache_node(text_mcb, text_no); } /* * Name caching routines */ /* * change_name changes the cached conference name. It is only called when * a conference name is changed or a conference is deleted. */ void cached_change_name( Conf_no name_num, String new_name ) { if ( name_num < 1 || name_num >= next_free_num ) restart_kom("cached_change_name(%d, ----): next_free_num==%d", name_num, next_free_num); s_clear( &small_conf_arr[name_num]->name ); s_strcpy( &small_conf_arr[name_num]->name, new_name); build_matching_info(); } extern Conf_type cached_get_conf_type (Conf_no conf_no) { if ( conf_no < 1 || conf_no >= next_free_num ) restart_kom("cached_get_conf_type(%d): next_free_num==%d", conf_no, next_free_num); if ( small_conf_arr [ conf_no ] == NULL ) restart_kom("cached_get_conf_type(%d): conference does not exist.\n", conf_no); return small_conf_arr [ conf_no ]->type; } /* * Various function calls to tell the cache that something is changed. */ void mark_person_as_changed(Pers_no pers_no) { Cache_node *node; node = get_pers_node(pers_no); TRACE2("Person %d is changed\n", pers_no); if ( node == NULL || node->s.exists == 0) restart_kom("mark_person_as_changed(%d): nonexistent.\n", pers_no); node->s.dirty = 1; pers_set_mru( pers_no ); } /* * Mark the conference as dirty, so that it will be written to * the disk. * * Also update all fields in the Small_conf except then name, so that * they are always current. * * NOTE: You must call cached_change_name when the name changes. * It is not necessary to call cached_change_name after * cached_create_conf. */ void mark_conference_as_changed(Conf_no conf_no) { Cache_node *node; Conference *conf_c; node = get_conf_node (conf_no); TRACE2("Conf. %d is changed\n", conf_no); if ( node == NULL || node->s.exists == 0) restart_kom("mark_conference_as_changed(%d): nonexistent.\n", conf_no); node->s.dirty = 1; conf_set_mru( conf_no ); conf_c = (Conference *) node->ptr; small_conf_arr[ conf_no ]->highest_local_no = (conf_c->texts.first_local_no - 1 + conf_c->texts.no_of_texts ); small_conf_arr[ conf_no ]->nice = conf_c->nice; small_conf_arr[ conf_no ]->type = conf_c->type; } void mark_text_as_changed( Text_no text_no ) { Cache_node *node; node = get_text_node (text_no); TRACE2("Text %d is changed.\n", text_no); if ( text_no < 1 || text_no >= next_text_num || node == NULL || node->s.exists == 0) { restart_kom("mark_text_as_changed(%d): nonexistent.\n", text_no); } node->s.dirty = 1; text_set_mru (text_no); } /* * Person-related calls */ extern Success cached_create_person( Pers_no person ) { Cache_node *node; TRACE2("Person %d is being created.\n", person); if ( person < 1 || person >= next_free_num ) { restart_kom("cached_create_person(%d): next_free_num == %d.\n", person, next_free_num); } if ( get_pers_node(person) != NULL ) { restart_kom("cached_create_person(%d): Person existed.\n", person); } create_cache_node (pers_mcb, person); node = get_pers_node (person); if ( node == NULL ) restart_kom("cached_create_person(): couldn't get cache_node.\n"); node->ptr = alloc_person(); node->s.dirty = 1; node->s.exists = 1; pers_set_mru( person ); return OK; } extern Person * cached_get_person_stat( Pers_no person ) { Cache_node *node; TRACE2("cached_get_person_stat %d\n", person); if ( person == 0 ) { kom_errno = KOM_CONF_ZERO; return NULL; } if ( person >= next_free_num ) { kom_errno = KOM_UNDEF_PERS; return NULL; } node = get_pers_node (person); if ( node == NULL || node->s.exists == 0 ) { kom_errno = KOM_UNDEF_PERS; return NULL; } if ( node->ptr != NULL ) { pers_set_mru( person ); ++pers_mcb->hits; return node->ptr; } if ( node->snap_shot != NULL ) { node->ptr = copy_person (node->snap_shot); pers_set_mru (person); ++pers_mcb->hits; return node->ptr; } node->ptr = read_person(file_a, node->pos, node->size); ++pers_mcb->misses; pers_set_mru (person); return node->ptr; } /* * Conference-related calls */ static int no_of_allocated_small_confs = 0; static void free_small_conf (Small_conf *sc) { if ( sc != NULL ) { --no_of_allocated_small_confs; s_clear ( &sc->name ); sfree (sc); } } static Small_conf * alloc_small_conf(void) { Small_conf *s; s = smalloc(sizeof(Small_conf)); *s = EMPTY_SMALL_CONF; ++no_of_allocated_small_confs; return s; } /* * Create a conference. * * Set up a Conference and cache the name in the small_conf_array. */ extern Conf_no cached_create_conf (String name) { Conference * conf_c; Conf_no conf_no; Cache_node *node; TRACE1("cached_create_conf( "); TRACESTR(name); TRACE1(" )\n"); if ( next_free_num >= MAX_CONF ) { kom_errno = KOM_INDEX_OUT_OF_RANGE; return 0; } conf_no = next_free_num++; create_cache_node (conf_mcb, conf_no); node = get_conf_node (conf_no); if ( node == NULL ) restart_kom("cached_create_conf(): failed to allocate cache_node.\n"); node->s.exists = 1; node->s.dirty = 1; node->ptr = conf_c = alloc_conference(); conf_set_mru(conf_no); zero_init_cache_node (pers_mcb, conf_no); small_conf_arr[ conf_no ] = alloc_small_conf(); conf_c->name = EMPTY_STRING; s_strcpy(&conf_c->name, name); cached_change_name( conf_no, name); TRACE2("Created conference number %d\n", conf_no); return conf_no; } extern Success cached_delete_conf( Conf_no conf ) { Cache_node *node; if ( conf == 0 ) { kom_errno = KOM_CONF_ZERO; return FAILURE; } if ( conf >= next_free_num ) { kom_errno = KOM_UNDEF_CONF; return FAILURE; } node = get_conf_node (conf); if ( node == NULL || node->s.exists == 0 ) { kom_errno = KOM_UNDEF_CONF; return FAILURE; } if ( node->lock_cnt > 0 ) log("WNG: cached_delete_conf(%d): lock_cnt === %d\n", conf, node->lock_cnt); free_conference(node->ptr); node->ptr = NULL; node->s.exists = 0; return OK; } Success cached_delete_person(Pers_no pers) { Cache_node *node; if ( pers == 0 ) { kom_errno = KOM_CONF_ZERO; return FAILURE; } if ( pers >= next_free_num ) { log("cached_delete_person(%lu): next_free_num == %lu\n", (u_long)pers, (u_long)next_free_num); kom_errno = KOM_UNDEF_PERS; return FAILURE; } node = get_pers_node (pers); if ( pers >= next_free_num || node == NULL || node->s.exists == 0 ) { log("cached_delete_person(): attempt to" " delete non-existing person.\n"); kom_errno = KOM_UNDEF_PERS; return FAILURE; } if ( node->lock_cnt > 0 ) log("cached_delete_pers(%lu): lock_cnt === %lu\n", (u_long)pers, (u_long)node->lock_cnt); free_person (node->ptr); node->ptr = NULL; node->s.exists = 0; return OK; } Success cached_delete_text(Text_no text) { Cache_node *node; if ( text == 0 ) { kom_errno = KOM_TEXT_ZERO; return FAILURE; } node = get_text_node (text); if ( text >= next_text_num || node == NULL || node->s.exists == 0 ) { log("cached_delete_text(): attempt to" " delete non-existing text %d.\n", text); kom_errno = KOM_NO_SUCH_TEXT; return FAILURE; } if ( node->lock_cnt > 0 ) log("cached_delete_text(%d): lock_cnt === %d\n", text, node->lock_cnt); free_text_stat(node->ptr); node->ptr = NULL; node->s.exists = 0; return OK; } extern Conference * cached_get_conf_stat (Conf_no conf_no) { Cache_node *node; TRACE2("cached_get_conf_stat %d\n", conf_no); if ( conf_no == 0 ) { kom_errno = KOM_CONF_ZERO; return NULL; } node = get_conf_node (conf_no); if ( conf_no >= next_free_num || node == NULL || node->s.exists == 0 ) { kom_errno = KOM_UNDEF_CONF; return NULL; } if ( node->ptr != NULL ) { conf_set_mru (conf_no); ++conf_mcb->hits; return node->ptr; } if ( node->snap_shot != NULL ) { node->ptr = copy_conf (node->snap_shot); conf_set_mru (conf_no); ++conf_mcb->hits; return node->ptr; } node->ptr = read_conference(file_a, node->pos, node->size); ++conf_mcb->misses; conf_set_mru (conf_no); return node->ptr; } /* * Return TRUE if conf_no exists. */ Bool cached_conf_exists(Conf_no conf_no) { Cache_node *node; if (conf_no == 0 || conf_no >= next_free_num ) return FALSE; node = get_conf_node (conf_no); return node != NULL && node->s.exists != 0; } /* * Calls to handle texts */ /* * +++ Should return Success. */ extern String cached_get_text( Text_no text ) { String the_string; Text_stat *t_stat; TRACE2("cached_get_text %d\n", text); if ( (t_stat = cached_get_text_stat (text)) == NULL ) return EMPTY_STRING; else { the_string.string = tmp_alloc( t_stat->no_of_chars ); the_string.len = t_stat->no_of_chars; fseek(text_file, t_stat->file_pos, SEEK_SET); if ( fread(the_string.string, sizeof(char), the_string.len, text_file) != the_string.len ) { log("WARNING: cached_get_text: " "couldn't read enough characters of text %d\n", text); return EMPTY_STRING; } return the_string; } } extern Text_stat * /* NULL on error */ cached_get_text_stat( Text_no text ) { Cache_node *node; TRACE2("cached_get_text_stat(%d); next_text_num == ", text); TRACE2("%d\n", next_text_num); if ( text == 0 ) { kom_errno = KOM_TEXT_ZERO; return NULL; } node = get_text_node (text); if ( text >= next_text_num || node == NULL || node->s.exists == 0 ) { TRACE1("cached_get_text_stat: no such text.\n"); kom_errno = KOM_NO_SUCH_TEXT; return NULL; } if ( node->ptr != NULL ) { TRACE1("Found in ptr.\n"); text_set_mru( text ); ++text_mcb->hits; return node->ptr; } if ( node->snap_shot != NULL ) { TRACE1("Found in snap_shot\n"); node->ptr = copy_text_stat(node->snap_shot); text_set_mru (text); ++text_mcb->hits; return node->ptr; } TRACE1("Found in file A.\n"); node->ptr = read_text_stat(file_a, node->pos, node->size); text_set_mru (text); ++text_mcb->misses; return node->ptr; } /* * The text is set up with an empty misc-field. The misc field is * then initialized by create_text. */ extern Text_no cached_create_text( String message) { Text_no tno; Cache_node *node; tno = next_text_num++; TRACE2("cached_create_text (len=%d)\n", message.len); if ( tno >= MAX_TEXT ) { kom_errno = KOM_INDEX_OUT_OF_RANGE; next_text_num = MAX_TEXT; return 0; } create_cache_node(text_mcb, tno); node = get_text_node (tno); if ( node == NULL ) restart_kom("cached_create_text(): couldn't create cache-node.\n"); node->s.exists = 1; node->s.dirty = 1; node->ptr = alloc_text_stat(); ((Text_stat *)node->ptr)->no_of_misc = 0; ((Text_stat *)node->ptr)->misc_items = NULL; ((Text_stat *)node->ptr)->no_of_marks = 0; ((Text_stat *)node->ptr)->no_of_lines = 0; ((Text_stat *)node->ptr)->no_of_chars = 0; fseek(text_file, 0, SEEK_END); ((Text_stat *)node->ptr)->file_pos = ftell(text_file); text_set_mru( tno ); if ( fwrite(message.string, sizeof(char), message.len, text_file) != message.len ) { log("WARNING: cached_create_text: Couldn't write the text %d\n", tno); } fflush(text_file); TRACE2("cached_create_text -> %d\n", tno); return tno; } EXPORT Text_no traverse_text(Text_no seed) { Cache_node *node; seed++; while ( seed < next_text_num ) { node = get_text_node (seed); if ( node != NULL && node->s.exists != 0 ) break; seed++; } return (seed >= next_text_num) ? 0 : seed ; } Pers_no traverse_person(Pers_no seed) { Cache_node *node; seed++; while ( seed < next_free_num ) { node = get_pers_node (seed); if (node != NULL && node->s.exists != 0 ) break; seed++; } return (seed >= next_free_num) ? 0 : seed ; } Conf_no traverse_conference(Conf_no seed) { Cache_node *node; seed++; while ( seed < next_free_num ) { node = get_conf_node (seed); if (node != NULL && node->s.exists != 0 ) break; seed++; } return (seed >= next_free_num) ? 0 : seed ; } extern Garb_nice cached_get_garb_nice (Conf_no conf_no) { return small_conf_arr [ conf_no ]->nice; } extern Local_text_no cached_get_highest_local_no (Conf_no conf_no) { return small_conf_arr[ conf_no ]->highest_local_no; } /* Lock a person struct in memory. Increase a referenc count. */ void cached_lock_person(Pers_no pers_no) { Cache_node *node; node = get_pers_node(pers_no); if ( node == NULL || node->s.exists == 0 ) restart_kom("cached_lock_person(%d): nonexistent.\n", pers_no); if ( node->ptr == NULL ) { Person *pers_stat_ptr; pers_stat_ptr = cached_get_person_stat( pers_no ); if ( pers_stat_ptr == NULL ) restart_kom("cached_lock_person(%d): couldn't read in person.\n", pers_no); if ( pers_stat_ptr != node->ptr ) restart_kom("cached_lock_person(%d): pers_stat_ptr == %d, node" "->ptr == %d.\n", pers_no, pers_stat_ptr, node->ptr); } node->lock_cnt++; } /* Decrease reference count. If zero, unlock person. */ void cached_unlock_person(Pers_no pers_no) { Cache_node *node; node = get_pers_node (pers_no); if ( node == NULL ) restart_kom("cached_unlock_person(): couldn't get cache-node.\n"); if ( node->lock_cnt <= 0 ) { log("cached_unlock_person(%d): lock_cnt == %d.\n", pers_no, node->lock_cnt); node->lock_cnt = 0; } else node->lock_cnt--; } /* Lock a conf struct in memory. Increase a reference count. */ void cached_lock_conf(Conf_no conf_no) { Cache_node *node; node = get_conf_node(conf_no); if ( node == NULL) restart_kom("cached_lock_conf(): can't get cache-node.\n"); if ( node->s.exists == 0 ) restart_kom("cached_lock_conf(%d): nonexistent.\n", conf_no); if ( node->ptr == NULL ) { Conference *conference_ptr; conference_ptr = cached_get_conf_stat( conf_no ); if ( conference_ptr == NULL ) restart_kom("cached_lock_conf(%d): couldn't read in conf.\n", conf_no); if ( conference_ptr != node->ptr ) restart_kom("cached_lock_conf(%d): conference_ptr == %d, node" "->ptr == %d.\n", conf_no, conference_ptr, node->ptr); } node->lock_cnt++; } /* Decrease reference count. If zero, unlock conf. */ void cached_unlock_conf(Conf_no conf_no) { Cache_node *node; node = get_conf_node(conf_no); if ( node == NULL ) restart_kom("cached_unlock_conf(): can't get node.\n"); if ( node->lock_cnt <= 0 ) { log("cached_unlock_conf(%d): lock_cnt == %d.\n", conf_no, node->lock_cnt); node->lock_cnt = 0; } else node->lock_cnt--; } /* * And here comes some functions to deal with lookup_names. */ /* Free the _contents_ of a match_table. The table itself i _not_ freed. */ static void free_match_table(Matching_info *match) { if ( match == NULL ) return; while ( ! s_empty( match->name )) { free_tokens( match->tokens ); /* match->name is not freed since it points into conf_arr[]. */ ++match; } } extern Success build_matching_info(void) { Conf_no i; Matching_info *match; Conf_no *conf; free_match_table(match_table); match_table = srealloc(match_table, next_free_num * sizeof(Matching_info)); conf_table = srealloc(conf_table, next_free_num * sizeof(Conf_no)); no_of_match_info = 0; match = match_table; conf = conf_table; for ( i = 1; i < next_free_num; i++ ) { if ( small_conf_arr[ i ] != NULL && ! s_empty ( small_conf_arr[ i ]->name ) ) { match->name = small_conf_arr[ i ]->name; match->tokens = tokenize(match->name, s_fcrea_str(WHITESPACE)); match->priority = 7; *conf = i; ++match; ++conf; ++no_of_match_info; } } match->name = EMPTY_STRING; match->tokens = NULL; match->priority = 0; *conf = 0; return OK; } /* Map conference name to number */ extern Success cached_lookup_name(const String name, Conf_list_old *result) { Parse_info tmp; int i; tmp = parse(name, match_table, FALSE, FALSE, s_fcrea_str(WHITESPACE), DEFAULT_COLLAT_TAB); if ( tmp.no_of_matches == -1 ) return FAILURE; if ( tmp.no_of_matches == 1 && tmp.indexes[ 0 ] == -1 ) { /* Return the entire list. */ result->no_of_conf_nos = no_of_match_info; result->conf_nos = tmp_alloc(no_of_match_info * sizeof(Conf_no)); result->type_of_conf = tmp_alloc(no_of_match_info * sizeof(Conf_type)); for ( i = 0; i < no_of_match_info; i++ ) { result->conf_nos[ i ] = conf_table[ i ]; result->type_of_conf[ i ] = small_conf_arr[ conf_table[i] ]->type; } } else { /* Return the conferences whose conf_nos are in indexes[]. */ result->no_of_conf_nos = tmp.no_of_matches; result->conf_nos = tmp_alloc(tmp.no_of_matches * sizeof(Conf_no)); result->type_of_conf = tmp_alloc(tmp.no_of_matches * sizeof(Conf_type)); for ( i = 0; i < tmp.no_of_matches; i++ ) { result->conf_nos[ i ] = conf_table[ tmp.indexes[ i ] ]; result->type_of_conf[ i ] = small_conf_arr[ conf_table[ tmp.indexes[ i ] ] ]->type; } } sfree(tmp.indexes); return OK; } static Bool is_clean(const char *fn) { FILE *fp; if ( (fp = fopen(fn, "rb")) == NULL ) return FALSE; if ( getc(fp) == 'C' && getc(fp) == 'L' && getc(fp) == 'E' && getc(fp) == 'A' && getc(fp) == 'N' ) { fclose(fp); return TRUE; } else { fclose(fp); return FALSE; } } static void pre_sync(void) { long i; Cache_node *node; async_sync_db(); /* Mark up what to save.*/ BUG(("Sync starting\n")); highest_text_no = next_text_num; highest_conf_no = next_free_num; for ( i = 1; i < highest_conf_no; i++ ) { node = get_conf_node(i); if ( node != NULL ) { node->s.exists_b = node->s.exists; if ( node->s.dirty != 0 && node->s.exists != 0) { free_conference(node->snap_shot); if ( node->lock_cnt == 0 ) { unlink_conf_lru(node); node->snap_shot = node->ptr; node->ptr = NULL; } else { node->snap_shot = copy_conf(node->ptr); } node->s.dirty = 0; } } } for ( i = 1; i < highest_conf_no; i++ ) { node = get_pers_node(i); if ( node != NULL ) { node->s.exists_b = node->s.exists; if ( node->s.dirty != 0 && node->s.exists != 0) { free_person(node->snap_shot); if ( node->lock_cnt == 0 ) { unlink_pers_lru(node); node->snap_shot = node->ptr; node->ptr = NULL; } else { node->snap_shot = copy_person(node->ptr); } node->s.dirty = 0; } } } for ( i = 1; i < highest_text_no; i++ ) { node = get_text_node(i); if ( node != NULL ) { node->s.exists_b = node->s.exists; if ( node->s.dirty != 0 && node->s.exists != 0) { free_text_stat(node->snap_shot); if ( node->lock_cnt == 0 ) { unlink_text_lru(node); node->snap_shot = node->ptr; node->ptr = NULL; } else { node->snap_shot = copy_text_stat(node->ptr); } node->s.dirty = 0; } } } /* All marking is done. Now open file B. */ if ( is_clean(datafilename) ) { if ( rename(datafilename, backupfilename) != 0 ) restart_kom("pre_sync: can't backup.\n"); } else log("pre_sync: datafile not clean. No backup taken.\n"); if ( file_b != NULL ) { log("pre_sync: Save in progress aborted.\n"); fclose(file_b); } if ( (file_b=fopen(datafilename, "wb") ) == NULL ) { log("WARNING: pre_sync: can't open file to save in.\n"); sync_state = sync_wait; return; } fprintf(file_b, "DIRTY\n"); /* DIRTY-FLAG */ fprintf(file_b, "%d\n", highest_conf_no); /* NEXT_FREE_NUM */ sync_state = sync_save_conf; sync_next = 1; } static void copy_file(FILE *from, FILE *to, long from_pos, long len) { char *buf; long result; buf = smalloc(len); if ( fseek(from, from_pos, SEEK_SET) == -1 ) { sync_state = sync_error; log("sync: copy_file(): fseek failed.\n"); sfree(buf); return; } if ( (result = fread(buf, 1, len, from)) != len ) { log("sync: copy_file(): fread failed.\n" "from_pos = %d, len = %d, result = %d\n", from_pos, len, result); sync_state = sync_error; sfree(buf); return; } if ( fseek(to, 0, SEEK_END) == -1 ) { sync_state = sync_error; log("sync: copy_file(): second fseek failed.\n"); sfree(buf); return; } if ( fwrite(buf, 1, len, to) != len ) { sync_state = sync_error; log("sync: copy_file(): fwrite failed.\n"); sfree(buf); return; } sfree(buf); } static void save_one_conf(void) { Cache_node *cn; if (sync_next < highest_conf_no) { cn = get_conf_node (sync_next); if ( cn == NULL ) { putc('@', file_b); putc('\n', file_b); } else { cn->pos_b = ftell(file_b); if ( cn->s.exists_b == 0 ) putc('@', file_b); else if ( cn->snap_shot != NULL ) { fprintf(file_b, "+ "); foutput_conference(file_b, cn->snap_shot); } else if ( cn->s.dirty == 0 && cn->ptr != NULL ) { fprintf(file_b, "+ "); foutput_conference(file_b, cn->ptr); } else { copy_file(file_a, file_b, cn->pos, cn->size - 1); } putc('\n', file_b); cn->size_b = ftell(file_b) - cn->pos_b; } sync_next++; } else /* All conferences are written. */ { sync_next = 1; sync_state = sync_save_pers; } } static void write_pers(FILE *fp, Person *p) { fprintf(fp, "+ %dH", PASSWD_LEN); fwrite(p->pwd, PASSWD_LEN, 1, fp); foutput_person(fp, p); } static void save_one_pers(void) { Cache_node *cn; if (sync_next < highest_conf_no) { cn = get_pers_node (sync_next); if ( cn == NULL ) { putc('@', file_b); putc('\n', file_b); } else { cn->pos_b = ftell(file_b); if ( cn->s.exists_b == 0 ) putc('@', file_b); else if ( cn->snap_shot != NULL ) { write_pers(file_b, cn->snap_shot); } else if ( cn->s.dirty == 0 && cn->ptr != NULL ) { write_pers(file_b, cn->ptr); } else { copy_file(file_a, file_b, cn->pos, cn->size - 1); } putc('\n', file_b); cn->size_b = ftell(file_b) - cn->pos_b; } sync_next++; } else /* All persons are written. */ { fprintf(file_b, "%d\n", highest_text_no); /* NEXT_TEXT_NUM */ sync_next = 1; sync_state = sync_save_text; } } static void post_sync(void) { int i; Cache_node *node; async_sync_db(); if ( file_a == NULL ) log("WARNING: post_sync(): file_a == NULL. This is only normal " "if this is the first sync ever on this data file.\n"); else fclose(file_a); if ( ( file_a = fopen(datafilename, "rb") ) == NULL ) { log("post_sync: can't open the file I just saved.\n"); sync_state = sync_wait; return; } for ( i = 1; i < highest_conf_no; i++ ) { node = get_conf_node(i); if ( node != NULL ) { node->pos = node->pos_b; node->size = node->size_b; free_conference(node->snap_shot); node->snap_shot = NULL; /* +++ Delete if it no longer exists. */ } } for ( i = 1; i < highest_conf_no; i++ ) { node = get_pers_node(i); if ( node != NULL ) { node->pos = node->pos_b; node->size = node->size_b; free_person(node->snap_shot); node->snap_shot = NULL; /* +++ Delete if it no longer exists. */ } } for ( i = 1; i < highest_text_no; i++ ) { node = get_text_node(i); if ( node != NULL ) { node->pos = node->pos_b; node->size = node->size_b; free_text_stat(node->snap_shot); node->snap_shot = NULL; /* +++ Delete if it no longer exists. */ } } } static void save_one_text(void) { Cache_node *cn; long offset; long offset2; while (sync_next < highest_text_no) { cn = get_text_node(sync_next); if ( cn == NULL ) { putc('@', file_b); putc('\n', file_b); sync_next++; continue; } else { cn->pos_b = ftell(file_b); if ( cn->s.exists_b == 0 ) putc('@', file_b); else if ( cn->snap_shot != NULL ) { fprintf(file_b, "+ "); foutput_text_stat(file_b, cn->snap_shot); } else if ( cn->s.dirty == 0 && cn->ptr != NULL ) { fprintf(file_b, "+ "); foutput_text_stat(file_b, cn->ptr); } else { copy_file(file_a, file_b, cn->pos, cn->size - 1); } putc('\n', file_b); cn->size_b = ftell(file_b) - cn->pos_b; sync_next++; break; } restart_kom("Unreachable statement reached."); } /* If all texts are written, do some clean-up. */ if (sync_next == highest_text_no) { if ( ferror(file_b) != 0 ) { log ("save_one_text(): ferror() detected.\n"); sync_state = sync_error; return; } offset = ftell(file_b); /* Make sure that the entire file resides on disk. This test seems to be necessary. The data file has been corrupted at least once. */ if (offset == -1) { log ("save_one_text(): ftell returned -1.\n"); sync_state = sync_error; return; } rewind(file_b); if ( ferror(file_b) != 0 ) { log ("save_one_text(): rewind failed.\n"); sync_state = sync_error; return; } fprintf(file_b, "CLEAN"); if ( ferror(file_b) != 0 ) { log ("save_one_text(): fprintf(CLEAN) failed.\n"); sync_state = sync_error; return; } if (fflush(file_b) != 0) { log ("save_one_text(): fflush failed.\n"); sync_state = sync_error; return; } if ( ferror(file_b) != 0 ) { log ("save_one_text(): ferror after fflush failed.\n"); sync_state = sync_error; return; } if ( fclose(file_b) != 0 ) { file_b = NULL; log("Sync: fclose() failed in save_one_text. Deleting " "file and retrying.\n"); remove(datafilename); sync_state = sync_wait; return; } file_b = fopen(datafilename, "rb"); if (file_b == NULL) { log("save_one_text(): failed to reopen file.\n"); remove (datafilename); sync_state = sync_wait; return; } if (fseek(file_b, 0, SEEK_END) != 0) { log("save_one_text(): fseek failed.\n"); sync_state = sync_error; return; } offset2 = ftell (file_b); if ( offset2 != offset ) { log ("save_one_text(): ftell returns different offsets" "(%l and %l).\n", offset, offset2); sync_state = sync_error; return; } fclose (file_b); file_b = NULL; sync_state = sync_ready; BUG(("Sync ready\n")); post_sync(); } } /* * Sync_part() should be called often as long as it returns FALSE. * Sync_part() returns TRUE when everything is written to a file. * Sync_part() should be called once in a while even when it returns * TRUE (because sync() checks to see if it is time to start yet * another save.) */ Bool sync_part(void) { static time_t last_sync_start = NO_TIME; if ( last_sync_start == NO_TIME ) { last_sync_start = time(NULL); sync_state = sync_ready; } switch(sync_state) { case sync_save_conf: save_one_conf(); break; case sync_save_pers: save_one_pers(); break; case sync_save_text: save_one_text(); break; case sync_ready: if ( difftime(time(NULL), last_sync_start) < 60 * SYNC_INTERVAL ) return TRUE; last_sync_start = time(NULL); pre_sync(); break; case sync_wait: if ( difftime(time(NULL), last_sync_start) < 60 * SYNC_RETRY_INTERVAL ) return TRUE; last_sync_start = time(NULL); pre_sync(); break; case sync_error: log("sync: Error saving new file. Discarding file and " "trying again.\n"); fclose(file_b); file_b = NULL; remove(datafilename); sync_state = sync_wait; break; default: restart_kom("sync(): sync_state==%d", sync_state); } if ( file_b != NULL && ferror(file_b) != 0) sync_state = sync_error; return FALSE; } static void setup_small_conf(Conf_no conf_no, Conference *conf_c) { small_conf_arr[ conf_no ] = alloc_small_conf(); s_strcpy(&small_conf_arr[ conf_no ]->name, conf_c->name); small_conf_arr[ conf_no ]->type = conf_c->type; small_conf_arr[ conf_no ]->highest_local_no = (conf_c->texts.first_local_no - 1 + conf_c->texts.no_of_texts ); small_conf_arr[ conf_no ]->nice = conf_c->nice; } extern Success init_cache(void) { int i; Cache_node *node; Conference tmp_conf = EMPTY_CONFERENCE; Person tmp_pers = EMPTY_PERSON; Text_stat tmp_text = EMPTY_TEXT_STAT; pers_mcb = create_cache_node_mcb(100, MAX_CONF); conf_mcb = create_cache_node_mcb(100, MAX_CONF); text_mcb = create_cache_node_mcb(100, MAX_TEXT); if ( (text_file = fopen(textfilename, "a+b")) == NULL ) { restart_kom("ERROR: init_cache: can't open text file \"%s\"." " errno = %d\n", textfilename, errno); } if ( is_clean(datafilename) ) { if ( (file_a = fopen(datafilename, "rb")) == NULL ) { log("WARNING: init_cache: can't open datafile.\n"); return FAILURE; } log("MSG: init_cache: using datafile.\n"); } else if ( is_clean(backupfilename) ) { if ( (file_a = fopen(backupfilename, "rb")) == NULL ) { log("WARNING: init_cache: can't open backupfile.\n"); return FAILURE; } log("MSG: init_cache: using backup file.\n"); } else { log("WARNING: init_cache: can't find old data base.\n"); return FAILURE; } fseek(file_a, 6, SEEK_SET); /* skip clean/dirty flag. */ next_free_num = fparse_long(file_a); /* NEXT_FREE_NUM */ for ( i = 1; i < next_free_num; i++ ) /* CONFS */ { fskipwhite(file_a); switch(getc(file_a)) { case '@': zero_init_cache_node(conf_mcb, i); break; case '+': create_cache_node(conf_mcb, i); node = get_conf_node(i); node->s.exists = 1; node->pos = ftell(file_a) - 1; /* Don't forget the '+' */ if ( fparse_conference(file_a, &tmp_conf) != OK ) restart_kom("init_cache(): fparse_conference failed. " "i == %d\n", i); node->size = ftell(file_a) - node->pos; setup_small_conf(i, &tmp_conf); clear_conference(&tmp_conf); break; } } build_matching_info(); for ( i = 1; i < next_free_num; i++ ) /* PERSONS */ { fskipwhite(file_a); switch(getc(file_a)) { case '@': zero_init_cache_node(pers_mcb, i); break; case '+': create_cache_node(pers_mcb, i); node = get_pers_node(i); node->s.exists = 1; node->pos = ftell(file_a) - 1; /* Don't forget the '+' */ if ( fparse_person(file_a, &tmp_pers) != OK ) restart_kom("init_cache: fparse_person failed. i == %d\n", i); node->size = ftell(file_a) - node->pos; clear_person(&tmp_pers); break; } } next_text_num = fparse_long(file_a); /* NEXT_TEXT_NUM */ for ( i = 1; i < next_text_num; i++ ) /* TEXT_STATS */ { fskipwhite(file_a); switch(getc(file_a)) { case '@': zero_init_cache_node(text_mcb, i); break; case '+': create_cache_node(text_mcb, i); node = get_text_node(i); node->s.exists = 1; node->pos = ftell(file_a) - 1; /* Don't forget the '+' */ if ( fparse_text_stat(file_a, &tmp_text) != OK ) restart_kom("init_cache(): fparse_text_stat failed. i == %d\n", i); node->size = ftell(file_a) - node->pos; clear_text_stat(&tmp_text); break; } } log("Read %d confs/persons and %d texts\n", next_free_num, next_text_num); return OK; } /* ramkomd compatibility: */ extern void cache_sync(void) { } extern void cache_sync_all(void) { pre_sync(); while ( sync_part() != TRUE ) ; } void free_all_cache (void) { int i; Cache_node *node; for ( i = 0; i < next_free_num; i++ ) { node = get_conf_node(i); if ( node != NULL ) { if ( node->snap_shot != NULL ) { free_conference (node->snap_shot); node->snap_shot = NULL; } if ( node->ptr != NULL ) { free_conference (node->ptr); node->ptr = NULL; } } destruct_cache_node (conf_mcb, i); node = get_pers_node(i); if ( node != NULL ) { if ( node->snap_shot != NULL ) { free_person (node->snap_shot); node->snap_shot = NULL; } if ( node->ptr != NULL ) { free_person (node->ptr); node->ptr = NULL; } } destruct_cache_node (pers_mcb, i); if ( small_conf_arr[i] != NULL ) { free_small_conf (small_conf_arr[i]); small_conf_arr[i] = NULL; } } for ( i = 0; i < next_text_num; i++ ) { node = get_text_node(i); if ( node != NULL ) { if ( node->snap_shot != NULL ) { free_text_stat (node->snap_shot); node->snap_shot = NULL; } if ( node->ptr != NULL ) { free_text_stat (node->ptr); node->ptr = NULL; } } destruct_cache_node (text_mcb, i); } free_match_table(match_table); free_cache_node_mcb(conf_mcb); free_cache_node_mcb(text_mcb); free_cache_node_mcb(pers_mcb); sfree (match_table); sfree (conf_table); } /* Is it allowed to delete this node from the cache? It is, unless the node is locked, dirty, or contains a snap-shot. */ static Bool throwable_p(Cache_node *node) { return (node->s.dirty == 0 && node->snap_shot == NULL && node->lock_cnt == 0); } static void limit_pers(void) { Cache_node *node; int i; node = pers_mcb->mru; /* Skip first CACHE_PERSONS clean persons. */ for ( i = 0; node != NULL && i < CACHE_PERSONS; i++ ) { while (node != NULL && !throwable_p(node)) node = node->next; if ( node != NULL ) node = node->next; } /* Delete any remaining clean persons */ while ( node != NULL ) { if (throwable_p(node)) { unlink_pers_lru(node); free_person (node->ptr); node->ptr = NULL; /* +++ delete cache-node if non-existent. */ } node = node->next; } } static void limit_conf(void) { Cache_node *node; int i; node = conf_mcb->mru; /* Skip first CACHE_CONFERENCES clean confs. */ for ( i = 0; node != NULL && i < CACHE_CONFERENCES; i++ ) { while (node != NULL && !throwable_p(node)) node = node->next; if ( node != NULL ) node = node->next; } /* Delete any remaining clean confs. */ while ( node != NULL ) { if (throwable_p(node)) { unlink_conf_lru(node); free_conference (node->ptr); node->ptr = NULL; /* +++ delete if non-existent. */ } node = node->next; } } static void limit_text_stat(void) { Cache_node *node; int i; node = text_mcb->mru; /* Skip first CACHE_TEXT_STATS clean text_stats. */ for ( i = 0; node != NULL && i < CACHE_TEXT_STATS; i++ ) { while (node != NULL && !throwable_p(node)) node = node->next; if (node != NULL) node = node->next; } /* Delete any remaining clean text_stats. */ while ( node != NULL ) { if (throwable_p(node)) { unlink_text_lru(node); free_text_stat (node->ptr); node->ptr = NULL; /* +++ delete if non-existent. */ } node = node->next; } } /* * Limit the number of 'clean' cache entries. */ EXPORT void cache_limit_size(void) { limit_pers(); limit_conf(); limit_text_stat(); } EXPORT void dump_cache_mem_usage(FILE *fp) { fprintf(fp, "---simple-cache.c:\n" "\tSmall_confs: %d\n", no_of_allocated_small_confs); } EXPORT void dump_cache_stats(FILE *fp) { fprintf(fp, "---simple-cache.c:\n"); fprintf(fp, "\tPersons (cache size: %d):\n" "\t hits: %lu\n" "\t miss: %lu\n", CACHE_PERSONS, pers_mcb->hits, pers_mcb->misses); fprintf(fp, "\tConferences (cache size: %d):\n" "\t hits: %lu\n" "\t miss: %lu\n", CACHE_CONFERENCES, conf_mcb->hits, conf_mcb->misses); fprintf(fp, "\tText_stats (cache size: %d):\n" "\t hits: %lu\n" "\t miss: %lu\n", CACHE_TEXT_STATS, text_mcb->hits, text_mcb->misses); } EXPORT int query_next_text_num(void) { return next_text_num; }