2626#include <signal.h>
2727#include <fcntl.h>
2828#include <unistd.h>
29+ #include <sys/time.h> /* use gettimeofday */
2930#include <limits.h> /* use PATH_MAX */
3031
3132#include "include/module.h"
@@ -146,6 +147,8 @@ struct irc_user {
146147 time_t lastactive ; /* Time of last JOIN, PART, PRIVMSG, NOTICE, etc. */
147148 time_t lastping ; /* Last ping sent */
148149 time_t lastpong ; /* Last pong received */
150+ struct timeval lastping_us ; /* Lag timer start (when we sent a PING) */
151+ int lag ; /* Lag, in ms (how long it took to get a PONG) */
149152 bbs_mutex_t lock ; /* User lock */
150153 char * awaymsg ; /* Away message */
151154 unsigned int away :1 ; /* User is currently away (default is 0, i.e. user is here) */
@@ -3823,6 +3826,7 @@ static void handle_client(struct irc_user *user)
38233826
38243827 for (;;) {
38253828 char * s = buf ;
3829+
38263830 /* XXX For some reason, using \r\n as the delimiter for bbs_readline breaks Ambassador.
38273831 * Doesn't seem like a bug in bbs_readline, though it is suspicious since
38283832 * the RFCs are very clear that CR LF is the delimiter.
@@ -3963,11 +3967,17 @@ static void handle_client(struct irc_user *user)
39633967 } else { /* Post-CAP/SASL */
39643968 char * current , * command = strsep (& s , " " );
39653969 if (!strcasecmp (command , "PONG" )) {
3970+ struct timeval now_us , diff_us ;
3971+ gettimeofday (& now_us , NULL );
39663972 bbs_mutex_lock (& user -> lock );
3973+ timersub (& now_us , & user -> lastping_us , & diff_us );
3974+ user -> lag = (int ) ((1000 * diff_us .tv_sec ) + (diff_us .tv_usec / 1000 ));
39673975 user -> lastpong = time (NULL );
39683976 bbs_mutex_unlock (& user -> lock );
39693977 } else if (!strcasecmp (command , "PING" )) { /* Usually servers ping clients, but clients can ping servers too */
3970- send_reply (user , "PONG %s\r\n" , S_IF (s )); /* Don't add another : because it's still in s, if present. */
3978+ /* The server PONG response slightly differs from the client PONG response.
3979+ * We include the hostname of the pinged server, before echoing back whatever was received. */
3980+ send_reply (user , "PONG %s %s\r\n" , irc_hostname , S_IF (s )); /* Don't add another : because it's still in s, if present. */
39713981 } else if (!strcasecmp (command , "PASS" )) {
39723982 REQUIRE_PARAMETER (user , s );
39733983 free_if (user -> password );
@@ -4306,6 +4316,16 @@ static int ping_alertpipe[2] = { -1, -1 };
43064316 * TL;DR This IRC server uses N+1 threads, where N is the number of clients connected.
43074317 */
43084318
4319+ /*! \todo RFC 1459 4.6.2 says a PING is sent... if no other activity detected from a connection.
4320+ * This means we don't actually need this separate thread to ping clients at all, handle_client can send PINGs if the poll timeout expires,
4321+ * with additional logic to disconnect if nothing further is heard afterwards. */
4322+ static void ping_client (struct irc_user * user , time_t now )
4323+ {
4324+ irc_other_thread_writef (user -> node , "PING :%" TIME_T_FMT "\r\n" , now );
4325+ user -> lastping = now ;
4326+ gettimeofday (& user -> lastping_us , NULL );
4327+ }
4328+
43094329/*! \brief Thread to periodically ping all clients and dump any that don't respond with a pong back in time */
43104330static void * ping_thread (void * unused )
43114331{
@@ -4340,8 +4360,7 @@ static void *ping_thread(void *unused)
43404360 bbs_debug (5 , "Shutting down client on node %d\n" , user -> node -> id );
43414361 bbs_socket_shutdown (user -> node -> fd ); /* Make the client handler thread break */
43424362 } else {
4343- irc_other_thread_writef (user -> node , "PING :%" TIME_T_FMT "\r\n" , now );
4344- user -> lastping = now ;
4363+ ping_client (user , now );
43454364 clients ++ ;
43464365 }
43474366 }
@@ -4370,11 +4389,13 @@ static int cli_irc_users(struct bbs_cli_args *a)
43704389 int i = 0 ;
43714390 time_t now = time (NULL );
43724391
4373- bbs_dprintf (a -> fdout , "%3s %2s %4s %-20s %4s %-15s %-20s %s\n" , "#" , "Op" , "Node" , "User" , "Ping" , "Modes" , "Nick" , "Hostmask" );
4392+ bbs_dprintf (a -> fdout , "%3s %2s %4s %-20s %4s %4s % -15s %-20s %s\n" , "#" , "Op" , "Node" , "User" , "Ping" , "Lag " , "Modes" , "Nick" , "Hostmask" );
43744393 RWLIST_RDLOCK (& users );
43754394 RWLIST_TRAVERSE (& users , user , entry ) {
43764395 char hostmask [128 ];
43774396 char node_id [15 ];
4397+ char pingbuf [20 ];
4398+ char lagbuf [20 ]; /* min size possible */
43784399 time_t ping = user -> lastpong ? now - user -> lastpong : -1 ;
43794400 ++ i ;
43804401 get_user_modes (modes , sizeof (modes ), user );
@@ -4384,8 +4405,19 @@ static int cli_irc_users(struct bbs_cli_args *a)
43844405 strcpy (node_id , "-" );
43854406 }
43864407 snprintf (hostmask , sizeof (hostmask ), HOSTMASK_FMT , HOSTMASK_ARGS (user ));
4387- bbs_dprintf (a -> fdout , "%3d %2s %4s %-20s %4" TIME_T_FMT " %-15s %-20s %s\n" ,
4388- i , user -> modes & USER_MODE_OPERATOR ? "*" : "" , node_id , user -> username , ping , modes , user -> nickname , hostmask );
4408+ if (user -> lastpong ) {
4409+ int lag_sec = user -> lag / 1000 ;
4410+ snprintf (lagbuf , sizeof (lagbuf ), "%01d.%02d" , lag_sec , (user -> lag - (lag_sec * 1000 )) / 10 );
4411+ } else {
4412+ strcpy (lagbuf , "-" );
4413+ }
4414+ if (ping != -1 ) {
4415+ snprintf (pingbuf , sizeof (pingbuf ), "%" TIME_T_FMT , ping );
4416+ } else {
4417+ strcpy (pingbuf , "-" );
4418+ }
4419+ bbs_dprintf (a -> fdout , "%3d %2s %4s %-20s %4s %4s %-15s %-20s %s\n" ,
4420+ i , user -> modes & USER_MODE_OPERATOR ? "*" : "" , node_id , user -> username , pingbuf , lagbuf , modes , user -> nickname , hostmask );
43894421 }
43904422 RWLIST_UNLOCK (& users );
43914423 bbs_dprintf (a -> fdout , "%d user%s online\n" , i , ESS (i ));
0 commit comments