os.getuid
The real uid is who started the process; the effective uid decides permissions (it differs in setuid programs); the saved uid lets a program switch back. All of these are Unix only. os.getlogin() also exists on Windows, but getpass.getuser() is usually the better way to get a user name.
Common call
os.geteuid() == 0
Returns
True when running with root privileges
Replaces
parsing the output of the id command
Watch out
Unix only: guard with hasattr or os.name on cross-platform code
os.getuid() / os.geteuid() / os.getgid() / os.getegid() / os.getgroups() / os.setuid(uid, /)
→ int
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| uid / gid | int | yes | setuid / seteuid / setgid / setegid: the new id. |
| ruid, euid (, suid) | int | yes | setreuid / setresuid (and the gid twins): real, effective (and saved) ids; -1 leaves one unchanged. |
| groups | sequence of int | yes | setgroups only: the new supplementary group list (normally needs root). |
| user, group | str, int | yes | getgrouplist(user, group) / initgroups(username, gid): a user name and its primary group id. |
Return value
int — get*id: a numeric id; getgroups / getgrouplist: list of ints; getresuid / getresgid: (real, effective, saved); getlogin: str. The set* calls return None.
Common patterns
Am I root? (Unix)
Permissions follow the effective uid.
import os is_root = hasattr(os, 'geteuid') and os.geteuid() == 0
Drop root privileges for good
Groups first, then gid, then uid last: once the uid is gone you can no longer change groups.
import os, pwd user = pwd.getpwnam('www-data') os.setgroups([]) os.setgid(user.pw_gid) os.setuid(user.pw_uid)
User name, portably
getpass checks LOGNAME, USER, LNAME and USERNAME, then the password database.
import getpass name = getpass.getuser()
Run a child as another user
subprocess switches the child to that user and group before running it (Unix, needs privileges).
import subprocess subprocess.run(['id'], user='nobody', group='nogroup', extra_groups=[])
Examples
1. Unix only
import os
all(hasattr(os, n) == (os.name == 'posix') for n in ('getuid', 'geteuid', 'getgid', 'getegid', 'getgroups', 'setuid'))
Returns
True2. getlogin exists on Unix and Windows
import os
hasattr(os, 'getlogin')
Returns
True3. getpass.getuser() returns a str
import getpass
isinstance(getpass.getuser(), str)
Returns
True4. Root is uid 0: the effective id decides
ruid, euid = 1000, 0 # a setuid-root program started by user 1000
(ruid == 0, euid == 0)
Returns
(False, True)5. EPERM: what an unprivileged set* call fails with
import errno
(errno.EPERM, errno.errorcode[errno.EPERM])
Returns
(1, 'EPERM')Pitfalls
1. Dropping the uid before the gid
Changing the gid needs privileges. After setuid(1000) the process is no longer root, so the following setgid fails and the process keeps the root group. This model applies the kernel rule; on Linux the real call raises the same PermissionError.
setuid, then setgid
ids = {'uid': 0, 'gid': 0} def setuid(uid): ids['uid'] = uid def setgid(gid): if ids['uid'] != 0: raise PermissionError('[Errno 1] Operation not permitted') ids['gid'] = gid setuid(1000) setgid(1000)
PermissionError: [Errno 1] Operation not permitted
setgid, then setuid
ids = {'uid': 0, 'gid': 0} def setuid(uid): ids['uid'] = uid def setgid(gid): if ids['uid'] != 0: raise PermissionError('[Errno 1] Operation not permitted') ids['gid'] = gid setgid(1000) setuid(1000) ids
{'uid': 1000, 'gid': 1000}
2. Checking the real uid for root
A setuid-root program has real uid = the caller and effective uid = 0. File permissions use the effective id, so test geteuid().
getuid() == 0
ruid, euid = 1000, 0 ruid == 0
False
geteuid() == 0
ruid, euid = 1000, 0 euid == 0
True
3. Calling os.getuid() on Windows
It does not exist there. Guard the call, or use getpass.getuser() when all you need is a name.
unguarded
import os os.name == 'posix' or not hasattr(os, 'getuid') # on Windows os.getuid() raises AttributeError
True
guarded
import os uid = os.getuid() if hasattr(os, 'getuid') else None uid is None or isinstance(uid, int)
True
When to use
Use it
- Refusing to run as root, or requiring root
- Servers that start as root to bind a port, then drop to an unprivileged user
- Checking group membership with getgroups()
Reach for something else
- Getting the user name → getpass.getuser() (works on Windows too)
- Mapping ids to names → pwd.getpwuid(uid).pw_name / grp.getgrgid(gid).gr_name
- Running a child as another user → subprocess user= / group= / extra_groups=
Notes
CPython impl
Direct wrappers of the getuid(2) / setuid(2) family; errors become OSError subclasses (PermissionError for EPERM)
Availability
Unix only for everything except getlogin (Unix, Windows). getresuid / getresgid / setresuid / setresgid: not macOS, not iOS. setuid / seteuid / setgid / setegid / setreuid / setregid / initgroups: not Android
getlogin
Returns the user logged in on the controlling terminal; on Linux it raises OSError when there is none (cron, services, our WSL check: errno 25). getpass.getuser() reads LOGNAME / USER / LNAME / USERNAME, then falls back to the password database
NGROUPS_MAX
Maximum number of supplementary groups (65536 on our Linux test system, the same as sysconf("SC_NGROUPS_MAX")); not defined on Windows
macOS getgroups
May return the group access list of the effective user rather than the list set with setgroups(), and is not limited to 16 entries on modern builds
FAQ
These functions are Unix only (docs: Availability: Unix), the ids differ per machine, and the set* calls change the identity of the whole process. The examples show availability, the id arithmetic and a model of the drop-privileges order instead.