Mercurial > noffle
comparison src/database.c @ 255:52f467c7213b noffle
[svn] * docs/noffle.1,src/Makefile.am,src/Makefile.in,src/content.c,
src/content.h,src/database.c,src/database.h,src/expire.c,
src/expire.h,src/noffle.c: Split out expire code from database.c,
change to remove articles in place (rather than rebuild article
database) and add separate command to rebuild article database
from articles listed in overviews. This may help if the article
database gets corrupted.
author | bears |
---|---|
date | Wed, 26 Jun 2002 14:15:44 +0100 |
parents | 7a830ce3211e |
children | 087e7039b569 |
comparison
equal
deleted
inserted
replaced
254:4c0f54d51591 | 255:52f467c7213b |
---|---|
1 /* | 1 /* |
2 database.c | 2 database.c |
3 | 3 |
4 $Id: database.c 379 2002-03-26 17:52:01Z mirkol $ | 4 $Id: database.c 387 2002-06-26 13:15:44Z bears $ |
5 | 5 |
6 Uses GNU gdbm library. Using Berkeley db (included in libc6) was | 6 Uses GNU gdbm library. Using Berkeley db (included in libc6) was |
7 cumbersome. It is based on Berkeley db 1.85, which has severe bugs | 7 cumbersome. It is based on Berkeley db 1.85, which has severe bugs |
8 (e.g. it is not recommended to delete or overwrite entries with | 8 (e.g. it is not recommended to delete or overwrite entries with |
9 overflow pages). | 9 overflow pages). |
14 #endif | 14 #endif |
15 | 15 |
16 #include <stdio.h> | 16 #include <stdio.h> |
17 #include <ctype.h> | 17 #include <ctype.h> |
18 #include <errno.h> | 18 #include <errno.h> |
19 #include <fcntl.h> | |
20 #include <gdbm.h> | 19 #include <gdbm.h> |
21 #include <unistd.h> | 20 #include <unistd.h> |
22 #include <sys/types.h> | 21 #include <sys/types.h> |
23 #include <sys/stat.h> | 22 #include <sys/stat.h> |
24 #include "configfile.h" | 23 #include "configfile.h" |
24 #include "content.h" | |
25 #include "database.h" | 25 #include "database.h" |
26 #include "itemlist.h" | 26 #include "group.h" |
27 #include "log.h" | 27 #include "log.h" |
28 #include "protocol.h" | 28 #include "protocol.h" |
29 #include "util.h" | 29 #include "util.h" |
30 #include "portable.h" | 30 #include "portable.h" |
31 | |
32 static const char ARTICLE_FILENAME_FMT[] = "%s/data/articles.gdbm"; | |
33 static const char ARTICLE_NEW_FILENAME_FMT[] = "%s/data/articles.gdbm.new"; | |
34 | |
31 | 35 |
32 static struct Db | 36 static struct Db |
33 { | 37 { |
34 GDBM_FILE dbf; | 38 GDBM_FILE dbf; |
35 | 39 |
58 } db = { NULL, "(unknown)", "", 0, 0, "", "", "", "", "", 0, 0, NULL }; | 62 } db = { NULL, "(unknown)", "", 0, 0, "", "", "", "", "", 0, 0, NULL }; |
59 | 63 |
60 static const char * | 64 static const char * |
61 errMsg( void ) | 65 errMsg( void ) |
62 { | 66 { |
63 if ( errno != 0 ) | 67 if ( gdbm_errno == GDBM_NO_ERROR ) |
64 return strerror( errno ); | 68 return strerror( errno ); |
65 return gdbm_strerror( gdbm_errno ); | 69 return gdbm_strerror( gdbm_errno ); |
66 } | 70 } |
67 | 71 |
68 Bool | 72 Bool |
70 { | 74 { |
71 Str name, host; | 75 Str name, host; |
72 int flags; | 76 int flags; |
73 | 77 |
74 ASSERT( db.dbf == NULL ); | 78 ASSERT( db.dbf == NULL ); |
75 snprintf( name, MAXCHAR, "%s/data/articles.gdbm", Cfg_spoolDir() ); | 79 snprintf( name, MAXCHAR, ARTICLE_FILENAME_FMT, Cfg_spoolDir() ); |
76 flags = GDBM_WRCREAT | GDBM_FAST; | 80 flags = GDBM_WRCREAT | GDBM_FAST; |
77 | 81 |
78 if ( ! ( db.dbf = gdbm_open( name, 512, flags, 0644, NULL ) ) ) | 82 if ( ! ( db.dbf = gdbm_open( name, 512, flags, 0644, NULL ) ) ) |
79 { | 83 { |
80 Log_err( "Error opening %s for r/w (%s)", name, errMsg() ); | 84 Log_err( "Error opening %s for r/w (%s)", name, errMsg() ); |
580 free( oldDptr ); | 584 free( oldDptr ); |
581 *msgId = cursor.dptr; | 585 *msgId = cursor.dptr; |
582 return ( cursor.dptr != NULL ); | 586 return ( cursor.dptr != NULL ); |
583 } | 587 } |
584 | 588 |
585 static int | 589 void |
586 calcExpireDays( const char *msgId ) | 590 Db_compact( void ) |
587 { | 591 { |
588 const char *xref; | 592 ASSERT( db.dbf ); |
589 ItemList *refs; | 593 if ( gdbm_reorganize( db.dbf ) != 0 ) |
590 const char *ref; | 594 Log_err( "Error compacting article base: %s", errMsg() ); |
591 int res; | 595 } |
592 | 596 |
593 xref = Db_xref( msgId ); | 597 /* |
594 if ( xref[ 0 ] == '\0' ) | 598 Helper functions for database rebuild. |
595 return -1; | 599 */ |
596 | 600 |
597 res = -1; | 601 static struct DbNew |
598 refs = new_Itl( xref, " :" ); | 602 { |
599 for ( ref = Itl_first( refs ); ref != NULL; ref = Itl_next( refs ) ) | 603 GDBM_FILE dbf; |
600 { | 604 |
601 int days; | 605 } dbNew = { NULL }; |
602 | 606 |
603 days = Cfg_expire( ref ); | 607 |
604 if ( days == 0 | 608 static Bool |
605 || ( days > res && res != 0 ) ) | 609 newOpen( void ) |
606 res = days; | 610 { |
611 Str name; | |
612 int flags; | |
613 | |
614 ASSERT( dbNew.dbf == NULL ); | |
615 snprintf( name, MAXCHAR, ARTICLE_NEW_FILENAME_FMT, Cfg_spoolDir() ); | |
616 flags = GDBM_WRCREAT | GDBM_FAST; | |
617 | |
618 if ( ! ( dbNew.dbf = gdbm_open( name, 512, flags, 0644, NULL ) ) ) | |
619 { | |
620 Log_err( "Error opening %s for r/w (%s)", name, errMsg() ); | |
621 return FALSE; | |
622 } | |
623 Log_dbg( LOG_DBG_NEWSBASE, "%s opened for r/w", name ); | |
624 return TRUE; | |
625 } | |
626 | |
627 static Bool | |
628 newClose( Bool makeMain ) | |
629 { | |
630 Str newName; | |
631 | |
632 ASSERT( dbNew.dbf ); | |
633 Log_dbg( LOG_DBG_NEWSBASE, "Closing new database" ); | |
634 gdbm_close( dbNew.dbf ); | |
635 dbNew.dbf = NULL; | |
636 | |
637 snprintf( newName, MAXCHAR, ARTICLE_NEW_FILENAME_FMT, Cfg_spoolDir() ); | |
638 | |
639 if ( makeMain ) | |
640 { | |
641 Str name; | |
642 | |
643 ASSERT( db.dbf ); | |
644 Db_close(); | |
645 snprintf( name, MAXCHAR, ARTICLE_FILENAME_FMT, Cfg_spoolDir() ); | |
646 if ( rename( newName, name ) != 0 ) | |
647 { | |
648 Log_err( "Rename %s to %s failed: %s", | |
649 newName, name, strerror( errno ) ); | |
650 return FALSE; | |
651 } | |
652 Log_dbg( LOG_DBG_NEWSBASE, "Renamed %s to %s", newName, name ); | |
653 return Db_open(); | |
654 } | |
655 else | |
656 { | |
657 if ( unlink( newName ) != 0 ) | |
658 { | |
659 Log_err( "Unlink %s failed: %s", newName, strerror( errno ) ); | |
660 return FALSE; | |
661 } | |
662 Log_dbg( LOG_DBG_NEWSBASE, "Deleted %s", newName ); | |
663 return TRUE; | |
664 } | |
665 } | |
666 | |
667 static Bool | |
668 newCopyArt( const char *msgId ) | |
669 { | |
670 datum key, val; | |
671 | |
672 ASSERT( db.dbf ); | |
673 ASSERT( dbNew.dbf ); | |
674 key.dptr = (void *)msgId; | |
675 key.dsize = strlen( msgId ) + 1; | |
676 | |
677 val = gdbm_fetch( db.dbf, key ); | |
678 if ( val.dptr != NULL ) | |
679 { | |
680 Bool res; | |
607 | 681 |
608 Itl_next( refs ); /* Throw away group number */ | 682 res = ( gdbm_store( dbNew.dbf, key, val, GDBM_INSERT ) == 0 ); |
609 } | 683 if ( ! res ) |
610 del_Itl( refs ); | 684 Log_err( "Could not store %s in new database (%s)", |
611 | 685 msgId, errMsg() ); |
612 return res; | 686 free( val.dptr ); |
687 return res; | |
688 } | |
689 Log_err( "%s not found in database", msgId ); | |
690 return FALSE; | |
691 } | |
692 | |
693 static Bool | |
694 newContains( const char *msgId ) | |
695 { | |
696 datum key; | |
697 | |
698 ASSERT( dbNew.dbf ); | |
699 key.dptr = (void*)msgId; | |
700 key.dsize = strlen( msgId ) + 1; | |
701 return gdbm_exists( dbNew.dbf, key ); | |
613 } | 702 } |
614 | 703 |
615 Bool | 704 Bool |
616 Db_expire( void ) | 705 Db_rebuild( void ) |
617 { | 706 { |
618 int cntDel, cntLeft, flags, expDays; | 707 const Over *ov; |
619 time_t nowTime, lastAccess; | 708 int i; |
709 Str grp; | |
620 const char *msgId; | 710 const char *msgId; |
621 Str name, tmpName; | 711 Bool err; |
622 GDBM_FILE tmpDbf; | 712 |
623 datum key, val; | 713 if ( ! Cont_firstGrp( grp ) ) |
624 Str expires; | 714 return FALSE; |
625 time_t texpires; | 715 if ( ! newOpen() ) |
626 | 716 return FALSE; |
627 if ( ! Db_open() ) | 717 |
628 return FALSE; | 718 Log_inf( "Rebuilding article database" ); |
629 snprintf( name, MAXCHAR, "%s/data/articles.gdbm", Cfg_spoolDir() ); | 719 err = FALSE; |
630 snprintf( tmpName, MAXCHAR, "%s/data/articles.gdbm.new", Cfg_spoolDir() ); | 720 do |
631 flags = GDBM_NEWDB | GDBM_FAST; | 721 { |
632 if ( ! ( tmpDbf = gdbm_open( tmpName, 512, flags, 0644, NULL ) ) ) | 722 if ( ! Grp_exists( grp ) ) |
633 { | 723 Log_err( "Overview file for unknown group %s exists", grp ); |
634 Log_err( "Error opening %s for read/write (%s)", tmpName, errMsg() ); | 724 else |
635 Db_close(); | |
636 return FALSE; | |
637 } | |
638 Log_inf( "Expiring articles" ); | |
639 cntDel = 0; | |
640 cntLeft = 0; | |
641 nowTime = time( NULL ); | |
642 if ( Db_first( &msgId ) ) | |
643 do | |
644 { | 725 { |
645 expDays = calcExpireDays( msgId ); | 726 Cont_read( grp ); |
646 lastAccess = Db_lastAccess( msgId ); | 727 for ( i = Cont_first(); i <= Cont_last(); ++i ) |
647 if ( Prt_searchHeader( Db_header( msgId ), "Expires", expires ) ) | |
648 texpires = Utl_parseNewsDate( expires ); | |
649 else | |
650 texpires = (time_t) -1; | |
651 | |
652 if ( expDays == -1 ) | |
653 Log_err( "Internal error: Failed expiry calculation on %s", | |
654 msgId ); | |
655 else if ( lastAccess == -1 ) | |
656 Log_err( "Internal error: Getting lastAccess of %s failed", | |
657 msgId ); | |
658 else if ( expDays > 0 | |
659 && difftime( nowTime, lastAccess ) > | |
660 ( (double) expDays * 24 * 3600 ) ) | |
661 { | |
662 #ifdef DEBUG | |
663 Str last, now; | |
664 | |
665 Utl_cpyStr( last, ctime( &lastAccess ) ); | |
666 last[ strlen( last ) - 1 ] = '\0'; | |
667 Utl_cpyStr( now, ctime( &nowTime ) ); | |
668 last[ strlen( now ) - 1 ] = '\0'; | |
669 Log_dbg( LOG_DBG_EXPIRE, | |
670 "Expiring %s: last access %s, time now %s", | |
671 msgId, last, now ); | |
672 #endif | |
673 ++cntDel; | |
674 } | |
675 else if ( ( texpires != (time_t) -1 ) | |
676 && nowTime > texpires ) | |
677 { | 728 { |
678 Log_dbg( LOG_DBG_EXPIRE, | 729 if ( ! Cont_validNumb( i ) ) |
679 "Expiring %s: Expires header activated", msgId ); | 730 continue; |
680 ++cntDel; | 731 |
732 if ( ( ov = Cont_get( i ) ) ) | |
733 { | |
734 msgId = Ov_msgId( ov ); | |
735 if ( msgId == NULL ) | |
736 { | |
737 err = TRUE; | |
738 Log_err( "Overview in %s has no msg id", grp ); | |
739 } | |
740 else if ( ! newContains( msgId ) ) | |
741 err |= ! newCopyArt( msgId ); | |
742 } | |
743 else | |
744 { | |
745 err = TRUE; | |
746 Log_err( "Overview %d not available in group %s", i, grp ); | |
747 } | |
681 } | 748 } |
682 else | |
683 { | |
684 ++cntLeft; | |
685 key.dptr = (void *)msgId; | |
686 key.dsize = strlen( msgId ) + 1; | |
687 | |
688 val = gdbm_fetch( db.dbf, key ); | |
689 if ( val.dptr != NULL ) | |
690 { | |
691 if ( gdbm_store( tmpDbf, key, val, GDBM_INSERT ) != 0 ) | |
692 Log_err( "Could not store %s in new database (%s)", | |
693 errMsg() ); | |
694 free( val.dptr ); | |
695 } | |
696 } | |
697 } | 749 } |
698 while ( Db_next( &msgId ) ); | 750 } |
699 Log_inf( "%lu articles deleted, %lu left", cntDel, cntLeft ); | 751 while ( Cont_nextGrp( grp ) ); |
700 gdbm_close( tmpDbf ); | 752 |
701 Db_close(); | 753 return newClose( ! err ); |
702 rename( tmpName, name ); | 754 } |
703 return TRUE; | 755 |
704 } |