+ /* "Hauptschleife": Aktive Verbindungen ueberwachen. Folgende Aktionen
+ * werden dabei durchgefuehrt, bis der Server terminieren oder neu
+ * starten soll:
+ *
+ * - neue Verbindungen annehmen,
+ * - Server-Verbindungen aufbauen,
+ * - geschlossene Verbindungen loeschen,
+ * - volle Schreibpuffer versuchen zu schreiben,
+ * - volle Lesepuffer versuchen zu verarbeiten,
+ * - Antworten von Resolver Sub-Prozessen annehmen.
+ */
+
+ fd_set read_sockets, write_sockets;
+ struct timeval tv;
+ time_t start, t;
+ LONG i, idx;
+
+ start = time( NULL );
+ while(( ! NGIRCd_Quit ) && ( ! NGIRCd_Restart ))
+ {
+ Check_Servers( );
+
+ Check_Connections( );
+
+ /* noch volle Lese-Buffer suchen */
+ for( i = 0; i < Pool_Size; i++ )
+ {
+ if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].rdatalen > 0 ))
+ {
+ /* Kann aus dem Buffer noch ein Befehl extrahiert werden? */
+ Handle_Buffer( i );
+ }
+ }
+
+ /* noch volle Schreib-Puffer suchen */
+ FD_ZERO( &write_sockets );
+ for( i = 0; i < Pool_Size; i++ )
+ {
+ if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].wdatalen > 0 ))
+ {
+ /* Socket der Verbindung in Set aufnehmen */
+ FD_SET( My_Connections[i].sock, &write_sockets );
+ }
+ }
+ /* Sockets mit im Aufbau befindlichen ausgehenden Verbindungen suchen */
+ for( i = 0; i < Pool_Size; i++ )
+ {
+ if(( My_Connections[i].sock > NONE ) && ( FD_ISSET( My_Connections[i].sock, &My_Connects ))) FD_SET( My_Connections[i].sock, &write_sockets );
+ }
+
+ /* von welchen Sockets koennte gelesen werden? */
+ t = time( NULL );
+ read_sockets = My_Sockets;
+ for( i = 0; i < Pool_Size; i++ )
+ {
+ if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].host[0] == '\0' ))
+ {
+ /* Hier muss noch auf den Resolver Sub-Prozess gewartet werden */
+ FD_CLR( My_Connections[i].sock, &read_sockets );
+ }
+ if(( My_Connections[i].sock > NONE ) && ( FD_ISSET( My_Connections[i].sock, &My_Connects )))
+ {
+ /* Hier laeuft noch ein asyncrones connect() */
+ FD_CLR( My_Connections[i].sock, &read_sockets );
+ }
+ if( My_Connections[i].delaytime > t )
+ {
+ /* Fuer die Verbindung ist eine "Penalty-Zeit" gesetzt */
+ FD_CLR( My_Connections[i].sock, &read_sockets );
+ FD_CLR( My_Connections[i].sock, &write_sockets );
+ }
+ }
+ for( i = 0; i < Conn_MaxFD + 1; i++ )
+ {
+ /* Pipes von Resolver Sub-Prozessen aufnehmen */
+ if( FD_ISSET( i, &Resolver_FDs ))
+ {
+ FD_SET( i, &read_sockets );
+ }
+ }
+
+ /* Timeout initialisieren */
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ /* Auf Aktivitaet warten */
+ i = select( Conn_MaxFD + 1, &read_sockets, &write_sockets, NULL, &tv );
+ if( i == 0 )
+ {
+ /* keine Veraenderung an den Sockets */
+ continue;
+ }
+ if( i == -1 )
+ {
+ /* Fehler (z.B. Interrupt) */
+ if( errno != EINTR )
+ {
+ Log( LOG_EMERG, "Conn_Handler(): select(): %s!", strerror( errno ));
+ Log( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE );
+ exit( 1 );
+ }
+ continue;
+ }
+
+ /* Koennen Daten geschrieben werden? */
+ for( i = 0; i < Conn_MaxFD + 1; i++ )
+ {
+ if( ! FD_ISSET( i, &write_sockets )) continue;
+
+ /* Es kann geschrieben werden ... */
+ idx = Socket2Index( i );
+ if( idx == NONE ) continue;
+
+ if( ! Handle_Write( idx ))
+ {
+ /* Fehler beim Schreiben! Diesen Socket nun
+ * auch aus dem Read-Set entfernen: */
+ FD_CLR( i, &read_sockets );
+ }
+ }
+
+ /* Daten zum Lesen vorhanden? */
+ for( i = 0; i < Conn_MaxFD + 1; i++ )
+ {
+ if( FD_ISSET( i, &read_sockets )) Handle_Read( i );
+ }
+ }
+} /* Conn_Handler */
+
+
+#ifdef PROTOTYPES
+GLOBAL BOOLEAN
+Conn_WriteStr( CONN_ID Idx, CHAR *Format, ... )
+#else
+GLOBAL BOOLEAN
+Conn_WriteStr( Idx, Format, va_alist )
+CONN_ID Idx;
+CHAR *Format;
+va_dcl
+#endif
+{
+ /* String in Socket schreiben. CR+LF wird von dieser Funktion
+ * automatisch angehaengt. Im Fehlerfall wird dir Verbindung
+ * getrennt und FALSE geliefert. */
+
+ CHAR buffer[COMMAND_LEN];
+ BOOLEAN ok;
+ va_list ap;
+
+ assert( Idx > NONE );
+ assert( Format != NULL );
+
+#ifdef PROTOTYPES
+ va_start( ap, Format );
+#else
+ va_start( ap );
+#endif
+ if( vsnprintf( buffer, COMMAND_LEN - 2, Format, ap ) == COMMAND_LEN - 2 )
+ {
+ Log( LOG_CRIT, "Text too long to send (connection %d)!", Idx );
+ Conn_Close( Idx, "Text too long to send!", NULL, FALSE );
+ return FALSE;
+ }
+
+#ifdef SNIFFER
+ if( NGIRCd_Sniffer ) Log( LOG_DEBUG, " -> connection %d: '%s'.", Idx, buffer );
+#endif
+
+ strcat( buffer, "\r\n" );
+ ok = Conn_Write( Idx, buffer, strlen( buffer ));
+
+ va_end( ap );
+ return ok;
+} /* Conn_WriteStr */
+
+
+GLOBAL BOOLEAN
+Conn_Write( CONN_ID Idx, CHAR *Data, INT Len )
+{
+ /* Daten in Socket schreiben. Bei "fatalen" Fehlern wird
+ * der Client disconnectiert und FALSE geliefert. */
+
+ assert( Idx > NONE );
+ assert( Data != NULL );
+ assert( Len > 0 );
+
+ /* Ist der entsprechende Socket ueberhaupt noch offen?
+ * In einem "Handler-Durchlauf" kann es passieren, dass
+ * dem nicht mehr so ist, wenn einer von mehreren
+ * Conn_Write()'s fehlgeschlagen ist. In diesem Fall
+ * wird hier einfach ein Fehler geliefert. */
+ if( My_Connections[Idx].sock <= NONE )
+ {
+ Log( LOG_DEBUG, "Skipped write on closed socket (connection %d).", Idx );
+ return FALSE;
+ }
+
+ /* pruefen, ob Daten im Schreibpuffer sind. Wenn ja, zunaechst
+ * pruefen, ob diese gesendet werden koennen */
+ if( My_Connections[Idx].wdatalen > 0 )
+ {
+ if( ! Try_Write( Idx )) return FALSE;
+ }
+
+ /* pruefen, ob im Schreibpuffer genuegend Platz ist */
+ if( WRITEBUFFER_LEN - My_Connections[Idx].wdatalen - Len <= 0 )
+ {
+ /* der Puffer ist dummerweise voll ... */
+ Log( LOG_NOTICE, "Write buffer overflow (connection %d)!", Idx );
+ Conn_Close( Idx, "Write buffer overflow!", NULL, FALSE );
+ return FALSE;
+ }
+
+ /* Daten in Puffer kopieren */
+ memcpy( My_Connections[Idx].wbuf + My_Connections[Idx].wdatalen, Data, Len );
+ My_Connections[Idx].wdatalen += Len;
+
+ /* pruefen, on Daten vorhanden sind und geschrieben werden koennen */
+ if( My_Connections[Idx].wdatalen > 0 )
+ {
+ if( ! Try_Write( Idx )) return FALSE;
+ }
+
+ return TRUE;
+} /* Conn_Write */
+
+
+GLOBAL VOID
+Conn_Close( CONN_ID Idx, CHAR *LogMsg, CHAR *FwdMsg, BOOLEAN InformClient )
+{
+ /* Verbindung schliessen. Evtl. noch von Resolver
+ * Sub-Prozessen offene Pipes werden geschlossen. */
+
+ CLIENT *c;
+
+ assert( Idx > NONE );
+ assert( My_Connections[Idx].sock > NONE );
+
+ c = Client_GetFromConn( Idx );
+
+ if( InformClient )
+ {
+ /* Statistik an Client melden, wenn User */
+ if(( c != NULL ) && ( Client_Type( c ) == CLIENT_USER ))
+ {
+ Conn_WriteStr( Idx, "NOTICE %s :%sConnection statistics: client %.1f kb, server %.1f kb.", Client_ThisServer( ), NOTICE_TXTPREFIX, (DOUBLE)My_Connections[Idx].bytes_in / 1024, (DOUBLE)My_Connections[Idx].bytes_out / 1024 );
+ }
+
+ /* ERROR an Client schicken (von RFC so vorgesehen!) */
+ if( FwdMsg ) Conn_WriteStr( Idx, "ERROR :%s", FwdMsg );
+ else Conn_WriteStr( Idx, "ERROR :Closing connection." );
+ if( My_Connections[Idx].sock == NONE ) return;
+ }
+
+ if( close( My_Connections[Idx].sock ) != 0 )
+ {
+ Log( LOG_ERR, "Error closing connection %d (socket %d) with %s:%d - %s!", Idx, My_Connections[Idx].sock, My_Connections[Idx].host, ntohs( My_Connections[Idx].addr.sin_port), strerror( errno ));
+ }
+ else
+ {
+ Log( LOG_INFO, "Connection %d (socket %d) with %s:%d closed (%.1fK in/%.1fK out).", Idx, My_Connections[Idx].sock, My_Connections[Idx].host, ntohs( My_Connections[Idx].addr.sin_port ), (DOUBLE)My_Connections[Idx].bytes_in / 1024, (DOUBLE)My_Connections[Idx].bytes_out / 1024 );
+ }