/* * ProFTPD - FTP server daemon * Copyright (c) 1997, 1998 Public Flood Software * Copyright (C) 1999, 2000 MacGyver aka Habeeb J. Dihu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ /* Shows a count of "who" is online via proftpd. Uses the /var/run/proftpd* * log files. * * $Id: ftpcount.c,v 1.10 2001/03/09 16:14:38 flood Exp $ */ #include "conf.h" #ifdef HAVE_GETOPT_H # include #endif #define MAX_CLASSES 100 struct Class { char *Class; unsigned long Count; }; static char *config_filename = CONFIG_FILE_PATH; #ifdef BUILDAS_FTPWHO static char *percent_complete ( unsigned long size, unsigned long done ) { static char sbuf [ 32 ]; memset ( sbuf, '\0', sizeof ( sbuf ) ); if ( done == 0 ) { sstrncpy ( sbuf, "0", sizeof ( sbuf ) ); } else if( size == 0 ) { sstrncpy ( sbuf, "Inf", sizeof ( sbuf ) ); } else if ( done >= size ) { sstrncpy ( sbuf, "100", sizeof ( sbuf ) ); } else { snprintf ( sbuf, sizeof ( sbuf ), "%.0f", ( ( double ) done / ( double ) size ) * 100.0 ); sbuf [ sizeof ( sbuf ) - 1 ] = '\0'; } return ( sbuf ); } static char *idle_time ( time_t *i ) { time_t now; long l; static char sbuf [ 7 ] ; memset ( sbuf, '\0', sizeof ( sbuf ) ); if ( ! i || ! *i ) return "-"; else { time ( &now ); l = now - *i; if ( l < 3600 ) snprintf ( sbuf, sizeof ( sbuf ), "%ldm%lds", ( l / 60 ), ( l % 60 ) ); else snprintf ( sbuf, sizeof ( sbuf ), "%ldh%ldm", ( l / 3600 ), ( ( l - ( l / 3600 ) * 3600 ) / 60 ) ); } return ( sbuf ); } #endif /* BUILDAS_FTPWHO */ /* scan_config_file ( ) is a kludge for 1.2 which does a very simplistic attempt * at determining what the "ScoreboardPath" directive is set to. It will be * replaced in 1.3 with the abstracted configure system ( hopefully ) . * jss 3/9/2001 */ static void scan_config_file ( void ) { FILE *fp; char buf [ 1024 ] = ""; char *cp, *path = NULL; if ( ! config_filename || ( fp = fopen ( config_filename,"r" ) ) == NULL ) return; while ( ! path && fgets ( buf, sizeof ( buf ) - 1, fp ) ) { int i = strlen ( buf ); if ( i && buf [ i - 1 ] == '\n' ) buf [ i - 1 ] = '\0'; for ( cp = buf ; *cp && isspace ( *cp ) ; cp++ ) ; if ( *cp == '#' || !*cp ) continue; i = strlen ( "ScoreboardPath" ); if ( strncasecmp ( cp,"ScoreboardPath",i ) != 0 ) continue; /* Found it! */ cp += i; /* strip whitespace */ while ( *cp && isspace ( *cp ) ) cp++; path = cp; /* If the scoreboard path argument is quoted, dequote */ if ( *cp == '"' ) { char *src = cp; cp++; path++; while ( *++src ) { switch ( *src ) { case '\\' : if ( *++src ) *cp++ = *src; break; case '"' : src++; break; default : *cp++ = *src; } } *cp = '\0'; } } fclose ( fp ); /* If we got something out of all this, go ahead and set it. */ if ( path ) log_run_setpath ( path ); } struct option_help { char *long_opt, *short_opt, *desc; } opts_help [] = { { "--help" , "-h", NULL }, { "--verbose" , "-v", "display add'l information for each connection" }, { "--path" , "-p", "specify full path to scoreboard directory" }, { "--config" , "-c", "specify full path to proftpd configuration file" }, #ifdef __JAK__ { "--ipaddress", "-i", "display the local IP address for the connection" }, { "--domain" , "-d", "display the local domain name for the connection" }, { "--IPADDRESS", "-I", "specify local IP address to display connections for" }, { "--DOMAIN" , "-D", "specify local domain name to display connections for" }, #endif { NULL } }; struct option opts [] = { { "help" , 0, NULL, 'h' }, { "verbose" , 0, NULL, 'v' }, { "path" , 1, NULL, 'p' }, { "config" , 1, NULL, 'c' }, #ifdef __JAK__ { "ipaddress" , 0, NULL, 'i' }, { "domain" , 0, NULL, 'd' }, { "IPADDRESS" , 0, NULL, 'I' }, { "DOMAIN" , 0, NULL, 'D' }, #endif { NULL , 0, NULL, 0 } }; void show_usage ( const char *progname, int exit_code ) { struct option_help *h; printf ( "usage: %s [ options ] \n",progname ); for ( h = opts_help ; h -> long_opt ; h++ ) { printf ( " %s,%s\n",h -> long_opt,h -> short_opt ); if ( !h -> desc ) printf ( " display %s usage\n",progname ); else printf ( " %s\n",h -> desc ); } exit ( exit_code ); } int main ( int argc, char **argv ) { logrun_t *l; pid_t oldpid = 0, mpid; int c = 0, tot = 0, i; int verbose = 0; #ifdef __JAK__ int show_ipaddress = 0; int show_domainname = 0; int filter_ipaddress = 0; int filter_domainname = 0; p_in_addr_t ipaddress; char *domainname = 0; struct hostent *host = 0; #endif struct Class Classes [ MAX_CLASSES ]; char *cp, *progname = *argv; // const char *cmdopts = "hp:v"; // JAK const char *cmdopts = "hp:c:vidI:D:"; // JAK // memset ( Classes, 0, MAX_CLASSES * sizeof ( struct Class ) ); memset ( Classes, 0, sizeof ( Classes ) ); if ( ( cp = rindex ( progname,'/' ) ) != NULL ) progname = cp + 1; opterr = 0; while ( ( c = #ifdef HAVE_GETOPT_LONG getopt_long ( argc, argv, cmdopts, opts, NULL ) #else /* HAVE_GETOPT_LONG */ getopt ( argc, argv, cmdopts ) #endif /* HAVE_GETOPT_LONG */ ) != -1 ) { switch ( c ) { case 'h' : show_usage ( progname,0 ); case 'v' : verbose++; break; #ifdef __JAK__ case 'i' : show_ipaddress = 1; break; case 'd' : show_domainname = 1; break; case 'I' : if ( ( optarg != 0 ) && ( *optarg != '-' ) && ( ( ipaddress.s_addr = inet_addr ( optarg ) ) != INADDR_NONE ) ) filter_ipaddress = 1; else { fprintf ( stderr, "-%c should be followed by an IP address\n", optopt ); show_usage ( progname, 1 ); } break; case 'D' : if ( ( optarg != 0 ) && ( *optarg != '-' ) ) { filter_domainname = 1; domainname = optarg; } else { fprintf ( stderr, "-%c should be followed by a domainname\n", optopt ); show_usage ( progname, 1 ); } break; #endif case 'p' : log_run_setpath ( optarg ); break; case 'c' : config_filename = strdup ( optarg ); break; case '?' : fprintf ( stderr, "unknown option: %c\n", ( char ) optopt ); show_usage ( progname, 1 ); } } /* First attempt to check the supplied/default scoreboard path. If this is * incorrect, try the config file kludge. * jss 3/9/2001 */ if ( log_run_checkpath () < 0 ) { scan_config_file (); if ( log_run_checkpath () < 0 ) { fprintf ( stderr, "%s: %s\n",log_run_getpath () ,strerror ( errno ) ); fprintf ( stderr, " ( Perhaps you need to specify the scoreboard path with --path, or change\n" ); fprintf ( stderr, " the compile-time default directory? ) \n" ); exit ( 1 ); } } c = 0; while ( ( l = log_read_run ( &mpid ) ) != NULL ) { if ( errno ) break; if ( ! c++ || oldpid != mpid ) { if ( tot ) printf ( " - %d user%s\n\n", tot, tot > 1 ? "s" : "" ); if ( !mpid ) printf ( "inetd FTP connections:\n" ); else printf ( "Master proftpd process %d:\n", ( int ) mpid ); oldpid = mpid; tot = 0; } #ifdef BUILDAS_FTPWHO /* * We're really running as ftpwho. */ #ifdef __JAK__ // See whether or not getting the domain name for 'l -> server_ip' makes sense. if ( ( show_domainname ) || ( filter_domainname ) ) host = gethostbyaddr ( ( const char * ) &l -> server_ip, sizeof ( p_in_addr_t ), AF_INET ); if ( ( ( ! filter_ipaddress ) || ( ( filter_ipaddress ) && ( l -> server_ip.s_addr == ipaddress.s_addr ) ) ) && ( ( ! filter_domainname ) || ( ( filter_domainname ) && ( host != 0 ) && ( strcasecmp ( domainname, host -> h_name ) == 0 ) ) ) ) { #endif if ( l -> transfer_size ) printf ( "%5d %-6s (%s%%) %s\n", ( int ) l -> pid, idle_time ( &l -> idle_since ), percent_complete ( l -> transfer_size, l -> transfer_complete ), l -> op ); else printf ( "%5d %-6s %s\n" , ( int ) l -> pid, idle_time ( &l -> idle_since ), l -> op ); if ( verbose ) { if ( l -> address [ 0 ] ) printf ( " (host: %s)\n", l -> address ); if ( l -> cwd [ 0 ] ) printf ( " (cwd: %s)\n", l -> cwd ); } #ifdef __JAK__ if ( show_ipaddress ) printf ( " (IP: %s)\n", inet_ntoa ( l -> server_ip ) ); if ( show_domainname ) { if ( host != 0 ) printf ( " (local: %s)\n", host -> h_name ); else printf ( " (IP: %s)\n", inet_ntoa ( l -> server_ip ) ); } #endif #endif // BUILDAS_FTPWHO for ( i = 0 ; i != MAX_CLASSES ; i++ ) { if ( Classes [ i ].Class == 0 ) { Classes [ i ].Class = strdup ( l -> class ); Classes [ i ].Count++; break; } if ( strcmp ( Classes [ i ].Class,l -> class ) == 0 ) { Classes [ i ].Count++; break; } } tot++; #ifdef __JAK__ } #endif } if ( tot ) { for ( i = 0 ; i != MAX_CLASSES ; i++ ) { if ( Classes [ i ].Class == 0 ) break; printf ( "Service class %-20s - %3lu user%s\n", Classes [ i ] .Class,Classes [ i ] .Count,Classes [ i ] .Count > 1 ? "s" : "" ); } /* Probably pointless now, wu-ftp does not show a total printf ( "Total %-20s - %3lu user%s\n", "", tot, tot > 1 ? "s" : "" ); */ } return ( 0 ); }