When building session monitoring tools on Linux systems, the w
command provides essential information about user activity - particularly through its IDLE column. However, interpreting this data correctly requires understanding three distinct time formats:
22:29m # Hours:Minutes format
46.00s # Seconds format
27:47 # Minutes:Seconds format
Let's examine each format with concrete examples:
# Seconds format (XX.XXs)
12.34s = 12 seconds and 34 hundredths
# Hours:Minutes format (XX:XXm)
3:22m = 3 hours and 22 minutes
# Minutes:Seconds format (XX:XX)
45:30 = 45 minutes and 30 seconds
Here's a Python script to parse all three formats and alert when idle time exceeds a threshold:
import subprocess
import re
from datetime import timedelta
def parse_idle_time(idle_str):
# Handle seconds format (XX.XXs)
if idle_str.endswith('s'):
return float(idle_str[:-1])
# Handle hours:minutes format (XX:XXm)
if idle_str.endswith('m'):
h, m = map(int, idle_str[:-1].split(':'))
return h * 3600 + m * 60
# Handle minutes:seconds format (XX:XX)
if ':' in idle_str:
m, s = map(int, idle_str.split(':'))
return m * 60 + s
return 0
def check_idle_sessions(threshold_minutes=30):
output = subprocess.check_output(['w', '-h']).decode()
threshold = threshold_minutes * 60
for line in output.splitlines()[1:]:
parts = re.split(r'\s+', line.strip())
user, tty, idle = parts[0], parts[1], parts[4]
idle_seconds = parse_idle_time(idle)
if idle_seconds > threshold:
print(f"Alert: {user} on {tty} idle for {idle} ({idle_seconds} seconds)")
check_idle_sessions()
Be aware of these special cases in the IDLE field:
.
indicates the user was active within the last minuteold
means the session has been idle for over 24 hours- Empty value typically means the session is the current one
For more precise tracking, consider these alternatives to the w
command:
# Using last command to check login history
last -a
# Using who command with idle time
who -u
# Checking terminal activity directly
ls -lu /dev/pts/*
The Linux w
command displays user sessions with three distinct IDLE time formats:
1. 46.00s - Seconds with decimal fractions (46 seconds)
2. 22:29m - Hours:Minutes format (22 hours 29 minutes)
3. 27:47 - Pure colon-separated value (27 minutes 47 seconds)
Here's how each format should be interpreted programmatically:
import re
from datetime import timedelta
def parse_idle_time(idle_str):
# Case 1: Seconds format (XX.YYs)
if re.match(r'^\d+\.\d{2}s$', idle_str):
return timedelta(seconds=float(idle_str[:-1]))
# Case 2: Hours:Minutes format (XX:XXm)
if re.match(r'^\d+:\d{2}m$', idle_str):
hours, minutes = map(int, idle_str[:-1].split(':'))
return timedelta(hours=hours, minutes=minutes)
# Case 3: Pure colon format (XX:XX)
if ':' in idle_str:
parts = idle_str.split(':')
if len(parts[0]) > 2: # Handle days format if present
days = int(parts[0])
return timedelta(days=days, hours=int(parts[1]), minutes=int(parts[2]))
return timedelta(minutes=int(parts[0]), seconds=int(parts[1]))
return timedelta(0) # Default case
Here's a complete Python implementation for session monitoring:
#!/usr/bin/env python3
import subprocess
import re
from datetime import datetime, timedelta
def get_sessions():
result = subprocess.run(['w', '-hi'],
stdout=subprocess.PIPE,
universal_newlines=True)
return result.stdout.splitlines()
def alert_long_idle_users(max_idle=timedelta(minutes=30)):
for line in get_sessions():
parts = line.split()
if len(parts) < 6:
continue
user, tty, idle = parts[0], parts[1], parts[4]
idle_time = parse_idle_time(idle)
if idle_time > max_idle:
message = f"User {user} on {tty} idle for {str(idle_time)}"
send_alert(message)
def send_alert(message):
# Implement your notification logic here
print(f"ALERT: {message}")
# Example: subprocess.run(['notify-send', message])
- The pure colon format (27:47) represents minutes:seconds when total time < 24 hours
- For sessions over 24 hours idle, the format changes to days+hours:minutes (e.g., 3+12:30)
- TTY console sessions show idle time differently than pts/x terminal sessions
- The
w
command gets its data from/proc
andutmp
For more precise tracking, consider these methods:
# Method 1: Check last activity time
last_activity = os.path.getatime(f"/dev/{tty}")
# Method 2: Use 'last' command
subprocess.run(['last', '-w', '-i'])
# Method 3: Parse utmp directly
import utmp
with open('/var/run/utmp', 'rb') as f:
buf = f.read()
for entry in utmp.read(buf):
print(entry)
When implementing session monitoring:
- Batch process checks instead of continuous polling
- Cache results for non-interactive users
- Consider system load when running frequent checks
- Use efficient string parsing (like re.compile for patterns)