summaryrefslogtreecommitdiff
path: root/scripts/show-users.pl
blob: 321fbcb02a3ea388f8e06a411479b2f0050f30f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#! /usr/bin/perl

# **** License ****
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
# 
# 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.
# 
# This code was originally developed by Vyatta, Inc.
# Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
# All Rights Reserved.
# 
# Author: Stephen Hemminger
# Date: Sept 2009
# Description: Show password accounts
# 
# **** End License ****

use lib "/opt/vyatta/share/perl5";
use Vyatta::Config;
use IO::Seekable;

use strict;
use warnings;

sub usage {
    print "Usage: $0 {type}\n";
    print " type := all | vyatta | locked | other | color\n";
    exit 1;
}

use constant {
    VYATTA	=> 0x1,
    OTHER	=> 0x2,
    LOCKED	=> 0x4,
};

my %filters = (
    'vyatta'	=> VYATTA,
    'other'	=> OTHER,
    'locked'	=> OTHER|LOCKED,
    'all'	=> VYATTA|OTHER|LOCKED,
);

my $filter = 0;
for (@ARGV) {
    my $mask = $filters{$_};
    unless ($mask) {
	warn "Unknown type $_\n";
	usage();
    }
    $filter |= $mask;
}
# Default is everything but locked accounts
$filter |= VYATTA|OTHER if ($filter == 0);

# Read list of Vyatta users
my $cfg = new Vyatta::Config;
$cfg->setLevel('system login user');
my %vuser = map { $_ => 1 } $cfg->listOrigNodes();

# Setup to access lastlog
open (my $lastlog, '<', "/var/log/lastlog")
    or die "can't open /var/log/lastlog:$!";
# Magic values based on binary format of last log
# See /usr/include/bits/utm.h
my $typedef = 'L Z32 Z256';
my $reclen = length(pack($typedef));

sub lastlog {
    my $uid = shift;

    sysseek($lastlog, $uid * $reclen, SEEK_SET)
	or die "seek failed: $!";

    my ($rec, $line, $host, $time);
    if (sysread($lastlog, $rec, $reclen) == $reclen) {
	my ($time, $line, $host) = unpack($typedef, $rec);
	return scalar(localtime($time)), $line, $host
	    if ($time != 0);
    }

    return ("never logged in", "", "");
}


# Walk password file
# Note: this only works as root
my %users;
setpwent();
while ( my ($u, $p, $uid) = getpwent()) {
    my $l = length($p);
    my $status;
    my $flag = 0;

    my $type = defined($vuser{$u}) ? 'vyatta' : 'other';
    if ($type eq 'vyatta') {
	$flag |= VYATTA;
    } elsif ($l != 1) {
	$flag |= OTHER;
    }

    # only works as root, otherwise shadow file is inaccessible
    if ($l == 0) {
	$type .= '!';
    } if ($l == 1) {
	$flag |= LOCKED;
	$type .= '-';
    }

    next if (($flag & $filter) == 0);

    my ($time, $line, $host) = lastlog($uid);
    # fields to printf
    $users{$u} = [ $type, $line, $host, $time ];
}
endpwent();
close $lastlog;

my $fmt =    "%-15s %-7s %-8s %-19s %s\n";
printf $fmt, "Username","Type","Tty", "From","Last login";

foreach my $u (sort keys %users) {
    printf $fmt, $u, @{$users{$u}};
}