From 38fa0a1f6907fa1ad119b6f14575454909617575 Mon Sep 17 00:00:00 2001 From: fabian Date: Mon, 20 Jun 2022 15:59:13 +0200 Subject: [PATCH 1/2] feat(db): store sessions in the database and use this information for session changes --- src/gallia/command/uds.py | 7 +++--- src/gallia/commands/scan/uds/sessions.py | 16 ++++++++++--- src/gallia/db/handler.py | 30 +++++++++++++++++++++++- src/gallia/services/uds/ecu.py | 24 ++++++++++++++++++- 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/gallia/command/uds.py b/src/gallia/command/uds.py index 9c31c23b3..3ed09563d 100644 --- a/src/gallia/command/uds.py +++ b/src/gallia/command/uds.py @@ -115,10 +115,7 @@ def implicit_logging(self, value: bool) -> None: self._apply_implicit_logging_setting() def _apply_implicit_logging_setting(self) -> None: - if self._implicit_logging: - self.ecu.db_handler = self.db_handler - else: - self.ecu.db_handler = None + self.ecu.implicit_logging = self._implicit_logging async def setup(self, args: Namespace) -> None: await super().setup(args) @@ -132,6 +129,8 @@ async def setup(self, args: Namespace) -> None: power_supply=self.power_supply, ) + self.ecu.db_handler = self.db_handler + if self.db_handler is not None: try: # No idea, but str(args.target) fails with a strange traceback. diff --git a/src/gallia/commands/scan/uds/sessions.py b/src/gallia/commands/scan/uds/sessions.py index db2bc358f..9865f01c6 100644 --- a/src/gallia/commands/scan/uds/sessions.py +++ b/src/gallia/commands/scan/uds/sessions.py @@ -62,7 +62,7 @@ async def set_session_with_hooks_handling( self, session: int, use_hooks: bool ) -> NegativeResponse | DiagnosticSessionControlResponse: resp = await self.ecu.set_session( - session, config=UDSRequestConfig(skip_hooks=True) + session, config=UDSRequestConfig(skip_hooks=True), use_db=False ) if ( @@ -78,7 +78,7 @@ async def set_session_with_hooks_handling( return resp resp_ = await self.ecu.set_session( - session, config=UDSRequestConfig(skip_hooks=False) + session, config=UDSRequestConfig(skip_hooks=False), use_db=False ) if isinstance(resp, NegativeResponse): @@ -168,7 +168,7 @@ async def main(self, args: Namespace) -> None: await self.ecu.reconnect() try: - resp = await self.ecu.set_session(0x01) + resp = await self.ecu.set_session(0x01, use_db=False) if isinstance(resp, NegativeResponse): self.logger.error( f"Could not change to default session: {resp}" @@ -240,6 +240,11 @@ async def main(self, args: Namespace) -> None: previous_session = session self.logger.result(f"* Session {g_repr(session)} ") + if self.db_handler is not None: + await self.db_handler.insert_session_transition( + session, res["stack"] + ) + self.logger.result( f"\tvia stack: {'->'.join([f'{g_repr(i)}' for i in res['stack']])}" ) @@ -260,6 +265,11 @@ async def main(self, args: Namespace) -> None: previous_session = session self.logger.result(f"* Session {g_repr(session)} ") + if self.db_handler is not None: + await self.db_handler.insert_session_transition( + session, res["stack"] + ) + self.logger.result( f"\tvia stack: {'->'.join([f'{g_repr(i)}' for i in res['stack']])} " f"(NRC: {res['error']})" diff --git a/src/gallia/db/handler.py b/src/gallia/db/handler.py index 81007a52b..f34f13489 100644 --- a/src/gallia/db/handler.py +++ b/src/gallia/db/handler.py @@ -97,6 +97,11 @@ def bytes_repr(data: bytes) -> str: exception text ); CREATE INDEX IF NOT EXISTS ix_scan_result_request_pdu ON scan_result(request_pdu); +CREATE TABLE IF NOT EXISTS session_transition ( + run int not null references scan_run(id) on update cascade on delete cascade, + destination int not null, + steps json check(json_valid(steps)) +); CREATE VIEW IF NOT EXISTS run_stats AS SELECT @@ -364,7 +369,7 @@ async def insert_scan_result( ) async def execute() -> None: - assert self.connection is not None + assert self.connection is not None, "Not connected to the database" done = False @@ -381,3 +386,26 @@ async def execute() -> None: await self.connection.commit() self.tasks.append(asyncio.create_task(execute())) + + async def insert_session_transition( + self, destination: int, steps: list[int] + ) -> None: + assert self.connection is not None, "Not connected to the database" + + query = "INSERT INTO session_transition VALUES(?, ?, ?)" + parameters = (self.scan_run, destination, json.dumps(steps)) + await self.connection.execute(query, parameters) + + async def get_session_transition(self, destination: int) -> list[int] | None: + assert self.connection is not None, "Not connected to the database" + + query = "SELECT steps FROM session_transition WHERE destination = ?" + parameters = (destination,) + cursor: aiosqlite.Cursor = await self.connection.execute(query, parameters) + row = await cursor.fetchone() + + if row is None: + return None + + result: list[int] = json.loads(row[0]) + return result diff --git a/src/gallia/services/uds/ecu.py b/src/gallia/services/uds/ecu.py index 5f3aaa317..09138f290 100644 --- a/src/gallia/services/uds/ecu.py +++ b/src/gallia/services/uds/ecu.py @@ -69,6 +69,7 @@ def __init__( self.power_supply = power_supply self.state = ECUState() self.db_handler: DBHandler | None = None + self.implicit_logging = True async def connect(self) -> None: ... @@ -264,6 +265,7 @@ async def set_session( self, level: int, config: UDSRequestConfig | None = None, + use_db: bool = True, ) -> service.NegativeResponse | service.DiagnosticSessionControlResponse: config = config if config is not None else UDSRequestConfig() @@ -272,6 +274,26 @@ async def set_session( resp = await self.diagnostic_session_control(level, config=config) + if ( + isinstance(resp, service.NegativeResponse) + and self.db_handler is not None + and use_db + ): + self.logger.debug( + "Could not switch to session. Trying with database transitions ..." + ) + + if self.db_handler is not None: + steps = await self.db_handler.get_session_transition(level) + + self.logger.debug(f"Found the following steps in database: {steps}") + + if steps is not None: + for step in steps: + await self.set_session(step, use_db=False) + + resp = await self.diagnostic_session_control(level, config=config) + if not isinstance(resp, service.NegativeResponse) and not config.skip_hooks: await self.set_session_post(level, config=config) @@ -483,7 +505,7 @@ async def _request( raise finally: try: - if self.db_handler is not None: + if self.implicit_logging and self.db_handler is not None: mode = LogMode.implicit if ( From c562a7af2cc14da55ef2b2bf1b4d3d448b1be6a3 Mon Sep 17 00:00:00 2001 From: fabian Date: Mon, 14 Nov 2022 16:34:25 +0100 Subject: [PATCH 2/2] feat(db): returned session transitions restricted to address of current run and session list --- src/gallia/db/handler.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/gallia/db/handler.py b/src/gallia/db/handler.py index f34f13489..22b658702 100644 --- a/src/gallia/db/handler.py +++ b/src/gallia/db/handler.py @@ -135,6 +135,7 @@ def __init__(self, database: Path): self.path = database self.connection: aiosqlite.Connection | None = None self.scan_run: int | None = None + self.target: str | None = None self.discovery_run: int | None = None self.meta: int | None = None self.logger = get_logger("db") @@ -239,6 +240,7 @@ async def insert_scan_run(self, target: str) -> None: cursor = await self.connection.execute(query, (target, self.meta)) self.scan_run = cursor.lastrowid + self.target = target await self.connection.commit() async def insert_scan_run_properties_pre( @@ -396,11 +398,36 @@ async def insert_session_transition( parameters = (self.scan_run, destination, json.dumps(steps)) await self.connection.execute(query, parameters) + async def get_sessions(self) -> list[int]: + assert self.connection is not None, "Not connected to the database" + assert self.target is not None, "Scan run not yet created, target unknown" + + query = ( + "SELECT DISTINCT destination " + "FROM session_transition st, " + " scan_run sr, " + " address ad " + "WHERE st.run = sr.id AND sr.address = ad.id " + "AND ad.url = ?" + ) + parameters = (self.target,) + + cursor: aiosqlite.Cursor = await self.connection.execute(query, parameters) + return list(x[0] for x in await cursor.fetchall()) + async def get_session_transition(self, destination: int) -> list[int] | None: assert self.connection is not None, "Not connected to the database" + assert self.target is not None, "Scan run not yet created, target unknown" - query = "SELECT steps FROM session_transition WHERE destination = ?" - parameters = (destination,) + query = ( + "SELECT steps " + "FROM session_transition st, " + " scan_run sr, " + " address ad " + "WHERE st.run = sr.id AND sr.address = ad.id " + "AND st.destination = ? AND ad.url = ?" + ) + parameters = (destination, self.target) cursor: aiosqlite.Cursor = await self.connection.execute(query, parameters) row = await cursor.fetchone()