diff --git a/display.py b/display.py index e4cf402..c141dd8 100644 --- a/display.py +++ b/display.py @@ -63,18 +63,19 @@ class Display: self.term = serial.Serial(dev, baudrate=115200, timeout=timeout) self.storage = bytearray() if daemon: - text = None + todo_file = STARTUP_TEXT + '.todo' try: - with open(STARTUP_TEXT) as f: + with open(todo_file) as f: text = f.read() print('new startup text:') print(text) except FileNotFoundError: - pass + text = None if text: self.reset() - self.show(*todo_text.split('\n')[0:3]) + self.show(*text.split('\n')[0:3]) self.set_startup() + os.rename(todo_file, STARTUP_TEXT) else: self.storage = None self.gethost(False) diff --git a/install.py b/install.py index eed8d8d..ebbef16 100755 --- a/install.py +++ b/install.py @@ -61,16 +61,277 @@ max-lease-time 7200; authoritative; """ -SERVICES = dict(router=router, frappy=frappy, display=display) +ROUTER_TEMPLATE = """[Unit] +Description = Routing to locally connected hardware +After = network.target + +[Service] +ExecStart = /usr/bin/python3 /root/aputools/router.py + +[Install] +WantedBy = multi-user.target +""" + +FRAPPY_TEMPLATE = """[Unit] +Description = Running frappy server +After = network.target + +[Service] +User = l_samenv +ExecStart = /usr/bin/python3 /home/l_samenv/frappy/bin/secop-server %s + +[Install] +WantedBy = multi-user.target +""" + +DISPLAY_TEMPLATE = """[Unit] +Description = status display +After = network.target + +[Service] +User = root +ExecStart = /usr/bin/python3 /root/aputools/display.py -d + +[Install] +WantedBy = multi-user.target +""" + +pip_requirements = { + 'l_samenv': {}, + 'root': {} +} net_addr = {} # dict of +dhcp_server_cfg = [] # configuration for dhcp +main_info = { # info to be determined depending on cfg + 'ifname': '', # name of interface of wan (dhcp) port + 'addr': '', # addr of wan port + 'current': None, + 'hostname': gethostname() # effective or given host name +} + for netif in os.scandir('/sys/class/net'): if netif.name != 'lo': # do not consider loopback interface with open(os.path.join(netif.path, 'address')) as f: addr = f.read().strip().lower() net_addr[netif.name] = addr +sorted_if = sorted(net_addr) +apuid = int(''.join(net_addr[sorted_if[0]].split(':')[-3:]), 16) & 0xfffffc + + +def frappy(cfg=None, port=None, requirements='', **kwds): + if not cfg: + return None + req = pip_requirements['l_samenv'] + req[cfg] = '\n'.join(requirements.split(',')) + if port: + cfg = '-p %s %s' % (port, cfg) + with open('/home/l_samenv/frappy/requirements.txt') as f: + req['frappy main'] = f.read() + return FRAPPY_TEMPLATE % cfg + + +def router(**opts): + if not opts: + return None + try: + with open('/root/aputools/requirements.txt') as f: + pip_requirements['root']['aputools'] = f.read() + except FileNotFoundError: + pass + return ROUTER_TEMPLATE + + +def display_update(cfg): + text = '\n'.join(cfg.get(key, '') for key in ('line0', 'line1', 'line2')) + text = text.replace('HOST', main_info['hostname']) \ + .replace('ADDR', main_info['addr']) + '\n' + try: + with open(STARTUP_TEXT) as f: + if f.read() == text: + return + except FileNotFoundError: + pass + print('new startup text:') + print(text) + with open(STARTUP_TEXT + '.todo', 'w') as f: + f.write(text) + + +def display(**opts): + if not opts: + return None + return DISPLAY_TEMPLATE + + +def pip(): + for user, requirements in pip_requirements.items(): + if user == 'root': + tmpname = join('/root', 'pip_requirements.tmp') + pipcmd = 'pip3 install -r %s' % tmpname + else: + tmpname = join('/home', user, 'pip_requirements.tmp') + pipcmd = 'sudo --user %s pip3 install --user -r %s' % (user, tmpname) + filename = tmpname.replace('.tmp', '.txt') + content = ''.join('# --- for %s ---\n%s\n' % kv for kv in requirements.items()) + if write_when_new(filename, content, True): + if doit: + os.rename(filename, tmpname) + if os.system(pipcmd) == 0: + os.rename(tmpname, filename) + else: + os.remove(tmpname) + else: + print(pipcmd) + # unix_cmd(pipcmd, stdout=None) + show.dirty = True + + +SERVICES = dict(router=router, frappy=frappy, display=display) + + +def unix_cmd(command, always=False, stdout=PIPE): + if doit or always: + if not always: + print('$ %s' % command) + result = Popen(command.split(), stdout=stdout).communicate()[0] + return (result or b'').decode() + else: + print('> %s' % command) + + +def write_when_new(filename, content, ignore_reduction=False): + if content is None: + lines = [] + else: + if not content.endswith('\n'): + content += '\n' + lines = content.split('\n') + try: + with open(filename) as fil: + old = fil.read() + except FileNotFoundError: + old = None + if old == content: + return False + if ignore_reduction: + content_set = set(v for v in lines if not v.startswith('#')) + old_set = set(v for v in (old or '').split('\n') if not v.startswith('#')) + content_set -= old_set + if content_set: + print('missing packages: %s' % (', '.join(content_set - old_set))) + else: + return False + if doit: + if lines: + with open(filename, 'w') as fil: + fil.write(content) + else: + os.remove(filename) + elif filename.endswith(more_info): + print('changes in', filename) + old = [] if old is None else old.split('\n') + top_lines = 0 # in case of empty loop + for top_lines, (ol, ll) in enumerate(zip(old, lines)): + if ol != ll: + break + bottom_lines = 0 + for bottom_lines, (ol, ll) in enumerate(zip(reversed(old[top_lines:]), reversed(lines[top_lines:]))): + if ol != ll: + break + if bottom_lines == 0: + old.append('') + lines.append('') + bottom_lines += 1 + print("===") + print('\n'.join(old[:top_lines])) + print('<<<') + print('\n'.join(old[top_lines:-bottom_lines])) + print('---') + print('\n'.join(lines[top_lines:-bottom_lines])) + print('>>>') + print('\n'.join(old[-bottom_lines:-1])) + print("===") + return content + + +def create_if(name, cfg): + result = dict( + TYPE='Ethernet', + NAME=name, + DEVICE=name, + BOOTPROTO='none', + ONBOOT='yes', + PROXY_METHOD='none', + BROWSER_ONLY='no', + IPV4_FAILURE_FATAL='yes', + IPV6INIT='no') + if cfg == 'off': + result['ONBOOT'] = 'no' + elif cfg.startswith('wan') or cfg == 'dhcp': + if main_info.get('mainif', name) != name: + raise ValueError('can not have more than one WAN/DHCP port') + main_info['mainif'] = name + main_info['addr'] = net_addr[name] + result['BOOTPROTO'] = 'dhcp' + # default: all <= 192.0.0.0 + # others have to be added explicitly + dhcp_server_cfg.append(('0.0.0.0/128.0.0.0', [])) + dhcp_server_cfg.append(('128.0.0.0/192.0.0.0', [])) + for nw in cfg.split(',')[1:]: + nw = IPv4Interface(nw).network + dhcp_server_cfg.append((nw.with_netmask, [])) + else: + cfgip = IPv4Interface(cfg) + network = cfgip.network + if network.prefixlen == 32: # or no prefix specified + otherip = IPv4Interface('%s/24' % cfgip.ip) + network = otherip.network + if str(cfgip.ip).endswith('.1'): + cfgip = network.network_address + 2 + else: + cfgip = network.network_address + 1 + dhcp_server_cfg.append((network.with_netmask, [(str(otherip.ip), str(otherip.ip))])) + result['IPADDR'] = str(cfgip) + else: # subnet with multiple adresses -> static adresses only. dhcp range not yet implemented + result['IPADDR'] = str(cfgip.ip) + result['NETMASK'] = network.netmask + result['PREFIX'] = str(network.prefixlen) + return result + + +def walk(action): + for dirpath, _, files in os.walk('.'): + syspath = dirpath[1:] # remove leading '.' + action.dirpath = dirpath + action.syspath = syspath + if files: + match, mismatch, missing = filecmp.cmpfiles(dirpath, syspath, files) + if mismatch: + newer = [f for f in mismatch if getmtime(join(syspath, f)) > getmtime(join(dirpath, f))] + if newer: + action.newer(newer) + if len(newer) < len(mismatch): + newer = set(newer) + action.older([f for f in mismatch if f not in newer]) + if missing: + if DEL in missing: + missing.remove(DEL) + with open(join(dirpath, DEL)) as fil: + to_delete = [] + for fname in fil: + fname = fname.strip() + if fname and exists(join(syspath, fname)): + if exists(join(dirpath, fname)): + print('ERROR: %s in %s, but also in repo -> ignored' % (fname, DEL)) + else: + to_delete.append(fname) + action.delete(to_delete) + if missing: + action.missing(missing) + class Walker: def __init__(self): @@ -128,435 +389,192 @@ class Do(Walker): os.remove(join(self.syspath, file)) -class Install: - ROUTER_TEMPLATE = """[Unit] - Description = Routing to locally connected hardware - After = network.target - - [Service] - ExecStart = /usr/bin/python3 /root/aputools/router.py - - [Install] - WantedBy = multi-user.target - """ - - FRAPPY_TEMPLATE = """[Unit] - Description = Running frappy server - After = network.target - - [Service] - User = l_samenv - ExecStart = /usr/bin/python3 /home/l_samenv/frappy/bin/secop-server %s - - [Install] - WantedBy = multi-user.target - """ - - DISPLAY_TEMPLATE = """[Unit] - Description = status display - After = network.target - - [Service] - User = root - ExecStart = /usr/bin/python3 /root/aputools/display.py -d - - [Install] - WantedBy = multi-user.target - """ - - main_if = '' - main_addr = '' - - def __init__(self, doit): - if not doit: - self.to_system = Show() - walk(self.to_system) - self.doit = doit - self.current = None - self.sorted_if = sorted(net_addr) - self.apuid = int(''.join(net_addr[sorted_if[0]].split(':')[-3:]), 16) & 0xfffffc - self.dhcp_server_cfg = [] # configuration for dhcp - self.pip_requirements = { - 'l_samenv': {}, - 'root': {} - } - cfgfile = None - cfgfiles = [] - for file in glob(CFGPATH % ('*', apuid)): - cfgfiles.append(file) - with open('/etc/hostname') as f: - hostname = f.read().strip() - self.hostname = hostname - if not cfgfiles: - disp = serial.Serial('/dev/ttyS1', baudrate=115200, timeout=1) - disp.write(b'\x1b\x1b\x01\xf3') - display_available = disp.read(8)[0:4] == b'\x1b\x1b\x05\xf3' - if display_available: - # leftmost interface labelled 'eth0' - typ = 'controlbox' - template = CONTROLBOX_TEMPLATE - else: - # rightmost interface - typ = 'bare apu' - template = CONFIG_TEMPLATE - print('no cfg file found for this', typ, f'with id {apuid:%6.6x} (hostname={hostname})') - self.hostname = input('enter host name: ') - if not self.hostname: - print('no hostname given') - return - cfgfile = CFGPATH % (self.hostname, apuid) - with open(cfgfile, 'w') as f: - f.write(template) - elif len(cfgfiles) > 1: - print('ERROR: ambiguous cfg files: %s' % ', '.join(cfgfiles)) - else: - cfgfile = cfgfiles[0] - if cfgfile != CFGPATH % (hostname, apuid): - if cfgfile: - self.hostname = basename(cfgfile).rpartition('_')[0] - if doit: - os.system('sh sethostname.sh') - else: - if cfgfile: - print('replace host name %r by %r' % (hostname, self.hostname)) - self.to_system.dirty = True - if cfgfile is None: - raise ValueError('no config file') - to_start = {} - ifname = '' - parser = ConfigParser() - try: - parser.read(cfgfile) - network = dict(parser['NETWORK']) - for ifname in net_addr: - content = create_if(ifname, network.pop(ifname, 'off')) - content = '\n'.join('%s=%s' % kv for kv in content.items()) - todo = write_when_new('/etc/sysconfig/network-scripts/ifcfg-%s' % ifname, content) - if todo and more_info == 'NONE': - print('change', ifname) - self.to_system.dirty = True - to_start[ifname] = 'if_restart' - if self.dhcp_server_cfg: - content = [DHCP_HEADER] - for subnet, rangelist in self.dhcp_server_cfg: - try: - adr, mask = subnet.split('/') - except Exception as e: - print(subnet, repr(e)) - continue - content.append('subnet %s netmask %s {\n' % (adr, mask)) - # content.append(' option netmask %s;\n' % mask) - for rng in rangelist: - content.append(' range %s %s;\n' % rng) - content.append('}\n') - content = ''.join(content) - todo = write_when_new('/etc/dhcp/dhcpd.conf', content) - if todo: - print('change dhcpd.conf') - to_start['dhcpd'] = 'restart' - self.to_system.dirty = True - elif doit: - unix_cmd('systemctl stop dhcpd') - unix_cmd('systemctl disable dhcpd') - content = [] - dirty = False - for section in parser.sections(): - if COMMENT not in parser[section]: - dirty = True - content.append('[%s]' % section) - content.append(COMMENT) - section_dict = dict(parser[section].items()) - if section == 'DISPLAY': - display_update(section_dict) - for key, value in section_dict.items(): - content.append('%s=%s' % (key, value)) - content.append('') - if dirty: - with open(cfgfile, 'w') as f: - f.write('\n'.join(content)) - except Exception as e: - print('ERROR: can not handle %s %s: %r' % (hostname, ifname, e)) - raise - - reload_systemd = False - for service, service_func in SERVICES.items(): - section = service.upper() - if parser.has_section(section): - servicecfg = service_func(**dict(parser[section])) - else: - servicecfg = None - result = unix_cmd('systemctl show -p WantedBy -p ActiveState %s' % service, True) - active = False - enabled = False - for line in result.split('\n'): - if line.startswith('WantedBy=m'): - enabled = True - elif line.strip() == 'ActiveState=active': - active = True - if servicecfg is None: - if active: - unix_cmd('systemctl stop %s' % service) - self.to_system.dirty = True - if enabled: - unix_cmd('systemctl disable %s' % service) - self.to_system.dirty = True - else: - if not enabled: - to_start[service] = 'enable' - elif not active: - to_start[service] = 'restart' - if write_when_new('/etc/systemd/system/%s.service' % service, servicecfg): - self.to_system.dirty = True - reload_systemd = True - if servicecfg and to_start.get('service') is None: - to_start[service] = 'restart' - - pip() - if reload_systemd: - unix_cmd('systemctl daemon-reload') - for service, action in to_start.items(): - self.to_system.dirty = True - if action == 'if_restart': - unix_cmd('ifdown %s' % service) - unix_cmd('ifup %s' % service) - else: - if action == 'restart': - unix_cmd('systemctl restart %s' % service) - unix_cmd('systemctl enable %s' % service) - result = [f'config file:\n {cfgfile}'] - for section in parser.sections(): - result.append(section) - for key, value in parser.items(section): - result.append(f' {key} = {value}') - result.append('') - self.current = '\n'.join(result) - - def unix_cmd(self, command, always=False, stdout=PIPE): - if self.doit or always: - if not always: - print('$ %s' % command) - result = Popen(command.split(), stdout=stdout).communicate()[0] - return (result or b'').decode() - else: - print('> %s' % command) - - def write_when_new(self, filename, content, ignore_reduction=False): - if content is None: - lines = [] - else: - if not content.endswith('\n'): - content += '\n' - lines = content.split('\n') - try: - with open(filename) as f: - old = f.read() - except FileNotFoundError: - old = None - if old == content: +def handle_config(): + cfgfile = None + cfgfiles = [] + for file in glob(CFGPATH % ('*', apuid)): + cfgfiles.append(file) + for i in [1, 2, 3]: + bad = glob(CFGPATH % ('*', apuid+i)) + if bad: + print('cfg files found with bad apu id (use net addr of leftmost plug)') + print(bad) return False - if ignore_reduction: - content_set = set(v for v in lines if not v.startswith('#')) - old_set = set(v for v in (old or '').split('\n') if not v.startswith('#')) - content_set -= old_set - if content_set: - print('missing packages: %s' % (', '.join(content_set - old_set))) - else: - return False - if self.doit: - if lines: - with open(filename, 'w') as f: - f.write(content) - else: - os.remove(filename) - elif filename.endswith(more_info): - print('changes in', filename) - old = [] if old is None else old.split('\n') - top_lines = 0 # in case of empty loop - for top_lines, (ol, ll) in enumerate(zip(old, lines)): - if ol != ll: - break - bottom_lines = 0 - for bottom_lines, (ol, ll) in enumerate(zip(reversed(old[top_lines:]), reversed(lines[top_lines:]))): - if ol != ll: - break - if bottom_lines == 0: - old.append('') - lines.append('') - bottom_lines += 1 - print("===") - print('\n'.join(old[:top_lines])) - print('<<<') - print('\n'.join(old[top_lines:-bottom_lines])) - print('---') - print('\n'.join(lines[top_lines:-bottom_lines])) - print('>>>') - print('\n'.join(old[-bottom_lines:-1])) - print("===") - return content - - @staticmethod - def frappy(cfg=None, port=None, requirements='', **kwds): - if not cfg: - return None - req = pip_requirements['l_samenv'] - req[cfg] = '\n'.join(requirements.split(',')) - if port: - cfg = '-p %s %s' % (port, cfg) - with open('/home/l_samenv/frappy/requirements.txt') as f: - req['frappy main'] = f.read() - return FRAPPY_TEMPLATE % cfg - - def router(self, **opts): - if not opts: - return None - try: - with open('/root/aputools/requirements.txt') as f: - pip_requirements['root']['aputools'] = f.read() - except FileNotFoundError: - pass - return self.ROUTER_TEMPLATE - - def display_update(self, cfg): - dirty = False - lines = [] - for key in ['line0', 'line1', 'line2']: - value = cfg.get(key, '') - if value.startswith('HOST'): - line = self.hostname - value = 'HOST: ' + line - elif value.startswith('ADDR'): - line = self.main_addr - value = 'ADDR: ' + line - else: - line = value - lines.append(line) - if cfg.get(key) != value: - if self.doit: - cfg[key] = value - dirty = True - if dirty: - text = '\n'.join(lines) - if self.doit: - with open(STARTUP_TEXT, 'w') as f: - f.write(text) - else: - print('new startup text:') - print('\n'.join(lines)) - - def display(self, **opts): - if not opts: - return None - return self.DISPLAY_TEMPLATE - - def pip(self): - for user, requirements in pip_requirements.items(): - if user == 'root': - tmpname = join('/root', 'pip_requirements.tmp') - pipcmd = 'pip3 install -r %s' % tmpname - else: - tmpname = join('/home', user, 'pip_requirements.tmp') - pipcmd = 'sudo --user %s pip3 install --user -r %s' % (user, tmpname) - filename = tmpname.replace('.tmp', '.txt') - content = ''.join('# --- for %s ---\n%s\n' % kv for kv in requirements.items()) - if write_when_new(filename, content, True): - if self.doit: - os.rename(filename, tmpname) - if os.system(pipcmd) == 0: - os.rename(tmpname, filename) - else: - os.remove(tmpname) - else: - print(pipcmd) - # unix_cmd(pipcmd, stdout=None) - self.to_system.dirty = True - - def create_if(self, name, cfg): - result = dict( - TYPE='Ethernet', - NAME=name, - DEVICE=name, - BOOTPROTO='none', - ONBOOT='yes', - PROXY_METHOD='none', - BROWSER_ONLY='no', - IPV4_FAILURE_FATAL='yes', - IPV6INIT='no') - if cfg == 'off': - result['ONBOOT'] = 'no' - elif cfg.startswith('wan') or cfg == 'dhcp': - if name != self.main_if and self.main_if - raise ValueError('can not have more than one WAN/DHCP port') - self.main_if = name - self.main_addr = net_addr[name] - result['BOOTPROTO'] = 'dhcp' - # default: all <= 192.0.0.0 - # others have to be added explicitly - self.dhcp_server_cfg.append(('0.0.0.0/128.0.0.0', [])) - self.dhcp_server_cfg.append(('128.0.0.0/192.0.0.0', [])) - for nw in cfg.split(',')[1:]: - nw = IPv4Interface(nw).network - self.dhcp_server_cfg.append((nw.with_netmask, [])) + newhostname = main['hostname'] + if not cfgfiles: + # determine if display is present + disp = serial.Serial('/dev/ttyS1', baudrate=115200, timeout=1) + disp.write(b'\x1b\x1b\x01\xf3') + display_available = disp.read(8)[0:4] == b'\x1b\x1b\x05\xf3' + if display_available: + # leftmost interface labelled 'eth0' + main['mainif'] = sorted_if[0] + typ = 'controlbox' + template = CONTROLBOX_TEMPLATE else: - cfgip = IPv4Interface(cfg) - network = cfgip.network - if network.prefixlen == 32: # or no prefix specified - otherip = IPv4Interface('%s/24' % cfgip.ip) - network = otherip.network - if str(cfgip.ip).endswith('.1'): - cfgip = network.network_address + 2 - else: - cfgip = network.network_address + 1 - self.dhcp_server_cfg.append((network.with_netmask, [(str(otherip.ip), str(otherip.ip))])) - result['IPADDR'] = str(cfgip) - else: # subnet with multiple adresses -> static adresses only. dhcp range not yet implemented - result['IPADDR'] = str(cfgip.ip) - result['NETMASK'] = network.netmask - result['PREFIX'] = str(network.prefixlen) - return result + # rightmost interface + main['mainif'] = sorted_if[-1] + typ = 'bare apu' + template = CONFIG_TEMPLATE + print('no cfg file found for this', typ, f'with id {apuid:%6.6x} (hostname={hostname})') + newhostname = input('enter host name: ') + if not newhostname: + print('no hostname given') + return False + cfgfile = CFGPATH % (newhostname, apuid) + with open(cfgfile, 'w') as f: + f.write(template) + elif len(cfgfiles) > 1: + print('ERROR: ambiguous cfg files: %s' % ', '.join(cfgfiles)) + else: + cfgfile = cfgfiles[0] + if cfgfile != CFGPATH % (hostname, apuid): + if cfgfile: + newhostname = basename(cfgfile).rpartition('_')[0] + if doit: + os.system('sh sethostname.sh') + else: + if cfgfile: + print('replace host name %r by %r' % (hostname, newhostname)) + show.dirty = True + main_info['hostname'] = newhostname + if cfgfile is None: + return False + to_start = {} + ifname = '' + parser = ConfigParser() + try: + parser.read(cfgfile) + network = dict(parser['NETWORK']) + for ifname in net_addr: + content = create_if(ifname, network.pop(ifname, 'off')) + content = '\n'.join('%s=%s' % kv for kv in content.items()) + todo = write_when_new('/etc/sysconfig/network-scripts/ifcfg-%s' % ifname, content) + if todo and more_info == 'NONE': + print('change', ifname) + show.dirty = True + to_start[ifname] = 'if_restart' + if dhcp_server_cfg: + content = [DHCP_HEADER] + for subnet, rangelist in dhcp_server_cfg: + try: + adr, mask = subnet.split('/') + except Exception as e: + print(subnet, repr(e)) + continue + content.append('subnet %s netmask %s {\n' % (adr, mask)) + #content.append(' option netmask %s;\n' % mask) + for rng in rangelist: + content.append(' range %s %s;\n' % rng) + content.append('}\n') + content = ''.join(content) + todo = write_when_new('/etc/dhcp/dhcpd.conf', content) + if todo: + print('change dhcpd.conf') + to_start['dhcpd'] = 'restart' + show.dirty = True + elif doit: + unix_cmd('systemctl stop dhcpd') + unix_cmd('systemctl disable dhcpd') + content = [] + dirty = False + for section in parser.sections(): + if COMMENT not in parser[section]: + dirty = True + content.append('[%s]' % section) + content.append(COMMENT) + section_dict = dict(parser[section].items()) + if section == 'DISPLAY': + display_update(section_dict) + for key, value in section_dict.items(): + content.append('%s=%s' % (key, value)) + content.append('') + if dirty: + with open(cfgfile, 'w') as f: + f.write('\n'.join(content)) + except Exception as e: + print('ERROR: can not handle %s %s: %r' % (hostname, ifname, e)) + raise + return False - def walk(self, action): - for dirpath, _, files in os.walk('.'): - syspath = dirpath[1:] # remove leading '.' - action.dirpath = dirpath - action.syspath = syspath - if files: - match, mismatch, missing = filecmp.cmpfiles(dirpath, syspath, files) - if mismatch: - newer = [f for f in mismatch if getmtime(join(syspath, f)) > getmtime(join(dirpath, f))] - if newer: - action.newer(newer) - if len(newer) < len(mismatch): - newer = set(newer) - action.older([f for f in mismatch if f not in newer]) - if missing: - if DEL in missing: - missing.remove(DEL) - with open(join(dirpath, DEL)) as f: - to_delete = [] - for line in f: - line = line.strip() - if fline and exists(join(syspath, line)): - if exists(join(dirpath, line)): - print('ERROR: %s in %s, but also in repo -> ignored' % (line, DEL)) - else: - to_delete.append(f) - action.delete(to_delete) - if missing: - action.missing(missing) + reload_systemd = False + for service, service_func in SERVICES.items(): + section = service.upper() + if parser.has_section(section): + servicecfg = service_func(**dict(parser[section])) + else: + servicecfg = None + result = unix_cmd('systemctl show -p WantedBy -p ActiveState %s' % service, True) + active = False + enabled = False + for line in result.split('\n'): + if line.startswith('WantedBy=m'): + enabled = True + elif line.strip() == 'ActiveState=active': + active = True + if servicecfg is None: + if active: + unix_cmd('systemctl stop %s' % service) + show.dirty = True + if enabled: + unix_cmd('systemctl disable %s' % service) + show.dirty = True + else: + if not enabled: + to_start[service] = 'enable' + elif not active: + to_start[service] = 'restart' + if write_when_new('/etc/systemd/system/%s.service' % service, servicecfg): + show.dirty = True + reload_systemd = True + if servicecfg and to_start.get('service') is None: + to_start[service] = 'restart' + + pip() + if reload_systemd: + unix_cmd('systemctl daemon-reload') + for service, action in to_start.items(): + show.dirty = True + if action == 'if_restart': + unix_cmd('ifdown %s' % service) + unix_cmd('ifup %s' % service) + else: + if action == 'restart': + unix_cmd('systemctl restart %s' % service) + unix_cmd('systemctl enable %s' % service) + result = [f'config file:\n {cfgfile}'] + for section in parser.sections(): + result.append(section) + for key, value in parser.items(section): + result.append(f' {key} = {value}') + result.append('') + return '\n'.join(result) -print('---') -to_install = Install(False) - -if not result: - print('fix first above errors') -elif to_system.dirty and more_info == 'NONE': +more_info = 'NONE' +while True: + doit = False print('---') - answer = input('do above? ') - if answer.lower().startswith('y'): - Install(True) - walk(Do()) - with open('/root/aputools/current', 'w') as fil: - fil.write(to_install.current) -else: - print('nothing to do') + show = Show() + walk(show) + result = handle_config() + + if not result: + print('fix first above errors') + break + if show.dirty and more_info == 'NONE': + print('---') + answer = input('enter y(es) to do above or a filename to check changes? ') + doit = True + if answer.lower() in {'y', 'yes'}: + handle_config() + walk(Do()) + with open('/root/aputools/current', 'w') as f: + f.write(result) + break + if not answer: + print('cancel') + break + else: + print('nothing to do') + break