1
2from __future__ import absolute_import, print_function
3import os, pwd, grp
4
5from bup import _helpers
6from bup.helpers import cache_key_value
7
8
9# Using __slots__ makes these much smaller (even than a namedtuple)
10
11class Passwd:
12    """Drop in replacement for pwd's structure with bytes instead of strings."""
13    __slots__ = ('pw_name', 'pw_passwd', 'pw_uid', 'pw_gid', 'pw_gecos',
14                 'pw_dir', 'pw_shell')
15    def __init__(self, name, passwd, uid, gid, gecos, dir, shell):
16        assert isinstance(name, bytes)
17        assert isinstance(passwd, bytes)
18        assert isinstance(gecos, bytes)
19        assert isinstance(dir, bytes)
20        assert isinstance(shell, bytes)
21        (self.pw_name, self.pw_passwd, self.pw_uid, self.pw_gid,
22         self.pw_gecos, self.pw_dir, self.pw_shell) = \
23             name, passwd, uid, gid, gecos, dir, shell
24
25def getpwuid(uid):
26    r = _helpers.getpwuid(uid)
27    return Passwd(*r) if r else None
28
29def getpwnam(name):
30    assert isinstance(name, bytes)
31    r = _helpers.getpwnam(name)
32    return Passwd(*r) if r else None
33
34
35class Group:
36    """Drop in replacement for grp's structure with bytes instead of strings."""
37    __slots__ = 'gr_name', 'gr_passwd', 'gr_gid', 'gr_mem'
38    def __init__(self, name, passwd, gid, mem):
39        assert isinstance(name, bytes)
40        assert isinstance(passwd, bytes)
41        for m in mem:
42            assert isinstance(m, bytes)
43        self.gr_name, self.gr_passwd, self.gr_gid, self.gr_mem = \
44            name, passwd, gid, mem
45
46def getgrgid(uid):
47    r = _helpers.getgrgid(uid)
48    return Group(*r) if r else None
49
50def getgrnam(name):
51    assert isinstance(name, bytes)
52    r = _helpers.getgrnam(name)
53    return Group(*r) if r else None
54
55
56_uid_to_pwd_cache = {}
57_name_to_pwd_cache = {}
58
59def pwd_from_uid(uid):
60    """Return password database entry for uid (may be a cached value).
61    Return None if no entry is found.
62    """
63    global _uid_to_pwd_cache, _name_to_pwd_cache
64    entry, cached = cache_key_value(getpwuid, uid, _uid_to_pwd_cache)
65    if entry and not cached:
66        _name_to_pwd_cache[entry.pw_name] = entry
67    return entry
68
69def pwd_from_name(name):
70    """Return password database entry for name (may be a cached value).
71    Return None if no entry is found.
72    """
73    assert isinstance(name, bytes)
74    global _uid_to_pwd_cache, _name_to_pwd_cache
75    entry, cached = cache_key_value(getpwnam, name, _name_to_pwd_cache)
76    if entry and not cached:
77        _uid_to_pwd_cache[entry.pw_uid] = entry
78    return entry
79
80
81_gid_to_grp_cache = {}
82_name_to_grp_cache = {}
83
84def grp_from_gid(gid):
85    """Return password database entry for gid (may be a cached value).
86    Return None if no entry is found.
87    """
88    global _gid_to_grp_cache, _name_to_grp_cache
89    entry, cached = cache_key_value(getgrgid, gid, _gid_to_grp_cache)
90    if entry and not cached:
91        _name_to_grp_cache[entry.gr_name] = entry
92    return entry
93
94
95def grp_from_name(name):
96    """Return password database entry for name (may be a cached value).
97    Return None if no entry is found.
98    """
99    assert isinstance(name, bytes)
100    global _gid_to_grp_cache, _name_to_grp_cache
101    entry, cached = cache_key_value(getgrnam, name, _name_to_grp_cache)
102    if entry and not cached:
103        _gid_to_grp_cache[entry.gr_gid] = entry
104    return entry
105
106
107_username = None
108def username():
109    """Get the user's login name."""
110    global _username
111    if not _username:
112        uid = os.getuid()
113        _username = pwd_from_uid(uid).pw_name or b'user%d' % uid
114    return _username
115
116
117_userfullname = None
118def userfullname():
119    """Get the user's full name."""
120    global _userfullname
121    if not _userfullname:
122        uid = os.getuid()
123        entry = pwd_from_uid(uid)
124        if entry:
125            _userfullname = entry.pw_gecos.split(b',')[0] or entry.pw_name
126        if not _userfullname:
127            _userfullname = b'user%d' % uid
128    return _userfullname
129