def __enter__(self): if os.getuid() != 0: if (self._uname and self._uname != pwd.getpwnam(self._uname).pw_name or self._gname and self._gname != grp.getgrnam(self._gname).gr_name): raise error.SnmpResponderError( 'Process is running under different UID/GID') else: return else: if not self._uname or not self._gname: raise error.SnmpResponderError( 'Must drop privileges to a non-privileged user&group') try: runningUid = pwd.getpwnam(self._uname).pw_uid runningGid = grp.getgrnam(self._gname).gr_gid except Exception: raise error.SnmpResponderError( 'getpwnam()/getgrnam() failed for %s/%s: %s' % (self._uname, self._gname, sys.exc_info()[1])) try: os.setgroups([]) except Exception: raise error.SnmpResponderError('setgroups() failed: %s' % sys.exc_info()[1]) try: if self._final: os.setgid(runningGid) os.setuid(runningUid) else: self._olduid = os.getuid() self._oldgid = os.getgid() os.setegid(runningGid) os.seteuid(runningUid) except Exception: raise error.SnmpResponderError( '%s failed for %s/%s: %s' % (self._final and 'setgid()/setuid()' or 'setegid()/seteuid()', runningGid, runningUid, sys.exc_info()[1])) os.umask(63) # 0077
def __exit__(self, *args): if self._olduid is None or self._oldgid is None: return try: os.setegid(self._oldgid) os.seteuid(self._olduid) except Exception: raise error.SnmpResponderError( 'setegid()/seteuid() failed for %s/%s: %s' % (self._oldgid, self._olduid, sys.exc_info()[1]))
def getAttrValue(self, attr, *nodes, **kwargs): scope = nodes while scope: obj = self.traverse([self.objects], scope) if obj and attr in obj: expect = kwargs.get('expect') if 'vector' in kwargs: if expect: try: return [expect(x) for x in obj[attr]] except Exception: raise error.SnmpResponderError( '%s value casting error at scope "%s" attribute "%s"' % (self, '.'.join(nodes), attr)) else: return obj[attr] else: if obj[attr]: if expect: try: return expect(obj[attr][0]) except Exception: raise error.SnmpResponderError( '%s value casting error at scope "%s" attribute "%s"' % (self, '.'.join(nodes), attr)) else: return obj[attr][0] else: return '' scope = scope[:-1] if 'default' in kwargs: return kwargs['default'] else: raise error.SnmpResponderError( '%s non-existing attribute "%s" at scope "%s"' % (self, attr, '.'.join(nodes)))
def load(self, filename): try: self.lines = open(filename).readlines() except OSError: raise error.SnmpResponderError('cant open config file %s: %s' % (filename, sys.exc_info()[1])) self.tokens = [] while self.lines: line = self.lines.pop(0) if line and line[0] == '#': continue tokens = re.findall(r'(?:[^\s,"]|"(?:\\.|[^"])*")+', line) for i in range(len(tokens)): if tokens[i] and tokens[i][0] == '"' and tokens[i][-1] == '"': tokens[i] = tokens[i][1:-1] if not tokens or not tokens[0] or tokens[0][0] == '#': continue for token in tokens: # Figure out the grammar type of the token if token and token[-1] == SYMBOL_OPTION: # It's an option symbol = SYMBOL_OPTION # Cut the trailing char from token token = token[:-1] elif token == '{': symbol = SYMBOL_SECTION_BEGIN elif token == '}': symbol = SYMBOL_SECTION_END else: symbol = SYMBOL_WORD # Attach read tokens to list of tokens self.tokens.append((token, symbol)) self.index = 0 self.length = len(self.tokens) return self
def loadPlugin(self, pluginId, pluginModuleName, pluginOptions): if pluginId in self.__plugins: raise error.SnmpResponderError('duplicate plugin ID %s' % pluginId) for pluginModulesDir in self.__path: log.info('scanning "%s" directory for plugin modules...' % pluginModulesDir) if not os.path.exists(pluginModulesDir): log.error('directory "%s" does not exist' % pluginModulesDir) continue modPath = os.path.join(pluginModulesDir, pluginModuleName + '.py') if not os.path.exists(modPath): log.error('Variation module "%s" not found' % modPath) continue ctx = { 'modulePath': modPath, 'moduleContext': {}, 'moduleOptions': pluginOptions } modData = open(modPath).read() try: exec(compile(modData, modPath, 'exec'), ctx) except Exception: raise error.SnmpResponderError( 'plugin module "%s" execution failure: %s' % (modPath, sys.exc_info()[1])) else: pluginModule = ctx try: if self.__progId not in pluginModule['hostProgs']: log.error( 'ignoring plugin module "%s" (unmatched program ID)' % modPath) continue if self.__apiVer not in pluginModule['apiVersions']: log.error( 'ignoring plugin module "%s" (incompatible API version)' % modPath) continue except KeyError: log.error( 'ignoring plugin module "%s" (missing versioning info)' % modPath) continue self.__plugins[pluginId] = pluginModule log.info('plugin module "%s" loaded' % modPath) break else: raise error.SnmpResponderError( 'plugin module "%s" not found in search path(s): %s' % (pluginModuleName, ', '.join(self.__path)))
def daemonize(pidfile): try: pid = os.fork() if pid > 0: # exit first parent os._exit(0) except OSError: raise error.SnmpResponderError('ERROR: fork #1 failed: %s' % sys.exc_info()[1]) # decouple from parent environment try: os.chdir('/') os.setsid() except OSError: pass os.umask(0) # do second fork try: pid = os.fork() if pid > 0: # exit from second parent os._exit(0) except OSError: raise error.SnmpResponderError('ERROR: fork #2 failed: %s' % sys.exc_info()[1]) def signal_cb(s, f): raise KeyboardInterrupt for s in signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT: signal.signal(s, signal_cb) # write pidfile def atexit_cb(): try: if pidfile: os.remove(pidfile) except OSError: pass atexit.register(atexit_cb) try: if pidfile: fd, nm = tempfile.mkstemp(dir=os.path.dirname(pidfile)) os.write(fd, ('%d\n' % os.getpid()).encode('utf-8')) os.close(fd) os.rename(nm, pidfile) except Exception: raise error.SnmpResponderError('Failed to create PID file %s: %s' % (pidfile, sys.exc_info()[1])) # redirect standard file descriptors sys.stdout.flush() sys.stderr.flush() si = open(os.devnull, 'r') so = open(os.devnull, 'a+') se = open(os.devnull, 'a+') os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno())
def daemonize(pidfile): raise error.SnmpResponderError( 'Windows is not inhabited with daemons!')
def load_section(self): obj = {'_name': '', '_children': []} state = 'FSM_START' while 1: # Initial state if state == 'FSM_START': try: token, symbol = self.scanner.get_token() except error.EofError: state = 'FSM_STOP' continue self.scanner.unget_token() # See if it's object closure sign if symbol == SYMBOL_SECTION_END: state = 'FSM_SECTION_END' # See if it's symbol sign elif symbol == SYMBOL_OPTION: state = 'FSM_OPTION_NAME' # Default is to start from parsing up new section else: state = 'FSM_SECTION_NAME' # If object name expected elif state == 'FSM_SECTION_NAME': self.scanner.get_token() self.scanner.unget_token() # Move to next FSM state state = 'FSM_SECTION_BEGIN' # If object body delimiter expected elif state == 'FSM_SECTION_BEGIN': self.scanner.get_token() # Get section begin token, symbol = self.scanner.get_token() # Now unget these tokens to be used at the # next FSM state self.scanner.unget_token() self.scanner.unget_token() # Make sure it's object's body start sign if symbol != SYMBOL_SECTION_BEGIN: raise error.SnmpResponderError( '%s missing object beginning sign: %s' % (self, token)) state = 'FSM_CHILD_BEGIN' # If inclusive object expected elif state == 'FSM_CHILD_BEGIN': name, symbol = self.scanner.get_token() self.scanner.get_token() child_object = self.load_section() child_object['_name'] = name # Attach child object to the list of enclosed objects obj['_children'].append(child_object) state = 'FSM_CHILD_END' # If object body closure delimiter expected elif state == 'FSM_CHILD_END': # Get next token token, symbol = self.scanner.get_token() # Make sure it's object's body end sign if symbol != SYMBOL_SECTION_END: raise error.SnmpResponderError( '%s missing object closure sign: %s' % (self, token)) # Move to the beginning of FSM state = 'FSM_START' # If object body closure delimiter expected elif state == 'FSM_SECTION_END': # Get next token token, symbol = self.scanner.get_token() # Now unget token to be used at upper level FSM instance self.scanner.unget_token() # Make sure it's object's body end sign if symbol != SYMBOL_SECTION_END: raise error.SnmpResponderError( '%s missing object closure sign: %s' % (self, token)) # Move to next FSM state state = 'FSM_STOP' # If attribute name expected elif state == 'FSM_OPTION_NAME': # Get next token token, symbol = self.scanner.get_token() # See if this attribute does not yet exist if token in obj: raise error.SnmpResponderError( '%s multiple option occurrence: %s' % (self, token)) # Accept token as attribute name obj[token] = [] # Now unget token to be used at the next FSM state self.scanner.unget_token() # Move to next FSM state state = 'FSM_OPTION_VALUE' # If option value expected elif state == 'FSM_OPTION_VALUE': option, symbol = self.scanner.get_token() # Read up one or more option values while 1: try: token, symbol = self.scanner.get_token() except error.EofError: state = 'FSM_STOP' break # If it's not a plain word if symbol != SYMBOL_WORD: self.scanner.unget_token() # See if it's object begin symbol if symbol == SYMBOL_SECTION_BEGIN: # Unget section begin sign self.scanner.unget_token() # Remove previously added last value of # the list as it turned to be section name del obj[option][-1] # Move to the beginning of FSM state = 'FSM_START' break # Accept token as attribute value if token.lower()[:2] == '0x': token = str(OctetString(hexValue=token[2:])) obj[option].append(token) # If FSM is gonna stop elif state == 'FSM_STOP': # Return object loaded return obj # If this FSM state is not known else: raise error.SnmpResponderError('%s unknown FSM state: %s' % (self, state))
def unget_token(self): if not self.index: raise error.SnmpResponderError('%s nothing to unget' % self) self.index -= 1
def parse(self): try: return self.load_section() except error.EofError: raise error.SnmpResponderError( '%s premature EOF while reading config file' % self)