#!/usr/bin/awk -f # radparse - munch RADIUS "detail" format data into something more useful # # Copyright (C) 1997-8 Christopher J. Fearnley # # ChangeLog: # # 25-Jan-98 Christopher J. Fearnley : Incorporate # Edward S. Marshall's changes. Thereby totally # revamping the command-line processing (BIG improvement in useability! -- # Thanks Edward!). Left name as radparse. # Add support in -r option to match usernames that are surrounded by # quotes. # # 18-Dec-97 Edward S. Marshall [emarshal@logic.net]: Heavily reformatted # for readability, and output mechanisms cleaned up significantly. # Command-line parsing completely rewritten. Modified output appearance # for our logfiles specifically. Note that this program specifically # requires GNU awk now (ftp://prep.ai.mit.edu/pub/gnu/gawk-*.tar.gz). # Renamed to "radparse" to eliminate confusion with "radiusmunch". # # 3-Mar-97 Christopher J. Fearnley : Initial release. # # This 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, # or (at your option) any later version. # # This 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. # Return the number of a given month. function monval( mon ) { if ( mon == "Jan" ) return "01";else if( mon == "Feb" ) return "02"; else if( mon == "Mar" ) return "03";else if( mon == "Apr" ) return "04"; else if( mon == "May" ) return "05";else if( mon == "Jun" ) return "06"; else if( mon == "Jul" ) return "07";else if( mon == "Aug" ) return "08"; else if( mon == "Sep" ) return "09";else if( mon == "Oct" ) return "10"; else if( mon == "Nov" ) return "11";else if( mon == "Dec" ) return "12"; else return "-1"; } # Get the key/value pair from a given line. function getval(line) { gsub( /[\"\t ]/, "", line );split( line, val, "=" ); for( idx in fld ) { if( fld[idx] == val[1] ) { sub( /@.*$/, "", val[2] );var[idx] = val[2];break; } } } # Usage report. function usage() { print \ "Usage: radparse [-t] [-l] [-r regex] [-y n] [-m n] [-d n] [file [file ...]]\n\n"\ "-l\t\tToggle display of all logins (off by default).\n"\ "-t\t\tToggle display of totals for each user (on by default).\n"\ "-r regex\tWhat users to look for (extended regular expression).\n"\ "\t\t(Defaults to '.*')\n"\ "-y n\t\tOnly print entries for the year 'n'.\n"\ "-m n\t\tOnly print entries for the month 'n'.\n"\ "-d n\t\tOnly print entries for the day 'n'.\n"\ "-h\t\tDisplay this help." > "/dev/stderr";fflush("/dev/stderr"); } BEGIN { RS = "";FS = "\n";users = ".*";logins = 0;totals = 1; # Correct number of arguments? if( ARGC < 2 ) { usage();totals = 0;exit( 1 ); } # Parse our command line. Getopt would rock here. for( ind = 1; ind <= ARGC; ind++ ) { if( ARGV[ind] == "-?" || ARGV[ind] == "-h" ) { usage();totals = 0;exit( 1 ); } # Toggle display of totals. if( ARGV[ind] == "-t" ) { totals = !totals;ARGV[ind] = "";continue; } # Toggle display of logins. if( ARGV[ind] == "-l" ) { logins = !logins;ARGV[ind] = "";continue; } # Get the regular expression of users to search for. if( ARGV[ind] == "-r" ) { ARGV[ind] = "";ind++; if( ind <= ARGC ) { users = ARGV[ind];ARGV[ind] = "";continue; } usage();totals = 0;exit( 1 ); } # Year to search. if( ARGV[ind] == "-y" ) { ARGV[ind] = "";ind++; if( ind <= ARGC ) { year = ARGV[ind];ARGV[ind] = "";continue; } usage();totals = 0;exit( 1 ); } # Month to search. if( ARGV[ind] == "-m" ) { ARGV[ind] = "";ind++; if( ind <= ARGC ) { month = ARGV[ind];ARGV[ind] = "";continue; } usage();totals = 0;exit( 1 ); } # Day to search. if( ARGV[ind] == "-d" ) { ARGV[ind] = "";ind++; if( ind <= ARGC ) { day = ARGV[ind];ARGV[ind] = "";continue; } usage();totals = 0;exit( 1 ); } # Must be an input file. Ignore it; awk will handle it. } # Get the username and the time they spent online. fld[1] = "User-Name"; fld[2] = "Acct-Session-Time"; # Get the server they came in on, the port on that server, and # the IP address that they were assigned (if any). fld[3] = "NAS-IP-Address"; fld[4] = "NAS-Port-Id"; fld[5] = "Framed-IP-Address"; fld[6] = "Acct-Session-Id"; for( ind in fld ) { ++numflds;varstomatch = varstomatch "|" fld[ind]; } sub( /\|/, "", varstomatch ); } # Only Stop records have accounting information /Acct-Status-Type = Stop/ && ( $0 ~ "User-Name = \"*" users "\"*\n" ) { # Get our timestamp information. split( $1, datetime, / +/ ); curmon = monval( datetime[2] );curday = datetime[3];curyear = datetime[5]; if( year != "" ) if( year != curyear ) next; if( month != "" ) { if( month != ( curmon + 0 ) ) next; if( day != "" ) { if( day != curday ) next; } } # Get our "interesting" variables. for( ind = 2; ind <= NF; ++ind ) { if( $ind ~ varstomatch ) { getval( $ind ); } } # Remove duplicates that are next to each other if(var[6]==lastid) next;lastid=var[6] # Are we displaying logins? if( logins ) { printf( "%2.2s/%2.2s/%2.2s %s %-16s %-6s %-15s %-2s %s\n", curmon, ( ( curday < 10 ) ? "0" : "" ) curday, substr( curyear, 3 ), datetime[4], var[1], var[2], var[3], var[4], var[5] ); } if( totals ) { timeused[var[1]] += var[2];numlogins[var[1]]++; } for( idx in var ) delete var[idx]; } END { # Are we displaying totals? if( !totals ) exit 0; # Convert to hours. timeconv = 60 * 60; # Print the header, and flush output before we start displaying the # sorted data, so that we don't end up with a header at the bottom. print "Username | logins | total hours | avg time" print "---------------------------------------------------" fflush(); # Dump the users to sort. for (ind in timeused) printf( "%-16s | %-6s | %-11.7g | %s\n", ind, numlogins[ind], totaltime = timeused[ind] / timeconv, totaltime / numlogins[ind] ) | "sort"; }