ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I am looking for a script for Red Hat which can detect if root is logged in at console for more than 15min and if yes send an email and snmp alert like may be using "who" or "finger" .. and I can run this as cron to check after every 15 min.
You can use the w command (LC_ALL=C LANG=C w -lh) to see which users are logged in, but personally I hate trying to parse its fields in a script. (The login time field, in particular, can be either a date or a time..)
Here is a helper C program that makes this much easier.
Note: this is the updated version, with DURATION calculation fixed. The previous version would consider the input as days in all cases.
and you can trigger something like the following script from your crontab:
Code:
#!/bin/bash
logins="$(w2 15m root)"
if [ -n "$logins" ]; then
# Yes, root has been logged for at least 15 minutes.
# Send e-mail to dummy@example.com
( echo ""
echo "Root has been logged in for over 15 minutes:"
printf 'user\tseconds\ttty\tfrom-ip-address\tfrom-host\n'
echo "$logins"
) | mailx -s "Root logged in too long" dummy@example.com
fi
Remember, however, that if you leave a console open for the weekend, you'll get something like two hundred emails.
Last edited by Nominal Animal; 05-22-2012 at 02:50 PM.
Reason: Fixed the duration calculation!
Thanks for your scripts but seems its not really working.. If I jsut execute w2 binary it gives me some result but in scripts with arguments does not seems to be working. Even if I put it in a variable on shell and echo that I dont get anything..
[root@rhel6-64-soe bin]# w
14:50:17 up 24 days, 23:30, 1 user, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 192.168.28.7 14:28 0.00s 0.08s 0.02s w
You have new mail in /var/spool/mail/root
Also I am looking for generic bash code like using may be "last" as I need to run the same scripts on HP-UNIX as well and on production system I cannot install complier..
Thanks for your scripts but seems its not really working..
It was a bug in the duration calculation, now fixed. It understood the duration as fractional days, regardless of what suffix you gave it.
Quote:
Originally Posted by iffarrukh
Also I am looking for generic bash code like using may be "last" as I need to run the same scripts on HP-UNIX as well and on production system I cannot install complier..
In that case, I'd use something like the following. Note that you may need to use last -a on the HP-UNIX; the -i option just turns the hostname into an IP address, which is safer to log since names may change.
Code:
#!/bin/bash
# Make sure we use the default POSIX locale (English).
export LC_ALL=C LANG=C
# Current time.
nowparts=($( date '+%a %b %d %H:%M' ))
nowwkday="${nowparts[0]}"
nowmonth="${nowparts[1]}"
nowday="${nowparts[2]}"
nowtime="${nowparts[3]}"
# Assume no action.
action=""
# Parse 'last' output.
while read user tty wkday month day time still logged in from ; do
# Still logged in?
[ "$still" = "still" ] || continue
[ "$logged" = "logged" ] || continue
[ "$in" = "in" ] || continue
# $wkday is Sun Mon Tue Wed Thu Fri Sat
# $month is Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
# $day is 1 to 31
# $now... have the same values for this moment in time.
#
# Todo: Compare the timestamps to see if there is at least
# 15 minutes in between, and if so, set 'action' variable.
#
done < <( last -ai root )
if [ -n "$action" ]; then
#
# root has been logged in for at least 15 minutes.
#
fi
But, like I said, parsing those date and time values to see if there is at least DURATION minutes in between, is quite annoying. It is much easier if you can use GNU awk, but do you have that installed on HP-UNIX? Run gawk --version to check.
Standard awk does not have useful date/time manipulation functions, but GNU awk (gawk) does, as extensions. Can you rely on always having gawk available?
It is not only likely to produce false positives (like scripts running under cron, for example), but it would miss any non-Bash sells (say if root ran exec zsh for example), but it also suffers from the exact same date/time parsing problem.
The TIME column is the accumulated CPU time, and will not grow if root just forgot to log out, and left the connection open.
______________________________________________________
GNU awk has support for mktime() which makes it possible to determine the number of seconds between two timestamps without horrendous amounts of code. With it, you only need to convert the month to a number (01 to 12).
______________________________________________________
Even better would be if you had date from GNU coreutils available. You could then use SECONDS=$(( $(date -d "later" +%s) - $(date -d "earlier" +%s) )) to calculate the number of seconds between the two timestamps. It supports quite freehand timestamps, stuff like 'now' and 'yesterday', and most inportantly, the wonky format these utilities use. Both May24 and 07:13 are be handled correctly, and give the expected result.
In this particular use case, SECONDS=$(( $(date +%s) - $(date -d "timestamp" +%s) )) would be perfect to parse the number of seconds since timestamp (i.e. login), but I do not know of any other date variants than GNU date that support +%s, -d May24, and -d 07:13.
@iffarrukh: Could you check if these work for you? In HP-UNIX?
Code:
date -d May23 +%s
date -d 00:00 +%s
date +%s
If the output is just three numbers in ascending order, and no errors, then the following should work for you:
Code:
#!/bin/bash
# Make sure we use the default POSIX locale (English).
export LC_ALL=C LANG=C
# Current time.
nowparts=($( date '+%a %b %d %H:%M' ))
nowwkday="${nowparts[0]}"
nowmonth="${nowparts[1]}"
nowday="${nowparts[2]}"
nowtime="${nowparts[3]}"
# Maximum login time in seconds by root.
loginsecs=0
# Parse 'last' output.
while read user tty wkday month day time still logged in from ; do
# Still logged in?
[ "$still" = "still" ] || continue
[ "$logged" = "logged" ] || continue
[ "$in" = "in" ] || continue
# Number of seconds logged in
seconds=$(( $(date +%s) - $(date -d "$month $day $time" +%s) ))
# Update loginsecs, if seconds is larger.
[ $seconds -gt $loginsecs ] && loginsecs=$seconds
done < <( last -ai root )
# At least 15 minutes logged in? (15 min = 900 seconds)
if [ $loginsecs -ge 900 ]; then
#
# root has been logged in for at least 15 minutes.
#
fi
Just as I thought, you cannot rely on date on HP-UNIX.
Would it be acceptable if the check would be skipped for the duration after the midnight?
Assuming you use 15 minutes as the limit, it would mean that from midnight to 15 minutes past, the check would be skipped, and no e-mail or warning sent at all, in any case. It would also mean that if the root logged in 15 minutes before the midnight, and stayed logged in for at most 30 minutes, it would not be detected by this script. Logging at 15 minutes before the midnight, and staying logged in for 31 minutes or more, would be detected, however. The practical effect is that around midnight, the allowed root login duration would be temporarily doubled.
Consider the following:
Code:
#!/bin/bash
# Use POSIX locale explicitly.
export LANG=C
export LC_ALL=C
# Maximum age of root logins, after which action is taken.
MAXMINUTES=15
# Current date and time. Get a consistent result,
now=($(date '+%b %d %H %M'))
# and then split the fields.
currmonth="${now[0]}" # abbreviation (locale!)
currday="${now[1]}" # day number
currhour="1${now[2]}" # hour + 100
currmin="1${now[3]}" # minute + 100
# Current time in minutes since midnight:
currminutes=$(( currhour * 60 + currmin - 6100 ))
# No action is taken between midnight and MAXMINUTES past.
(( currminutes <= MAXMINUTES )) && exit 0
The above will simply exit (with success exit status) if run between midnight and MAXMINUTES past midnight.
Since hours and minutes will always have two digits, and a leading zero would cause the value to be interpreted as octal (making 08 and 09 fail!), I add a leading zero. For example, at 08:09, date will output '108*60+109', and the left side of the arithmetic expression will evaluate to 489, the number of minutes since midnight at 08:09.
At this point, you know that if root logged in on a different day (day number or month name differs), then it has been logged on for longer than MAXMINUTES minutes. Only if the date matches, is there any need to check. To check the number of minutes during the same day, we only need to manipulate the timestamp a bit.
So, let's continue:
Code:
# List of users who have been logged in for too long.
users=""
# List of users we consider. Use empty list for all users.
userlist="root"
# Parse 'last' output:
while read user tty wkday month day time still logged in from ; do
# Still logged in?
[ "$still" = "still" ] || continue
[ "$logged" = "logged" ] || continue
[ "$in" = "in" ] || continue
# Different month? Different day?
if [ "$month" != "$currmonth" ] || [ "$day" != "$currday" ]; then
# Logged in for longer than allowed.
users="$users $user"
continue
fi
# Calculate the time after midnight in minutes.
timepart=(${time//[:.]/ })
hour="1${timepart[0]}"
min="1${timepart[1]}"
minutes=$(( currminutes - hour * 60 - min + 6100 ))
# Logged in for too long?
if (( minutes > MAXMINUTES )); then
users="$users $user"
continue
fi
done < <( last -ai $userlist 2>/dev/null || last -a $userlist 2>/dev/null)
users="${users## }"
if [ -n "$users" ]; then
#
# $users have been logged in for too long.
#
echo "Logged for too long: $users"
fi
This one has a few additional tricks. If last -ai fails, it automatically tries last -a instead.
Just please remember that like I initially stated, this does not apply the MAXMINUTES limit consistently: before and after midnight, MAXMINUTES is effectively doubled. Whether that is acceptable to you I cannot say. It is certainly possible to calculate the number of minutes between two dates and times, but in my opinion, your HP-UNIX environment is too restricted to do it, unless you switch to Perl or some other full-featured programming language. Personally, I do not like to be confined to proprietary environments like your HP-UNIX, so I'm afraid I won't be of much help you with that. (To me, it's like being told that hammers are not allowed, use this frozen banana instead.)
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.