summaryrefslogtreecommitdiff
path: root/pkg/linux/bitmask-root
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/linux/bitmask-root')
-rwxr-xr-xpkg/linux/bitmask-root202
1 files changed, 181 insertions, 21 deletions
diff --git a/pkg/linux/bitmask-root b/pkg/linux/bitmask-root
index c1a2adfd..78503af9 100755
--- a/pkg/linux/bitmask-root
+++ b/pkg/linux/bitmask-root
@@ -32,14 +32,15 @@ The `openvpn start` action is special: it calls exec on openvpn and replaces
the current process.
"""
# TODO should be tested with python3, which can be the default on some distro.
-
from __future__ import print_function
+import atexit
import os
import re
import signal
import socket
import subprocess
import sys
+import time
import traceback
@@ -227,6 +228,135 @@ def get_process_list():
return filter(None, res)
+class Daemon(object):
+ """
+ A generic daemon class.
+ """
+ def __init__(self, pidfile, stdin='/dev/null',
+ stdout='/dev/null', stderr='/dev/null'):
+ self.stdin = stdin
+ self.stdout = stdout
+ self.stderr = stderr
+ self.pidfile = pidfile
+
+ def daemonize(self):
+ """
+ Do the UNIX double-fork magic, see Stevens' "Advanced
+ Programming in the UNIX Environment" for details (ISBN 0201563177)
+ http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
+ """
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # exit first parent
+ sys.exit(0)
+ except OSError, e:
+ sys.stderr.write(
+ "fork #1 failed: %d (%s)\n" % (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
+ sys.exit(0)
+ except OSError, e:
+ sys.stderr.write(
+ "fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
+ sys.exit(1)
+
+ # redirect standard file descriptors
+ sys.stdout.flush()
+ sys.stderr.flush()
+ si = file(self.stdin, 'r')
+ so = file(self.stdout, 'a+')
+ se = file(self.stderr, 'a+', 0)
+ os.dup2(si.fileno(), sys.stdin.fileno())
+ os.dup2(so.fileno(), sys.stdout.fileno())
+ os.dup2(se.fileno(), sys.stderr.fileno())
+
+ # write pidfile
+ atexit.register(self.delpid)
+ pid = str(os.getpid())
+ file(self.pidfile, 'w+').write("%s\n" % pid)
+
+ def delpid(self):
+ """
+ Delete the pidfile.
+ """
+ os.remove(self.pidfile)
+
+ def start(self, *args):
+ """
+ Start the daemon.
+ """
+ # Check for a pidfile to see if the daemon already runs
+ try:
+ pf = file(self.pidfile, 'r')
+ pid = int(pf.read().strip())
+ pf.close()
+ except IOError:
+ pid = None
+
+ if pid:
+ message = "pidfile %s already exist. Daemon already running?\n"
+ sys.stderr.write(message % self.pidfile)
+ sys.exit(1)
+
+ # Start the daemon
+ self.daemonize()
+ self.run(args)
+
+ def stop(self):
+ """
+ Stop the daemon.
+ """
+ # Get the pid from the pidfile
+ try:
+ pf = file(self.pidfile, 'r')
+ pid = int(pf.read().strip())
+ pf.close()
+ except IOError:
+ pid = None
+
+ if not pid:
+ message = "pidfile %s does not exist. Daemon not running?\n"
+ sys.stderr.write(message % self.pidfile)
+ return # not an error in a restart
+
+ # Try killing the daemon process
+ try:
+ while 1:
+ os.kill(pid, signal.SIGTERM)
+ time.sleep(0.1)
+ except OSError, err:
+ err = str(err)
+ if err.find("No such process") > 0:
+ if os.path.exists(self.pidfile):
+ os.remove(self.pidfile)
+ else:
+ print(str(err))
+ sys.exit(1)
+
+ def restart(self):
+ """
+ Restart the daemon.
+ """
+ self.stop()
+ self.start()
+
+ def run(self):
+ """
+ This should be overridden by derived classes.
+ """
+
+
def run(command, *args, **options):
"""
Run an external command.
@@ -383,29 +513,59 @@ def openvpn_stop(args):
##
-def set_dns_nameserver(ip_address):
+class NameserverSetter(Daemon):
"""
- Add the tunnel DNS server to `resolv.conf`
-
- :param ip_address: the ip to add to `resolv.conf`
- :type ip_address: str
+ A daemon that will add leap nameserver inside the tunnel
+ to the system `resolv.conf`
"""
- if os.path.isfile(RESOLVCONF):
- process = run(RESOLVCONF, "-a", "bitmask", input=True)
- process.communicate("nameserver %s\n" % ip_address)
- else:
- bail("ERROR: package openresolv or resolvconf not installed.")
+ def run(self, *args):
+ """
+ Run when daemonized.
+ """
+ if args:
+ ip_address = args[0]
+ self.set_dns_nameserver(ip_address)
+
+ def set_dns_nameserver(self, ip_address):
+ """
+ Add the tunnel DNS server to `resolv.conf`
+
+ :param ip_address: the ip to add to `resolv.conf`
+ :type ip_address: str
+ """
+ if os.path.isfile(RESOLVCONF):
+ process = run(RESOLVCONF, "-a", "bitmask", input=True)
+ process.communicate("nameserver %s\n" % ip_address)
+ else:
+ bail("ERROR: package openresolv or resolvconf not installed.")
+
+nameserver_setter = NameserverSetter('/tmp/leap-dns-up.pid')
-def restore_dns_nameserver():
+
+class NameserverRestorer(Daemon):
"""
- Remove tunnel DNS server from `resolv.conf`
+ A daemon that will restore the previous nameservers.
"""
- if os.path.isfile(RESOLVCONF):
- run(RESOLVCONF, "-d", "bitmask")
- else:
- print("%s: ERROR: package openresolv or resolvconf not installed." %
- (SCRIPT,))
+
+ def run(self):
+ """
+ Run when daemonized.
+ """
+ self.restore_dns_nameserver()
+
+ def restore_dns_nameserver(self):
+ """
+ Remove tunnel DNS server from `resolv.conf`
+ """
+ if os.path.isfile(RESOLVCONF):
+ run(RESOLVCONF, "-d", "bitmask")
+ else:
+ print("%s: ERROR: package openresolv "
+ "or resolvconf not installed." %
+ (SCRIPT,))
+
+nameserver_restorer = NameserverRestorer('/tmp/leap-dns-down.pid')
##
@@ -619,16 +779,16 @@ def main():
elif command == "firewall_start":
try:
firewall_start(args)
- set_dns_nameserver(NAMESERVER)
+ nameserver_setter.start(NAMESERVER)
except Exception as ex:
- restore_dns_nameserver()
+ nameserver_restorer.start()
firewall_stop()
bail("ERROR: could not start firewall", ex)
elif command == "firewall_stop":
try:
- restore_dns_nameserver()
firewall_stop()
+ nameserver_restorer.start()
except Exception as ex:
bail("ERROR: could not stop firewall", ex)