comparison src/server.c @ 149:bfeea2bc09b6 noffle

[svn] Output to buffer, release lock and then send output.
author bears
date Thu, 26 Oct 2000 22:13:28 +0100
parents 8b9366fc1361
children 22b81617d427
comparison
equal deleted inserted replaced
148:b226cebd6a84 149:bfeea2bc09b6
1 /* 1 /*
2 server.c 2 server.c
3 3
4 $Id: server.c 217 2000-09-23 10:40:35Z enz $ 4 $Id: server.c 225 2000-10-26 21:13:28Z 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
53 { 53 {
54 Bool running; 54 Bool running;
55 time_t lastServerOpen; 55 time_t lastServerOpen;
56 int artPtr; 56 int artPtr;
57 Str grp; /* selected group, "" if none */ 57 Str grp; /* selected group, "" if none */
58 Bool readAlarmFlag; 58 DynStr *reply;
59 } server = { FALSE, 0L, 0, "", FALSE }; 59 Bool eotAfterReply;
60 } server = { FALSE, 0L, 0, "", NULL, FALSE };
60 61
61 typedef struct Cmd 62 typedef struct Cmd
62 { 63 {
63 const char *name; 64 const char *name;
64 const char *syntax; 65 const char *syntax;
170 ASSERT( stat <= 999 ); 171 ASSERT( stat <= 999 );
171 va_start( ap, fmt ); 172 va_start( ap, fmt );
172 vsnprintf( s, MAXCHAR, fmt, ap ); 173 vsnprintf( s, MAXCHAR, fmt, ap );
173 va_end( ap ); 174 va_end( ap );
174 snprintf( line, MAXCHAR, "%u %s", stat, s ); 175 snprintf( line, MAXCHAR, "%u %s", stat, s );
176 DynStr_appLn( server.reply, line );
175 Log_dbg( "[S] %s", line ); 177 Log_dbg( "[S] %s", line );
176 printf( "%s\r\n", line );
177 } 178 }
178 179
179 static void 180 static void
180 putTxtLn( const char *fmt, ... ) 181 putTxtLn( const char *fmt, ... )
181 { 182 {
183 va_list ap; 184 va_list ap;
184 185
185 va_start( ap, fmt ); 186 va_start( ap, fmt );
186 vsnprintf( line, MAXCHAR, fmt, ap ); 187 vsnprintf( line, MAXCHAR, fmt, ap );
187 va_end( ap ); 188 va_end( ap );
188 Prt_putTxtLn( line, stdout ); 189 DynStr_appLn( server.reply, line );
189 } 190 }
190 191
191 static void 192 static void
192 putTxtBuf( const char *buf ) 193 putTxtBuf( const char *buf )
193 { 194 {
194 if ( buf ) 195 if ( buf )
195 Prt_putTxtBuf( buf, stdout ); 196 DynStr_app( server.reply, buf );
196 } 197 }
197 198
198 static void 199 static void
199 putEndOfTxt( void ) 200 putEndOfTxt( void )
200 { 201 {
201 Prt_putEndOfTxt( stdout ); 202 server.eotAfterReply = TRUE;
202 } 203 }
203 204
204 static void 205 static void
205 putSyntax( const Cmd *cmd ) 206 putSyntax( const Cmd *cmd )
206 { 207 {
207 putStat( STAT_SYNTAX_ERR, "Syntax error. Usage: %s", cmd->syntax ); 208 putStat( STAT_SYNTAX_ERR, "Syntax error. Usage: %s", cmd->syntax );
208 } 209 }
209 210
210 static void 211 static void
211 readAlarm( int sig ) 212 initOutput( void )
212 { 213 {
213 UNUSED( sig ); 214 server.reply = new_DynStr( 100 );
214 215 server.eotAfterReply = FALSE;
215 server.readAlarmFlag = TRUE; 216 }
216 return; 217
217 } 218 static void
218 219 sendOutput( void )
219 static sig_t 220 {
220 installSignalHandler( int sig, sig_t handler ) 221 Prt_putTxtBuf( DynStr_str( server.reply ), stdout );
221 { 222 if ( server.eotAfterReply )
222 struct sigaction act, oldAct; 223 Prt_putEndOfTxt( stdout );
223 224 fflush( stdout );
224 act.sa_handler = handler; 225 Log_dbg( "[S FLUSH]" );
225 sigemptyset( &act.sa_mask ); 226 del_DynStr( server.reply );
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, -1 );
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, -1 );
263 alarm( 0 );
264 installSignalHandler( SIGALRM, oldHandler );
265 if ( server.readAlarmFlag )
266 return 0;
267 else if ( r )
268 return 1;
269 return -1;
270 } 227 }
271 228
272 static Bool 229 static Bool
273 getTxtLn( Str line, Bool *err ) 230 getTxtLn( Str line, Bool *err )
274 { 231 {
697 { 654 {
698 Str line; 655 Str line;
699 const char *g; 656 const char *g;
700 657
701 putStat( STAT_GRPS_FOLLOW, "Groups" ); 658 putStat( STAT_GRPS_FOLLOW, "Groups" );
702 fflush( stdout );
703 Log_dbg( "[S FLUSH]" );
704 if ( Grp_exists( pat ) ) 659 if ( Grp_exists( pat ) )
705 { 660 {
706 (*printProc)( line, pat ); 661 (*printProc)( line, pat );
707 if ( ! Prt_putTxtLn( line, stdout ) ) 662 putTxtLn( line );
708 Log_err( "Writing to stdout failed." ); 663 }
709 }
710 else 664 else
711 { 665 {
712 if ( Grp_firstGrp( &g ) ) 666 if ( Grp_firstGrp( &g ) )
713 do 667 do
714 if ( Wld_match( g, pat ) ) 668 if ( Wld_match( g, pat ) )
715 { 669 {
716 (*printProc)( line, g ); 670 (*printProc)( line, g );
717 if ( ! Prt_putTxtLn( line, stdout ) ) 671 putTxtLn( line );
718 Log_err( "Writing to stdout failed." );
719 } 672 }
720 while ( Grp_nextGrp( &g ) ); 673 while ( Grp_nextGrp( &g ) );
721 } 674 }
722 putEndOfTxt(); 675 putEndOfTxt();
723 } 676 }
842 if ( ! Grp_exists( arg ) ) 795 if ( ! Grp_exists( arg ) )
843 putStat( STAT_NO_SUCH_GRP, "No such group" ); 796 putStat( STAT_NO_SUCH_GRP, "No such group" );
844 else 797 else
845 { 798 {
846 changeToGrp( arg ); 799 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
855 first = Cont_first(); 800 first = Cont_first();
856 last = Cont_last(); 801 last = Cont_last();
857 putStat( STAT_GRP_SELECTED, "Article list" ); 802 putStat( STAT_GRP_SELECTED, "Article list" );
858 for ( i = first; i <= last; ++i ) 803 for ( i = first; i <= last; ++i )
859 if ( ( ov = Cont_get( i ) ) ) 804 if ( ( ov = Cont_get( i ) ) )
1013 Bool err; 958 Bool err;
1014 959
1015 UNUSED(arg); 960 UNUSED(arg);
1016 UNUSED(cmd); 961 UNUSED(cmd);
1017 962
1018 putStat( STAT_SEND_ART, "Continue (end with period)" );
1019 fflush( stdout );
1020 Log_dbg( "[S FLUSH]" );
1021 s = new_DynStr( 10000 );
1022
1023
1024 /* 963 /*
1025 * The article may take some time coming in, so release the 964 * The article may take some time coming in, so release the
1026 * lock while collecting it. 965 * lock while collecting it.
1027 */ 966 */
967 putStat( STAT_SEND_ART, "Continue (end with period)" );
968 sendOutput();
1028 closeServer(); 969 closeServer();
1029 970
971 s = new_DynStr( 10000 );
1030 err = FALSE; 972 err = FALSE;
1031 while ( ! err && getTxtLn( line, &err ) ) 973 while ( ! err && getTxtLn( line, &err ) )
1032 DynStr_appLn( s, line ); 974 DynStr_appLn( s, line );
1033 975
976 initOutput();
1034 if ( ! initServer() ) 977 if ( ! initServer() )
1035 { 978 {
1036 del_DynStr( s ); 979 del_DynStr( s );
1037 return FALSE; 980 return FALSE;
1038 } 981 }
1039 982
1040 if ( ! err 983 if ( ! err
1041 && Post_open( DynStr_str( s ) ) 984 && Post_open( DynStr_str( s ), 0 )
1042 && Post_post() ) 985 && Post_post() )
1043 { 986 {
1044 putStat( STAT_POST_OK, "Message posted" ); 987 putStat( STAT_POST_OK, "Message posted" );
1045 if ( Online_true() ) 988 if ( Online_true() )
1046 postArts(); 989 postArts();
1348 1291
1349 UNUSED( cmd ); 1292 UNUSED( cmd );
1350 1293
1351 if ( ! testGrpSelected() ) 1294 if ( ! testGrpSelected() )
1352 return TRUE; 1295 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
1362 parseRange( arg, &first, &last, &n ); 1296 parseRange( arg, &first, &last, &n );
1363 if ( n == 0 ) 1297 if ( n == 0 )
1364 first = last = server.artPtr; 1298 first = last = server.artPtr;
1365 putStat( STAT_OVERS_FOLLOW, "Overview %ld-%ld", first, last ); 1299 putStat( STAT_OVERS_FOLLOW, "Overview %ld-%ld", first, last );
1366 for ( i = first; i <= last; ++i ) 1300 for ( i = first; i <= last; ++i )
1368 putTxtLn( "%lu\t%s\t%s\t%s\t%s\t%s\t%d\t%d\t", 1302 putTxtLn( "%lu\t%s\t%s\t%s\t%s\t%s\t%d\t%d\t",
1369 Ov_numb( ov ), Ov_subj( ov ), Ov_from( ov ), 1303 Ov_numb( ov ), Ov_subj( ov ), Ov_from( ov ),
1370 Ov_date( ov ), Ov_msgId( ov ), Ov_ref( ov ), 1304 Ov_date( ov ), Ov_msgId( ov ), Ov_ref( ov ),
1371 Ov_bytes( ov ), Ov_lines( ov ) ); 1305 Ov_bytes( ov ), Ov_lines( ov ) );
1372 putEndOfTxt(); 1306 putEndOfTxt();
1307 Grp_setLastAccess( server.grp, time( NULL ) );
1373 return TRUE; 1308 return TRUE;
1374 } 1309 }
1375 1310
1376 static void 1311 static void
1377 putFatal( const char *fmt, ... ) 1312 putFatal( const char *fmt, ... )
1382 va_start( ap, fmt ); 1317 va_start( ap, fmt );
1383 vsnprintf( s, MAXCHAR, fmt, ap ); 1318 vsnprintf( s, MAXCHAR, fmt, ap );
1384 va_end( ap ); 1319 va_end( ap );
1385 Log_err( s ); 1320 Log_err( s );
1386 putStat( STAT_PROGRAM_FAULT, "%s", s ); 1321 putStat( STAT_PROGRAM_FAULT, "%s", s );
1387 fflush( stdout );
1388 Log_dbg( "[S FLUSH]" );
1389 } 1322 }
1390 1323
1391 /* Parse line, execute command and return FALSE, if it was the quit command. */ 1324 /* Parse line, execute command and return FALSE, if it was the quit command. */
1392 static Bool 1325 static Bool
1393 parseAndExecute( Str line ) 1326 parseAndExecute( Str line )
1404 n = sizeof( commands ) / sizeof( commands[ 0 ] ); 1337 n = sizeof( commands ) / sizeof( commands[ 0 ] );
1405 for ( i = 0, c = commands; i < n; ++i, ++c ) 1338 for ( i = 0, c = commands; i < n; ++i, ++c )
1406 if ( strcmp( c->name, s ) == 0 ) 1339 if ( strcmp( c->name, s ) == 0 )
1407 { 1340 {
1408 ret = c->cmdProc( Utl_stripWhiteSpace( arg ), c ); 1341 ret = c->cmdProc( Utl_stripWhiteSpace( arg ), c );
1409 fflush( stdout );
1410 Log_dbg( "[S FLUSH]" );
1411 return ret; 1342 return ret;
1412 } 1343 }
1413 } 1344 }
1414 putStat( STAT_NO_SUCH_CMD, "Command not recognized" ); 1345 putStat( STAT_NO_SUCH_CMD, "Command not recognized" );
1415 fflush( stdout );
1416 Log_dbg( "[S FLUSH]" );
1417 return TRUE; 1346 return TRUE;
1418 } 1347 }
1419 1348
1420 static void 1349 static void
1421 putWelcome( void ) 1350 putWelcome( void )
1422 { 1351 {
1352 initOutput();
1423 putStat( STAT_READY_POST_ALLOW, "NNTP server NOFFLE %s", 1353 putStat( STAT_READY_POST_ALLOW, "NNTP server NOFFLE %s",
1424 Cfg_version() ); 1354 Cfg_version() );
1425 fflush( stdout ); 1355 sendOutput();
1426 Log_dbg( "[S FLUSH]" );
1427 } 1356 }
1428 1357
1429 static Bool 1358 static Bool
1430 initServer( void ) 1359 initServer( void )
1431 { 1360 {
1447 1376
1448 void 1377 void
1449 Server_run( void ) 1378 Server_run( void )
1450 { 1379 {
1451 Bool done; 1380 Bool done;
1452 int r;
1453 Str line; 1381 Str line;
1454 1382
1455 putWelcome(); 1383 putWelcome();
1456 done = FALSE; 1384 done = FALSE;
1457 while ( ! done ) 1385 while ( ! done )
1458 { 1386 {
1459 /* 1387 if ( Prt_getLn( line, stdin, -1 ) )
1460 * If we've had the lock for more than 2 seconds,
1461 * force it to be released. Otherwise, if we have
1462 * the lock but have had it for < 2 secs,
1463 * wait for a command line for a maximum
1464 * of 2 seconds. This is all an attempt at striking a
1465 * balance between efficient processing of commands
1466 * and hogging the lock.
1467 */
1468 if ( server.running )
1469 { 1388 {
1470 time_t now; 1389 initOutput();
1471 1390
1472 now = time( NULL ); 1391 if ( ! initServer() )
1473 if ( difftime( now, server.lastServerOpen ) > 2.0 ) 1392 {
1393 putFatal( "Cannot init server" );
1394 done = TRUE;
1395 }
1396 else
1397 {
1398 if ( ! parseAndExecute( line ) )
1399 done = TRUE;
1400 }
1401
1402 if ( server.running )
1474 closeServer(); 1403 closeServer();
1475 } 1404
1476 r = waitCmdLn( line, ( server.running ) ? 2 : 0 ); 1405 sendOutput();
1477 if ( r < 0 ) 1406 }
1407 else
1478 { 1408 {
1479 Log_inf( "Client disconnected. Terminating." ); 1409 Log_inf( "Client disconnected. Terminating." );
1480 done = TRUE; 1410 done = TRUE;
1481 } 1411 }
1482 else if ( r == 0 )
1483 {
1484 if ( server.running )
1485 closeServer();
1486 }
1487 else /* ( r > 0 ) */
1488 {
1489 if ( ! server.running )
1490 {
1491 if ( ! initServer() )
1492 {
1493 putFatal( "Cannot init server" );
1494 done = TRUE;
1495 }
1496 }
1497 if ( ! parseAndExecute( line ) )
1498 done = TRUE;
1499 }
1500 } 1412 }
1501 if ( server.running ) 1413 if ( server.running )
1502 closeServer(); 1414 closeServer();
1503 } 1415 }