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