| #!/usr/bin/env python | 
 |  | 
 | from subprocess import call | 
 | import sys | 
 | import subprocess | 
 | import dbus | 
 | import string | 
 | import socket | 
 | import os | 
 | import fcntl | 
 | import time | 
 | import glib | 
 | import gobject | 
 | import dbus.service | 
 | import dbus.mainloop.glib | 
 |  | 
 | DBUS_NAME = 'org.openbmc.LogManager' | 
 | ERRL_INTF_NAME = 'org.openbmc.Errl' | 
 | SRVC_INTF_NAME = 'org.openbmc.Service' | 
 | OBJ_NAME_RSYSLOG = '/org/openbmc/LogManager/rsyslog' | 
 |  | 
 | ''' | 
 |     Object Path > /org/openbmc/LogManager/rsyslog | 
 |         Interface:Method > org.openbmc.Service.Enable dict:string:string | 
 |         Interface:Method > org.openbmc.Service.Disable | 
 | ''' | 
 |  | 
 | class JournalUtils (): | 
 |     def _isvalidip(self, family, ipaddr): | 
 |         if family == socket.AF_INET: | 
 |             try: | 
 |                 socket.inet_pton(socket.AF_INET, ipaddr) | 
 |             except AttributeError:  # no inet_pton here, sorry | 
 |                 try: | 
 |                     socket.inet_aton(ipaddr) | 
 |                 except socket.error: | 
 |                     return False | 
 |                 return ipaddr.count('.') == 3 | 
 |             except socket.error:  # not a valid address | 
 |                 return False | 
 |  | 
 |             return True | 
 |  | 
 |         elif family == socket.AF_INET6: | 
 |             try: | 
 |                 socket.inet_pton(socket.AF_INET6, ipaddr) | 
 |             except socket.error:  # not a valid address | 
 |                 return False | 
 |             return True | 
 |  | 
 |         else: return False | 
 |  | 
 | class Rsyslog (dbus.service.Object): | 
 |     def __init__(self, bus, name): | 
 |         self.bus = bus | 
 |         self.name = name | 
 |         dbus.service.Object.__init__(self,bus,name) | 
 |  | 
 |     @dbus.service.method(dbus.PROPERTIES_IFACE, "ss", "v") | 
 |     def Get(self, iface, ppty): | 
 |         return self.GetAll(iface)[ppty] | 
 |  | 
 |     @dbus.service.method(dbus.PROPERTIES_IFACE, 's', 'a{sv}') | 
 |     def GetAll(self, iface): | 
 |         if iface == ERRL_INTF_NAME: | 
 |             status, remote_ip, remote_port = self.Status() | 
 |             return {'status': status, 'ipaddr': remote_ip, 'port': remote_port } | 
 |         else: | 
 |             raise dbus.exceptions.DBusException('org.openbmc.UnknownInterface', | 
 |                                 'This object does not implement the %s interface' % iface) | 
 |  | 
 |     @dbus.service.method(SRVC_INTF_NAME, "a{sv}", "x") | 
 |     def Enable (self, argv_dict): | 
 |         remote_ip = "" | 
 |         remote_port = 0 | 
 |  | 
 |         params = len (argv_dict) | 
 |         if params > 2 : ValueError("Invalid Parameters") | 
 |  | 
 |         for property_name in argv_dict: | 
 |             if property_name == "ipaddr": | 
 |                 remote_ip = argv_dict [property_name] | 
 |             elif property_name == "port": | 
 |                 remote_port = argv_dict [property_name] | 
 |             else:  | 
 |                 raise ValueError("Invalid Argument: IP Address/Port expected.") | 
 |  | 
 |         if not remote_ip:  | 
 |             cur_remote = self._GetConfig ('Remote') | 
 |             if not cur_remote:  | 
 |                 raise ValueError("Invalid Remote Syslog IP Address") | 
 |             else: | 
 |                 cur_remote = cur_remote[3:] | 
 |                 remote_ip, port_str = cur_remote.split (":") | 
 |                 remote_port = int(port_str) | 
 |         if not util._isvalidip (socket.AF_INET, remote_ip): raise ValueError, "Malformed IP Address" | 
 |         if not remote_port : remote_port = 514 | 
 |         if remote_port > 65535 : raise ValueError("Invalid Remote Syslog Port") | 
 |          | 
 |         remote_addr = remote_ip + ":" + str(remote_port) | 
 |         r = self._ModifyService('Remote', remote_addr) | 
 |  | 
 |         cur_options = self._GetConfig ('Options') | 
 |         new_options = self._GetOptions() | 
 |  | 
 |         if cur_options != new_options: | 
 |             r = self._ModifyService('Options', new_options) | 
 |             r = self._RestartService () | 
 |  | 
 |         return r | 
 |  | 
 |     @dbus.service.method(SRVC_INTF_NAME, "as", "x") | 
 |     def Disable (self, argv_list): | 
 |         params = len (argv_list) | 
 |         if params : ValueError("Invalid Parameters") | 
 |  | 
 |         remote = self._GetConfig ('Remote') | 
 |         if not remote : return 0 | 
 |  | 
 |         r = self._ModifyService('Options', '-C') # FIXME: Restore current options minus the remote. | 
 |         r = self._RestartService () | 
 |         return r | 
 |  | 
 |     def Status (self): | 
 |         remote = self._GetConfig ('Remote') | 
 |         if not remote : return ("Disabled", "0.0.0.0", 0) | 
 |  | 
 |         cur_remote = remote[3:] | 
 |         remote_ip, remote_port = cur_remote.split (":") | 
 |          | 
 |         options = self._GetConfig ('Options') | 
 |         if not options : return ("Disabled", remote_ip, remote_port) | 
 |  | 
 |         if remote in options : return ("Enabled", remote_ip, remote_port) | 
 |  | 
 |         return ("Disabled", remote_ip, remote_port) | 
 |  | 
 |     def _ModifyService (self, opt, val): | 
 |         if not os.path.isfile(syslog_service_bbx_file): | 
 |             r = call (["cp", syslog_service_lib_file, syslog_service_bbx_file]) | 
 |             r = call (["ln", "-s", syslog_service_bbx_file, syslog_service_cfg_file]) | 
 |  | 
 |         if not os.path.isfile(syslog_service_env_file): | 
 |             env_file = open(syslog_service_env_file, 'w') | 
 |             env_file.write ("OPTIONS=\"-C\"") | 
 |             env_file.close() | 
 |  | 
 |         if opt not in OptionKeys: raise ValueError("Invalid Option") | 
 |  | 
 |         self._ModifyParam (opt, val) | 
 |  | 
 |         return 0 | 
 |  | 
 |     def _StopService (self): | 
 |         r = call (["systemctl", "stop", "syslog"]) | 
 |         r = call (["systemctl", "--no-reload", "kill", "syslog"]) | 
 |         return r | 
 |  | 
 |     def _StartService (self): | 
 |         r = call (["systemctl", "daemon-reload"]) | 
 |         r = call (["systemctl", "start", "syslog"]) | 
 |         return r | 
 |  | 
 |     def _RestartService (self): | 
 |         r = self._StopService() | 
 |         r = self._StartService() | 
 |         return r | 
 |      | 
 |     def _ModifyParam (self, opt, val): | 
 |         env_file = open(syslog_service_env_file, 'r')  | 
 |         tmp_file = open(syslog_service_tmp_file, 'w') | 
 |  | 
 |         optkey = OptionKeySwitchMap [opt]['key'] | 
 |         for line in env_file: | 
 |             if line[0] == '#':  | 
 |                 tmp_file.write(line) | 
 |                 continue | 
 |             curkey = line.strip().split ("=")[0] | 
 |             if curkey != optkey :  | 
 |                 tmp_file.write(line) | 
 |  | 
 |         tmp_file.write(optkey + "=\""  + OptionKeySwitchMap[opt]['switch'] + val + "\"" + "\n") | 
 |  | 
 |         env_file.close () | 
 |         tmp_file.close () | 
 |  | 
 |         r = call (["cp", syslog_service_tmp_file, syslog_service_env_file]) | 
 |         return r | 
 |  | 
 |     def _GetConfig (self, opt): | 
 |         with open(syslog_service_env_file, "r") as f: | 
 |             for line in f: | 
 |                 if line[0] == '#': continue | 
 |                 config = line.split ("=") | 
 |                 var = config [0] | 
 |                 if var == OptionKeySwitchMap[opt]['key']: | 
 |                     val = config [1] | 
 |                     val = val[1:-2] # FIXME: Why is there a trailing space ??? | 
 |                     return val | 
 |         return "" | 
 |  | 
 |     def _GetOptions(self): | 
 |         cfg = {} | 
 |         i = 0 | 
 |  | 
 |         for opt in OptionKeys: | 
 |             if opt == 'Options' : continue | 
 |             cfg [i] = self._GetConfig(opt) | 
 |             i+=1 | 
 |              | 
 |         options = '' | 
 |         j = 0 | 
 |         while j<i-1: | 
 |             if cfg[j] : options += cfg [j] | 
 |             j+=1 | 
 |  | 
 |         return options | 
 |  | 
 | def main(): | 
 |     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) | 
 |     bus = dbus.SystemBus() | 
 |     name = dbus.service.BusName(DBUS_NAME, bus) | 
 |  | 
 |     global util | 
 |     global rsys | 
 |     global syslog_service_lib_file | 
 |     global syslog_service_bbx_file | 
 |     global syslog_service_cfg_file | 
 |     global syslog_service_env_file | 
 |     global syslog_service_tmp_file | 
 |     global OptionKeys | 
 |     global OptionKeySwitchMap | 
 |  | 
 |     OptionKeys = ['Options', 'Outfile', 'Priority', 'Smaller', 'RotateSize', 'RotateNum', 'Remote', 'LocalAndNet', 'DropDup', 'SharedMem', 'ConfFile', 'MarkTime', 'Printk'] | 
 |     OptionKeySwitchMap = { | 
 |         'Options'       : { 'switch' : "",    'key' : "OPTIONS" }, | 
 |         'Outfile'       : { 'switch' : "-O ", 'key' : "OBMC_SYSLOG_OUTFILE" }, | 
 |         'Priority'      : { 'switch' : "-O ", 'key' : "OBMC_SYSLOG_PRIORITY" }, | 
 |         'Smaller'       : { 'switch' : "-S ", 'key' : "OBMC_SYSLOG_SMALLER" }, | 
 |         'RotateSize'    : { 'switch' : "-s ", 'key' : "OBMC_SYSLOG_ROTATESIZE" }, | 
 |         'RotateNum'     : { 'switch' : "-b ", 'key' : "OBMC_SYSLOG_ROTATENUM" }, | 
 |         'Remote'        : { 'switch' : "-R ", 'key' : "OBMC_SYSLOG_REMOTE" }, | 
 |         'LocalAndNet'   : { 'switch' : "-L ", 'key' : "OBMC_SYSLOG_LOCALNET" }, | 
 |         'DropDup'       : { 'switch' : "-D ", 'key' : "OBMC_SYSLOG_DROPDUP" }, | 
 |         'SharedMem'     : { 'switch' : "-C ", 'key' : "OBMC_SYSLOG_SHAREDMEM" }, | 
 |         'ConfFile'      : { 'switch' : "-f ", 'key' : "OBMC_SYSLOG_CONFFILE" }, | 
 |         'MarkTime'      : { 'switch' : "-m ", 'key' : "OBMC_SYSLOG_MARKTIME" }, | 
 |         'Printk'        : { 'switch' : "-K ", 'key' : "OBMC_SYSLOG_PRINTK" } | 
 |     } | 
 |  | 
 |     syslog_service_lib_file = '/lib/systemd/system/busybox-syslog.service' | 
 |     syslog_service_bbx_file = '/etc/systemd/system/busybox-syslog.service' | 
 |     syslog_service_cfg_file = '/etc/systemd/system/syslog.service' | 
 |     syslog_service_env_file = '/etc/default/busybox-syslog' | 
 |     syslog_service_tmp_file = '/tmp/busybox-syslog.tmp' | 
 |  | 
 |     util    = JournalUtils () | 
 |     rsys    = Rsyslog (bus, OBJ_NAME_RSYSLOG) | 
 |  | 
 |     mainloop = gobject.MainLoop() | 
 |     print("Started") | 
 |     mainloop.run() | 
 |  | 
 | if __name__ == '__main__': | 
 |     sys.exit(main()) | 
 |  |