Skip to content
Browse files

Implement background daemon support

  • Loading branch information...
1 parent 727bbbb commit f970e9ccbd68f6b3b9f1344b7e5f6beefed472ed @joneskoo committed
Showing with 95 additions and 11 deletions.
  1. +95 −11 batteryinfo.py
View
106 batteryinfo.py
@@ -1,16 +1,17 @@
#!/usr/bin/env python3
-import os.path
+import os
import plistlib
+import sys
import sqlite3
import time
-
from optparse import OptionParser
from subprocess import call
from tempfile import NamedTemporaryFile
# DBFILE = $HOME/.batterylogx.sqlite
DBFILE = os.path.join(os.environ['HOME'], '.batterylogx.sqlite')
+PIDFILE = DBFILE.replace('sqlite', 'pid')
# Initialize database
conn = sqlite3.connect(DBFILE, isolation_level=None)
c = conn.cursor()
@@ -54,6 +55,79 @@ def log_data(verbose=False):
print("Voltage: %d mV Amperage %d mA Capacity %d mAh" % (
d['voltage'], d['amperage'], d['capacity']))
+def get_daemon_pid():
+ '''Get daemon pid from pidfile and return it if it is running'''
+ try:
+ with open(PIDFILE, 'r') as f:
+ try:
+ pid = int(f.readline())
+ os.kill(pid, 0)
+ except ValueError:
+ return None # Invalid pidfile content
+ except OSError:
+ return None # PID not running any more or not owned by us
+ return pid
+ except IOError:
+ return None # File does not exist
+
+def kill_daemon():
+ '''Kill backgrounded daemon process and exit'''
+ pid = get_daemon_pid()
+ if pid:
+ try:
+ print("Killing backgrounded logger (pid %d)" % (pid))
+ os.kill(pid, 15)
+ return True
+ except Exception as e:
+ print("Failed:", str(e))
+ return False
+ else:
+ print("Daemon not running or pidfile missing")
+ return False
+
+def mainloop(verbose):
+ # Create the logging table if it doesn't exist
+ create_table()
+
+ # Main loop
+ while True:
+ log_data(verbose)
+ time.sleep(30)
+
+def daemonize(mainloop, *args):
+ # http://code.activestate.com/recipes/66012/
+ # do the UNIX double-fork magic, see Stevens' "Advanced
+ # Programming in the UNIX Environment" for details (ISBN 0201563177)
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # exit first parent
+ sys.exit(0)
+ except OSError as e:
+ print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
+ sys.exit(1)
+
+ # decouple from parent environment
+ os.chdir("/")
+ os.setsid()
+ os.umask(0)
+
+ # do second fork
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # exit from second parent, print eventual PID before
+ print("PID %d" % pid)
+ with open(PIDFILE, 'w') as f:
+ f.write("%d\n" % pid)
+ sys.exit(0)
+ except OSError as e:
+ print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror)
+ sys.exit(1)
+
+ # start the daemon main loop
+ mainloop(*args)
+
def main():
parser = OptionParser()
parser.add_option("-k", "--kill", dest="kill", action="store_true",
@@ -62,19 +136,29 @@ def main():
default=False, help="Run logger in background")
(options, args) = parser.parse_args()
+
+ if options.kill:
+ if kill_daemon():
+ sys.exit(0) # Killed successfully
+ else:
+ sys.exit(1) # Failed
+
+ pid = get_daemon_pid()
+ if pid:
+ print("Already running (PID %d). Use -k to kill." % pid)
+ sys.exit(1)
+
if options.daemon:
verbose = False
+ print("OK, logging in background to %s." % DBFILE)
+ daemonize(mainloop, verbose)
else:
verbose = True
print("Logging to stdout and %s\n" % DBFILE)
-
- # Create the logging table if it doesn't exist
- create_table()
-
- # Main loop
- while True:
- log_data(verbose)
- time.sleep(30)
+ mainloop(verbose)
if __name__ == '__main__':
- main()
+ try:
+ main()
+ except KeyboardInterrupt:
+ sys.exit(0)

0 comments on commit f970e9c

Please sign in to comment.
Something went wrong with that request. Please try again.