def sendMsgToStorage(cmd): '''Forwarder of messages to storage''' log.debug("In sendMsgToStorage - cmd: {0}".format(cmd)) try: if not util.connLock.acquire(True, 5): raise IKException( ErrorCode.operationFailed, "Failed to acquire the lock for connecting to storage.") log.debug("Sending...") connHere.send(cmd) log.debug("Sent. Receiving...") if connHere.poll(15): ret = connHere.recv() log.debug("In sendMsgToStorage - ret: {0}".format(ret)) else: ret = composeResponse( "0", "Socket timed out or no data to receive.") log.debug("Nothing to receive") util.connLock.release() except Exception as e: ret = composeResponse("0", str(e)) log.error( "In sendMsgToStorage exception received: {0}".format(ret)) return ret
def chGetItem(self, cmd): '''Get item''' feeder = self._getItemFeeder(cmd) if self.acc == df.PL_SECURE: nam = feeder.__next__() try: ret = self.fs.getItem(nam).value() return composeResponse('1', ret) except IKException as e: if e.code == ErrorCode.objectNotExists: if not self.defencread: try: self.read_sec_file() except IKException as e1: return composeResponse( '0', "Failed to read secure tokens: " + str(e1)) self.defencread = True else: raise return composeResponse('1', self.fs.getItem(nam).value()) lres = [] for i in feeder: lres.append(self.fs.getItem(i).value()) return composeResponse('1', lres if len(lres) != 1 else lres[0])
def handleCmd( cmd ): '''Switch command and intercept exceptions.''' try: bresp = CmdSwitcher.switchCmd( cmd ) except IKException as e: bresp = util.composeResponse( '0', str( e ) ) except Exception as e: bresp = util.composeResponse( '0', e.args[0] ) return bresp
def chPathExists(self, cmd): '''Return True if a token path exists''' par = cmd["params"][0] try: self.fs.getItem(par) return composeResponse() except IKException as e: if e.code == ErrorCode.objectNotExists: return composeResponse("0") else: raise
def chRemoveSection(self, cmd): '''Remove section''' feeder = self._getItemFeeder(cmd) for i in feeder: self.fs.removeItem(i).markChanged() return composeResponse()
def chRename(self, cmd): '''Rename item''' feeder = self._getItemFeeder(cmd) for i in feeder: self.fs.rename(i).markChanged() return composeResponse()
def chListItems(self, cmd): '''List items''' par = cmd.get("params", None) swNovals = df.NOVALUES in cmd swTree = df.TREE in cmd swRecur = df.RECURS in cmd lres = [] if not par: self.fs[''].listItems(lres=lres, bTree=swTree, nIndent=0, bNovals=swNovals, relPath=None, bRecur=swRecur) else: item = par[0] if item[0] != '/': item = "{0}/{1}".format( PERSPATH if df.PERS in cmd else SESPATH, item) sect = self.fs.getItem(item) sect.listItems(lres=lres, bTree=swTree, nIndent=0, bNovals=swNovals, relPath=None, bRecur=swRecur) return composeResponse('1', lres)
def chReport(self, cmd): '''Report server variables.''' par = cmd["params"][0] retCode = '1' if par == df.ACCESS: resp = "{0}".format(self.srv.acc) elif par == df.REP_STORAGE: path = None if len(cmd["params"]) > 1: if len(cmd["params"] == 2): path = cmd["params"][1] else: raise IKException(ErrorCode.unrecognizedSyntax, " ".join(cmd["params"])) resp = self.fs.stat(path) elif par == df.REP_SERVER: # TODO: bs = getShared("bytesSent") br = getShared("bytesReceived") resp = "Bytes sent: {0}\n".format(bs) resp += "Bytes received: {0}\n".format(br) elif par == df.REP_COMMANDS: resp = util.printMap(self.info["cmd"], 0) pass elif par == df.DATAFILE: resp = self.fs.datafile else: retCode = '0' resp = "Unrecognized command parameter: " + par return composeResponse(retCode, resp)
def chLoadFileSec(self, cmd): '''Rename item''' file = cmd.get("params") if not file: file = None else: file = file[0] self.read_sec_file(file) return composeResponse()
def switchCmd( cmd ): '''Switch command''' if cmd["cmd"] not in CmdSwitcher.cmdHandlers: cmd['res'] = 0 return util.composeResponse( '0', "Unrecognized command: ", cmd["cmd"] ) cmd['res'] = 1 CmdSwitcher.info["cmds"][cmd["cmd"]] = 1 + CmdSwitcher.info["cmds"].setdefault(cmd["cmd"], 0) hnd = CmdSwitcher.cmdHandlers[cmd["cmd"]] return hnd( cmd )
def processCmd(self, cmd): '''Process command. Finds the handler for the command and passes 'cmd' dictionary to it. If the handler is not found, sets the 'res' field of the dictionary to 0.''' if cmd["cmd"] not in self.cmdMap: cmd["res"] = 0 return util.composeResponse( '0', "Unrecognized command: ", cmd["cmd"] ) cmd["res"] = 1 CmdSwitcher.info["cmds"][cmd["cmd"]] = 1 + CmdSwitcher.info["cmds"].setdefault(cmd["cmd"], 0) hnd = self.cmdMap[cmd["cmd"]] return hnd( cmd )
def chGetAttr(self, cmd): '''Return item's attribute''' """Query for attributes of a storage item. Syntax: GETATTR <itempath> [attrname] Without 'attrname' returns 'st_stat' attributes like function 'getattr'. With 'attrname' works like 'getxattr'. """ attrNames = None par = cmd["params"][0] if df.ATTRS in cmd: attrNames = cmd[df.ATTRS] m = self.fs.getItemAttr(par, attrNames) return composeResponse('1', m)
def chShowLog(self, cmd): '''Show server log''' par = cmd["params"] n = 20 if par: try: n = int(par[0]) except ValueError: raise IKException( ErrorCode.unrecognizedParameter, par[0], moreInfo="Number of lines only must contain digits.") return composeResponse('1', util.getLog(n))
def chCheckServer(self, cmd): '''Check regd server''' # Polling the file storage log.debug("Checking storage...") bresp = CmdSwitcher.switchCmd({"cmd": fs.FS_CHECK}) log.debug("Storage checked.") lresp = util.parsePacket(bresp) fsresp = "Storage:\n" + str(lresp[1]) resp = "Server:\nUp and running since {0}\nUptime:{1}.\n".format( str(self.timestarted).rpartition(".")[0], str(datetime.now() - self.timestarted).rpartition(".")[0]) return composeResponse('1', resp + fsresp)
def chCreateSection(self, cmd): '''Create section''' feeder = self._getItemFeeder(cmd) for i in feeder: if not self._isPathValid(path=i, cmd=cmd): raise IKException(ErrorCode.unsupportedParameterValue, i, "Path is not valid") sec = self.fs.addItem(i) if df.ATTRS in cmd: sec.setAttrs(cmd[df.ATTRS]) sec.readFromFile(updateFromStorage=True) sec.markChanged() return composeResponse()
def chAddToken(self, cmd): '''Add item''' dest, addMode, attrs = self._getAddOptions(cmd) cnt = 0 #with self.lock_seriz: for tok in cmd["params"]: if (dest and dest.startswith( BINPATH ) ) or \ tok.startswith( BINPATH ): self.handleBinToken(dest, tok, cmd) continue # Without --dest or --pers options tokens with relative path # are always added to session tokens if not dest and tok[0] != '/': dest = SESPATH binaryVal = None if df.BINARY in cmd: if not cmd[df.BINARY] or len(cmd[df.BINARY]) < cnt + 1: raise IKException(ErrorCode.unknownDataFormat, tok) binaryVal = cmd[df.BINARY][cnt] if not attrs: attrs = {} attrs[stor.SItem.persPathAttrName] = df.BINARY cnt += 1 if dest: tok = joinPath(dest, tok) if binaryVal: tk = None pt = tok else: tk = tok pt = None if not self._isPathValid(tok=tk, path=pt, cmd=cmd): raise IKException(ErrorCode.unsupportedParameterValue, tok[:50], "Path is not valid.") sec = self.fs.addItem(tok=tok, addMode=addMode, binaryVal=binaryVal, attrs=attrs) if sec: sec.markChanged() return composeResponse()
def chInfo(self, cmd): '''Report server information''' # pylint: disable=unused-variable accl = getShared("accLevel") dfile = getShared("dataFile") sfile = getShared("sockFile") resp = "{0} : {1}\n".format(app.APPNAME, df.__description__) resp += "License: {0}\n".format(df.__license__) resp += "Homepage: {0}\n\n".format(df.__homepage__) resp += "Server version: {0}\n".format(df.rversion) resp += "Server datafile: {0}\n".format(dfile) resp += "Server access: {0}\n".format(accl) resp += "Server socket file: {0}\n".format(sfile) return composeResponse("1", resp)
def listenForMessages(self, func): while self.cont: if self.conn.poll(30): try: cmd = self.conn.recv() try: resp = self.processCmd( cmd ) except IKException as e: resp = util.composeResponse( "0", "In listenForMessages - exception received: {0}. Continue listening...".format( e ) ) log.error( resp ) self.conn.send( resp ) except Exception as e: fh = io.StringIO() traceback.print_exc( file=fh ) log.error( "In listenForMessages - fatal exception received: {0}. Quit listening...".format( fh.getvalue() ) ) raise else: func()
def chSetAttr(self, cmd): '''Set an item's attribute''' """Set attributes of a storage item. Syntax: SETATTR <itempath> <attrname=attrval ...>""" par = cmd["params"][0] item = self.fs.setItemAttr(par, cmd[df.ATTRS]) item.markChanged() # When the backing storage path attribute is set, the storage file # is overwritten with the current content of the item. In order to # read the file contents into an item, loadFile function is used. if [ x for x in cmd[df.ATTRS] if x.startswith(stor.SItem.persPathAttrName + "=") ]: # TODO: move the call to fs.serialize to item, where thread sync is #with self.lock_seriz: self.fs.serialize() return composeResponse()
def chLoadFile(self, cmd): '''Load tokens from a file.''' dest, addMode, attrs = self._getAddOptions(cmd) if not dest: dest = SESPATH swFromPars = df.FROM_PARS in cmd if not swFromPars: for filename in cmd["params"]: if os.path.exists(filename): self.fs.readFromFile(filePath=filename, dest=dest, addMode=addMode) else: raise IKException(ErrorCode.objectNotExists, filename, "File not found") else: for file in cmd["params"]: fh = io.BytesIO(file.encode()) self.fs.readFromFile(fh=fh, dest=dest, addMode=addMode) return composeResponse()
def chCopyFile(self, cmd): '''Copy file contents to an item''' dest, addMode, attrs = self._getAddOptions(cmd) swFromPars = df.FROM_PARS in cmd src = cmd["params"][0] dst = cmd["params"][1] ret = None if dst[0] == ':': # cp from file to token if not self._isPathValid(path=dst[1:], cmd=cmd): raise IKException(ErrorCode.unsupportedParameterValue, dst[1:], "Path is not valid") if swFromPars: val = src else: with open(src) as f: val = f.read() tok = dst[1:] + " =" + val self.fs.addItem(tok=util.joinPath(dest, tok), addMode=addMode, attrs=attrs) ret = "" elif src[0] == ':': # cp from token to file src = src[1:] if src[0] != '/': src = "{0}/{1}".format(PERSPATH if df.PERS in cmd else SESPATH, src) else: if not self._isPathValid(path=src, cmd=cmd): raise IKException(ErrorCode.unsupportedParameterValue, src, "Path is not valid") # File is written on the client side ret = self.fs.getItem(src).val return composeResponse('1', ret)
def chFsStop(self, cmd): self.cont = False log.info("Stopping storage... Serializing...") self.serialize() log.info("Storage exiting.") return composeResponse()
def chFsCheck(self, cmd): resp = "Up and running since {0}\nUptime:{1}.".format( str(self.timestarted).rpartition(".")[0], str(datetime.now() - self.timestarted).rpartition(".")[0]) return composeResponse('1', resp)
def chClearSessionTokens(self, cmd): '''Clear session tokens''' self.clearTokensSec() self.clearSessionTokens() return composeResponse()
def chVersion(self, cmd): '''Return regd version.''' return composeResponse('1', "Regd server version: " + df.__version__)
def chStopServer( self, cmd ): '''Stop regd server''' log.debug("In chStopServer") return composeResponse( '1', "Exiting..." )
def _handle_connection( self, connection, client_address, storage_lock ): '''Connection handler''' if not self.host: creds = connection.getsockopt( socket.SOL_SOCKET, socket.SO_PEERCRED, struct.calcsize( "3i" ) ) pid, uid, gid = struct.unpack( "3i", creds ) log.debug( "new connection: pid: {0}; uid: {1}; gid: {2}".format( pid, uid, gid ) ) else: log.debug( "new connection: client address: %s" % ( str( client_address ) ) ) connection.settimeout( 5 ) data = bytearray() util.recvPack( connection, data ) bytesReceived = len( data ) log.debug( "data: %s" % ( data[:1000] ) ) data = data[10:] # .decode( 'utf-8' ) bresp = bytearray() cmd = None perm = False # dcmd - command dictionary. Contains three fields: # cmd - command name, received from client # params - command parameters, received from client # res - internal command processing result, set by the command processor or # command handler. This result has the server semantics, rather than the # command semantics: if it's 0 - this means a general program error, e.g. # non-existing command name. It's meant to be handled by the server before # sending response to the client. try: dcmd = util.parsePacket( data ) # 'internal' switch is only used within regd server if "internal" in dcmd: raise Exception("Unrecognized syntax.") cmd = dcmd["cmd"] log.debug( "command received: {0}".format( cmd ) ) except Exception as e: bresp = composeResponse( "0", "Exception while parsing the command: " + str( e ) ) else: # Check permission and persistency if self.host: # IP-based server if not self.trustedIps: perm = True else : try: clientIp = ipaddress.IPv4Address( client_address[0] ) except ValueError: log.error( "Client IP address format is not recognized." ) else: for i in self.trustedIps: if clientIp in i: perm = True log.debug( "Client IP is trusted." ) break if not perm: log.error( "Client IP is NOT trusted : '%s : %s" % ( client_address[0], client_address[1] ) ) else: # File socket server if self.useruid == uid: perm = True elif uid in self.trustedUserids: perm = True elif cmd not in defs.secure_cmds: if self.acc == defs.PL_PUBLIC: perm = True elif self.acc == defs.PL_PUBLIC_READ: if cmd in defs.pubread_cmds: perm = True log.debug( "perm: {0}".format( perm ) ) if not perm: bresp = composeResponse( "0", str( IKException( ErrorCode.permissionDenied, cmd ) ) ) #elif not self.datafile and defs.PERS in cmd: # bresp = composeResponse( "0", str( IKException( ErrorCode.operationFailed, None, "Persistent tokens are not enabled." ) ) ) if not len( bresp ): util.connLock = storage_lock bresp = CmdSwitcher.handleCmd( dcmd ) try: bytesSent = util.sendPack( connection, bresp ) except OSError as er: log.error( "Socket error {0}: {1}\nClient address: {2}\n".format( er.errno, er.strerror, client_address ) ) else: if type( bytesSent ) is int: info.setShared( "bytesReceived", bytesReceived, defs.SUM ) info.setShared( "bytesSent", bytesSent, defs.SUM ) if cmd == defs.STOP_SERVER and perm: self.sigsock_w.send("stop".encode()) return