Commit 923c8305 authored by Léo-Paul Géneau's avatar Léo-Paul Géneau 👾

Stop daemonizing

Run pim-dm in foreground
parent 61a28c20
...@@ -41,7 +41,7 @@ To interact with the protocol you need to execute the `pim-dm` command. You may ...@@ -41,7 +41,7 @@ To interact with the protocol you need to execute the `pim-dm` command. You may
#### Start protocol process #### Start protocol process
In order to start the protocol you first need to explicitly start it. This will start a daemon process, which will be running in the background. The command is the following: In order to start the protocol you first need to explicitly start it. This will start pim-dm process, which will be running in foreground. The command is the following:
``` ```
sudo pim-dm -start [-mvrf MULTICAST_TABLE_ID] [-uvrf UNICAST_TABLE_ID] sudo pim-dm -start [-mvrf MULTICAST_TABLE_ID] [-uvrf UNICAST_TABLE_ID]
``` ```
...@@ -57,6 +57,14 @@ If `-uvrf` is not defined, the default unicast table id will be used (table id 2 ...@@ -57,6 +57,14 @@ If `-uvrf` is not defined, the default unicast table id will be used (table id 2
After starting the protocol process, if the default multicast table is not used, the following commands (for adding interfaces and listing state) need to have the argument `-mvrf` defined to specify the corresponding daemon process. After starting the protocol process, if the default multicast table is not used, the following commands (for adding interfaces and listing state) need to have the argument `-mvrf` defined to specify the corresponding daemon process.
#### Run in background
To start pim-dm process in background, one can use
```
sudo setsid pim-dm -start [-mvrf MULTICAST_TABLE_ID] [-uvrf UNICAST_TABLE_ID] &
```
or run it with a daemonizing tool (systemd, supervisord, OpenRC ...).
#### Multi daemon support #### Multi daemon support
......
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import argparse, glob, os, signal, socket, sys, traceback
import sys
import time
import glob
import socket
import argparse
import threading
import traceback
import _pickle as pickle import _pickle as pickle
from prettytable import PrettyTable from prettytable import PrettyTable
from pimdm import Main from pimdm import Main
from pimdm.tree import pim_globals from pimdm.tree import pim_globals
from pimdm.daemon.Daemon import Daemon from pimdm.utils import exit
VERSION = "1.4.0" VERSION = "1.4.0"
PROCESS_DIRECTORY = '/var/run/pim-dm'
PROCESS_SOCKET = os.path.join(PROCESS_DIRECTORY, 'pim_uds_socket{}')
PROCESS_LOG_FOLDER = '/var/log/pimdm'
PROCESS_LOG_STDOUT_FILE = os.path.join(PROCESS_LOG_FOLDER, 'stdout{}')
PROCESS_LOG_STDERR_FILE = os.path.join(PROCESS_LOG_FOLDER, 'stderror{}')
def clean_process_dir():
os.remove(process_file_path())
os.remove(process_socket_path())
if not os.listdir(PROCESS_DIRECTORY):
os.rmdir(PROCESS_DIRECTORY)
def client_socket(data_to_send, print_output=True): def client_socket(data_to_send, print_output=True):
# Create a UDS socket # Create a UDS socket
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# Connect the socket to the port where the server is listening # Connect the socket to the port where the server is listening
server_address = pim_globals.DAEMON_SOCKET.format(pim_globals.MULTICAST_TABLE_ID) server_address = PROCESS_SOCKET.format(pim_globals.MULTICAST_TABLE_ID)
#print('connecting to %s' % server_address) #print('connecting to %s' % server_address)
try: try:
sock.connect(server_address) sock.connect(server_address)
...@@ -40,95 +44,70 @@ def client_socket(data_to_send, print_output=True): ...@@ -40,95 +44,70 @@ def client_socket(data_to_send, print_output=True):
#print('closing socket') #print('closing socket')
sock.close() sock.close()
def is_running():
return os.path.exists(process_file_path())
class MyDaemon(Daemon): def main_loop(sock):
def run(self): while True:
Main.main()
server_address = pim_globals.DAEMON_SOCKET.format(pim_globals.MULTICAST_TABLE_ID)
# Make sure the socket does not already exist
try: try:
os.unlink(server_address) connection, client_address = sock.accept()
except OSError: data = connection.recv(256 * 1024)
if os.path.exists(server_address): print(sys.stderr, 'sending data back to the client')
raise print(pickle.loads(data))
args = pickle.loads(data)
if 'ipv4' not in args and 'ipv6' not in args or not (args.ipv4 or args.ipv6):
args.ipv4 = True
args.ipv6 = False
# Create a UDS socket if 'list_interfaces' in args and args.list_interfaces:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) connection.sendall(pickle.dumps(Main.list_enabled_interfaces(ipv4=args.ipv4, ipv6=args.ipv6)))
elif 'list_neighbors' in args and args.list_neighbors:
# Bind the socket to the port connection.sendall(pickle.dumps(Main.list_neighbors(ipv4=args.ipv4, ipv6=args.ipv6)))
sock.bind(server_address) elif 'list_state' in args and args.list_state:
connection.sendall(pickle.dumps(Main.list_state(ipv4=args.ipv4, ipv6=args.ipv6)))
# Listen for incoming connections elif 'add_interface' in args and args.add_interface:
sock.listen(1) Main.add_pim_interface(args.add_interface[0], False, ipv4=args.ipv4, ipv6=args.ipv6)
while True: connection.shutdown(socket.SHUT_RDWR)
try: elif 'add_interface_sr' in args and args.add_interface_sr:
connection, client_address = sock.accept() Main.add_pim_interface(args.add_interface_sr[0], True, ipv4=args.ipv4, ipv6=args.ipv6)
data = connection.recv(256 * 1024) connection.shutdown(socket.SHUT_RDWR)
print(sys.stderr, 'sending data back to the client') elif 'add_interface_igmp' in args and args.add_interface_igmp:
print(pickle.loads(data)) Main.add_membership_interface(interface_name=args.add_interface_igmp[0], ipv4=True, ipv6=False)
args = pickle.loads(data) connection.shutdown(socket.SHUT_RDWR)
if 'ipv4' not in args and 'ipv6' not in args or not (args.ipv4 or args.ipv6): elif 'add_interface_mld' in args and args.add_interface_mld:
args.ipv4 = True Main.add_membership_interface(interface_name=args.add_interface_mld[0], ipv4=False, ipv6=True)
args.ipv6 = False connection.shutdown(socket.SHUT_RDWR)
elif 'remove_interface' in args and args.remove_interface:
if 'list_interfaces' in args and args.list_interfaces: Main.remove_interface(args.remove_interface[0], pim=True, ipv4=args.ipv4, ipv6=args.ipv6)
connection.sendall(pickle.dumps(Main.list_enabled_interfaces(ipv4=args.ipv4, ipv6=args.ipv6))) connection.shutdown(socket.SHUT_RDWR)
elif 'list_neighbors' in args and args.list_neighbors: elif 'remove_interface_igmp' in args and args.remove_interface_igmp:
connection.sendall(pickle.dumps(Main.list_neighbors(ipv4=args.ipv4, ipv6=args.ipv6))) Main.remove_interface(args.remove_interface_igmp[0], membership=True, ipv4=True, ipv6=False)
elif 'list_state' in args and args.list_state: connection.shutdown(socket.SHUT_RDWR)
connection.sendall(pickle.dumps(Main.list_state(ipv4=args.ipv4, ipv6=args.ipv6))) elif 'remove_interface_mld' in args and args.remove_interface_mld:
elif 'add_interface' in args and args.add_interface: Main.remove_interface(args.remove_interface_mld[0], membership=True, ipv4=False, ipv6=True)
Main.add_pim_interface(args.add_interface[0], False, ipv4=args.ipv4, ipv6=args.ipv6) connection.shutdown(socket.SHUT_RDWR)
connection.shutdown(socket.SHUT_RDWR) elif 'list_instances' in args and args.list_instances:
elif 'add_interface_sr' in args and args.add_interface_sr: connection.sendall(pickle.dumps(Main.list_instances()))
Main.add_pim_interface(args.add_interface_sr[0], True, ipv4=args.ipv4, ipv6=args.ipv6) elif 'stop' in args and args.stop:
connection.shutdown(socket.SHUT_RDWR)
elif 'add_interface_igmp' in args and args.add_interface_igmp:
Main.add_membership_interface(interface_name=args.add_interface_igmp[0], ipv4=True, ipv6=False)
connection.shutdown(socket.SHUT_RDWR)
elif 'add_interface_mld' in args and args.add_interface_mld:
Main.add_membership_interface(interface_name=args.add_interface_mld[0], ipv4=False, ipv6=True)
connection.shutdown(socket.SHUT_RDWR)
elif 'remove_interface' in args and args.remove_interface:
Main.remove_interface(args.remove_interface[0], pim=True, ipv4=args.ipv4, ipv6=args.ipv6)
connection.shutdown(socket.SHUT_RDWR)
elif 'remove_interface_igmp' in args and args.remove_interface_igmp:
Main.remove_interface(args.remove_interface_igmp[0], membership=True, ipv4=True, ipv6=False)
connection.shutdown(socket.SHUT_RDWR)
elif 'remove_interface_mld' in args and args.remove_interface_mld:
Main.remove_interface(args.remove_interface_mld[0], membership=True, ipv4=False, ipv6=True)
connection.shutdown(socket.SHUT_RDWR)
elif 'list_instances' in args and args.list_instances:
connection.sendall(pickle.dumps(Main.list_instances()))
elif 'stop' in args and args.stop:
Main.stop()
connection.shutdown(socket.SHUT_RDWR)
break
elif 'test' in args and args.test:
Main.test(args.test[0], args.test[1])
connection.shutdown(socket.SHUT_RDWR)
elif 'config' in args and args.config:
Main.set_config(args.config[0])
connection.shutdown(socket.SHUT_RDWR)
elif 'get_config' in args and args.get_config:
connection.sendall(pickle.dumps(Main.get_config()))
elif 'drop' in args and args.drop:
Main.drop(args.drop[0], int(args.drop[1]))
except Exception as e:
connection.sendall(pickle.dumps(e))
connection.shutdown(socket.SHUT_RDWR) connection.shutdown(socket.SHUT_RDWR)
traceback.print_exc() break
finally: elif 'test' in args and args.test:
# Clean up the connection Main.test(args.test[0], args.test[1])
connection.shutdown(socket.SHUT_RDWR)
elif 'get_config' in args and args.get_config:
connection.sendall(pickle.dumps(Main.get_config()))
elif 'drop' in args and args.drop:
Main.drop(args.drop[0], int(args.drop[1]))
except Exception as e:
connection.sendall(pickle.dumps(e))
connection.shutdown(socket.SHUT_RDWR)
traceback.print_exc()
finally:
# Clean up the connection
if 'connection' in locals():
connection.close() connection.close()
sock.close()
def args_parser():
def main():
"""
Entry point for PIM-DM
"""
parser = argparse.ArgumentParser(description='PIM-DM protocol', prog='pim-dm') parser = argparse.ArgumentParser(description='PIM-DM protocol', prog='pim-dm')
group = parser.add_mutually_exclusive_group(required=True) group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-start", "--start", action="store_true", default=False, help="Start PIM") group.add_argument("-start", "--start", action="store_true", default=False, help="Start PIM")
...@@ -175,76 +154,146 @@ def main(): ...@@ -175,76 +154,146 @@ def main():
metavar='UNICAST_VRF_NUMBER', type=int, metavar='UNICAST_VRF_NUMBER', type=int,
help="Define unicast table id for getting unicast information (RPF checks, RPC costs, ...). " help="Define unicast table id for getting unicast information (RPF checks, RPC costs, ...). "
"This information can only be defined at startup with -start command") "This information can only be defined at startup with -start command")
return parser
def list_instances(args):
t = PrettyTable(['Instance PID', 'Multicast VRF', 'Unicast VRF'])
for multicast_table_id in glob.glob(os.path.join(PROCESS_DIRECTORY, '*')):
pim_globals.MULTICAST_TABLE_ID = multicast_table_id
t_new = client_socket(args, print_output=False)
t.add_row(t_new.split("|"))
print(t)
def run_config(conf_file_path):
try:
from pimdm import Config
pim_globals.MULTICAST_TABLE_ID, pim_globals.UNICAST_TABLE_ID = Config.get_vrfs(conf_file_path)
start(conf_file_path)
except ModuleNotFoundError:
print("PYYAML needs to be installed. Execute \"pip3 install pyyaml\"")
sys.exit(0)
except ImportError:
print("PYYAML needs to be installed. Execute \"pip3 install pyyaml\"")
sys.exit(0)
def print_multicast_routes(args):
if args.ipv4 or not args.ipv6:
os.system("ip mroute show table " + str(pim_globals.MULTICAST_TABLE_ID))
elif args.ipv6:
os.system("ip -6 mroute show table " + str(pim_globals.MULTICAST_TABLE_ID))
def main():
"""
Entry point for PIM-DM
"""
parser = args_parser()
args = parser.parse_args() args = parser.parse_args()
#print(parser.parse_args())
# This script must be run as root! # This script must be run as root!
if os.geteuid() != 0: if os.geteuid() != 0:
sys.exit('PIM-DM must be run as root!') sys.exit('PIM-DM must be run as root!')
if args.list_instances: if args.list_instances:
pid_files = glob.glob("/tmp/Daemon-pim*.pid") list_instances(args)
t = PrettyTable(['Instance PID', 'Multicast VRF', 'Unicast VRF'])
for pid_file in pid_files:
d = MyDaemon(pid_file)
pim_globals.MULTICAST_TABLE_ID = pid_file[15:-4]
if not d.is_running():
continue
t_new = client_socket(args, print_output=False)
t.add_row(t_new.split("|"))
print(t)
return return
pim_globals.MULTICAST_TABLE_ID = args.multicast_vrf[0] pim_globals.MULTICAST_TABLE_ID = args.multicast_vrf[0]
pim_globals.UNICAST_TABLE_ID = args.unicast_vrf[0] pim_globals.UNICAST_TABLE_ID = args.unicast_vrf[0]
daemon = MyDaemon(pim_globals.DAEMON_PROCESS_FILE.format(pim_globals.MULTICAST_TABLE_ID))
if args.start: if args.start:
print("start") start()
daemon.start()
sys.exit(0) sys.exit(0)
elif args.stop: elif args.stop:
client_socket(args) client_socket(args)
daemon.stop()
sys.exit(0) sys.exit(0)
elif args.config: elif args.config:
try: run_config(os.path.abspath(args.config[0]))
from pimdm import Config
args.config[0] = os.path.abspath(args.config[0])
pim_globals.MULTICAST_TABLE_ID, pim_globals.UNICAST_TABLE_ID = Config.get_vrfs(args.config[0])
daemon = MyDaemon(pim_globals.DAEMON_PROCESS_FILE.format(pim_globals.MULTICAST_TABLE_ID))
if not daemon.is_running():
x = threading.Thread(target=daemon.start, args=())
x.start()
x.join()
while not daemon.is_running():
time.sleep(1)
except ModuleNotFoundError:
print("PYYAML needs to be installed. Execute \"pip3 install pyyaml\"")
sys.exit(0)
except ImportError:
print("PYYAML needs to be installed. Execute \"pip3 install pyyaml\"")
sys.exit(0)
elif args.verbose: elif args.verbose:
os.system("tail -f {}".format(pim_globals.DAEMON_LOG_STDOUT_FILE.format(pim_globals.MULTICAST_TABLE_ID))) os.system("tail -f {}".format(PROCESS_LOG_STDOUT_FILE.format(pim_globals.MULTICAST_TABLE_ID)))
sys.exit(0) sys.exit(0)
elif args.multicast_routes: elif args.multicast_routes:
if args.ipv4 or not args.ipv6: print_multicast_routes(args)
os.system("ip mroute show table " + str(pim_globals.MULTICAST_TABLE_ID))
elif args.ipv6:
os.system("ip -6 mroute show table " + str(pim_globals.MULTICAST_TABLE_ID))
sys.exit(0) sys.exit(0)
elif not daemon.is_running(): elif not is_running():
print("PIM-DM is not running") print("PIM-DM is not running")
parser.print_usage() parser.print_usage()
sys.exit(0) sys.exit(0)
client_socket(args) client_socket(args)
def process_file_path():
return os.path.join(PROCESS_DIRECTORY, str(pim_globals.MULTICAST_TABLE_ID))
def process_socket_path():
return PROCESS_SOCKET.format(pim_globals.MULTICAST_TABLE_ID)
def get_server_address():
server_address = process_socket_path()
# Make sure the socket does not already exist
if os.path.exists(server_address):
raise Exception(server_address + ' already exists !')
return server_address
def exit_main(cleanup):
exit.acquire(0)
while cleanup:
try:
cleanup.pop()()
except Exception:
traceback.print_exc()
exit.release()
def start(conf_file_path=None):
exit.signal(0, signal.SIGINT, signal.SIGTERM)
process_file = process_file_path()
if is_running():
sys.stderr.write(process_file + ' exists. Process already running ?\n')
sys.exit(1)
cleanup = [clean_process_dir]
try:
os.makedirs(PROCESS_DIRECTORY, exist_ok=True)
os.mknod(process_file)
os.makedirs(PROCESS_LOG_FOLDER, exist_ok=True)
os.chdir(PROCESS_LOG_FOLDER)
os.umask(0)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
so = open(PROCESS_LOG_STDOUT_FILE.format(pim_globals.MULTICAST_TABLE_ID), 'a+')
cleanup.append(so.close)
se = open(PROCESS_LOG_STDERR_FILE.format(pim_globals.MULTICAST_TABLE_ID), 'a+')
cleanup.append(se.close)
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
print("start")
cleanup.insert(0, Main.stop)
Main.main()
if conf_file_path:
Main.set_config(conf_file_path)
# Create a UDS socket
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
cleanup.insert(0, sock.close)
# Bind the socket to the port
sock.bind(get_server_address())
# Listen for incoming connections
sock.listen(1)
main_loop(sock)
finally:
exit_main(cleanup)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
"""Generic linux daemon base class for python 3.x."""
import sys, os, time, atexit, signal
from pimdm.tree import pim_globals
class Daemon:
"""A generic Daemon class.
Usage: subclass the Daemon class and override the run() method."""
def __init__(self, pidfile): self.pidfile = pidfile
def daemonize(self):
"""Deamonize class. UNIX double fork mechanism."""
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as err:
sys.stderr.write('fork #1 failed: {0}\n'.format(err))
sys.exit(1)
# decouple from parent environment
os.makedirs('/var/log/pimdm/', exist_ok=True)
os.chdir('/var/log/pimdm/')
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError as err:
sys.stderr.write('fork #2 failed: {0}\n'.format(err))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = open(os.devnull, 'r')
so = open('stdout' + str(pim_globals.MULTICAST_TABLE_ID), 'a+')
se = open('stderror' + str(pim_globals.MULTICAST_TABLE_ID), 'a+')
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())
with open(self.pidfile, 'w+') as f:
f.write(pid + '\n')
def delpid(self):
os.remove(self.pidfile)
def start(self):
"""Start the Daemon."""
# Check for a pidfile to see if the Daemon already runs
if self.is_running():
message = "pidfile {0} already exist. " + \
"Daemon already running?\n"
sys.stderr.write(message.format(self.pidfile))
sys.exit(1)
# Start the Daemon
self.daemonize()
self.run()
def stop(self):
"""Stop the Daemon."""
# Get the pid from the pidfile
try:
with open(self.pidfile, 'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
if not pid:
message = "pidfile {0} does not exist. " + \
"Daemon not running?\n"
sys.stderr.write(message.format(self.pidfile))
return # not an error in a restart
# Try killing the Daemon process
try:
while 1:
#os.killpg(os.getpgid(pid), signal.SIGTERM)
os.kill(pid, signal.SIGTERM)
time.sleep(0.1)
except OSError as err:
e = str(err.args)
if e.find("No such process") > 0:
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
print(str(err.args))
sys.exit(1)
def run(self):
"""You should override this method when you subclass Daemon.
It will be called after the process has been daemonized by
start() or restart()."""
def is_running(self):
try:
with open(self.pidfile, 'r') as pf:
pid = int(pf.read().strip())
except IOError:
return False
""" Check For the existence of a unix pid. """
try:
os.kill(pid, 0)
return True
except:
return False
# Protocol files
DAEMON_PROCESS_FILE = '/tmp/Daemon-pim{}.pid'
DAEMON_SOCKET = '/tmp/pim_uds_socket{}'
DAEMON_LOG_FOLDER = '/var/log/pimdm/'
DAEMON_LOG_STDOUT_FILE = DAEMON_LOG_FOLDER + 'stdout{}'
# PIM-DM TIMER VARIABLES # PIM-DM TIMER VARIABLES
ASSERT_TIME = 180 ASSERT_TIME = 180
GRAFT_RETRY_PERIOD = 3 GRAFT_RETRY_PERIOD = 3
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment