def _select(self, config, ls): if len(ls) != 2: log.warning('invalid select statement') else: r = self.macros.set_read_map(ls[1]) log.trace('config: %s: _select: %s %s %r' % \ (self.init_name, r, ls[1], self.macros.maps()))
def _process_data(self, results, directive, info, data): new_data = [] for l in results[1]: if l.startswith('%error'): l = self._expand(l) raise error.general('config error: %s' % (l[7:])) elif l.startswith('%warning'): l = self._expand(l) log.stderr('warning: %s' % (l[9:])) log.warning(l[9:]) if not directive: l = self._expand(l) ls = self.tags.split(l, 1) log.trace('config: %s: _tag: %s %s' % (self.init_name, l, ls)) if len(ls) > 1: info = ls[0].lower() if info[-1] == ':': info = info[:-1] info_data = ls[1].strip() else: info_data = ls[0].strip() if info is not None: self._info_append(info, info_data) else: log.warning("invalid format: '%s'" % (info_data[:-1])) else: log.trace('config: %s: _data: %s %s' % (self.init_name, l, new_data)) new_data.append(l) return (directive, info, data + new_data)
def __init__(self, name, opts, macros = None, directives = None, ignores = None): self.opts = opts if macros is None: self.macros = opts.defaults else: self.macros = macros self.init_name = name self.directives = ['%include'] if directives: self.directives += directives self.ignores = ignores log.trace('config: %s' % (name)) self.disable_macro_reassign = False self.configpath = [] self.wss = re.compile(r'\s+') self.tags = re.compile(r':+') self.sf = re.compile(r'%\([^\)]+\)') for arg in self.opts.args: if arg.startswith('--with-') or arg.startswith('--without-'): label = arg[2:].lower().replace('-', '_') self.macros.define(label) self._includes = [] self.load_depth = 0 self.lc = 0 self.name = 'none'
def _ifs(self, config, ls, label, iftrue, isvalid, dir, info): in_iftrue = True data = [] while True: if isvalid and \ ((iftrue and in_iftrue) or (not iftrue and not in_iftrue)): this_isvalid = True else: this_isvalid = False r = self._parse(config, dir, info, roc = True, isvalid = this_isvalid) if r[0] == 'control': if r[1] == '%end': self._error(label + ' without %endif') raise error.general('terminating build') if r[1] == '%endif': log.trace('config: %s: _ifs: %s %s' % (self.init_name, r[1], this_isvalid)) return data if r[1] == '%else': in_iftrue = False elif r[0] == 'directive': if this_isvalid: if r[1] == '%include': self.load(r[2][0]) continue dir, info, data = self._process_directive(r, dir, info, data) elif r[0] == 'data': if this_isvalid: dir, info, data = self._process_data(r, dir, info, data) else: dir, info, data = self._process_block(r, dir, info, data)
def open(self, command, capture = True, shell = False, cwd = None, env = None, stdin = None, stdout = None, stderr = None, timeout = None): """Open a command with arguments. Provide the arguments as a list or a string.""" if self.verbose: s = command if type(command) is list: def add(x, y): return x + ' ' + str(y) s = functools.reduce(add, command, '')[1:] what = 'spawn' if shell: what = 'shell' log.output(what + ': ' + s) if self.output is None: raise error.general('capture needs an output handler') if shell and self.shell_exe: command = arg_list(command) command[:0] = self.shell_exe if not stdin and self.input: stdin = subprocess.PIPE if not stdout: stdout = subprocess.PIPE if not stderr: stderr = subprocess.PIPE proc = None if cwd is None: cwd = self.path if env is None: env = self.environment try: # Work around a problem on Windows with commands that # have a '.' and no extension. Windows needs the full # command name. if sys.platform == "win32" and type(command) is list: if command[0].find('.') >= 0: r, e = os.path.splitext(command[0]) if e not in ['.exe', '.com', '.bat']: command[0] = command[0] + '.exe' log.trace('exe: %s' % (command)) proc = subprocess.Popen(command, shell = shell, cwd = cwd, env = env, stdin = stdin, stdout = stdout, stderr = stderr, close_fds = False) if not capture: return (0, proc) if self.output is None: raise error.general('capture needs an output handler') exit_code = self.capture(proc, command, timeout) if self.verbose: log.output('exit: ' + str(exit_code)) except OSError as ose: exit_code = ose.errno if self.verbose: log.output('exit: ' + str(ose)) return (exit_code, proc)
def _disable(self, config, ls): if len(ls) != 2: log.warning('invalid disable statement') else: if ls[1] == 'select': self.macros.lock_read_map() log.trace('config: %s: _disable_select: %s' % (self.init_name, ls[1])) else: log.warning('invalid disable statement: %s' % (ls[1]))
def _run(self, args, check=False): e = execute.capture_execution() if path.exists(self.path): cwd = self.path else: cwd = None cmd = [self.git] + args log.trace('cmd: (%s) %s' % (str(cwd), ' '.join(cmd))) exit_code, proc, output = e.spawn(cmd, cwd=path.host(cwd)) log.trace(output) if check: self._git_exit_code(exit_code, cmd, output) return exit_code, output
def load(self, name): names = self.expand(name).split(':') for n in names: log.trace('opening: %s' % (n)) if path.exists(n): try: mc = open(path.host(n), 'r') macros = self.parse(mc) mc.close() self.files += [n] return except IOError as err: pass raise error.general('opening macro file: %s' % \ (path.host(self.expand(name))))
def start(self): '''Start the TFTP server. Returns once started.''' # pylint: disable=attribute-defined-outside-init if log.tracing: log.trace('] tftp: server: %s:%i' % (self.host, self.port)) if self.host == 'all': host = '' else: host = self.host try: self.server = udp_server((host, self.port), udp_handler) except Exception as exp: raise error.general('tftp server create: %s' % (exp)) # We cannot set tftp in __init__ because the object is created # in a separate package. self.server.tftp = self self.server_thread = threading.Thread(target=self.server.serve_forever) self.server_thread.daemon = True self.server_thread.start()
def handle(self): client_ip = self.client_address[0] client_port = self.client_address[1] client = '%s:%i' % (client_ip, client_port) session = tftp_session() finished = session.data(client_ip, client_port, self.request[0]) if not finished: timeout = session.get_timeout(self.server.proxy.session_timeout, 1) host = self.server.proxy.get_host(client_ip) if host is not None: session_count = self.server.proxy.get_session_count() log.notice(' ] %6d: session: %s -> %s: start' % (session_count, client, host)) host_ip, host_server_port = host_port_split(host) host_port = host_server_port sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(timeout) log.trace( ' > ' + session.decode(client_ip, client_port, self.request[0])) sock.sendto(self.request[0], (host_ip, host_port)) while not finished: try: data, address = sock.recvfrom(16 * 1024) except socket.error as se: log.notice(' ] session: %s -> %s: error: %s' % (client, host, se)) return except socket.gaierror as se: log.notice(' ] session: %s -> %s: error: %s' % (client, host, se)) return except: return finished = session.data(address[0], address[1], data) if address[0] == host_ip: if host_port == host_server_port: host_port = address[1] if address[1] == host_port: log.trace( ' < ' + session.decode(address[0], address[1], data)) sock.sendto(data, (client_ip, client_port)) elif address[0] == client_ip and address[1] == client_port: log.trace(' > ' + session.decode(address[0], address[1], data)) sock.sendto(data, (host_ip, host_port)) log.notice(' ] %6d: session: %s -> %s: end' % (session_count, client, host)) else: mac = getmac.get_mac_address(ip=client_ip) log.trace(' . request: host not found: %s (%s)' % (client, mac))
def host_setup(opts): """ Basic sanity check. All executables and directories must exist.""" checks = { 'none': _check_none, 'triplet': _check_triplet, 'dir': _check_dir, 'exe': _check_exe } sane = True for d in list(opts.defaults.keys()): try: (test, constraint, value) = opts.defaults.get(d) except: if opts.defaults.get(d) is None: raise error.general('invalid default: %s: not found' % (d)) else: raise error.general('invalid default: %s [%r]' % (d, opts.defaults.get(d))) if test != 'none': value = opts.defaults.expand(value) if test not in checks: raise error.general('invalid check test: %s [%r]' % (test, opts.defaults.get(d))) ok = checks[test](opts, d, value, constraint) if ok: tag = ' ' else: tag = '*' log.trace('%c %15s: %r -> "%s"' % (tag, d, opts.defaults.get(d), value)) if sane and not ok: sane = False return sane
def _check_exe(_opts, macro, value, constraint, silent=False): if len(value) == 0 or constraint == 'none': return True orig_value = value if path.isabspath(value): if path.isfile(value): return True if os.name == 'nt': if path.isfile('%s.exe' % (value)): return True value = path.basename(value) absexe = True else: absexe = False paths = os.environ['PATH'].split(os.pathsep) if _check_paths(value, paths): if absexe: if not silent: log.notice( 'warning: exe: absolute exe found in path: (%s) %s' % (macro, orig_value)) return True if constraint == 'optional': if not silent: log.trace('warning: exe: optional exe not found: (%s) %s' % (macro, orig_value)) return True if not silent: log.notice('error: exe: not found: (%s) %s' % (macro, orig_value)) return False
def handle_session(self, index): '''Handle the TFTP session data.''' # pylint: disable=too-many-locals # pylint: disable=broad-except # pylint: disable=too-many-branches # pylint: disable=too-many-statements client_ip = self.client_address[0] client_port = self.client_address[1] client = '%s:%i' % (client_ip, client_port) self._notice('] tftp: %d: start: %s' % (index, client)) try: session = tftp_session(client_ip, client_port, self.server.tftp.base, self.server.tftp.forced_file, self.server.tftp.reader) data = bytearray(self.request[0]) response = session.process(client_ip, client_port, data) if response is not None: if log.tracing and self.server.tftp.packet_trace: log.trace(' > ' + session.decode(client_ip, client_port, data)) timeout = session.get_timeout(self.server.tftp.timeout, 1) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(('', 0)) sock.settimeout(timeout) while response is not None: if log.tracing and self.server.tftp.packet_trace: log.trace( ' < ' + session.decode(client_ip, client_port, response)) sock.sendto(response, (client_ip, client_port)) if session.finished: break try: data, address = sock.recvfrom(2 + 2 + session.get_block_size()) data = bytearray(data) if log.tracing and self.server.tftp.packet_trace: log.trace( ' > ' + session.decode(address[0], address[1], data)) except socket.error as serr: if log.tracing: log.trace('] tftp: %d: receive: %s: error: %s' \ % (index, client, serr)) return except socket.gaierror as serr: if log.tracing: log.trace('] tftp: %d: receive: %s: error: %s' \ % (index, client, serr)) return response = session.process(address[0], address[1], data) except error.general as gerr: self._notice('] tftp: %dd: error: %s' % (index, gerr)) except error.internal as ierr: self._notice('] tftp: %d: error: %s' % (index, ierr)) except error.exit: pass except KeyboardInterrupt: pass except Exception as exp: if self.server.tftp.exception_is_raise: raise self._notice('] tftp: %d: error: %s: %s' % (index, type(exp), exp)) self._notice('] tftp: %d: end: %s' % (index, client))
def _notice(self, text): if self.server.tftp.notices: log.notice(text) else: log.trace(text)
def _error_response(self, code, message): if log.tracing: log.trace('tftp: error: %s:%d: %d: %s' % (self.host, self.port, code, message)) self.finished = True return self._response('ERROR', self._pack_bytes([code, message, 0]))
def _if(self, config, ls, isvalid, dir, info, invert = False): def add(x, y): return x + ' ' + str(y) istrue = False if isvalid: if len(ls) == 2: s = ls[1] else: s = (ls[1] + ' ' + ls[2]) ifls = s.split() if len(ifls) == 1: # # Check if '%if %{x} == %{nil}' has both parts as nothing # which means '%if ==' is always True and '%if !=' is always false. # if ifls[0] == '==': istrue = True elif ifls[0] == '!=': istrue = False else: istrue = _check_bool(ifls[0]) if istrue == None: self._error('invalid if bool value: ' + functools.reduce(add, ls, '')) istrue = False elif len(ifls) == 2: if ifls[0] == '!': istrue = _check_bool(ifls[1]) if istrue == None: self._error('invalid if bool value: ' + functools.reduce(add, ls, '')) istrue = False else: istrue = not istrue else: # # Check is something is being checked against empty, # ie '%if %{x} == %{nil}' # The logic is 'something == nothing' is False and # 'something != nothing' is True. # if ifls[1] == '==': istrue = False elif ifls[1] == '!=': istrue = True else: self._error('invalid if bool operator: ' + functools.reduce(add, ls, '')) elif len(ifls) == 3: if ifls[1] == '==': if ifls[0] == ifls[2]: istrue = True else: istrue = False elif ifls[1] == '!=' or ifls[1] == '=!': if ifls[0] != ifls[2]: istrue = True else: istrue = False elif ifls[1] == '>': if ifls[0] > ifls[2]: istrue = True else: istrue = False elif ifls[1] == '>=' or ifls[1] == '=>': if ifls[0] >= ifls[2]: istrue = True else: istrue = False elif ifls[1] == '<=' or ifls[1] == '=<': if ifls[0] <= ifls[2]: istrue = True else: istrue = False elif ifls[1] == '<': if ifls[0] < ifls[2]: istrue = True else: istrue = False else: self._error('invalid %if operator: ' + functools.reduce(add, ls, '')) else: self._error('malformed if: ' + functools.reduce(add, ls, '')) if invert: istrue = not istrue log.trace('config: %s: _if: %s %s' % (self.init_name, ifls, str(istrue))) return self._ifs(config, ls, '%if', istrue, isvalid, dir, info)
def _parse(self, config, dir, info, roc = False, isvalid = True): # roc = return on control def _clean(line): line = line[0:-1] b = line.find('#') if b >= 0: line = line[1:b] return line.strip() # # Need to add code to count matching '{' and '}' and if they # do not match get the next line and add to the string until # they match. This closes an opening '{' that is on another # line. # for l in config: self.lc += 1 l = _clean(l) if len(l) == 0: continue log.trace('config: %s: %03d: %s %s' % \ (self.init_name, self.lc, str(isvalid), l)) lo = l if isvalid: l = self._expand(l) if len(l) == 0: continue if l[0] == '%': ls = self.wss.split(l, 2) los = self.wss.split(lo, 2) if ls[0] == '%disable': if isvalid: self._disable(config, ls) elif ls[0] == '%select': if isvalid: self._select(config, ls) elif ls[0] == '%error': if isvalid: return ('data', ['%%error %s' % (self._name_line_msg(l[7:]))]) elif ls[0] == '%warning': if isvalid: return ('data', ['%%warning %s' % (self._name_line_msg(l[9:]))]) elif ls[0] == '%define' or ls[0] == '%global': if isvalid: self._define(config, ls) elif ls[0] == '%undefine': if isvalid: self._undefine(config, ls) elif ls[0] == '%if': d = self._if(config, ls, isvalid, dir, info) if len(d): log.trace('config: %s: %%if: %s' % (self.init_name, d)) return ('data', d) elif ls[0] == '%ifn': d = self._if(config, ls, isvalid, dir, info, True) if len(d): log.trace('config: %s: %%ifn: %s' % (self.init_name, d)) return ('data', d) elif ls[0] == '%ifos': d = self._ifos(config, ls, isvalid, dir, info) if len(d): return ('data', d) elif ls[0] == '%ifarch': d = self._ifarch(config, True, ls, isvalid, dir, info) if len(d): return ('data', d) elif ls[0] == '%ifnarch': d = self._ifarch(config, False, ls, isvalid, dir, info) if len(d): return ('data', d) elif ls[0] == '%endif': if roc: return ('control', '%endif', '%endif') log.warning("unexpected '" + ls[0] + "'") elif ls[0] == '%else': if roc: return ('control', '%else', '%else') log.warning("unexpected '" + ls[0] + "'") elif ls[0].startswith('%defattr'): return ('data', [l]) elif ls[0] == '%bcond_with': if isvalid: # # Check if already defined. Would be by the command line or # even a host specific default. # if self._label('with_' + ls[1]) not in self.macros: self._define(config, (ls[0], 'without_' + ls[1])) elif ls[0] == '%bcond_without': if isvalid: if self._label('without_' + ls[1]) not in self.macros: self._define(config, (ls[0], 'with_' + ls[1])) else: pt = self._parse_token(lo, los, l, ls) if pt is not None: return pt if self.ignores is not None: for r in self.ignores: if r.match(ls[0]) is not None: return ('data', [l]) if isvalid: for d in self.directives: if ls[0].strip() == d: return ('directive', ls[0].strip(), ls[1:]) log.warning("unknown directive: '" + ls[0] + "'") return ('data', [lo]) else: return ('data', [lo]) return ('control', '%end', '%end')
def load(self, name): def common_end(left, right): end = '' while len(left) and len(right): if left[-1] != right[-1]: return end end = left[-1] + end left = left[:-1] right = right[:-1] return end if self.load_depth == 0: self.in_error = False self.lc = 0 self.name = name self.conditionals = {} self.load_depth += 1 save_name = self.name save_lc = self.lc self.name = name self.lc = 0 # # Locate the config file. Expand any macros then add the # extension. Check if the file exists, therefore directly # referenced. If not see if the file contains ':' or the path # separator. If it does split the path else use the standard config dir # path in the defaults. # exname = self.expand(name) # # Macro could add an extension. # if exname.endswith('.cfg'): configname = exname else: configname = '%s.cfg' % (exname) name = '%s.cfg' % (name) if ':' in configname: cfgname = path.basename(configname) else: cfgname = common_end(configname, name) if not path.exists(configname): if ':' in configname: configdirs = path.dirname(configname).split(':') else: configdirs = self.define('_configdir').split(':') for cp in configdirs: configname = path.join(path.abspath(cp), cfgname) if path.exists(configname): break configname = None if configname is None: raise error.general('no config file found: %s' % (cfgname)) try: log.trace('config: %s: _open: %s' % (self.init_name, path.host(configname))) config = open(path.host(configname), 'r') except IOError as err: raise error.general('error opening config file: %s' % (path.host(configname))) self.configpath += [configname] self._includes += [configname] try: dir = None info = None data = [] while True: r = self._parse(config, dir, info) if r[0] == 'control': if r[1] == '%end': break log.warning("unexpected '%s'" % (r[1])) elif r[0] == 'directive': if r[1] == '%include': self.load(r[2][0]) continue dir, info, data = self._process_directive(r, dir, info, data) elif r[0] == 'data': dir, info, data = self._process_data(r, dir, info, data) else: self._error("%d: invalid parse state: '%s" % (self.lc, r[0])) if dir is not None: self._directive_extend(dir, data) except: config.close() raise config.close() self.name = save_name self.lc = save_lc self.load_depth -= 1