home *** CD-ROM | disk | FTP | other *** search
- /* $Header: mthreads.c,v 4.3.3.3 91/01/18 19:05:00 davison Trn $
- **
- ** $Log: mthreads.c,v $
- ** Revision 4.3.3.3 91/01/18 19:05:00 davison
- ** Modified the way signals are handled to avoid endless loops. Added -s & -z
- ** options. Fixed a truncate bug and a problem with new groups not processing.
- **
- ** Revision 4.3.3.2 90/08/20 16:43:19 davison
- ** Implemented new command-line interface and database upgrading.
- **
- ** Revision 4.3.3.1 90/07/24 22:24:17 davison
- ** Initial Trn Release
- **
- */
-
- /* mthreads.c -- for making and updating a discussion-thread database
- **
- ** We use the active file as our high/low counts for each group, and create
- ** an active2 file of our own to keep track of the high/lows of the database.
- ** When fully updated, the two files should be identical. This gives us
- ** quick access to the last processed high/low counts without opening
- ** each data file, PLUS it allows trn to use the fake active file as if it
- ** were the real thing to keep it from seeing articles before they are
- ** processed. If the active2 file is removed or corrupted, it will be
- ** automatically repaired in the normal course of operation. We update
- ** the file IN PLACE so that trn can keep it open all the time. Normally
- ** the size of the file does not change, so it is easy to do. In those
- ** rare instances where a news admin shuffles the real active file, we
- ** take it all in stride by throwing a little memory at the problem.
- **
- ** Usage: mthreads [-d[MM]] [-e[HHMM]] [-aDfknv] [hierarchy_list]
- */
-
- #include "EXTERN.h"
- #include "common.h"
- #ifdef SERVER
- #include "server.h"
- #endif
- #include "INTERN.h"
- #include "mthreads.h"
-
- #ifdef TZSET
- #include <time.h>
- #else
- #include <sys/time.h>
- #include <sys/timeb.h>
- #endif
-
- FILE *fp_lock, *fp_log;
-
- struct stat filestat;
-
- static char line[256];
- static char line2[256];
-
- /* If you want to change the field size, do it once and then leave it alone. */
- char fmt_active2[] = "%s %06ld %06ld %c\n";
-
- char *filename;
-
- typedef struct _active_line {
- struct _active_line *link;
- char *name;
- long last;
- long first;
- char type;
- } ACTIVE_LINE;
-
- #define Nullact Null(ACTIVE_LINE*)
-
- ACTIVE_LINE *line_root = Nullact, *last_line = Nullact, *pline = Nullact;
-
- bool force_flag = FALSE, kill_mthreads = FALSE, no_processing = FALSE;
- bool add_new = FALSE, rebuild = FALSE, zap_thread = FALSE, grevious_error;
- int daemon_delay = 0, log_verbosity = 0, debug = 0, slow_down = 0;
- long expire_time = 0;
- char *hierarchy_list = NULL;
- long truncate_len = -1;
-
- char nullstr[] = "";
-
- BMAP my_bmap, mt_bmap;
-
- #ifdef TZSET
- time_t tnow;
- #else
- struct timeb ftnow;
- #endif
-
- #define TIMER_FIRST 1
- #define TIMER_DEFAULT (10 * 60)
-
- int added_groups, removed_groups, action;
-
- #define NG_DEFAULT 0
- #define NG_MATCH 1
- #define NG_SKIP 2
-
- #ifdef SERVER
- char *server;
- #else
- time_t last_modified;
- #endif
-
- SIGRET alarm_handler(), int_handler(), severe_handler();
- void makethreads(), wrap_it_up();
-
- main( argc, argv )
- int argc;
- char *argv[];
- {
- int fd;
- long pid;
-
- while( --argc ) {
- if( **++argv == '-' ) {
- while( *++*argv ) {
- switch( **argv ) {
- case 'a': /* automatically thread new groups */
- add_new = TRUE;
- break;
- case 'D':
- debug++;
- break;
- case 'd':
- if( *++*argv <= '9' && **argv >= '0' ) {
- daemon_delay = atoi( *argv ) * 60;
- while( *++*argv <= '9' && **argv >= '0' ) {
- ;
- }
- } else {
- daemon_delay = TIMER_DEFAULT;
- }
- --*argv;
- break;
- case 'e': {
- struct tm *ts;
- long desired;
-
- (void) time( &expire_time );
- ts = localtime( &expire_time );
-
- if( *++*argv <= '9' && **argv >= '0' ) {
- desired = atol( *argv );
- if( desired/100 > 23 || desired%100 > 59 ) {
- fprintf( stderr, "Illegal expire time: '%04d'\n",
- desired );
- exit( 1 );
- }
- desired = (desired/100)*60 + desired%100;
- while( *++*argv <= '9' && **argv >= '0' ) {
- ;
- }
- } else {
- desired = 30; /* 0030 = 12:30am */
- }
- --*argv;
- desired -= ts->tm_hour * 60 + ts->tm_min;
- if( desired < 0 ) {
- desired += 24 * 60;
- }
- expire_time += desired * 60 - ts->tm_sec;
- break;
- }
- case 'f':
- force_flag = TRUE;
- break;
- case 'k':
- kill_mthreads = TRUE;
- break;
- case 'n':
- no_processing = TRUE;
- break;
- case 's':
- slow_down++;
- break;
- case 'v':
- log_verbosity++;
- break;
- case 'z':
- zap_thread = TRUE;
- break;
- default:
- fprintf( stderr, "Unknown option: '%c'\n", **argv );
- exit( 1 );
- }
- }
- } else {
- if( hierarchy_list ) {
- fprintf( stderr, "Specify the newsgroups in one comma-separated list.\n" );
- exit( 1 );
- }
- hierarchy_list = *argv;
- }
- }
-
- /* Set up a nice friendly umask. */
- umask( 002 );
-
- /* What time is it? */
- #ifdef TZSET
- (void) time( &tnow );
- (void) tzset();
- #else
- (void) ftime( &ftnow );
- #endif
-
- /* Make sure we're not already running by creating a lock file.
- ** (I snagged this method from C news.)
- */
- sprintf( line, "%s.%d", file_exp( "%X/LOCK" ), getpid() );
- if( (fp_lock = fopen( line, "w" )) == Nullfp ) {
- fprintf( stderr, "Unable to create lock temporary `%s'.\n", line );
- exit( 1 );
- }
- fprintf( fp_lock, "%d\n", getpid() );
- fclose( fp_lock );
-
- /* Try to link to lock file. */
- filename = file_exp( "%X/LOCKmthreads" );
- dolink:
- if( link( line, filename ) < 0 ) {
- long otherpid;
- /* Try to avoid possible race with daemon starting up. */
- sleep (5);
- if( (fp_lock = fopen( filename, "r")) == Nullfp ) {
- fprintf( stderr, "unable to open %s\n", filename );
- unlink( line );
- exit( 1 );
- }
- if( fscanf( fp_lock, "%ld", &otherpid ) != 1) {
- fprintf( stderr, "unable to read pid from %s\n", filename );
- unlink( line );
- fclose( fp_lock );
- exit( 1 );
- }
- fclose( fp_lock );
- if( kill( otherpid, kill_mthreads ? SIGTERM : 0 ) == -1
- && errno == ESRCH ) {
- if( unlink( filename ) == -1 ) {
- fprintf( stderr, "unable to unlink lockfile %s\n", filename );
- unlink( line );
- exit( 1 );
- }
- if( !kill_mthreads ) {
- goto dolink;
- }
- }
- unlink( line );
- if( kill_mthreads ) {
- fprintf( stderr, "killing currently running mthreads.\n" );
- exit( 0 );
- } else {
- fprintf( stderr, "mthreads is already running.\n" );
- exit( 1 );
- }
- }
-
- unlink( line ); /* remove temporary LOCK.<pid> file */
-
- if( kill_mthreads ) {
- fprintf( stderr, "mthreads is not running.\n" );
- exit( 1 );
- }
-
- /* Open our log file */
- filename = file_exp( "%X/mt.log" );
- if( (fp_log = fopen( filename, "a" )) == Nullfp ) {
- fprintf( stderr, "Unable to open `%s'.\n", filename );
- exit( 1 );
- }
-
- #ifdef SIGHUP
- if( sigset( SIGHUP, SIG_IGN ) != SIG_IGN ) {
- sigset( SIGHUP, int_handler );
- }
- #endif
- if( sigset( SIGINT, SIG_IGN ) != SIG_IGN ) {
- sigset( SIGINT, int_handler );
- }
- #ifdef SIGQUIT
- if( sigset( SIGQUIT, SIG_IGN ) != SIG_IGN ) {
- sigset( SIGQUIT, int_handler );
- }
- #endif
- sigset( SIGTERM, int_handler );
- #ifdef SIGBUS
- sigset( SIGBUS, severe_handler );
- #endif
- sigset( SIGSEGV, severe_handler );
- #ifdef SIGTTIN
- sigset( SIGTTIN, SIG_IGN );
- sigset( SIGTTOU, SIG_IGN );
- #endif
- sigset( SIGALRM, SIG_IGN );
- #ifdef lint
- alarm_handler(); /* foolishness for lint's sake */
- int_handler( SIGINT );
- severe_handler( SIGSEGV );
- #endif
-
- /* Ensure this machine has the right byte-order for the database */
- filename = file_exp( "%X/db.init" );
- if( (fp_lock = fopen( filename, FOPEN_RB )) == Nullfp
- || fread( &mt_bmap, 1, sizeof (BMAP), fp_lock ) < sizeof (BMAP)-1 ) {
- if( fp_lock != Nullfp ) {
- fclose( fp_lock );
- }
- write_db_init:
- mybytemap( &mt_bmap );
- if( (fp_lock = fopen( filename, FOPEN_WB )) == Nullfp ) {
- log_entry( "Unable to create file: `%s'.\n", filename );
- exit( 1 );
- }
- mt_bmap.version = DB_VERSION;
- fwrite( &mt_bmap, 1, sizeof (BMAP), fp_lock );
- fclose( fp_lock );
- } else {
- int i;
-
- fclose( fp_lock );
- if( mt_bmap.version != DB_VERSION ) {
- if( mt_bmap.version == DB_VERSION-1 ) {
- rebuild = TRUE;
- log_entry( "Upgrading database to version %d.\n", DB_VERSION );
- goto write_db_init;
- }
- log_entry( "** Database is not the right version (%d instead of %d) **\n",
- mt_bmap.version, DB_VERSION );
- exit( 1 );
- }
- mybytemap( &my_bmap );
- for( i = 0; i < sizeof (LONG); i++ ) {
- if( my_bmap.l[i] != mt_bmap.l[i]
- || (i < sizeof (WORD) && my_bmap.w[i] != mt_bmap.w[i]) ) {
- log_entry( "\
- ** Byte-order conflict -- re-run from a compatible machine **\n\
- \t\tor remove the current thread files, including db.init **\n" );
- exit( 1 );
- }
- }
- }
-
- #ifdef SERVER
- server = getserverbyfile( SERVER_FILE );
- if( server == NULL ) {
- log_entry( "Couldn't find name of news server.\n" );
- exit( 1 );
- }
- #endif
-
- /* If we're not in daemon mode, run through once and quit. */
- if( !daemon_delay ) {
- log_entry( "mthreads single pass started.\n" );
- setbuf( stdout, Nullch );
- extra_expire = (expire_time != 0);
- makethreads();
- } else {
- /* For daemon mode, we cut ourself off from anything tty-related and
- ** run in the background (involves forks, but no knives).
- */
- close( 0 );
- if( open( "/dev/null", 2 ) != 0 ) {
- fprintf( stderr, "unable to open /dev/null!\n" );
- exit( 1 );
- }
- close( 1 );
- close( 2 );
- dup( 0 );
- dup( 0 );
- while( (pid = fork()) < 0 ) {
- sleep( 2 );
- }
- if( pid ) {
- exit( 0 );
- }
- #ifdef TIOCNOTTY
- if( (fd = open( "/dev/tty", 1 )) >= 0 ) {
- ioctl( fd, TIOCNOTTY, (int*)0 );
- close( fd );
- }
- #else
- (void) setpgrp();
- while( (pid = fork()) < 0 ) {
- sleep( 2 );
- }
- if( pid ) {
- exit( 0 );
- }
- #endif
- /* Put our pid in the lock file for death detection */
- if( (fp_lock = fopen( file_exp( "%X/LOCKmthreads" ), "w" )) != Nullfp ) {
- fprintf( fp_lock, "%d\n", getpid() );
- fclose( fp_lock );
- }
-
- log_entry( "mthreads daemon started.\n" );
-
- #ifndef SERVER
- last_modified = 0;
- #endif
- sigset( SIGALRM, alarm_handler );
-
- /* Start timer -- first interval is shorter than all others */
- alarm( TIMER_FIRST );
- for( ;; ) {
- if( caught_interrupt ) {
- wrap_it_up( 0 );
- /* NORETURN */
- }
- pause(); /* let alarm go off */
- if( caught_interrupt ) {
- wrap_it_up( 0 );
- /* NORETURN */
- }
- alarm( 0 );
-
- /* Re-open our log file, if needed */
- if( !fp_log && !(fp_log = fopen( file_exp( "%X/mt.log" ), "a" )) ) {
- wrap_it_up( 1 );
- }
- #ifndef SERVER
- if( stat( file_exp( ACTIVE ), &filestat ) < 0 ) {
- log_entry( "Unable to stat active file -- quitting.\n" );
- wrap_it_up( 1 );
- }
- #endif
- if( expire_time && time( 0L ) > expire_time ) {
- expire_time += 24L * 60 * 60;
- extra_expire = TRUE;
- }
- #ifdef SERVER
- if( 1 ) { /* always compare files */
- #else
- if( extra_expire || filestat.st_mtime != last_modified ) {
- last_modified = filestat.st_mtime;
- #endif
- makethreads();
- }
- alarm( daemon_delay );
- fclose( fp_log ); /* close the log file while we sleep */
- fp_log = Nullfp;
- } /* for */
- }/* if */
-
- wrap_it_up( 0 );
- }
-
- SIGRET
- alarm_handler()
- {
- sigset( SIGALRM, alarm_handler );
- }
-
- SIGRET
- int_handler( sig )
- int sig;
- {
- /* Flag interrupt occurred -- main loop attempts an orderly retreat. */
- if( ++caught_interrupt >= 3 ) {
- wrap_it_up( 1 );
- }
- /* Re-open our log file, if needed */
- if( fp_log || (fp_log = fopen( file_exp( "%X/mt.log" ), "a" )) ) {
- if( sig == SIGTERM ) {
- log_entry( "mthreads halted.\n", sig);
- } else {
- log_entry( "Interrupt %d received.\n", sig);
- }
- }
- if( !daemon_delay ) {
- printf( "interrupt %d!\n", sig );
- }
- }
-
- /* Severe interrupts require severe action -- abort immediately, possibly
- ** removing the thread file on the way.
- */
- SIGRET
- severe_handler( sig )
- int sig;
- {
- /* Let's be a bit paranoid here -- avoid any possibility of looping. */
- if( caught_interrupt >= 10 ) {
- wrap_it_up( 1 );
- }
- caught_interrupt = 10;
-
- /* Destroy offending thread file if requested to do so. */
- if( zap_thread ) {
- unlink( thread_name( line ) );
- }
- /* Re-open our log file, if needed */
- if( fp_log || (fp_log = fopen( file_exp( "%X/mt.log" ), "a" )) ) {
- log_error( "** Severe signal: %d **\n", sig );
- if( zap_thread ) {
- log_entry( "Destroyed thread file for %s\n", line );
- }
- }
- if( !daemon_delay ) {
- printf( "Severe signal: %d!\n", sig);
- if( zap_thread ) {
- printf( "Destroyed thread file for %s\n", line );
- }
- }
- wrap_it_up( 1 );
- }
-
- void
- wrap_it_up( ret )
- int ret;
- {
- unlink( file_exp( "%X/LOCKmthreads" ) ); /* remove lock */
-
- exit( ret );
- }
-
- /* Process the active file, creating/modifying the active2 file and
- ** creating/modifying the thread data files.
- */
- void
- makethreads()
- {
- register char *cp, *cp2;
- FILE *fp_active, *fp_active2, *fp_active3;
- long first, last, first2, last2;
- char ch, ch2;
- char data_file_open;
- bool update_successful;
- bool eof_active = FALSE, eof_active2 = FALSE;
-
- #ifdef SERVER
- switch( server_init( server ) ) {
- case OK_NOPOST:
- case OK_CANPOST:
- break;
- case ERR_ACCESS:
- log_entry( "Server %s rejected connection -- quitting.\n", server );
- wrap_it_up( 1 );
- default:
- log_entry( "Couldn't connect with server %s -- sleeping.\n", server );
- return;
- }
- put_server( "LIST" ); /* ask server for the active file */
- get_server( line, sizeof line );
- if( *line != CHAR_OK ) {
- log_entry( "Unable to get active file from server -- sleeping.\n" );
- close_server();
- return;
- }
- if( (fp_active = fopen( file_exp( ACTIVE1 ), "w+" )) == Nullfp ) {
- log_entry( "Unable to write the active1 file.\n" );
- wrap_it_up( 1 );
- }
- while( 1 ) {
- if( caught_interrupt ) {
- wrap_it_up( 0 );
- /* NORETURN */
- }
- if( get_server( line, sizeof line ) < 0 ) {
- log_entry( "Server failed to send entire active file -- sleeping.\n" );
- fclose( fp_active );
- close_server();
- return;
- }
- if( *line == '.' ) {
- break;
- }
- fputs( line, fp_active );
- putc( '\n', fp_active );
- }
- fseek( fp_active, 0L, 0 ); /* rewind for read */
- #else
- if( (fp_active = fopen( file_exp( ACTIVE ), "r" )) == Nullfp ) {
- log_entry( "Unable to open the active file.\n" );
- wrap_it_up( 1 );
- }
- #endif
- filename = file_exp( ACTIVE2 );
- if( (fp_active3 = fopen( filename, "r+" )) == Nullfp ) {
- if( (fp_active3 = fopen( filename, "w" )) == Nullfp ) {
- log_entry( "Unable to open the active2 file for update.\n" );
- wrap_it_up( 1 );
- }
- }
- if( (fp_active2 = fopen( filename, "r" )) == Nullfp ) {
- log_entry( "Unable to open the active2 file.\n" );
- wrap_it_up( 1 );
- }
- if( caught_interrupt ) {
- wrap_it_up( 0 );
- /* NORETURN */
- }
- if( extra_expire && log_verbosity ) {
- log_entry( "Using enhanced expiration for this pass.\n" );
- }
-
- processed_groups = added_groups = removed_groups = 0;
- added_articles = expired_articles = 0;
-
- /* Loop through entire active file. */
- for( ;; ) {
- if( eof_active || !fgets( line, sizeof line, fp_active ) ) {
- if( eof_active2 && !line_root ) {
- break;
- }
- eof_active = TRUE;
- ch = 'x';
- } else {
- if( !(cp = index( line, ' ' )) ) {
- log_entry( "active line has no space: %s\n", line );
- continue;
- }
- *cp = '\0';
- if( sscanf( cp+1, "%ld %ld %c", &last, &first, &ch ) != 3 ) {
- log_entry( "active digits corrupted: %s %s\n", line, cp+1 );
- continue;
- }
- }
- if( debug || log_verbosity > 3 ) {
- log_entry( "Processing %s:\n", line );
- }
- data_file_open = 0;
- /* If we've allocated some lines in memory while searching for
- ** newsgroups (they've scrambled the active file on us), check
- ** them first.
- */
- last_line = Nullact;
- for( pline = line_root; pline; pline = pline->link ) {
- if( eof_active || strEQ( line, pline->name ) ) {
- strcpy( line2, pline->name );
- free( pline->name );
- first2 = pline->first;
- last2 = pline->last;
- ch2 = pline->type;
- if( last_line ) {
- last_line->link = pline->link;
- } else {
- line_root = pline->link;
- }
- free( pline );
- break;
- }
- last_line = pline;
- }/* for */
- /* If not found yet, check the active2 file. */
- if( !pline ) {
- for( ;; ) {
- if( eof_active2 || !fgets( line2, sizeof line2, fp_active2 ) ) {
- /* At end of file, check if the thread data file exists.
- ** If so, use its high/low values. Else, default to
- ** some initial values.
- */
- eof_active2 = TRUE;
- if( eof_active ) {
- break;
- }
- strcpy( line2, line );
- if( (data_file_open = init_data( thread_name( line ) )) ) {
- last2 = total.last;
- first2 = total.first;
- ch2 = 'y';
- } else {
- total.first = first2 = first;
- if( add_new ) {
- total.last = last2 = first - 1;
- ch2 = (ch == '=' ? 'x' : ch);
- added_groups++;
- } else {
- total.last = last2 = last;
- ch2 = (ch == '=' ? 'X' : toupper( ch ));
- }
- }
- data_file_open++; /* (1 == empty, 2 == open) */
- break;
- }
- if( !(cp2 = index( line2, ' ' )) ) {
- log_entry( "active2 line has no space: %s\n", line2 );
- continue;
- }
- *cp2 = '\0';
- if( sscanf( cp2+1,"%ld %ld %c",&last2,&first2,&ch2 ) != 3 ) {
- log_entry( "active2 digits corrupted: %s %s\n",
- line2, cp2+1 );
- continue;
- }
- /* Check if we're still in-sync */
- if( eof_active || strEQ( line, line2 ) ) {
- break;
- }
- /* Nope, we've got to go looking for this line somewhere
- ** down in the file. Save each non-matching line in memory
- ** as we go.
- */
- pline = (ACTIVE_LINE*)safemalloc( sizeof (ACTIVE_LINE) );
- pline->name = savestr( line2 );
- pline->last = last2;
- pline->first = first2;
- pline->type = ch2;
- pline->link = Nullact;
- if( !last_line ) {
- line_root = pline;
- } else {
- last_line->link = pline;
- }
- last_line = pline;
- }/* for */
- if( eof_active && eof_active2 ) {
- break;
- }
- }/* if !pline */
- if( eof_active ) {
- strcpy( line, line2 );
- if( truncate_len < 0 ) {
- truncate_len = ftell( fp_active3 );
- }
- }
- if( rebuild ) {
- unlink( thread_name( line ) );
- }
- update_successful = FALSE;
- if( hierarchy_list ) {
- action = ngmatch( hierarchy_list, line );
- } else {
- action = NG_DEFAULT;
- }
- switch( action ) {
- case NG_DEFAULT:
- if( ch2 < 'a' ) {
- action = NG_SKIP;
- } else {
- action = NG_MATCH;
- }
- break;
- case NG_MATCH: /* add if unthreaded */
- if( ch2 < 'a' ) {
- total.last = last2 = first2 - 1;
- added_groups++;
- }
- break;
- case NG_SKIP: /* remove if threaded */
- if( ch2 >= 'a' && !debug ) {
- unlink( thread_name( line ) );
- removed_groups++;
- }
- break;
- }
- if( caught_interrupt || (debug && action != NG_MATCH) ) {
- dont_read_data( data_file_open ); /* skip silently */
- } else if( ch == 'x' || ch == '=' ) {
- if( !daemon_delay ) { /* skip 'x'ed groups */
- putchar( 'x' );
- }
- ch = (action == NG_SKIP ? 'X' : 'x');
- if( (ch2 >= 'a' && ch2 != 'x') || force_flag ) {
- /* Remove thread file if group is newly 'x'ed out */
- unlink( thread_name( line ) );
- }
- update_successful = TRUE;
- dont_read_data( data_file_open );
- } else if( action == NG_SKIP ) { /* skip excluded groups */
- if( !daemon_delay ) {
- putchar( 'X' );
- }
- ch = toupper( ch );
- if( force_flag ) {
- unlink( thread_name( line ) );
- }
- update_successful = TRUE;
- dont_read_data( data_file_open );
- } else if( no_processing ) {
- if( !daemon_delay ) {
- putchar( ',' );
- }
- ch2 = ch;
- dont_read_data( data_file_open );
- } else if( !force_flag && !extra_expire && !rebuild
- && first == first2 && last == last2 ) {
- /* We're up-to-date here. Skip it. */
- if( !daemon_delay ) {
- putchar( '.' );
- }
- update_successful = TRUE;
- dont_read_data( data_file_open );
- } else {
- /* Looks like we need to process something. */
- #ifdef SERVER
- sprintf( line2, "GROUP %s", line );
- put_server( line2 ); /* go to next group */
- if( get_server( line2, sizeof line2 ) < 0 || *line2 != CHAR_OK ) {
- log_entry( "NNTP failure on group `%s'.\n", line );
- #else
- cp = line2;
- while( (cp = index( cp, '.' )) ) {
- *cp = '/';
- }
- filename = file_exp( line2 ); /* relative to spool dir */
- if( chdir( filename ) < 0 ) {
- if (errno != ENOENT) {
- log_entry( "Unable to chdir to `%s'.\n", filename );
- }
- #endif
- if( !daemon_delay ) {
- putchar( '*' );
- }
- dont_read_data( data_file_open );
- } else {
- filename = thread_name( line );
- /* Try to open the data file only if we didn't try it
- ** in the name matching code above.
- */
- if( !data_file_open-- ) { /* (0 == haven't tried yet) */
- if( !(data_file_open = init_data( filename )) ) {
- total.last = first - 1;
- total.first = first;
- }
- }
- strcpy( line2, filename );
- cp = rindex( line2, '/' ) + 1;
-
- if( data_file_open ) { /* (0 == empty, 1 == open) */
- if( !read_data() ) { /* did read fail? */
- #ifndef DEBUG
- unlink( filename ); /* trash input file */
- #else
- strcpy( cp, "bad.read" );
- rename( filename, line2 );
- #endif
- data_file_open = init_data( filename );
- total.last = first - 1;
- total.first = first;
- }
- }
- grevious_error = FALSE;
- process_articles( first, last );
- processed_groups++;
- if( caught_interrupt ) {
- processed_groups--; /* save nothing -- no update */
- } else if( !added_count && !expired_count && last == last2 ) {
- (void) write_data( Nullch );
- if( !daemon_delay ) {
- putchar( ':' );
- }
- update_successful = TRUE;
- } else if( !total.root ) {
- /* When the data file goes empty, remove it. */
- unlink( filename );
- expired_articles += expired_count;
- if( !daemon_delay ) {
- putchar( '-' );
- }
- update_successful = TRUE;
- } else {
- strcpy( cp, NEW_THREAD ); /* write data as .new */
- if( write_data( line2 ) && !grevious_error ) {
- rename( line2, filename );
- added_articles += added_count;
- expired_articles += expired_count;
- if( !daemon_delay ) {
- putchar( '#' );
- }
- update_successful = TRUE;
- } else {
- #ifndef DEBUG
- unlink( line2 ); /* blow-away bad write */
- #else
- cp = rindex( filename, '/' ) + 1;
- strcpy( cp, "bad.write" );
- rename( line2, filename );
- #endif
- if( !daemon_delay ) {
- putchar( '!' );
- }
- }/* if */
- }/* if */
- }/* if */
- }/* if */
- /* Finally, update the active2 entry for this newsgroup. */
- if( update_successful ) {
- fprintf( fp_active3, fmt_active2, line, last, first, ch );
- } else {
- fprintf( fp_active3, fmt_active2, line, last2, first2, ch2 );
- }
- /* If we're not out of sync, keep active2 file flushed. */
- if( !line_root ) {
- fflush( fp_active3 );
- }
- }/* for */
-
- #ifdef SERVER
- close_server();
- #endif
- fclose( fp_active );
- fclose( fp_active2 );
- fclose( fp_active3 );
-
- if( truncate_len >= 0 ) {
- #ifdef TRUNCATE
- if( truncate( file_exp( ACTIVE2 ), truncate_len ) == -1 )
- log_entry( "Unable to truncate the active2 file.\n" );
- #else
- #ifdef CHSIZE
- int fd;
- if( (fd = open( file_exp( ACTIVE2 ), O_RDWR )) == -1 )
- log_entry( "Unable to open the active2 file for truncation.\n" );
- else {
- if( chsize( fd, truncate_len ) == -1 )
- log_entry( "Unable to truncate the active2 file.\n" );
- close( fd );
- }
- #else
- filename = file_exp( ACTIVE2 );
- sprintf( line, "%s.new", filename );
- if( (fp_active3 = fopen( line, "w" )) == Nullfp ) {
- log_entry( "Unable to create the active2.new file.\n" );
- } else if( (fp_active2 = fopen( filename, "r" )) == Nullfp ) {
- fclose( fp_active3 );
- unlink( line );
- log_entry( "Unable to open the active2 file.\n" );
- } else {
- while( ftell( fp_active3 ) < truncate_len ) {
- if( !fgets( line2, sizeof line2, fp_active2 ) ) {
- break;
- }
- fputs( line2, fp_active3 );
- }
- sprintf( line2, "%s.old", filename );
- rename( filename, line2 );
- rename( line, filename );
- fclose( fp_active2 );
- fclose( fp_active3 );
- }
- #endif /* not XENIX */
- #endif /* not TRUNCATE */
- truncate_len = -1;
- }
-
- sprintf( line, "Processed %d group%s: added %d article%s, expired %d.\n",
- processed_groups, processed_groups == 1 ? nullstr : "s",
- added_articles, added_articles == 1 ? nullstr : "s",
- expired_articles );
-
- if( processed_groups ) {
- log_entry( line );
- }
-
- if( !daemon_delay ) {
- putchar( '\n' );
- fputs( line, stdout );
- }
- if( added_groups ) {
- sprintf( line, "Turned %d group%s on.\n", added_groups,
- added_groups == 1 ? nullstr : "s" );
- log_entry( line );
- if( !daemon_delay ) {
- fputs( line, stdout );
- }
- }
- if( removed_groups ) {
- sprintf( line, "Turned %d group%s off.\n", removed_groups,
- removed_groups == 1 ? nullstr : "s" );
- log_entry( line );
- if( !daemon_delay ) {
- fputs( line, stdout );
- }
- }
- extra_expire = FALSE;
- rebuild = FALSE;
- }
-
- /*
- ** ngmatch - newsgroup name matching
- **
- ** returns NG_MATCH for a positive patch, NG_SKIP for a negative match,
- ** and NG_DEFAULT if the group doesn't match at all.
- **
- ** "all" in a pattern is a wildcard that matches exactly one word;
- ** it does not cross "." (NGDELIM) delimiters.
- **
- ** This matching code was borrowed from C news.
- */
-
- #define ALL "all" /* word wildcard */
-
- #define NGNEG '!'
- #define NGSEP ','
- #define NGDELIM '.'
-
- int
- ngmatch( ngpat, grp )
- char *ngpat, *grp;
- {
- register char *patp; /* point at current pattern */
- register char *patcomma;
- register int depth;
- register int faildeepest = 0, hitdeepest = 0; /* in case no match */
- register bool negation;
-
- for( patp = ngpat; patp != Nullch; patp = patcomma ) {
- negation = FALSE;
- patcomma = index( patp, NGSEP );
- if( patcomma != Nullch ) {
- *patcomma = '\0'; /* will be restored below */
- }
- if( *patp == NGNEG ) {
- ++patp;
- negation = TRUE;
- }
- depth = onepatmatch( patp, grp ); /* try 1 pattern, 1 group */
- if( patcomma != Nullch ) {
- *patcomma++ = NGSEP; /* point after the comma */
- }
- if( depth == 0 ) { /* mis-match */
- ; /* ignore it */
- } else if( negation ) {
- /* record depth of deepest negated matched word */
- if( depth > faildeepest ) {
- faildeepest = depth;
- }
- } else {
- /* record depth of deepest plain matched word */
- if( depth > hitdeepest ) {
- hitdeepest = depth;
- }
- }
- }
- if( hitdeepest > faildeepest ) {
- return NG_MATCH;
- } else if( faildeepest ) {
- return NG_SKIP;
- } else {
- return NG_DEFAULT;
- }
- }
-
- /*
- ** Match a pattern against a group by looking at each word of pattern in turn.
- **
- ** On a match, return the depth (roughly, ordinal number * k) of the rightmost
- ** word that matches. If group runs out first, the match fails; if pattern
- ** runs out first, it succeeds. On a failure, return zero.
- */
- int
- onepatmatch( patp, grp )
- char *patp, *grp;
- {
- register char *rpatwd; /* used by word match (inner loop) */
- register char *patdot, *grdot; /* point at dots after words */
- register char *patwd, *grwd; /* point at current words */
- register int depth = 0;
-
- for( patwd = patp, grwd = grp;
- patwd != Nullch && grwd != Nullch;
- patwd = patdot, grwd = grdot
- ) {
- register bool match = FALSE;
- register int incr = 20;
-
- /* null-terminate words */
- patdot = index(patwd, NGDELIM);
- if( patdot != Nullch ) {
- *patdot = '\0'; /* will be restored below */
- }
- grdot = index( grwd, NGDELIM );
- if( grdot != Nullch ) {
- *grdot = '\0'; /* will be restored below */
- }
- /*
- * Match one word of pattern with one word of group.
- * A pattern word of "all" matches any group word,
- * but isn't worth as much.
- */
- #ifdef FAST_STRCMP
- match = STREQ( patwd, grwd );
- if( !match && STREQ( patwd, ALL ) ) {
- match = TRUE;
- --incr;
- }
- #else
- for( rpatwd = patwd; *rpatwd == *grwd++; ) {
- if( *rpatwd++ == '\0' ) {
- match = TRUE; /* literal match */
- break;
- }
- }
- if( !match ) {
- /* ugly special case match for "all" */
- rpatwd = patwd;
- if( *rpatwd++ == 'a' && *rpatwd++ == 'l'
- && *rpatwd++ == 'l' && *rpatwd == '\0' ) {
- match = TRUE;
- --incr;
- }
- }
- #endif /* FAST_STRCMP */
-
- if( patdot != Nullch ) {
- *patdot++ = NGDELIM; /* point after the dot */
- }
- if( grdot != Nullch ) {
- *grdot++ = NGDELIM;
- }
- if( !match ) {
- depth = 0; /* words differed - mismatch */
- break;
- }
- depth += incr;
- }
- /* if group name ran out before pattern, then match fails */
- if( grwd == Nullch && patwd != Nullch ) {
- depth = 0;
- }
- return depth;
- }
-
- /* Generate a log entry with timestamp.
- */
- /*VARARGS1*/
- void
- log_entry( fmt, arg1, arg2 )
- char *fmt;
- long arg1;
- long arg2;
- {
- time_t now;
- char *ctime();
-
- (void) time( &now );
- fprintf( fp_log, "%.12s ", ctime( &now )+4 );
- fprintf( fp_log, fmt, arg1, arg2 );
- fflush( fp_log );
- }
-
- /* Generate an log entry, with 'E'rror flagging (non-daemon mode), time-stamp,
- ** and newsgroup name.
- */
- /*VARARGS1*/
- void
- log_error( fmt, arg1, arg2, arg3 )
- char *fmt;
- long arg1;
- long arg2;
- long arg3;
- {
- log_entry( "%s: ", line );
- fprintf( fp_log, fmt, arg1, arg2, arg3 );
- fflush( fp_log );
- if( *fmt == '*' ) {
- grevious_error = TRUE;
- if( !daemon_delay ) {
- putchar( 'E' );
- }
- }
- else {
- if( !daemon_delay ) {
- putchar( 'e' );
- }
- }
- }
-
- #ifndef RENAME
- int
- rename( old, new )
- char *old, *new;
- {
- struct stat st;
-
- if( stat( old, &st ) == -1 ) {
- return -1;
- }
- if( unlink( new ) == -1 && errno != ENOENT ) {
- return -1;
- }
- if( link( old, new ) == -1 ) {
- return -1;
- }
- if( unlink( old ) == -1 ) {
- int e = errno;
- (void) unlink( new );
- errno = e;
- return -1;
- }
- return 0;
- }
- #endif /*RENAME*/
-