Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 1 | #!/usr/bin/python -u |
| 2 | |
| 3 | import gobject |
| 4 | import dbus |
| 5 | import dbus.service |
| 6 | import dbus.mainloop.glib |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 7 | import os |
| 8 | import os.path as path |
Adriana Kobylak | 256be78 | 2016-08-24 15:43:16 -0500 | [diff] [blame] | 9 | import sys |
Lei YU | 1d8b7cd | 2016-12-21 11:01:28 +0800 | [diff] [blame] | 10 | from obmc.dbuslib.bindings import DbusProperties, DbusObjectManager, get_dbus |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 11 | from IPy import IP |
Adriana Kobylak | 256be78 | 2016-08-24 15:43:16 -0500 | [diff] [blame] | 12 | |
Brad Bishop | 966fcd5 | 2016-09-29 09:22:32 -0400 | [diff] [blame] | 13 | settings_file_path = os.path.join( |
| 14 | sys.prefix, |
| 15 | 'share', |
| 16 | 'phosphor-settings') |
Adriana Kobylak | 256be78 | 2016-08-24 15:43:16 -0500 | [diff] [blame] | 17 | sys.path.insert(1, settings_file_path) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 18 | import settings_file as s |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 19 | import re |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 20 | import obmc.mapper |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 21 | |
| 22 | DBUS_NAME = 'org.openbmc.settings.Host' |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 23 | CONTROL_INTF = 'org.openbmc.Settings' |
| 24 | |
Gunnar Mills | a16abc5 | 2018-01-27 10:02:34 -0600 | [diff] [blame^] | 25 | |
| 26 | def walk_nest(d, keys=()): |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 27 | """Arrange dictionary keys and values. |
| 28 | |
| 29 | Walk the dictionary and establish every possible path |
| 30 | returned to and processed by 'create_object' below |
| 31 | """ |
| 32 | if isinstance(d, dict): |
| 33 | for k, v in d.iteritems(): |
| 34 | for rv in walk_nest(v, keys + (k, )): |
| 35 | yield rv |
| 36 | else: |
| 37 | yield keys, d |
| 38 | |
Gunnar Mills | a16abc5 | 2018-01-27 10:02:34 -0600 | [diff] [blame^] | 39 | |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 40 | def create_object(settings): |
| 41 | """Create and format objects. |
| 42 | |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 43 | Parse dictionary file and return all objects and settings |
| 44 | in the following format: {obj_name {settings}} |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 45 | """ |
Sergey Solomin | f68b864 | 2016-11-14 16:26:17 -0600 | [diff] [blame] | 46 | bus = get_dbus() |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 47 | mapper = obmc.mapper.Mapper(bus) |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 48 | allobjects = {} |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 49 | queries = {} |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 50 | for compound_key, val in walk_nest(settings): |
| 51 | obj_name = compound_key[0].lower() |
Gunnar Mills | a16abc5 | 2018-01-27 10:02:34 -0600 | [diff] [blame^] | 52 | obj_name = obj_name.replace(".", "/") |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 53 | obj_name = "/" + obj_name + "0" |
| 54 | |
Gunnar Mills | a16abc5 | 2018-01-27 10:02:34 -0600 | [diff] [blame^] | 55 | for i in compound_key[2:len(compound_key) - 2]: |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 56 | obj_name = obj_name + "/" + i |
| 57 | |
| 58 | setting = compound_key[len(compound_key) - 2] |
| 59 | attribute = compound_key[len(compound_key) - 1] |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 60 | if settings.get(compound_key[0], {}).get('query', {}): |
| 61 | q = queries.setdefault(obj_name, {}) |
| 62 | s = q.setdefault( |
| 63 | setting, {'name': None, 'type': None, 'default': None}) |
| 64 | else: |
| 65 | o = allobjects.setdefault(obj_name, {}) |
| 66 | s = o.setdefault( |
| 67 | setting, {'name': None, 'type': None, 'default': None}) |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 68 | s[attribute] = val |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 69 | for settings in queries.itervalues(): |
| 70 | for setting in settings.itervalues(): |
| 71 | if setting['type'] is not 'instance_query': |
| 72 | continue |
Lei YU | 1d8b7cd | 2016-12-21 11:01:28 +0800 | [diff] [blame] | 73 | paths = mapper.get_subtree_paths(setting['subtree'], 0, |
| 74 | retries=10, interval=0.1) |
Matt Spinler | 371dd02 | 2016-12-15 12:48:04 -0600 | [diff] [blame] | 75 | |
| 76 | if setting['keyregex'] == 'host': |
| 77 | # Always create at least one host object. |
| 78 | paths = set(paths + ['/org/openbmc/control/host0']) |
| 79 | |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 80 | for path in paths: |
| 81 | m = re.search(setting['matchregex'], path) |
| 82 | if not m: |
| 83 | continue |
| 84 | allobjects.setdefault( |
| 85 | "/org/openbmc/settings/" + m.group(1), settings) |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 86 | return allobjects |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 87 | |
Gunnar Mills | a16abc5 | 2018-01-27 10:02:34 -0600 | [diff] [blame^] | 88 | |
Lei YU | 1d8b7cd | 2016-12-21 11:01:28 +0800 | [diff] [blame] | 89 | class HostSettingsObject(DbusProperties, DbusObjectManager): |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 90 | def __init__(self, bus, name, settings, path): |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 91 | super(HostSettingsObject, self).__init__( |
| 92 | conn=bus, |
| 93 | object_path=name, |
| 94 | validator=self.input_validator) |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 95 | self.bus = bus |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 96 | self.path = path |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 97 | self.name = name |
| 98 | self.settings = settings |
Gunnar Mills | a16abc5 | 2018-01-27 10:02:34 -0600 | [diff] [blame^] | 99 | self.fname = name[name.rfind("/") + 1:] + '-' |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 100 | |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 101 | # Needed to ignore the validation on default networkconfig values as |
| 102 | # opposed to user giving the same. |
| 103 | self.adminmode = True |
| 104 | |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 105 | if not os.path.exists(path): |
| 106 | os.mkdir(path) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 107 | |
| 108 | # Listen to changes in the property values and sync them to the BMC |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 109 | bus.add_signal_receiver( |
| 110 | self.settings_signal_handler, |
| 111 | dbus_interface="org.freedesktop.DBus.Properties", |
| 112 | signal_name="PropertiesChanged", |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 113 | path=name) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 114 | |
| 115 | # Create the dbus properties |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 116 | for setting in settings.itervalues(): |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 117 | if setting['type'] is 'instance_query': |
| 118 | continue |
| 119 | self.set_settings_property( |
Yi Li | 5ebab48 | 2016-11-22 16:34:18 +0800 | [diff] [blame] | 120 | setting['name'], setting['type'], setting['default'], |
| 121 | self.fname) |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 122 | # Done with consuming factory settings. |
| 123 | self.adminmode = False |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 124 | |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 125 | def get_bmc_value(self, name, fname): |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 126 | try: |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 127 | with open(path.join(self.path, fname + name), 'r') as f: |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 128 | return f.read() |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 129 | except (IOError): |
| 130 | pass |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 131 | return None |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 132 | |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 133 | # Create dbus properties based on bmc value. |
| 134 | # This will be either a value previously set, |
| 135 | # or the default file value if the BMC value |
| 136 | # does not exist. |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 137 | def set_settings_property(self, attr_name, attr_type, value, fname): |
Patrick Williams | 3b8d055 | 2017-04-06 14:14:15 -0500 | [diff] [blame] | 138 | default = value |
| 139 | |
| 140 | # Read from file. |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 141 | bmcv = self.get_bmc_value(attr_name, fname) |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 142 | if bmcv: |
| 143 | value = bmcv |
Patrick Williams | 3b8d055 | 2017-04-06 14:14:15 -0500 | [diff] [blame] | 144 | |
| 145 | # Perform type mapping. |
| 146 | type_map = {"i": int, "s": str, "b": bool}[attr_type] |
| 147 | real_value = type_map(value) |
| 148 | real_default = type_map(default) |
| 149 | |
| 150 | try: |
| 151 | self.Set(DBUS_NAME, attr_name, real_value) |
| 152 | except ValueError: |
| 153 | print("Persistent value for {} is invalid: {}{} had '{}', " |
| 154 | "using '{}'.".format(attr_name, fname, attr_name, |
| 155 | value, default)) |
| 156 | self.Set(DBUS_NAME, attr_name, real_default) |
| 157 | self.set_system_settings(attr_name, real_default, fname) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 158 | |
| 159 | # Save the settings to the BMC. This will write the settings value in |
| 160 | # individual files named by the property name to the BMC. |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 161 | def set_system_settings(self, name, value, fname): |
| 162 | bmcv = self.get_bmc_value(name, fname) |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 163 | if bmcv != value: |
Sergey Solomin | 62b55f3 | 2016-10-13 10:40:27 -0500 | [diff] [blame] | 164 | filepath = path.join(self.path, fname + name) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 165 | with open(filepath, 'w') as f: |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 166 | f.write(str(value)) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 167 | |
| 168 | # Signal handler for when one ore more settings properties were updated. |
| 169 | # This will sync the changes to the BMC. |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 170 | def settings_signal_handler( |
Yi Li | 5ebab48 | 2016-11-22 16:34:18 +0800 | [diff] [blame] | 171 | self, interface_name, changed_properties, invalidated_properties): |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 172 | for name, value in changed_properties.items(): |
Yi Li | 5ebab48 | 2016-11-22 16:34:18 +0800 | [diff] [blame] | 173 | self.set_system_settings(name, value, self.fname) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 174 | |
| 175 | # Placeholder signal. Needed to register the settings interface. |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 176 | @dbus.service.signal(DBUS_NAME, signature='s') |
| 177 | def SettingsUpdated(self, sname): |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 178 | pass |
| 179 | |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 180 | def validate_regex(self, regex, value): |
| 181 | if not re.compile(regex).search(value): |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 182 | raise ValueError("Invalid input. Data does not satisfy regex") |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 183 | |
| 184 | def validate_range(self, min, max, value): |
| 185 | if value not in range(min, max): |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 186 | raise ValueError("Invalid input. Data not in allowed range") |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 187 | |
Vishwanatha Subbanna | eb1bea8 | 2017-01-12 16:42:44 +0530 | [diff] [blame] | 188 | def validate_list(self, lst, value): |
| 189 | if value not in map(lambda val: val, lst): |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 190 | raise ValueError("Invalid input. Data not in allowed values") |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 191 | |
| 192 | # validate host network configuration |
| 193 | # need "ipaddress=,prefix=,gateway=,mac=,addr_type=" |
| 194 | # Must be able to handle any order |
| 195 | def validate_net_config(self, value): |
| 196 | if self.adminmode: |
| 197 | return |
| 198 | |
| 199 | # Need all of these to be given by the user. |
| 200 | user_config = [] |
| 201 | all_config = ['ipaddress', 'prefix', 'gateway', 'mac', 'addr_type'] |
| 202 | |
| 203 | # This has a hard data format mentioned above so no blanks allowed. |
| 204 | if value.count(" ") or value.count("=") != 5: |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 205 | raise ValueError("Invalid Network Data. No white spaces allowed") |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 206 | |
| 207 | config = value.split(',') |
| 208 | for key_value in config: |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 209 | key, value = key_value.split('=') |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 210 | if not key or not value: |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 211 | raise ValueError("Invalid key or Data") |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 212 | |
| 213 | # Add the current key seen so we can compare at the end to see |
| 214 | # if all values have been given |
| 215 | user_config.append(key.lower()) |
| 216 | |
| 217 | if key.lower() == 'ipaddress' or key.lower() == 'gateway': |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 218 | IP(value) |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 219 | |
| 220 | elif key.lower() == 'mac': |
| 221 | regex = '([a-fA-F0-9]{2}[:|\-]?){6}' |
| 222 | self.validate_regex(regex, value) |
| 223 | |
| 224 | elif key.lower() == 'prefix': |
| 225 | self.validate_range(0, 33, int(value)) |
| 226 | |
| 227 | elif key.lower() == 'addr_type': |
| 228 | allowed = ["STATIC", "DYNAMIC"] |
Vishwanatha Subbanna | eb1bea8 | 2017-01-12 16:42:44 +0530 | [diff] [blame] | 229 | self.validate_list(allowed, value) |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 230 | |
| 231 | # Did user pass everything ?? |
| 232 | if set(all_config) - set(user_config): |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 233 | raise ValueError( |
| 234 | "Invalid Network Data. All information is mandatory") |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 235 | |
| 236 | # Validate to see if the changes are in order |
| 237 | def input_validator(self, iface, proprty, value): |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 238 | # User entered key is not present |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 239 | shk = None |
| 240 | for attr in self.settings.itervalues(): |
| 241 | if attr['name'] == proprty: |
| 242 | shk = attr |
| 243 | |
| 244 | if shk is None: |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 245 | raise KeyError("Invalid Property") |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 246 | |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 247 | validation = shk.get('validation', None) |
| 248 | |
| 249 | if validation == 'list': |
Vishwanatha Subbanna | eb1bea8 | 2017-01-12 16:42:44 +0530 | [diff] [blame] | 250 | self.validate_list(shk['allowed'], value) |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 251 | |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 252 | elif validation == 'range': |
Gunnar Mills | a16abc5 | 2018-01-27 10:02:34 -0600 | [diff] [blame^] | 253 | self.validate_range(shk['min'], shk['max'] + 1, value) |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 254 | |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 255 | elif validation == 'regex': |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 256 | self.validate_regex(shk['regex'], value) |
| 257 | |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 258 | elif validation == 'custom': |
Brad Bishop | c1e5e9f | 2016-09-29 09:40:01 -0400 | [diff] [blame] | 259 | getattr(self, shk['method'])(value) |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 260 | |
Gunnar Mills | a16abc5 | 2018-01-27 10:02:34 -0600 | [diff] [blame^] | 261 | |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 262 | if __name__ == '__main__': |
| 263 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |
| 264 | |
Brad Bishop | 5ef7fe6 | 2016-05-17 08:39:17 -0400 | [diff] [blame] | 265 | bus = get_dbus() |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 266 | allobjects = create_object(s.SETTINGS) |
| 267 | lastobject = None |
| 268 | objs = [] |
| 269 | for o, settings in allobjects.iteritems(): |
| 270 | objs.append(HostSettingsObject(bus, o, settings, "/var/lib/obmc/")) |
| 271 | objs[-1].unmask_signals() |
Sergey Solomin | 4a2433f | 2016-10-03 10:22:57 -0500 | [diff] [blame] | 272 | mainloop = gobject.MainLoop() |
Brad Bishop | 2cbef3d | 2016-08-31 12:40:13 -0400 | [diff] [blame] | 273 | name = dbus.service.BusName(DBUS_NAME, bus) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 274 | print "Running HostSettingsService" |
| 275 | mainloop.run() |
Brad Bishop | 31c42f0 | 2016-09-29 09:35:32 -0400 | [diff] [blame] | 276 | |
| 277 | # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 |