Mercurial > noffle
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 } |
