aboutsummaryrefslogtreecommitdiffstats
path: root/idatui/launch.py
diff options
context:
space:
mode:
authorblasty <peter@haxx.in>2026-09-25 14:59:48 +0200
committerblasty <peter@haxx.in>2026-09-25 14:59:48 +0200
commit15425df0a6e09984fdab3f86354620d4d8656806 (patch)
treecf92f86fd343cbfc04f19be895a337b304c576ec /idatui/launch.py
parentMerge PR #2 from mrexodia: fix pseudocode/listing tag parsing, ida-nexus 0.13.0 (diff)
downloadida-tui-15425df0a6e09984fdab3f86354620d4d8656806.tar.gz
ida-tui-15425df0a6e09984fdab3f86354620d4d8656806.tar.xz
ida-tui-15425df0a6e09984fdab3f86354620d4d8656806.zip
Offer a writable database location instead of failing the open
`ida-tui /bin/ls` asked IDA to create /bin/ls.i64, which a normal user cannot write, and what came back was the backend's account of it: idalib worker launcher 2425195 exited with status 1 [ida-nexus] Failed to open database /usr/bin/ls after a full analysis wait, naming neither the file it could not create nor the reason. Nothing in the message points at permissions, and there was no way forward from it. idatui/idbpath.py answers the question before anything is opened: where would the database go (mirroring ida_nexus's own derivation), can it go there (os.access only — the pre-flight check must not create files), and where instead. The proposal is stable per binary (~/.local/share/idatui/db/<name>-<sha1(realpath)[:8]>.i64) so reopening the same binary resumes the same names and comments, and hashed so two busyboxes from different directories cannot collide. DATA_HOME rather than CACHE_HOME on purpose: an .i64 holds the whole session, and cache directories are something cleaners may empty. The answer becomes NexusClient(output_database=...) — the option project mode already uses to keep everything in its sidecar. `--idb PATH` (also forwarded by `pane spawn`) says where without being asked. An RPC-driven TUI is never asked: `pane spawn` polls for ready and would sit out its whole timeout in front of a modal nobody can answer, so it relocates and says so in the status line. A failed open re-offers relocation after a real create probe, since os.access does not know about ACLs, a lying network mount or a full disk. tests/test_idbpath.py (pure, 32 checks) pins the path arithmetic, the two properties of the proposal and that neither check touches the user's directory; tests/test_idb_relocate_ui.py (textual, no worker, 24 checks) drives the dialog, Esc, an edited path, the untouched common case, --idb, the RPC path, and that output_database actually reaches the client. Verified end to end against a real /bin/ls: 400 functions, and a rename saved in one session comes back in the next.
Diffstat (limited to 'idatui/launch.py')
-rw-r--r--idatui/launch.py28
1 files changed, 28 insertions, 0 deletions
diff --git a/idatui/launch.py b/idatui/launch.py
index 6e3b432..01778f3 100644
--- a/idatui/launch.py
+++ b/idatui/launch.py
@@ -103,6 +103,12 @@ def main(argv: list[str] | None = None) -> int:
metavar="FILE",
help="Tenet execution trace to explore alongside the binary",
)
+ p.add_argument(
+ "--idb",
+ metavar="FILE",
+ help="keep the database here instead of beside the binary (an .i64 path; "
+ "the default for a binary in a directory you cannot write)",
+ )
g = p.add_argument_group(
"loading a headerless blob",
"An ELF/PE/Mach-O says what it is. A raw firmware dump doesn't, and IDA "
@@ -145,6 +151,27 @@ def main(argv: list[str] | None = None) -> int:
if args.ida_args:
load["ida_args"] = args.ida_args
+ # Where the database goes, for a binary whose own directory cannot hold one
+ # (``/bin/ls`` -> ``/bin/ls.i64``). Validated HERE, before anything is staged
+ # or imported, so a contradictory invocation costs nothing.
+ idb_path = None
+ if args.idb:
+ from . import idbpath
+
+ if args.project:
+ # A project already keeps every database in its sidecar, so the two
+ # options mean contradictory things. Refusing beats ignoring one.
+ _log(
+ "--idb is for a single binary; a project keeps its databases in "
+ "its sidecar"
+ )
+ return 2
+ try:
+ idb_path = idbpath.ensure_parent(args.idb)
+ except OSError as e:
+ _log(f"cannot create the directory for --idb {args.idb}: {e}")
+ return 2
+
project = None
binary = None
if args.project:
@@ -249,6 +276,7 @@ def main(argv: list[str] | None = None) -> int:
trace_path=(
os.path.abspath(os.path.expanduser(args.trace)) if args.trace else ""
),
+ idb_path=idb_path,
).run()
return 0