Shutdown, Reboot and Log Off on Windows and Linux

There is no standard function to shutdown, reboot or log the user off on both Windows and Linux systems, so you will need to use ad hoc code for each operating system. However, you might create a function that executes the proper code based on the value of the standard os.name string if you are working on a cross-platform project.

Windows

Under Windows you might run a command via subprocess or interface with the system API through the pywin32 package to achieve this goal.

If you opt for subprocess, then the code is as simple as:

import subprocess

# Shutdown immediately.
subprocess.run(["shutdown", "/s", "/t", "0"])
# Restart immediately.
subprocess.run(["shutdown", "/r", "/t", "0"])
# Sign off immediately.
subprocess.run(["shutdown", "/l"])

Easy, right? Additionally, you can add the /t option to specify the time to wait (in seconds) before executing the action. For example:

seconds = 30
print(f"Reboot in {seconds} seconds.")
subprocess.run(["shutdown", "/r", "/t", str(seconds)])

If for some reason you want to abort the operation, use:

# Abort scheduled operation.
subprocess.run(["shutdown", "/a"])

This command aborts any operations previously scheduled via shutdown.

If you want to use the Win32 API, there is a function called ExitWindowsEx with the following header which will do the work.

BOOL ExitWindowsEx(UINT uFlags, DWORD dwReason);

The first parameter specifies what type of operation should be performed. It must be one of the following: EWX_SHUTDOWN, EWX_REBOOT, EWX_LOGOFF. The second parameter indicates the reason why the operation is executed. You should look at the System Shutdown Reason Codes to choose the proper value for this parameter. In the following examples we will just use zero.

Make sure pywin32 is installed before calling ExitWindowsEx by running:

py -m pip install pywin32

Now to log the user off, use:

from win32api import ExitWindowsEx
from win32con import EWX_LOGOFF

ExitWindowsEx(EWX_LOGOFF, 0)

Both shutting down and rebooting require a special privilege (SE_SHUTDOWN_NAME) to be set in the calling process. So the following will not work, even if the process runs with admin rights:

from win32api import ExitWindowsEx
from win32con import EWX_SHUTDOWN

# This will fail with a privilege error.
ExitWindowsEx(EWX_SHUTDOWN, 0)

For this to work, you will need to enable the privilege in the current process before making the call through the AdjustTokenPrivileges function, like this:

from win32api import CloseHandle, ExitWindowsEx, GetCurrentProcess
from win32con import (
    EWX_SHUTDOWN,
    TOKEN_ADJUST_PRIVILEGES,
    TOKEN_QUERY
)
from win32security import (
    AdjustTokenPrivileges,
    LookupPrivilegeValue,
    OpenProcessToken,
    SE_PRIVILEGE_ENABLED,
    SE_SHUTDOWN_NAME
)
privilege_id = LookupPrivilegeValue(None, SE_SHUTDOWN_NAME)
process_handle = GetCurrentProcess()
process_token = OpenProcessToken(
    process_handle,
    TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
)
if process_token is None:
    print("Could not get process token.")
    exit()
AdjustTokenPrivileges(
    process_token,
    False,
    ((privilege_id, SE_PRIVILEGE_ENABLED),)
)
ExitWindowsEx(EWX_SHUTDOWN, 0)
CloseHandle(process_token)

Linux

On Linux run the shutdown command via the subprocess standard module:

import subprocess

# Shutdown
subprocess.run(["shutdown", "-h"])
# Reboot
subprocess.run(["shutdown", "-r"])  # Option 1
subprocess.run("reboot")            # Option 2

You can also schedule the operation by doing:

# Reboot in...
subprocess.run(["shutdown", "-r", "+1"], shell=True)   # 1 minute
subprocess.run(["shutdown", "-r", "+5"], shell=True)   # 5 minutes
subprocess.run(["shutdown", "-r", "now"], shell=True)  # Immediately

Or specify a specific time:

# Shut down at 16:30
subprocess.run(["shutdown", "-h", "16:30"], shell=True)

The shell=True option allows you to press CTRL + C (or CTRL + D) to abort the operation.

Unlike Windows, the logout function depends on the desktop environment your Linux distribution uses. This is why a single command that works across distributions cannot be provided. As an example, in GNOME you can use:

# Sign off
subprocess.run("gnome-session-quit")