comparison src/server.c @ 127:3c71e28c8eef noffle

[svn] Release-1-0 mergedocs/NOTES
author bears
date Tue, 25 Jul 2000 13:14:54 +0100
parents f50cc311e29a
children 8b9366fc1361
comparison
equal deleted inserted replaced
126:7c7a7c96d35b 127:3c71e28c8eef
1 /* 1 /*
2 server.c 2 server.c
3 3
4 $Id: server.c 167 2000-06-30 07:10:45Z enz $ 4 $Id: server.c 183 2000-07-25 12:14:54Z bears $
5 */ 5 */
6 6
7 #if HAVE_CONFIG_H 7 #if HAVE_CONFIG_H
8 #include <config.h> 8 #include <config.h>
9 #endif 9 #endif
50 #include "portable.h" 50 #include "portable.h"
51 51
52 struct 52 struct
53 { 53 {
54 Bool running; 54 Bool running;
55 time_t lastServerOpen;
55 int artPtr; 56 int artPtr;
56 Str grp; /* selected group, "" if none */ 57 Str grp; /* selected group, "" if none */
57 } server = { FALSE, 0, "" }; 58 Bool readAlarmFlag;
59 } server = { FALSE, 0L, 0, "", FALSE };
58 60
59 typedef struct Cmd 61 typedef struct Cmd
60 { 62 {
61 const char *name; 63 const char *name;
62 const char *syntax; 64 const char *syntax;
85 static Bool doXpat( char *arg, const Cmd *cmd ); 87 static Bool doXpat( char *arg, const Cmd *cmd );
86 static Bool doXOver( char *arg, const Cmd *cmd ); 88 static Bool doXOver( char *arg, const Cmd *cmd );
87 static Bool notImplemented( char *arg, const Cmd *cmd ); 89 static Bool notImplemented( char *arg, const Cmd *cmd );
88 static void putStat( unsigned int stat, const char *fmt, ... ); 90 static void putStat( unsigned int stat, const char *fmt, ... );
89 91
92 static void closeServer( void );
93 static Bool initServer( void );
94
90 Cmd commands[] = 95 Cmd commands[] =
91 { 96 {
92 { "article", "ARTICLE [msg-id|n]", &doArt }, 97 { "article", "ARTICLE [msg-id|n]", &doArt },
93 { "body", "BODY [msg-id|n]", &doBody }, 98 { "body", "BODY [msg-id|n]", &doBody },
94 { "head", "HEAD [msg-id|n]", &doHead }, 99 { "head", "HEAD [msg-id|n]", &doHead },
105 { "next", "NEXT", &doNext }, 110 { "next", "NEXT", &doNext },
106 { "post", "POST", &doPost }, 111 { "post", "POST", &doPost },
107 { "quit", "QUIT", &doQuit }, 112 { "quit", "QUIT", &doQuit },
108 { "slave", "SLAVE (ignored)", &doSlave }, 113 { "slave", "SLAVE (ignored)", &doSlave },
109 { "stat", "STAT [msg-id|n]", &doStat }, 114 { "stat", "STAT [msg-id|n]", &doStat },
110 { "xhdr", "XHDR over-field [m[-[n]]]", &doXhdr }, 115 { "xhdr", "XHDR over-field [msg-id|m[-[n]]]", &doXhdr },
111 { "xpat", "XPAT over-field m[-[n]] pat", &doXpat }, 116 { "xpat", "XPAT over-field msg-id|m[-[n]] pat", &doXpat },
112 { "xover", "XOVER [m[-[n]]]", &doXOver } 117 { "xover", "XOVER [m[-[n]]]", &doXOver }
113 }; 118 };
114 119
115 /* 120 /*
116 Notice interest in reading this group. 121 Notice interest in reading this group.
200 putSyntax( const Cmd *cmd ) 205 putSyntax( const Cmd *cmd )
201 { 206 {
202 putStat( STAT_SYNTAX_ERR, "Syntax error. Usage: %s", cmd->syntax ); 207 putStat( STAT_SYNTAX_ERR, "Syntax error. Usage: %s", cmd->syntax );
203 } 208 }
204 209
205 static Bool 210 static void
206 getLn( Str line ) 211 readAlarm( int sig )
207 { 212 {
208 return Prt_getLn( line, stdin ); 213 UNUSED( sig );
214
215 server.readAlarmFlag = TRUE;
216 return;
217 }
218
219 static sig_t
220 installSignalHandler( int sig, sig_t handler )
221 {
222 struct sigaction act, oldAct;
223
224 act.sa_handler = handler;
225 sigemptyset( &act.sa_mask );
226 act.sa_flags = 0;
227 if ( sig != SIGALRM )
228 act.sa_flags |= SA_RESTART;
229 if ( sigaction( sig, &act, &oldAct ) < 0 )
230 return SIG_ERR;
231 return oldAct.sa_handler;
232 }
233
234 /*
235 * Returns: < 0 on error, 0 on timeout, > 0 on success.
236 * If timeout is zero, wait indefinitely.
237 */
238 static int
239 waitCmdLn( Str line, int timeoutSeconds )
240 {
241 sig_t oldHandler;
242 int r;
243
244 ASSERT( timeoutSeconds >= 0 );
245
246 /* Special case - no timeout. */
247 if ( timeoutSeconds == 0 )
248 {
249 r = Prt_getLn( line, stdin );
250 return ( r ) ? 1 : -1;
251 }
252
253 server.readAlarmFlag = FALSE;
254 oldHandler = installSignalHandler( SIGALRM, readAlarm );
255 if ( oldHandler == SIG_ERR )
256 {
257 Log_err( "client.c:connectWithTimeout: signal failed." );
258 return -1;
259 }
260 if ( alarm( ( unsigned int ) timeoutSeconds ) != 0 )
261 Log_err( "server.c:waitCmdLn: Alarm was already set." );
262 r = Prt_getLn( line, stdin );
263 alarm( 0 );
264 installSignalHandler( SIGALRM, oldHandler );
265 if ( server.readAlarmFlag )
266 return 0;
267 else if ( r )
268 return 1;
269 return -1;
209 } 270 }
210 271
211 static Bool 272 static Bool
212 getTxtLn( Str line, Bool *err ) 273 getTxtLn( Str line, Bool *err )
213 { 274 {
634 static void 695 static void
635 printGroups( const char *pat, void (*printProc)( Str, const char* ) ) 696 printGroups( const char *pat, void (*printProc)( Str, const char* ) )
636 { 697 {
637 Str line; 698 Str line;
638 const char *g; 699 const char *g;
639 FILE *f;
640 sig_t lastHandler;
641 int ret;
642 700
643 putStat( STAT_GRPS_FOLLOW, "Groups" ); 701 putStat( STAT_GRPS_FOLLOW, "Groups" );
644 fflush( stdout ); 702 fflush( stdout );
645 Log_dbg( "[S FLUSH]" ); 703 Log_dbg( "[S FLUSH]" );
646 if ( Grp_exists( pat ) ) 704 if ( Grp_exists( pat ) )
649 if ( ! Prt_putTxtLn( line, stdout ) ) 707 if ( ! Prt_putTxtLn( line, stdout ) )
650 Log_err( "Writing to stdout failed." ); 708 Log_err( "Writing to stdout failed." );
651 } 709 }
652 else 710 else
653 { 711 {
654 lastHandler = signal( SIGPIPE, SIG_IGN ); 712 if ( Grp_firstGrp( &g ) )
655 f = popen( "sort", "w" ); 713 do
656 if ( f == NULL ) 714 if ( Wld_match( g, pat ) )
657 { 715 {
658 Log_err( "Cannot open pipe to 'sort'" ); 716 (*printProc)( line, g );
659 if ( Grp_firstGrp( &g ) ) 717 if ( ! Prt_putTxtLn( line, stdout ) )
660 do 718 Log_err( "Writing to stdout failed." );
661 if ( Wld_match( g, pat ) ) 719 }
662 { 720 while ( Grp_nextGrp( &g ) );
663 (*printProc)( line, g );
664 if ( ! Prt_putTxtLn( line, stdout ) )
665 Log_err( "Writing to stdout failed." );
666 }
667 while ( Grp_nextGrp( &g ) );
668 }
669 else
670 {
671 if ( Grp_firstGrp( &g ) )
672 do
673 if ( Wld_match( g, pat ) )
674 {
675 (*printProc)( line, g );
676 if ( ! Prt_putTxtLn( line, f ) )
677 {
678 Log_err( "Writing to 'sort' pipe failed." );
679 break;
680 }
681 }
682 while ( Grp_nextGrp( &g ) );
683 ret = pclose( f );
684 if ( ret != EXIT_SUCCESS )
685 Log_err( "sort command returned %d", ret );
686 fflush( stdout );
687 Log_dbg( "[S FLUSH]" );
688 signal( SIGPIPE, lastHandler );
689 }
690 } 721 }
691 putEndOfTxt(); 722 putEndOfTxt();
692 } 723 }
693 724
694 static void 725 static void
811 if ( ! Grp_exists( arg ) ) 842 if ( ! Grp_exists( arg ) )
812 putStat( STAT_NO_SUCH_GRP, "No such group" ); 843 putStat( STAT_NO_SUCH_GRP, "No such group" );
813 else 844 else
814 { 845 {
815 changeToGrp( arg ); 846 changeToGrp( arg );
847
848 /*
849 * The output may take some time, so release the lock
850 * while outputting (all the required data is in RAM
851 * at this point).
852 */
853 closeServer();
854
816 first = Cont_first(); 855 first = Cont_first();
817 last = Cont_last(); 856 last = Cont_last();
818 putStat( STAT_GRP_SELECTED, "Article list" ); 857 putStat( STAT_GRP_SELECTED, "Article list" );
819 for ( i = first; i <= last; ++i ) 858 for ( i = first; i <= last; ++i )
820 if ( ( ov = Cont_get( i ) ) ) 859 if ( ( ov = Cont_get( i ) ) )
978 1017
979 putStat( STAT_SEND_ART, "Continue (end with period)" ); 1018 putStat( STAT_SEND_ART, "Continue (end with period)" );
980 fflush( stdout ); 1019 fflush( stdout );
981 Log_dbg( "[S FLUSH]" ); 1020 Log_dbg( "[S FLUSH]" );
982 s = new_DynStr( 10000 ); 1021 s = new_DynStr( 10000 );
1022
1023
1024 /*
1025 * The article may take some time coming in, so release the
1026 * lock while collecting it.
1027 */
1028 closeServer();
1029
983 err = FALSE; 1030 err = FALSE;
984 while ( ! err && getTxtLn( line, &err ) ) 1031 while ( ! err && getTxtLn( line, &err ) )
985 DynStr_appLn( s, line ); 1032 DynStr_appLn( s, line );
1033
1034 if ( ! initServer() )
1035 {
1036 del_DynStr( s );
1037 return FALSE;
1038 }
986 1039
987 if ( ! err 1040 if ( ! err
988 && Post_open( DynStr_str( s ) ) 1041 && Post_open( DynStr_str( s ) )
989 && Post_post() ) 1042 && Post_post() )
990 { 1043 {
1031 for ( i = *first; i <= *last; ++i ) 1084 for ( i = *first; i <= *last; ++i )
1032 if ( Cont_validNumb( i ) ) 1085 if ( Cont_validNumb( i ) )
1033 ++(*numb); 1086 ++(*numb);
1034 } 1087 }
1035 1088
1089 enum XhdrType { SUBJ, FROM, DATE, MSG_ID, REF, BYTES, LINES, XREF, UNKNOWN };
1090
1091 static enum XhdrType
1092 whatXhdrField( const char * fieldname )
1093 {
1094 Str name;
1095
1096 Utl_cpyStr( name, fieldname );
1097 Utl_toLower( name );
1098 if ( strcmp( name, "subject" ) == 0 )
1099 return SUBJ;
1100 else if ( strcmp( name, "from" ) == 0 )
1101 return FROM;
1102 else if ( strcmp( name, "date" ) == 0 )
1103 return DATE;
1104 else if ( strcmp( name, "message-id" ) == 0 )
1105 return MSG_ID;
1106 else if ( strcmp( name, "references" ) == 0 )
1107 return REF;
1108 else if ( strcmp( name, "bytes" ) == 0 )
1109 return BYTES;
1110 else if ( strcmp( name, "lines" ) == 0 )
1111 return LINES;
1112 else if ( strcmp( name, "xref" ) == 0 )
1113 return XREF;
1114 else
1115 return UNKNOWN;
1116 }
1117
1118 static void
1119 getXhdrField( enum XhdrType what, const Over * ov, Str res )
1120 {
1121 const char * msgId;
1122 Str host;
1123
1124 switch ( what )
1125 {
1126 case SUBJ:
1127 Utl_cpyStr( res, Ov_subj( ov ) );
1128 break;
1129 case FROM:
1130 Utl_cpyStr( res, Ov_from( ov ) );
1131 break;
1132 case DATE:
1133 Utl_cpyStr( res, Ov_date( ov ) );
1134 break;
1135 case MSG_ID:
1136 Utl_cpyStr( res, Ov_msgId( ov ) );
1137 break;
1138 case REF:
1139 Utl_cpyStr( res, Ov_ref( ov ) );
1140 break;
1141 case BYTES:
1142 snprintf( res, MAXCHAR, "%d", Ov_bytes( ov ) );
1143 break;
1144 case LINES:
1145 snprintf( res, MAXCHAR, "%d", Ov_lines( ov ) );
1146 break;
1147 case XREF:
1148 msgId = Ov_msgId( ov );
1149 /*
1150 * Gen info messages don't have an Xref header. When INN is asked
1151 * for a header that doesn't exist in a message, it reports the
1152 * header value as '(none)', so do the same.
1153 */
1154 if ( Pseudo_isGeneralInfo( msgId ) )
1155 Utl_cpyStr( res, "none" );
1156 else
1157 {
1158 gethostname( host, MAXCHAR );
1159 snprintf( res, MAXCHAR, "%s %s", host, Db_xref( msgId ) );
1160 }
1161 break;
1162 default:
1163 ASSERT( FALSE );
1164 }
1165 }
1166
1036 /* 1167 /*
1037 Note this only handles a subset of headers. But they are all 1168 Note this only handles a subset of headers. But they are all
1038 the headers any newsreader should need to work properly. 1169 the headers any newsreader should need to work properly.
1039 1170
1040 That last sentence will at some stage be proved wrong. 1171 That last sentence will at some stage be proved wrong.
1041 */ 1172 */
1042 static Bool 1173 static Bool
1043 doXhdr( char *arg, const Cmd *cmd ) 1174 doXhdr( char *arg, const Cmd *cmd )
1044 { 1175 {
1045 int first, last, i, n, numb; 1176 enum XhdrType what;
1046 enum { SUBJ, FROM, DATE, MSG_ID, REF, BYTES, LINES, XREF } what; 1177 const char *p;
1047 const char *p, *msgId;
1048 const Over *ov;
1049 Str whatStr; 1178 Str whatStr;
1050 1179
1051 if ( ! testGrpSelected() )
1052 return TRUE;
1053 if ( sscanf( arg, "%s", whatStr ) != 1 ) 1180 if ( sscanf( arg, "%s", whatStr ) != 1 )
1054 { 1181 {
1055 putSyntax( cmd ); 1182 putSyntax( cmd );
1056 return TRUE; 1183 return TRUE;
1057 } 1184 }
1058 Utl_toLower( whatStr ); 1185 what = whatXhdrField( whatStr );
1059 if ( strcmp( whatStr, "subject" ) == 0 ) 1186 if ( what == UNKNOWN )
1060 what = SUBJ;
1061 else if ( strcmp( whatStr, "from" ) == 0 )
1062 what = FROM;
1063 else if ( strcmp( whatStr, "date" ) == 0 )
1064 what = DATE;
1065 else if ( strcmp( whatStr, "message-id" ) == 0 )
1066 what = MSG_ID;
1067 else if ( strcmp( whatStr, "references" ) == 0 )
1068 what = REF;
1069 else if ( strcmp( whatStr, "bytes" ) == 0 )
1070 what = BYTES;
1071 else if ( strcmp( whatStr, "lines" ) == 0 )
1072 what = LINES;
1073 else if ( strcmp( whatStr, "xref" ) == 0 )
1074 what = XREF;
1075 else
1076 { 1187 {
1077 putStat( STAT_HEAD_FOLLOWS, "Unknown header (empty list follows)" ); 1188 putStat( STAT_HEAD_FOLLOWS, "Unknown header (empty list follows)" );
1078 putEndOfTxt(); 1189 putEndOfTxt();
1079 return TRUE; 1190 return TRUE;
1080 } 1191 }
1081 p = Utl_restOfLn( arg, 1 ); 1192 p = Utl_restOfLn( arg, 1 );
1082 if ( p[ 0 ] == '<' ) 1193 if ( p[ 0 ] == '<' )
1083 { 1194 {
1084 first = last = Cont_find( p ); 1195 Over * ov;
1085 if ( first < 0 ) 1196 Str field;
1086 numb = 0; 1197
1198 /* Argument is message ID */
1199 ov = Db_over( p );
1200 if ( ov == NULL )
1201 {
1202 putStat( STAT_NO_SUCH_ID, "No such article" );
1203 return TRUE;
1204 }
1205 putStat( STAT_HEAD_FOLLOWS, "%s header %s", whatStr, p ) ;
1206 getXhdrField( what, ov, field );
1207 putTxtLn( "%s %s", p, field );
1208 del_Over( ov );
1087 } 1209 }
1088 else 1210 else
1211 {
1212 const Over * ov;
1213 int first, last, i, n, numb;
1214 Str field;
1215
1216 /* Argument is article no. or range */
1217 if ( ! testGrpSelected() )
1218 return TRUE;
1089 parseRange( p, &first, &last, &numb ); 1219 parseRange( p, &first, &last, &numb );
1090 if ( numb == 0 ) 1220 if ( numb == 0 )
1091 putStat( STAT_NO_ART_SELECTED, "No articles selected" ); 1221 {
1092 else 1222 putStat( STAT_NO_ART_SELECTED, "No articles selected" );
1093 { 1223 return TRUE;
1224 }
1094 putStat( STAT_HEAD_FOLLOWS, "%s header %lu-%lu", 1225 putStat( STAT_HEAD_FOLLOWS, "%s header %lu-%lu",
1095 whatStr, first, last ) ; 1226 whatStr, first, last ) ;
1096 for ( i = first; i <= last; ++i ) 1227 for ( i = first; i <= last; ++i )
1097 if ( ( ov = Cont_get( i ) ) ) 1228 if ( ( ov = Cont_get( i ) ) )
1098 { 1229 {
1099 n = Ov_numb( ov ); 1230 n = Ov_numb( ov );
1100 switch ( what ) 1231 getXhdrField( what, ov, field );
1101 { 1232 putTxtLn( "%lu %s", n, field );
1102 case SUBJ:
1103 putTxtLn( "%lu %s", n, Ov_subj( ov ) );
1104 break;
1105 case FROM:
1106 putTxtLn( "%lu %s", n, Ov_from( ov ) );
1107 break;
1108 case DATE:
1109 putTxtLn( "%lu %s", n, Ov_date( ov ) );
1110 break;
1111 case MSG_ID:
1112 putTxtLn( "%lu %s", n, Ov_msgId( ov ) );
1113 break;
1114 case REF:
1115 putTxtLn( "%lu %s", n, Ov_ref( ov ) );
1116 break;
1117 case BYTES:
1118 putTxtLn( "%lu %d", n, Ov_bytes( ov ) );
1119 break;
1120 case LINES:
1121 putTxtLn( "%lu %d", n, Ov_lines( ov ) );
1122 break;
1123 case XREF:
1124 msgId = Ov_msgId( ov );
1125 if ( Pseudo_isGeneralInfo( msgId ) )
1126 putTxtLn( "%lu %s:%lu", n, server.grp, n );
1127 else
1128 putTxtLn( "%lu %s", n, Db_xref( msgId ) );
1129 break;
1130 default:
1131 ASSERT( FALSE );
1132 }
1133 } 1233 }
1134 putEndOfTxt(); 1234 }
1135 } 1235 putEndOfTxt();
1136 return TRUE; 1236 return TRUE;
1137 } 1237 }
1138 1238
1139 static Bool 1239 static Bool
1140 doXpat( char *arg, const Cmd *cmd ) 1240 doXpat( char *arg, const Cmd *cmd )
1141 { 1241 {
1142 int first, last, i, n; 1242 enum XhdrType what;
1143 enum { SUBJ, FROM, DATE, MSG_ID, REF } what; 1243 Str whatStr, articles, pat;
1144 const Over *ov; 1244
1145 Str whatStr, pat; 1245 if ( sscanf( arg, "%s %s %s", whatStr, articles, pat ) != 3 )
1146 1246 {
1147 if ( ! testGrpSelected() ) 1247 putSyntax( cmd );
1148 return TRUE; 1248 return TRUE;
1149 if ( sscanf( arg, "%s %d-%d %s", whatStr, &first, &last, pat ) != 4 ) 1249 }
1150 { 1250 what = whatXhdrField( whatStr );
1151 if ( sscanf( arg, "%s %d- %s", whatStr, &first, pat ) == 3 ) 1251 if ( what == UNKNOWN )
1152 last = Cont_last(); 1252 {
1153 else if ( sscanf( arg, "%s %d %s", whatStr, &first, pat ) == 3 ) 1253 putStat( STAT_HEAD_FOLLOWS, "Unknown header (empty list follows)" );
1154 last = first;
1155 else
1156 {
1157 putSyntax( cmd );
1158 return TRUE;
1159 }
1160 }
1161 Utl_toLower( whatStr );
1162 if ( strcmp( whatStr, "subject" ) == 0 )
1163 what = SUBJ;
1164 else if ( strcmp( whatStr, "from" ) == 0 )
1165 what = FROM;
1166 else if ( strcmp( whatStr, "date" ) == 0 )
1167 what = DATE;
1168 else if ( strcmp( whatStr, "message-id" ) == 0 )
1169 what = MSG_ID;
1170 else if ( strcmp( whatStr, "references" ) == 0 )
1171 what = REF;
1172 else
1173 {
1174 putStat( STAT_HEAD_FOLLOWS, "invalid header (empty list follows)" );
1175 putEndOfTxt(); 1254 putEndOfTxt();
1176 return TRUE; 1255 return TRUE;
1177 } 1256 }
1178 putStat( STAT_HEAD_FOLLOWS, "header" ) ; 1257 if ( articles[ 0 ] == '<' )
1179 for ( i = first; i <= last; ++i ) 1258 {
1180 if ( ( ov = Cont_get( i ) ) ) 1259 Over * ov;
1181 { 1260 Str field;
1182 n = Ov_numb( ov ); 1261
1183 switch ( what ) 1262 /* Argument is message ID */
1263 ov = Db_over( articles );
1264 if ( ov == NULL )
1265 {
1266 putStat( STAT_NO_SUCH_ID, "No such article" );
1267 return TRUE;
1268 }
1269 putStat( STAT_HEAD_FOLLOWS, "%s header %s", whatStr, articles ) ;
1270 getXhdrField( what, ov, field );
1271 if ( Wld_match( field, pat ) )
1272 putTxtLn( "%s %s", articles, field );
1273 del_Over( ov );
1274 }
1275 else
1276 {
1277 const Over * ov;
1278 Str field;
1279 int first, last, i, n, numb;
1280
1281 /* Argument is article no. or range */
1282 if ( ! testGrpSelected() )
1283 return TRUE;
1284 parseRange( articles, &first, &last, &numb );
1285 if ( numb == 0 )
1286 {
1287 putStat( STAT_NO_ART_SELECTED, "No articles selected" );
1288 return TRUE;
1289 }
1290 putStat( STAT_HEAD_FOLLOWS, "%s header %lu-%lu",
1291 whatStr, first, last ) ;
1292 for ( i = first; i <= last; ++i )
1293 if ( ( ov = Cont_get( i ) ) )
1184 { 1294 {
1185 case SUBJ: 1295 n = Ov_numb( ov );
1186 if ( Wld_match( Ov_subj( ov ), pat ) ) 1296 getXhdrField( what, ov, field );
1187 putTxtLn( "%lu %s", n, Ov_subj( ov ) ); 1297 if ( Wld_match( field, pat ) )
1188 break; 1298 putTxtLn( "%lu %s", n, field );
1189 case FROM:
1190 if ( Wld_match( Ov_from( ov ), pat ) )
1191 putTxtLn( "%lu %s", n, Ov_from( ov ) );
1192 break;
1193 case DATE:
1194 if ( Wld_match( Ov_date( ov ), pat ) )
1195 putTxtLn( "%lu %s", n, Ov_date( ov ) );
1196 break;
1197 case MSG_ID:
1198 if ( Wld_match( Ov_msgId( ov ), pat ) )
1199 putTxtLn( "%lu %s", n, Ov_msgId( ov ) );
1200 break;
1201 case REF:
1202 if ( Wld_match( Ov_ref( ov ), pat ) )
1203 putTxtLn( "%lu %s", n, Ov_ref( ov ) );
1204 break;
1205 default:
1206 ASSERT( FALSE );
1207 } 1299 }
1208 } 1300 }
1209 putEndOfTxt(); 1301 putEndOfTxt();
1210 return TRUE; 1302 return TRUE;
1211 } 1303 }
1212 1304
1213 static Bool 1305 static Bool
1256 1348
1257 UNUSED( cmd ); 1349 UNUSED( cmd );
1258 1350
1259 if ( ! testGrpSelected() ) 1351 if ( ! testGrpSelected() )
1260 return TRUE; 1352 return TRUE;
1353 Grp_setLastAccess( server.grp, time( NULL ) );
1354
1355 /*
1356 * All the info we require is now in RAM, and we may generate
1357 * lots of output (consider XOVER 2-3999), so release the lock
1358 * while responding.
1359 */
1360 closeServer();
1361
1261 parseRange( arg, &first, &last, &n ); 1362 parseRange( arg, &first, &last, &n );
1262 if ( n == 0 ) 1363 if ( n == 0 )
1263 first = last = server.artPtr; 1364 first = last = server.artPtr;
1264 putStat( STAT_OVERS_FOLLOW, "Overview %ld-%ld", first, last ); 1365 putStat( STAT_OVERS_FOLLOW, "Overview %ld-%ld", first, last );
1265 for ( i = first; i <= last; ++i ) 1366 for ( i = first; i <= last; ++i )
1330 { 1431 {
1331 ASSERT( ! server.running ); 1432 ASSERT( ! server.running );
1332 if ( ! Lock_openDatabases() ) 1433 if ( ! Lock_openDatabases() )
1333 return FALSE; 1434 return FALSE;
1334 server.running = TRUE; 1435 server.running = TRUE;
1436 server.lastServerOpen = time( NULL );
1335 return TRUE; 1437 return TRUE;
1336 } 1438 }
1337 1439
1338 static void 1440 static void
1339 closeServer( void ) 1441 closeServer( void )
1347 Server_run( void ) 1449 Server_run( void )
1348 { 1450 {
1349 Bool done; 1451 Bool done;
1350 int r; 1452 int r;
1351 Str line; 1453 Str line;
1352 struct timeval timeOut;
1353 fd_set readSet;
1354 1454
1355 putWelcome(); 1455 putWelcome();
1356 done = FALSE; 1456 done = FALSE;
1357 while ( ! done ) 1457 while ( ! done )
1358 { 1458 {
1359 FD_ZERO( &readSet ); 1459 /*
1360 FD_SET( STDIN_FILENO, &readSet ); 1460 * If we've had the lock for more than 2 seconds,
1361 /* Never hold lock more than 5 seconds (empirically good value, 1461 * force it to be released. Otherwise, if we have
1362 avoids to close/open databases, if clients sends several 1462 * the lock but have had it for < 2 secs,
1363 commands, but releases the lock often enough, for allowing 1463 * wait for a command line for a maximum
1364 multiple persons to read news at the same time) */ 1464 * of 2 seconds. This is all an attempt at striking a
1365 timeOut.tv_sec = 5; 1465 * balance between efficient processing of commands
1366 timeOut.tv_usec = 0; 1466 * and hogging the lock.
1367 r = select( STDIN_FILENO + 1, &readSet, NULL, NULL, &timeOut ); 1467 */
1468 if ( server.running )
1469 {
1470 time_t now;
1471
1472 now = time( NULL );
1473 if ( difftime( now, server.lastServerOpen ) > 2.0 )
1474 closeServer();
1475 }
1476 r = waitCmdLn( line, ( server.running ) ? 2 : 0 );
1368 if ( r < 0 ) 1477 if ( r < 0 )
1478 {
1479 Log_inf( "Client disconnected. Terminating." );
1369 done = TRUE; 1480 done = TRUE;
1481 }
1370 else if ( r == 0 ) 1482 else if ( r == 0 )
1371 { 1483 {
1372 if ( server.running ) 1484 if ( server.running )
1373 closeServer(); 1485 closeServer();
1374 } 1486 }
1375 else /* ( r > 0 ) */ 1487 else /* ( r > 0 ) */
1376 { 1488 {
1377 if ( ! server.running ) 1489 if ( ! server.running )
1378 { 1490 {
1379 if ( ! initServer() ) 1491 if ( ! initServer() )
1380 { 1492 {
1381 putFatal( "Cannot init server" ); 1493 putFatal( "Cannot init server" );
1382 done = TRUE; 1494 done = TRUE;
1383 } 1495 }
1384 } 1496 }
1385 if ( ! getLn( line ) ) 1497 if ( ! parseAndExecute( line ) )
1386 {
1387 Log_inf( "Client disconnected. Terminating." );
1388 done = TRUE;
1389 }
1390 else if ( ! parseAndExecute( line ) )
1391 done = TRUE; 1498 done = TRUE;
1392 } 1499 }
1393 } 1500 }
1394 if ( server.running ) 1501 if ( server.running )
1395 closeServer(); 1502 closeServer();