Hariharasubramanian R | 0ae14c7 | 2016-04-01 07:29:39 -0500 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | |
| 3 | from subprocess import call |
| 4 | import sys |
| 5 | import subprocess |
| 6 | import dbus |
| 7 | import string |
| 8 | import socket |
| 9 | import os |
| 10 | import fcntl |
| 11 | import time |
| 12 | import glib |
| 13 | import gobject |
| 14 | import dbus.service |
| 15 | import dbus.mainloop.glib |
| 16 | |
| 17 | DBUS_NAME = 'org.openbmc.LogManager' |
| 18 | ERRL_INTF_NAME = 'org.openbmc.Errl' |
| 19 | SRVC_INTF_NAME = 'org.openbmc.Service' |
| 20 | OBJ_NAME_RSYSLOG = '/org/openbmc/LogManager/rsyslog' |
| 21 | |
| 22 | ''' |
| 23 | Object Path > /org/openbmc/LogManager/rsyslog |
| 24 | Interface:Method > org.openbmc.Service.Enable dict:string:string |
| 25 | Interface:Method > org.openbmc.Service.Disable |
| 26 | ''' |
| 27 | |
| 28 | class JournalUtils (): |
| 29 | def _isvalidip(self, family, ipaddr): |
| 30 | if family == socket.AF_INET: |
| 31 | try: |
| 32 | socket.inet_pton(socket.AF_INET, ipaddr) |
| 33 | except AttributeError: # no inet_pton here, sorry |
| 34 | try: |
| 35 | socket.inet_aton(ipaddr) |
| 36 | except socket.error: |
| 37 | return False |
| 38 | return ipaddr.count('.') == 3 |
| 39 | except socket.error: # not a valid address |
| 40 | return False |
| 41 | |
| 42 | return True |
| 43 | |
| 44 | elif family == socket.AF_INET6: |
| 45 | try: |
| 46 | socket.inet_pton(socket.AF_INET6, ipaddr) |
| 47 | except socket.error: # not a valid address |
| 48 | return False |
| 49 | return True |
| 50 | |
| 51 | else: return False |
| 52 | |
| 53 | class Rsyslog (dbus.service.Object): |
| 54 | def __init__(self, bus, name): |
| 55 | self.bus = bus |
| 56 | self.name = name |
| 57 | dbus.service.Object.__init__(self,bus,name) |
| 58 | |
| 59 | @dbus.service.method(dbus.PROPERTIES_IFACE, "ss", "v") |
| 60 | def Get(self, iface, ppty): |
| 61 | return self.GetAll(iface)[ppty] |
| 62 | |
| 63 | @dbus.service.method(dbus.PROPERTIES_IFACE, 's', 'a{sv}') |
| 64 | def GetAll(self, iface): |
| 65 | if iface == ERRL_INTF_NAME: |
| 66 | status, remote_ip, remote_port = self.Status() |
| 67 | return {'status': status, 'ipaddr': remote_ip, 'port': remote_port } |
| 68 | else: |
| 69 | raise dbus.exceptions.DBusException('org.openbmc.UnknownInterface', |
| 70 | 'This object does not implement the %s interface' % iface) |
| 71 | |
| 72 | @dbus.service.method(SRVC_INTF_NAME, "a{sv}", "x") |
| 73 | def Enable (self, argv_dict): |
| 74 | remote_ip = "" |
| 75 | remote_port = 0 |
| 76 | |
| 77 | params = len (argv_dict) |
| 78 | if params > 2 : ValueError("Invalid Parameters") |
| 79 | |
| 80 | for property_name in argv_dict: |
| 81 | if property_name == "ipaddr": |
| 82 | remote_ip = argv_dict [property_name] |
| 83 | elif property_name == "port": |
| 84 | remote_port = argv_dict [property_name] |
| 85 | else: |
| 86 | raise ValueError("Invalid Argument: IP Address/Port expected.") |
| 87 | |
| 88 | if not remote_ip: |
| 89 | cur_remote = self._GetConfig ('Remote') |
| 90 | if not cur_remote: |
| 91 | raise ValueError("Invalid Remote Syslog IP Address") |
| 92 | else: |
| 93 | cur_remote = cur_remote[3:] |
| 94 | remote_ip, port_str = cur_remote.split (":") |
| 95 | remote_port = int(port_str) |
| 96 | if not util._isvalidip (socket.AF_INET, remote_ip): raise ValueError, "Malformed IP Address" |
| 97 | if not remote_port : remote_port = 514 |
| 98 | if remote_port > 65535 : raise ValueError("Invalid Remote Syslog Port") |
| 99 | |
| 100 | remote_addr = remote_ip + ":" + str(remote_port) |
| 101 | r = self._ModifyService('Remote', remote_addr) |
| 102 | |
| 103 | cur_options = self._GetConfig ('Options') |
| 104 | new_options = self._GetOptions() |
| 105 | |
| 106 | if cur_options != new_options: |
| 107 | r = self._ModifyService('Options', new_options) |
| 108 | r = self._RestartService () |
| 109 | |
| 110 | return r |
| 111 | |
| 112 | @dbus.service.method(SRVC_INTF_NAME, "as", "x") |
| 113 | def Disable (self, argv_list): |
| 114 | params = len (argv_list) |
| 115 | if params : ValueError("Invalid Parameters") |
| 116 | |
| 117 | remote = self._GetConfig ('Remote') |
| 118 | if not remote : return 0 |
| 119 | |
| 120 | r = self._ModifyService('Options', '-C') # FIXME: Restore current options minus the remote. |
| 121 | r = self._RestartService () |
| 122 | return r |
| 123 | |
| 124 | def Status (self): |
| 125 | remote = self._GetConfig ('Remote') |
| 126 | if not remote : return ("Disabled", "0.0.0.0", 0) |
| 127 | |
| 128 | cur_remote = remote[3:] |
| 129 | remote_ip, remote_port = cur_remote.split (":") |
| 130 | |
| 131 | options = self._GetConfig ('Options') |
| 132 | if not options : return ("Disabled", remote_ip, remote_port) |
| 133 | |
| 134 | if remote in options : return ("Enabled", remote_ip, remote_port) |
| 135 | |
| 136 | return ("Disabled", remote_ip, remote_port) |
| 137 | |
| 138 | def _ModifyService (self, opt, val): |
| 139 | if not os.path.isfile(syslog_service_bbx_file): |
| 140 | r = call (["cp", syslog_service_lib_file, syslog_service_bbx_file]) |
| 141 | r = call (["ln", "-s", syslog_service_bbx_file, syslog_service_cfg_file]) |
| 142 | |
| 143 | if not os.path.isfile(syslog_service_env_file): |
| 144 | env_file = open(syslog_service_env_file, 'w') |
| 145 | env_file.write ("OPTIONS=\"-C\"") |
| 146 | env_file.close() |
| 147 | |
| 148 | if opt not in OptionKeys: raise ValueError("Invalid Option") |
| 149 | |
| 150 | self._ModifyParam (opt, val) |
| 151 | |
| 152 | return 0 |
| 153 | |
| 154 | def _StopService (self): |
| 155 | r = call (["systemctl", "stop", "syslog"]) |
| 156 | r = call (["systemctl", "--no-reload", "kill", "syslog"]) |
| 157 | return r |
| 158 | |
| 159 | def _StartService (self): |
| 160 | r = call (["systemctl", "daemon-reload"]) |
| 161 | r = call (["systemctl", "start", "syslog"]) |
| 162 | return r |
| 163 | |
| 164 | def _RestartService (self): |
| 165 | r = self._StopService() |
| 166 | r = self._StartService() |
| 167 | return r |
| 168 | |
| 169 | def _ModifyParam (self, opt, val): |
| 170 | env_file = open(syslog_service_env_file, 'r') |
| 171 | tmp_file = open(syslog_service_tmp_file, 'w') |
| 172 | |
| 173 | optkey = OptionKeySwitchMap [opt]['key'] |
| 174 | for line in env_file: |
| 175 | if line[0] == '#': |
| 176 | tmp_file.write(line) |
| 177 | continue |
| 178 | curkey = line.strip().split ("=")[0] |
| 179 | if curkey != optkey : |
| 180 | tmp_file.write(line) |
| 181 | |
| 182 | tmp_file.write(optkey + "=\"" + OptionKeySwitchMap[opt]['switch'] + val + "\"" + "\n") |
| 183 | |
| 184 | env_file.close () |
| 185 | tmp_file.close () |
| 186 | |
| 187 | r = call (["cp", syslog_service_tmp_file, syslog_service_env_file]) |
| 188 | return r |
| 189 | |
| 190 | def _GetConfig (self, opt): |
| 191 | with open(syslog_service_env_file, "r") as f: |
| 192 | for line in f: |
| 193 | if line[0] == '#': continue |
| 194 | config = line.split ("=") |
| 195 | var = config [0] |
| 196 | if var == OptionKeySwitchMap[opt]['key']: |
| 197 | val = config [1] |
| 198 | val = val[1:-2] # FIXME: Why is there a trailing space ??? |
| 199 | return val |
| 200 | return "" |
| 201 | |
| 202 | def _GetOptions(self): |
| 203 | cfg = {} |
| 204 | i = 0 |
| 205 | |
| 206 | for opt in OptionKeys: |
| 207 | if opt == 'Options' : continue |
| 208 | cfg [i] = self._GetConfig(opt) |
| 209 | i+=1 |
| 210 | |
| 211 | options = '' |
| 212 | j = 0 |
| 213 | while j<i-1: |
| 214 | if cfg[j] : options += cfg [j] |
| 215 | j+=1 |
| 216 | |
| 217 | return options |
| 218 | |
| 219 | def main(): |
| 220 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |
| 221 | bus = dbus.SystemBus() |
| 222 | name = dbus.service.BusName(DBUS_NAME, bus) |
| 223 | |
| 224 | global util |
| 225 | global rsys |
| 226 | global syslog_service_lib_file |
| 227 | global syslog_service_bbx_file |
| 228 | global syslog_service_cfg_file |
| 229 | global syslog_service_env_file |
| 230 | global syslog_service_tmp_file |
| 231 | global OptionKeys |
| 232 | global OptionKeySwitchMap |
| 233 | |
| 234 | OptionKeys = ['Options', 'Outfile', 'Priority', 'Smaller', 'RotateSize', 'RotateNum', 'Remote', 'LocalAndNet', 'DropDup', 'SharedMem', 'ConfFile', 'MarkTime', 'Printk'] |
| 235 | OptionKeySwitchMap = { |
| 236 | 'Options' : { 'switch' : "", 'key' : "OPTIONS" }, |
| 237 | 'Outfile' : { 'switch' : "-O ", 'key' : "OBMC_SYSLOG_OUTFILE" }, |
| 238 | 'Priority' : { 'switch' : "-O ", 'key' : "OBMC_SYSLOG_PRIORITY" }, |
| 239 | 'Smaller' : { 'switch' : "-S ", 'key' : "OBMC_SYSLOG_SMALLER" }, |
| 240 | 'RotateSize' : { 'switch' : "-s ", 'key' : "OBMC_SYSLOG_ROTATESIZE" }, |
| 241 | 'RotateNum' : { 'switch' : "-b ", 'key' : "OBMC_SYSLOG_ROTATENUM" }, |
| 242 | 'Remote' : { 'switch' : "-R ", 'key' : "OBMC_SYSLOG_REMOTE" }, |
| 243 | 'LocalAndNet' : { 'switch' : "-L ", 'key' : "OBMC_SYSLOG_LOCALNET" }, |
| 244 | 'DropDup' : { 'switch' : "-D ", 'key' : "OBMC_SYSLOG_DROPDUP" }, |
| 245 | 'SharedMem' : { 'switch' : "-C ", 'key' : "OBMC_SYSLOG_SHAREDMEM" }, |
| 246 | 'ConfFile' : { 'switch' : "-f ", 'key' : "OBMC_SYSLOG_CONFFILE" }, |
| 247 | 'MarkTime' : { 'switch' : "-m ", 'key' : "OBMC_SYSLOG_MARKTIME" }, |
| 248 | 'Printk' : { 'switch' : "-K ", 'key' : "OBMC_SYSLOG_PRINTK" } |
| 249 | } |
| 250 | |
| 251 | syslog_service_lib_file = '/lib/systemd/system/busybox-syslog.service' |
| 252 | syslog_service_bbx_file = '/etc/systemd/system/busybox-syslog.service' |
| 253 | syslog_service_cfg_file = '/etc/systemd/system/syslog.service' |
| 254 | syslog_service_env_file = '/etc/default/busybox-syslog' |
| 255 | syslog_service_tmp_file = '/tmp/busybox-syslog.tmp' |
| 256 | |
| 257 | util = JournalUtils () |
| 258 | rsys = Rsyslog (bus, OBJ_NAME_RSYSLOG) |
| 259 | |
| 260 | mainloop = gobject.MainLoop() |
| 261 | print("Started") |
| 262 | mainloop.run() |
| 263 | |
| 264 | if __name__ == '__main__': |
| 265 | sys.exit(main()) |
| 266 | |