-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathwho.py
134 lines (107 loc) · 4.29 KB
/
who.py
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
130
131
132
133
134
#!/usr/bin/env python
import argparse
import sys
import time
from pathlib import Path
from cstruct import NATIVE_ORDER, MemCStruct, getdef, parse
DEFAULT_FILENAME = "/var/run/utmp"
parse(
"""
/* Values for ut_type field, below */
#define EMPTY 0 /* Record does not contain valid info
(formerly known as UT_UNKNOWN on Linux) */
#define RUN_LVL 1 /* Change in system run-level (see
init(1)) */
#define BOOT_TIME 2 /* Time of system boot (in ut_tv) */
#define NEW_TIME 3 /* Time after system clock change
(in ut_tv) */
#define OLD_TIME 4 /* Time before system clock change
(in ut_tv) */
#define INIT_PROCESS 5 /* Process spawned by init(1) */
#define LOGIN_PROCESS 6 /* Session leader process for user login */
#define USER_PROCESS 7 /* Normal process */
#define DEAD_PROCESS 8 /* Terminated process */
#define ACCOUNTING 9 /* Not implemented */
#define UT_LINESIZE 32
#define UT_NAMESIZE 32
#define UT_HOSTSIZE 256
typedef int pid_t;
typedef long time_t;
"""
)
class ExitStatus(MemCStruct):
__def__ = """
struct ExitStatus {
short e_termination; /* Process termination status. */
short e_exit; /* Process exit status. */
}
"""
class Timeval(MemCStruct):
__def__ = """
struct {
int32_t tv_sec; /* Seconds. */
int32_t tv_usec; /* Microseconds. */
}
"""
def str_from_c(string):
return string.decode().split("\0")[0]
class Utmp(MemCStruct):
__byte_order__ = NATIVE_ORDER
__def__ = """
typedef struct ExitStatus ExitStatus;
struct {
short ut_type; /* Type of record */
pid_t ut_pid; /* PID of login process */
char ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */
char ut_id[4]; /* Terminal name suffix, or inittab(5) ID */
char ut_user[UT_NAMESIZE]; /* Username */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or kernel version for run-level messages */
ExitStatus ut_exit; /* Exit status of a process marked as DEAD_PROCESS; not used by Linux init (1 */
int32_t ut_session; /* Session ID (getsid(2)), used for windowing */
struct {
int32_t tv_sec; /* Seconds */
int32_t tv_usec; /* Microseconds */
} ut_tv; /* Time entry was made */
int32_t ut_addr_v6[4]; /* Internet address of remote host; IPv4 address uses just ut_addr_v6[0] */
char __unused[20]; /* Reserved for future use */
}
"""
@property
def user(self):
return str_from_c(self.ut_user)
@property
def line(self):
return str_from_c(self.ut_line)
@property
def time(self):
return time.strftime("%Y-%m-%d %H:%M", time.gmtime(self.ut_tv.tv_sec))
@property
def host(self):
if str_from_c(self.ut_host):
host = str_from_c(self.ut_host)
return f"({host})"
elif self.ut_id:
ut_id = str_from_c(self.ut_id)
return f"id={ut_id}"
else:
return ""
def __str__(self):
return f"{self.user:<10s} {self.line:<12s} {self.time:<15s} {self.ut_pid:>15} {self.host:<8s}"
def print_info(self, show_all):
if show_all or self.ut_type in (getdef('LOGIN_PROCESS'), getdef('USER_PROCESS')):
print(self)
def main():
parser = argparse.ArgumentParser(description="Print information about users who are currently logged in.")
parser.add_argument("-a", "--all", action="store_true", dest="show_all", help="show all enties")
parser.add_argument("file", nargs="?", help="if FILE is not specified use /var/run/utmp", default=DEFAULT_FILENAME)
args = parser.parse_args()
utmp = Utmp()
try:
with Path(args.file).open("rb") as f:
while utmp.unpack(f):
utmp.print_info(args.show_all)
except (IOError, OSError) as ex:
print(ex)
sys.exit(1)
if __name__ == "__main__":
main()