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

Stop daemonizing

Run pim-dm in foreground
parent 37f3bf61
......@@ -41,7 +41,7 @@ To interact with the protocol you need to execute the `pim-dm` command. You may
#### 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]
```
......@@ -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.
#### 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
......
#!/usr/bin/env python3
import os
import sys
import time
import glob
import socket
import argparse
import threading
import traceback
import argparse, glob, os, signal, socket, sys, traceback
import _pickle as pickle
from prettytable import PrettyTable
from pimdm import Main
from pimdm.tree import pim_globals
from pimdm.daemon.Daemon import Daemon
from pimdm.utils import exit
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):
# Create a UDS socket
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# 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)
try:
sock.connect(server_address)
......@@ -40,27 +44,10 @@ def client_socket(data_to_send, print_output=True):
#print('closing socket')
sock.close()
def is_running():
return os.path.exists(process_file_path())
class MyDaemon(Daemon):
def run(self):
Main.main()
server_address = pim_globals.DAEMON_SOCKET.format(pim_globals.MULTICAST_TABLE_ID)
# Make sure the socket does not already exist
try:
os.unlink(server_address)
except OSError:
if os.path.exists(server_address):
raise
# Create a UDS socket
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# Bind the socket to the port
sock.bind(server_address)
# Listen for incoming connections
sock.listen(1)
def main_loop(sock):
while True:
try:
connection, client_address = sock.accept()
......@@ -102,15 +89,11 @@ class MyDaemon(Daemon):
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:
......@@ -121,14 +104,10 @@ class MyDaemon(Daemon):
traceback.print_exc()
finally:
# Clean up the connection
if 'connection' in locals():
connection.close()
sock.close()
def main():
"""
Entry point for PIM-DM
"""
def args_parser():
parser = argparse.ArgumentParser(description='PIM-DM protocol', prog='pim-dm')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-start", "--start", action="store_true", default=False, help="Start PIM")
......@@ -175,76 +154,146 @@ def main():
metavar='UNICAST_VRF_NUMBER', type=int,
help="Define unicast table id for getting unicast information (RPF checks, RPC costs, ...). "
"This information can only be defined at startup with -start command")
args = parser.parse_args()
return parser
#print(parser.parse_args())
# This script must be run as root!
if os.geteuid() != 0:
sys.exit('PIM-DM must be run as root!')
if args.list_instances:
pid_files = glob.glob("/tmp/Daemon-pim*.pid")
def 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
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()
# This script must be run as root!
if os.geteuid() != 0:
sys.exit('PIM-DM must be run as root!')
if args.list_instances:
list_instances(args)
return
pim_globals.MULTICAST_TABLE_ID = args.multicast_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:
print("start")
daemon.start()
start()
sys.exit(0)
elif args.stop:
client_socket(args)
daemon.stop()
sys.exit(0)
elif args.config:
try:
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)
run_config(os.path.abspath(args.config[0]))
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)
elif args.multicast_routes:
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))
print_multicast_routes(args)
sys.exit(0)
elif not daemon.is_running():
elif not is_running():
print("PIM-DM is not running")
parser.print_usage()
sys.exit(0)
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__":
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
ASSERT_TIME = 180
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