Python detect linux shutdown and run a command before shutting down
When linux is shut down, all processes receive SIGTERM
and if they won't terminate after some timeout they are killed with SIGKILL
. You can implement a signal handler to properly shutdown your application using the signal
module. systemd
(opposed to upstart
in earlier Ubuntu verions) additionally sends SIGHUP
on shutdown.
To verfiy that this actually works, I tried the following script on two Ubuntu VMs (12.04 and 16.04). The system waits for 10s (12.04/upstart) or 90s (16.04/systemd) before issuing SIGKILL
.
The script ignores SIGHUP
(which would otherwise also kill the process ungracefully) and will continuously print the time since the SIGTERM
signal has been received to a text file.
Note I used disown
(built-in bash command) to detach the process from the terminal.
python signaltest.py &
disown
signaltest.py
import signal
import time
stopped = False
out = open('log.txt', 'w')
def stop(sig, frame):
global stopped
stopped = True
out.write('caught SIGTERM\n')
out.flush()
def ignore(sig, frsma):
out.write('ignoring signal %d\n' % sig)
out.flush()
signal.signal(signal.SIGTERM, stop)
signal.signal(signal.SIGHUP, ignore)
while not stopped:
out.write('running\n')
out.flush()
time.sleep(1)
stop_time = time.time()
while True:
out.write('%.4fs after stop\n' % (time.time() - stop_time))
out.flush()
time.sleep(0.1)
The last line printed into log.txt
was:
10.1990s after stop
for 12.04 and
90.2448s after stop
for 16.04.
shutting down computer (linux) using python
Many of the linux distributions out there require super user privileges to execute shutdown
or halt
, but then, how come that if you're sitting on your computer you can power it off without being root? You open a menu, hit Shutdown and it shutdowns without you becoming root
, right?
Well... the rationale behind this is that if you have physical access to the computer, you could pretty much pull the power cord and power it off anyways, so nowadays, many distributions allow power-off though access to the local System Bus accessible through dbus
. Problem with dbus
(or the services exposed through it, rather)? It's constantly changing. I'd recommend installing a dbus viewer tool such as D-feet (be advised: it's still pretty hard to visualize, but it may help)
Take a look to these Dbus shutdown scripts.
If you still have HAL in your distrubution (is on the way to being deprecated) try this:
import dbus
sys_bus = dbus.SystemBus()
hal_srvc = sys_bus.get_object('org.freedesktop.Hal',
'/org/freedesktop/Hal/devices/computer')
pwr_mgmt = dbus.Interface(hal_srvc,
'org.freedesktop.Hal.Device.SystemPowerManagement')
shutdown_method = pwr_mgmt.get_dbus_method("Shutdown")
shutdown_method()
This works on a Ubuntu 12.04 (I just powered off my computer to make sure it worked). If you have something newer... well, it may not work. It's the downside of this method: it is very distribution specific.
You might have to install the dbus-python
package for this to work (http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html)
UPDATE 1:
I've been doing a little bit of research and it looks like this is done in newer Ubuntu versions through ConsoleKit. I've tested the code below in my Ubuntu 12.04 (which has the deprecated HAL and the newer ConsoleKit) and it did shut my computer off:
>>> import dbus
>>> sys_bus = dbus.SystemBus()
>>> ck_srv = sys_bus.get_object('org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager')
>>> ck_iface = dbus.Interface(ck_srv, 'org.freedesktop.ConsoleKit.Manager')
>>> stop_method = ck_iface.get_dbus_method("Stop")
>>> stop_method()
UPDATE 2:
Probably why can you do this without being root
deserves a bit of a wider explanation. Let's focus on the newer ConsoleKit
(HAL
is way more complicated and messy, IMHO).
The ConsoleKit
is a service running as root
in your system:
borrajax@borrajax:/tmp$ ps aux|grep console-kit
root 1590 0.0 0.0 1043056 3876 ? Sl Dec05 0:00 /usr/sbin/console-kit-daemon --no-daemon
Now, d-bus
is just a message passing system. You have a service, such as ConsoleKit that exposes an interface to d-bus
. One of the methods exposed is the Stop
(shown above). ConsoleKit's permissions are controlled with PolKit, which (despite on being based on regular Linux permissions) offers a bit of a finer grain of control for "who can do what". For instance, PolKit can say things like "If the user is logged into the computer, then allow him to do something. If it's remotely connected, then don't.". If PolKit determines that your user is allowed to call ConsoleKit's Stop
method, that request will be passed by (or through) d-bus
to ConsoleKit (which will subsequently shutdown your computer because it can... because it worth's it... because it's root
)
Further reading:
- What are ConsoleKit and PolicyKit? How do they work?
- ArchWiki PolKit
To summarize: You can't switch a computer off without being root
. But you can tell a service that is running as root
to shutdown the system for you.
UPDATE 3:
On December 2021, seven years after the original answer was written I had to do this again. This time, in a Ubuntu 18.04.
Unsurprisingly, things seem to have changed a bit:
- The PowerOff functionality seems to be handled via a new
org.freedesktop.login1
service, which is part of the """new""" (cough! cough!) SystemD machinery. - The
dbus
Python package seems to have been deprecated and/or considered "legacy". There is, however, a new PyDbus library to be used instead.
So we can still power off machines with an unprivileged script:
#! /usr/bin/python3
from pydbus import SystemBus
bus = SystemBus()
proxy = bus.get('org.freedesktop.login1', '/org/freedesktop/login1')
if proxy.CanPowerOff() == 'yes':
proxy.PowerOff(False) # False for 'NOT interactive'
Update 3.1:
It looks like it's not as new as I thought X-D
There's already an answer by @Roeften in this very same thread.
BONUS:
I read in one of your comments that you wanna switch the computer off after a time consuming task to prevent it from overheating... Did you know that you can probably power it on at a given time using RTC? (See this and this) Pretty cool, uh? (I got so excited when I found out I could do this... ) :-D
How to detect pending system shutdown on Linux?
There is no way to determine if a SIGTERM
is a part of a shutdown sequence. To detect a shutdown sequence you can either use use rc.d
scripts like ereOn and Eric Sepanson suggested or use mechanisms like DBus.
However, from a design point of view it makes no sense to ignore SIGTERM
even if it is not part of a shutdown. SIGTERM
's primary purpose is to politely ask apps to exit cleanly and it is not likely that someone with enough privileges will issue a SIGTERM
if he/she does not want the app to exit.
Shell script to run task and shutdown after task is done
What you have in your example should actually only shutdown once the python command has completed, unless the python program forks or backgrounds early.
Another way to run it would be to make the shutdown conditional upon the success of the first command
python command && sudo shutdown -h now
Of course this still will not help you if the python program does anything like forking or daemonizing. Simply try running the python script alone and take note if control returns immediately to the console or not.
Executing code on server while shutting down the computer
You can use screen
to maintain a session and achieve the goal that allows you to run the code on the server.
Refer to this: https://www.gnu.org/software/screen/
Some basic commands:
- Create session named RunWork
screen -S RunWork
- List all sessions:
screen -ls
- Open a session
screen -r SessionID
...
Fast system shutdown in Python
Using System Request (SysRq) and than calling echo b > /proc/sysrq-trigger
to immediately reboot the system, without unmounting or syncing filesystems.
import os
os.system('echo 1 > /proc/sys/kernel/sysrq && echo b > /proc/sysrq-trigger')
Check this answer for more info:
https://unix.stackexchange.com/questions/163370/is-there-a-fastest-way-to-shutdown-the-system
Related Topics
How to Declare Custom Exceptions in Modern Python
How to Convert Index of a Pandas Dataframe into a Column
Sending a Password Over Ssh or Scp with Subprocess.Popen
Total Memory Used by Python Process
How to Sort Pandas Dataframe from One Column
How to Extract a Floating Number from a String
How to Join Two Dataframes For Which Column Values Are Within a Certain Range
How to Import the Class Within the Same Directory or Sub Directory
Check If All Elements in a List Are Identical
Accessing Dict Keys Like an Attribute
Find Nearest Value in Numpy Array
Python Program with Notification in Gnome Shell Doesn't Work
Modulenotfounderror: No Module Named 'Pydip', Although It's Installed
How to Sudo the Current Process
Copy Data from the Clipboard on Linux, MAC and Windows with a Single Python Script