Conversation
- Add `--sensors` flag to expose `/sys/class/power_supply` and `/sys/class/thermal` to the container. - Add `--gpu` flag (alias for `--hw-access`) which now triggers specific GPU device scanning and permission fixes. - Update `mount.c` to scan for Mali, Adreno, DMA heaps, and DRI devices when `--hw-access`/`--gpu` is enabled, and `chmod 0666` them for container access. - Add `gpu-check` command to diagnose GPU availability and permissions. - Update `main.c` and `droidspace.h` to support new flags and commands. - Update `check.c` with `print_gpu_check` implementation. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds GPU probing/permission updates, optional RW exposure of battery/thermal sensors, new CLI flags and a gpu-check command, network-namespace orchestration (host/NAT/macvlan) with Init↔Monitor handshake, setup_dev signature change to accept config, and Android UI/state plumbing for sensors and network mode. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant CLI as "droidspaces CLI"
participant Monitor as "Monitor"
participant Init as "Init (container)"
participant Mount as "mount/setup_dev"
participant Net as "ds_configure_network_namespace"
participant Kernel as "Kernel (/dev, /sys, net)"
User->>CLI: start container (--gpu/--sensors/--network-mode) or gpu-check
CLI->>Monitor: spawn monitor (passes cfg)
Monitor->>Init: fork init, share cfg and pipes
Init->>Kernel: if sensors -> list sys/class/power_supply & sys/class/thermal
Init->>Kernel: bind-mount sensor paths RW
Init->>Mount: call setup_dev(cfg)
Mount->>Kernel: mount devtmpfs and scan /dev
Mount->>Kernel: chmod/chown GPU nodes per cfg->gpu_mode/gpu_group
Init->>Monitor: signal init_ready via pipe
Monitor->>Net: call ds_configure_network_namespace(container_pid, cfg)
Net->>Kernel: create veth/macvlan, set host IP and NAT (if NAT)
Net->>Init: move peer into container netns and signal network-ready
Init->>Kernel: inside netns -> rename iface, set MAC/IP, bring up
CLI->>CLI: if gpu-check -> call print_gpu_check()
print_gpu_check->>Kernel: probe device nodes and permissions
CLI-->>User: display results / exit
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/main.c (1)
49-50: Consider documenting the short options-gand-s.The help text shows
--gpuand--sensorsbut doesn't mention that-gand-sare available as short forms, unlike other options (e.g.,-r, --rootfs=PATH).📝 Suggested documentation update
- printf(" --gpu Enable GPU access (alias for --hw-access)\n"); - printf(" --sensors Expose battery/thermal sensors to container\n"); + printf(" -g, --gpu Enable GPU access (alias for --hw-access)\n"); + printf(" -s, --sensors Expose battery/thermal sensors to container\n");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.c` around lines 49 - 50, The help text for the GPU and sensors options currently only shows the long flags (--gpu and --sensors); update the corresponding printf calls in main.c that print those lines to also include the short option forms (-g and -s) (i.e., change the strings for the printf invocations that contain "--gpu" and "--sensors" so they list "-g, --gpu" and "-s, --sensors" respectively) so the short aliases are documented consistently with other options like "-r, --rootfs=PATH".src/mount.c (1)
149-183: Consider using stricter pattern matching for GPU device detection.The
strstrmatching is fairly broad and could match unintended devices. For example,strstr(entry->d_name, "dri")would match any device name containing "dri" as a substring. While false positives are unlikely in/dev, a stricter approach using exact prefix/suffix matching or regex would be safer.Additionally, the directory recursion only handles one level deep, which is appropriate for
/dev/dri/*and/dev/dma_heap/*, but worth noting.♻️ Optional: Use exact name matching for known devices
- const char *gpu_patterns[] = {"mali", "kgsl", "dri", "dma_heap", NULL}; + /* Match exact names or prefixes for known GPU devices */ + const char *gpu_exact[] = {"dri", "dma_heap", NULL}; + const char *gpu_prefix[] = {"mali", "kgsl", NULL}; DIR *dir = opendir(dev_path); if (dir) { struct dirent *entry; int found_gpu = 0; while ((entry = readdir(dir)) != NULL) { - for (int i = 0; gpu_patterns[i]; i++) { - if (strstr(entry->d_name, gpu_patterns[i])) { + int match = 0; + for (int i = 0; gpu_exact[i]; i++) { + if (strcmp(entry->d_name, gpu_exact[i]) == 0) { + match = 1; + break; + } + } + if (!match) { + for (int i = 0; gpu_prefix[i]; i++) { + if (strncmp(entry->d_name, gpu_prefix[i], strlen(gpu_prefix[i])) == 0) { + match = 1; + break; + } + } + } + if (match) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/mount.c` around lines 149 - 183, The GPU detection loop uses strstr(entry->d_name, gpu_patterns[i]) which can match unintended substrings; change it to stricter matching by replacing strstr checks with explicit prefix/exact checks (e.g., use strncmp(entry->d_name, pattern, strlen(pattern)) == 0 for known prefixes like "mali"/"kgsl" and strcmp == 0 or fnmatch for exact names like "dma_heap"/"dri" entries), keep using opendir/readdir/stat and chmod as-is, and ensure the same logic around S_ISDIR handling (the blocks around gpu_patterns, stat, chmod, found_gpu) is updated to use the new matching so only intended device names are processed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/check.c`:
- Around line 218-219: print_gpu_check calls check_gpu_node("/dev/genlock", ...)
but mount.c's GPU device pattern list (the array/constant used by the routine
that applies --gpu permission fixes, e.g., the GPU device patterns like "mali",
"kgsl", "dri", "dma_heap" used by is_gpu_device or the GPU pattern array in
mount.c) does not include "genlock", so --gpu won't fix it; to fix, add
"genlock" to that GPU device pattern list in mount.c (the same array/constant
used by the permission-fix code) so substring matching will catch /dev/genlock,
ensuring print_gpu_check output and --gpu behavior are consistent with
check_gpu_node("/dev/genlock", "Genlock Device").
---
Nitpick comments:
In `@src/main.c`:
- Around line 49-50: The help text for the GPU and sensors options currently
only shows the long flags (--gpu and --sensors); update the corresponding printf
calls in main.c that print those lines to also include the short option forms
(-g and -s) (i.e., change the strings for the printf invocations that contain
"--gpu" and "--sensors" so they list "-g, --gpu" and "-s, --sensors"
respectively) so the short aliases are documented consistently with other
options like "-r, --rootfs=PATH".
In `@src/mount.c`:
- Around line 149-183: The GPU detection loop uses strstr(entry->d_name,
gpu_patterns[i]) which can match unintended substrings; change it to stricter
matching by replacing strstr checks with explicit prefix/exact checks (e.g., use
strncmp(entry->d_name, pattern, strlen(pattern)) == 0 for known prefixes like
"mali"/"kgsl" and strcmp == 0 or fnmatch for exact names like "dma_heap"/"dri"
entries), keep using opendir/readdir/stat and chmod as-is, and ensure the same
logic around S_ISDIR handling (the blocks around gpu_patterns, stat, chmod,
found_gpu) is updated to use the new matching so only intended device names are
processed.
- Implemented strict GPU device matching in `src/mount.c` (mali/kgsl prefix, dri/dma_heap/genlock exact). - Added `genlock` support for Adreno GPUs. - Updated `src/main.c` help text to show short flags (-g, -s). - Added `gpu-check` command to verify driver availability. - Added `--sensors` flag to expose battery/thermal info. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/mount.c (1)
174-189: Recursive depth is limited to one level—verify this covers all target devices.The code handles directories like
/dev/driand/dev/dma_heapby iterating their immediate contents. This is correct for the known device hierarchy. However, if future GPU drivers introduce deeper nesting (e.g.,/dev/dri/by-path/...), they would not be covered.Given that the current device landscape matches this structure, this is acceptable. Consider adding a comment noting the single-level assumption for future maintainers.
📝 Optional: Add clarifying comment
if (S_ISDIR(st.st_mode)) { - /* Recursively chmod directory contents */ + /* Chmod immediate directory contents (single level). + * Known GPU directories (dri, dma_heap) have flat structures. */ DIR *sub = opendir(full_path);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/mount.c` around lines 174 - 189, The directory handling only applies one-level recursion: when S_ISDIR(st.st_mode) you open the directory with opendir(full_path) and chmod each immediate entry via sub_p and chmod(sub_p, 0666), so deeper nested paths (e.g., /dev/dri/by-path/...) won’t be processed; leave logic as-is but add a concise comment above this block clarifying the intentional single-level assumption (mentioning S_ISDIR, opendir(full_path), readdir loop and chmod(sub_p, ...)) so future maintainers understand this limitation and can change to a recursive walk if deeper nesting becomes required.src/main.c (1)
110-111: Pass 1 optstring does not include short optionsgs.The first-pass optstring on line 132 (
"+r:i:n:p:h:d:fHISPvVB:") doesn't includegors, while pass 2 (line 141) does. This works correctly because:
opterr=0suppresses errors for unrecognized options- The
long_optionsarray still handles--gpu/--sensorsin pass 1- optind is reset before pass 2
For consistency and future maintainability, consider updating pass 1's optstring to match, though the current implementation is functionally correct.
🔧 Optional: Sync pass 1 optstring
- while (getopt_long(argc, argv, "+r:i:n:p:h:d:fHISPvVB:", long_options, + while (getopt_long(argc, argv, "+r:i:n:p:h:d:fHISPvVB:gs", long_options, NULL) != -1)Also applies to: 141-141
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.c` around lines 110 - 111, The first getopt pass uses the optstring "+r:i:n:p:h:d:fHISPvVB:" but omits the short options 'g' and 's' while the second pass includes them; update the first-pass optstring to include 'g' and 's' (i.e., add 'g' and 's' into the string used in the initial getopt loop) so both passes accept the same short options and remain consistent with the long_options entries (refer to the optstring used in the first getopt call and the long_options entries for "gpu" and "sensors").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/main.c`:
- Around line 110-111: The first getopt pass uses the optstring
"+r:i:n:p:h:d:fHISPvVB:" but omits the short options 'g' and 's' while the
second pass includes them; update the first-pass optstring to include 'g' and
's' (i.e., add 'g' and 's' into the string used in the initial getopt loop) so
both passes accept the same short options and remain consistent with the
long_options entries (refer to the optstring used in the first getopt call and
the long_options entries for "gpu" and "sensors").
In `@src/mount.c`:
- Around line 174-189: The directory handling only applies one-level recursion:
when S_ISDIR(st.st_mode) you open the directory with opendir(full_path) and
chmod each immediate entry via sub_p and chmod(sub_p, 0666), so deeper nested
paths (e.g., /dev/dri/by-path/...) won’t be processed; leave logic as-is but add
a concise comment above this block clarifying the intentional single-level
assumption (mentioning S_ISDIR, opendir(full_path), readdir loop and
chmod(sub_p, ...)) so future maintainers understand this limitation and can
change to a recursive walk if deeper nesting becomes required.
- Add `enableSensors` field to `ContainerInfo` data model. - Update `ContainerCommandBuilder` to generate `--sensors` flag. - Update `ContainerConfigScreen` and `EditContainerScreen` to include "Enable Sensors" toggle. - Update `ContainerInstallationViewModel` and navigation to support the new flag. - Update `strings.xml` with new labels. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- Backend (C): - Add `--sensors` (`-s`) flag to expose `/sys/class/power_supply` and `/sys/class/thermal`. - Add `--gpu` (`-g`) flag (alias for `--hw-access`) to enable GPU logic. - Implement strict GPU device matching (mali, kgsl, genlock, dri, dma_heap) in `mount.c`. - Add `gpu-check` command to `check.c` for diagnostics. - Update `main.c` help text and argument parsing. - Frontend (Android): - Add `enableSensors` field to `ContainerInfo`. - Add "Enable Sensors" toggle to `ContainerConfigScreen` and `EditContainerScreen`. - Update `ContainerCommandBuilder` to pass `--sensors` flag. - Update `ContainerInstallationViewModel` and `DroidspacesNavigation` to support the new flow. - Update `strings.xml` with new descriptions. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/mount.c`:
- Around line 186-194: The code sets found_gpu when a device is detected but
logs success even if chmod fails; introduce an updated_gpu (int updated_gpu = 0)
and only set updated_gpu = 1 when chmod(...) returns 0 for full_path or for each
sub_p inside the directory loop; on chmod failure call the existing logging
function (or perror) to record the error and do not treat the device as
successfully updated; change the success log and any logic that currently uses
found_gpu to check updated_gpu instead (apply the same change to the other
occurrence around lines 198-200).
- Around line 182-187: The code currently calls chmod(sub_p, 0666) for every
readdir entry (loop using sub, sub_e, full_path) and elsewhere for matched
entries, which can make directories world-writable; change this to call lstat()
on the path and only apply chmod when S_ISCHR(st_mode) || S_ISBLK(st_mode).
Update both the readdir loop (variables sub, sub_e, sub_p/full_path) and the
matched-entry chmod sites to perform lstat into a struct stat and guard the
chmod call with the character/block device test.
- Around line 145-148: Replace the hardcoded chmod 0666 in the hw_access mode
GPU-device handling with a configurable permission and ownership model: add a
new CLI flag (e.g., --gpu-mode) that sets a mode_t variable (default 0660) and
an optional group ID (or --gpu-group) for group ownership, then when scanning
for known GPU devices (Mali, Adreno, DMA heaps) use that gpu_mode in the chmod
call and call chown to set group ownership when provided instead of always using
0666; keep an explicit unsafe option (or accept "0666") that must be passed to
allow world-writable devices, and update the code paths referenced at the GPU
device handling locations (hw_access mode device-scan code around the current
lines 186 and 191) to use these new variables.
- Backend (C): - Add `--gpu-mode` (default 0660) and `--gpu-group` flags. - Refactor `setup_dev` to take `struct ds_config*`. - Implement `lstat` checks to ensure only char/block devices are modified. - Implement `updated_gpu` logic to only log success on actual changes. - Fix `getopt` optstring consistency in `main.c`. - Add explicit comment about directory recursion limits in `mount.c`. - Frontend (Android): - Add "Enable Sensors" toggle to GUI. - Updated translations and descriptions. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/mount.c (1)
191-210: Extract repeated GPU node permission logic into a helper.The
chmod/chownsequence is duplicated forsub_pandfull_path. A helper will reduce drift and make future policy changes safer.♻️ Proposed refactor
+static void apply_gpu_node_perms(const char *node, mode_t mode, gid_t group, + int *updated_gpu) { + if (chmod(node, mode) == 0) + *updated_gpu = 1; + else + ds_warn("Failed to chmod GPU node %s: %s", node, strerror(errno)); + + if (group != (gid_t)-1) { + if (chown(node, -1, group) == 0) + *updated_gpu = 1; + else + ds_warn("Failed to chown GPU node %s: %s", node, strerror(errno)); + } +} @@ - if (chmod(sub_p, cfg->gpu_mode) == 0) updated_gpu = 1; - else ds_warn("Failed to chmod GPU node %s: %s", sub_p, strerror(errno)); - - if (cfg->gpu_group != (gid_t)-1) { - if (chown(sub_p, -1, cfg->gpu_group) == 0) updated_gpu = 1; - else ds_warn("Failed to chown GPU node %s: %s", sub_p, strerror(errno)); - } + apply_gpu_node_perms(sub_p, cfg->gpu_mode, cfg->gpu_group, &updated_gpu); @@ - if (chmod(full_path, cfg->gpu_mode) == 0) updated_gpu = 1; - else ds_warn("Failed to chmod GPU node %s: %s", full_path, strerror(errno)); - - if (cfg->gpu_group != (gid_t)-1) { - if (chown(full_path, -1, cfg->gpu_group) == 0) updated_gpu = 1; - else ds_warn("Failed to chown GPU node %s: %s", full_path, strerror(errno)); - } + apply_gpu_node_perms(full_path, cfg->gpu_mode, cfg->gpu_group, &updated_gpu);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/mount.c` around lines 191 - 210, Extract the duplicated chmod/chown logic into a small helper (e.g., update_gpu_node_permissions or set_gpu_node_perms) that accepts the target path, the gpu_mode, gpu_group and returns/updates a flag indicating whether permissions changed; inside the helper call chmod(path, cfg->gpu_mode) and on failure call ds_warn with strerror(errno), then if cfg->gpu_group != (gid_t)-1 call chown(path, -1, cfg->gpu_group) with the same warning behavior and update the returned/pointed updated_gpu indicator when either succeeds. Replace the two duplicated blocks that operate on sub_p and full_path with calls to this helper so both code paths share the same logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main.c`:
- Around line 179-184: The code currently assigns cfg.gpu_mode and cfg.gpu_group
using strtoul without validating the parse or range, which can silently yield 0
or -1 and later break chmod/chown; update the parsing in the --gpu-mode and
--gpu-group branches to call strtoul with an endptr and check errno, ensure
endptr != optarg and *endptr == '\0', verify the result is within a safe range
(for cfg.gpu_mode enforce a valid octal permission range like 0..0777 when using
base 8; for cfg.gpu_group ensure the parsed unsigned long fits into gid_t and is
not (gid_t)-1), and on failure log an error and either reject/exit or keep a
safe default instead of applying the invalid value before calling
chmod(gpu_node, cfg.gpu_mode) or chown(gpu_node, -1, cfg.gpu_group).
---
Nitpick comments:
In `@src/mount.c`:
- Around line 191-210: Extract the duplicated chmod/chown logic into a small
helper (e.g., update_gpu_node_permissions or set_gpu_node_perms) that accepts
the target path, the gpu_mode, gpu_group and returns/updates a flag indicating
whether permissions changed; inside the helper call chmod(path, cfg->gpu_mode)
and on failure call ds_warn with strerror(errno), then if cfg->gpu_group !=
(gid_t)-1 call chown(path, -1, cfg->gpu_group) with the same warning behavior
and update the returned/pointed updated_gpu indicator when either succeeds.
Replace the two duplicated blocks that operate on sub_p and full_path with calls
to this helper so both code paths share the same logic.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/boot.csrc/droidspace.hsrc/main.csrc/mount.c
🚧 Files skipped from review as they are similar to previous changes (1)
- src/boot.c
- Backend (C): - Introduce `--gpu-mode` (default 0660) and `--gpu-group` flags. - Implement `update_gpu_node_permissions` helper to centralize permission logic. - Use `lstat` and `S_ISCHR`/`S_ISBLK` checks to ensure only device nodes are modified. - Refactor `setup_dev` to accept `struct ds_config` for cleaner config passing. - Improve error handling for `strtoul` parsing in `main.c`. - Feature Completion: - Supports Pixel 9 Pro XL GPU requirements (Mali, Genlock). - Enables sensor exposure via `--sensors`. - Android GUI integration complete. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- Fix CI failures: - Silence `-Werror=unused-result` warnings by checking return values. - Relax `-Wformat-truncation` checks in Makefile to allow LTO builds. - Update `all-build` in Makefile to exit on error (fail-fast). - Implement GPU/Sensors Support: - Add `--gpu-mode` (default 0660) and `--gpu-group` flags. - Implement `update_gpu_node_permissions` with `lstat` safety checks (only chmod/chown char/block devices). - Add "Enable Sensors" toggle to Android GUI and C backend (`-s`). - Strict matching for GPU devices including `genlock`. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- Fix CI failures: - Configure `Android/app/build.gradle.kts` to fallback to debug signing for release builds if keys are missing. - Silence `-Werror=unused-result` warnings by explicitly checking return values. - Relax `-Wformat-truncation` checks in Makefile to allow LTO builds. - Update `all-build` in Makefile to exit on error (fail-fast). - Implement GPU/Sensors Support: - Add `--gpu-mode` (default 0660) and `--gpu-group` flags. - Implement `update_gpu_node_permissions` with `lstat` safety checks. - Add "Enable Sensors" toggle to Android GUI and C backend (`-s`). - Strict matching for GPU devices including `genlock` (Pixel 9). Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- CI Fixes: - Corrected `build.gradle.kts` to correctly fallback to the debug signing config for release builds when keys are missing, preventing the "file not found" error during `validateSigningRelease`. - Removed strict format overflow warnings from Makefile to allow LTO compilation. - Suppressed unused result warnings in C code with explicit `if` checks. - GPU/Sensor Features: - Added configurable GPU permissions (`--gpu-mode`, `--gpu-group`). - Implemented safe permission updates with `lstat` and file type checks. - Added "Enable Sensors" toggle to Android GUI. - Added `gpu-check` command and help updates. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- Fix Android CI: - Configure `Android/app/build.gradle.kts` to robustly fallback to debug signing if release keys are missing, preventing `validateSigningRelease` errors. - Fix Backend CI: - Update `Makefile` to correctly locate toolchains in the CI environment (`find-cc` helper logic). - Suppress strict format warnings and check return values for system calls to pass `-Werror`. - Complete Features: - Enable GPU driver support (Pixel 9/Mali/Genlock) with strict device matching and configurable permissions (`--gpu-mode`, `--gpu-group`). - Add `--sensors` flag and Android UI toggle for battery/thermal monitoring. - Implement diagnostics (`gpu-check`). Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- Fix CI: - Add logic to `build.gradle.kts` to auto-generate a debug keystore if missing, preventing build failures in CI environments. - Silence compiler warnings in C backend using explicit return value checks. - Fix Makefile cross-compiler detection for CI environment. - Finalize Features: - Implement full GPU support with strict device matching (Pixel 9/Mali/Genlock). - Add configurable GPU permissions (`--gpu-mode`, `--gpu-group`) and safety checks (`lstat`, `S_ISCHR`). - Add Sensors support (`--sensors`) and Android GUI toggle. - Fix parsing and help text issues identified in review. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- Fix Android CI: - Configure `Android/app/build.gradle.kts` to robustly fallback to debug signing if release keys are missing, preventing `validateSigningRelease` errors. - Implement auto-generation of debug keystore if default one is missing in CI environment. - Fix Backend CI: - Update `Makefile` to correctly locate toolchains in the CI environment (`find-cc` helper logic). - Suppress strict format warnings and check return values for system calls to pass `-Werror`. - Complete Features: - Enable GPU driver support (Pixel 9/Mali/Genlock) with strict device matching and configurable permissions (`--gpu-mode`, `--gpu-group`). - Add `--sensors` flag and Android UI toggle for battery/thermal monitoring. - Implement diagnostics (`gpu-check`). - Update UI strings to explicitly mention "GPU / Hardware Access". Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- Fix CI: - Add robust keystore generation in `build.gradle.kts` to prevent CI build failures. - Silence compiler warnings using explicit checks to pass `-Werror`. - Fix Makefile cross-compiler detection. - Features: - Add support for Pixel accelerators: `edgetpu` (Tensor TPU), `udmabuf` (zero-copy), `video*` (hardware codecs). - Rename "Hardware Access" to "GPU / Hardware Access" in GUI to reduce confusion. - Full Sensors support with UI toggle. - Configurable GPU permissions and safe `lstat` checks. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- CI Fixes: - Add robust keystore generation in `build.gradle.kts` to prevent CI failures. - Silence compiler warnings with explicit checks to pass `-Werror`. - Fix Makefile cross-compiler detection. - Features: - Add support for Pixel accelerators: `edgetpu`, `udmabuf`, `video*`. - Full GPU support with strict matching (including `genlock`) and configurable permissions (`--gpu-mode`). - Add `--sensors` flag and Android UI toggle for battery/thermal monitoring. - Rename "Hardware Access" to "GPU / Hardware Access" in GUI to clarify functionality. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- Fix Host Networking: - Disable aggressive iptables REDIRECT/MASQUERADE rules in `src/android.c` that caused loss of internet connectivity on the Android host when running containers. - Fix CI: - Update `build.gradle.kts` to auto-generate a debug keystore if missing, preventing CI build failures. - Silence compiler warnings and strict format checks. - Features: - Full GPU support (Pixel 9/Mali/Genlock) with strict matching and configurable permissions (`--gpu-mode`). - Sensors support (`--sensors`) with Android UI integration. - Rename "Hardware Access" to "GPU / Hardware Access" in GUI and CLI output. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
…ing harmful iptables rules. - src/android.c: Remove iptables flush/redirect commands that broke Android networking. - src/mount.c: Add support for mali, genlock, dma_heap, and other GPU devices. Implement safe permission updates. - src/check.c: Add `gpu-check` command to diagnose accelerator availability. - Android/app: Update UI strings and build configuration for auto-keystore generation. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- src/droidspace.h: Add DS_NET_MODE enum and configuration field. - src/main.c: Parse --network-mode (default: host). - src/container.c: Enable CLONE_NEWNET for non-host modes. - src/network.c: Implement `ds_configure_network_namespace` to setup veth+NAT or macvlan. - NAT mode: Generates random MAC, assigns unique subnet based on PID, enables host masquerading. - Macvlan mode: Bridges to physical interface (experimental). Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- Android/app/src/main/java/com/droidspaces/app/util/ContainerManager.kt: Add networkMode field to ContainerInfo and config persistence. - Android/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.kt: Append --network-mode flag to start/restart commands. - Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt: Add Network Mode UI selector. - Android/app/src/main/java/com/droidspaces/app/ui/screen/EditContainerScreen.kt: Add Network Mode UI selector to edit screen. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (3)
Android/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.kt (2)
67-69: Consider quotingnetworkModefor consistency with other parameters.While
networkModeis constrained to "host", "nat", or "macvlan" in the UI, other dynamic values likednsServersusequote()for shell safety. For consistency and defense-in-depth:♻️ Suggested change
if (container.networkMode != "host") { - parts.add("--network-mode=${container.networkMode}") + parts.add("--network-mode=${quote(container.networkMode)}") }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Android/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.kt` around lines 67 - 69, The networkMode value is added unquoted which is inconsistent with other dynamic parameters and could risk shell-safety; in ContainerCommandBuilder (where parts.add("--network-mode=${container.networkMode}") is used) wrap the container.networkMode with the existing quote() helper the same way dnsServers and other params do (e.g. parts.add("--network-mode=${quote(container.networkMode)}") ), ensuring the value is quoted before adding to parts.
125-126: Same quoting inconsistency inbuildRestartCommand.Apply the same
quote()wrapper here for consistency withbuildStartCommand.♻️ Suggested change
if (container.enableSensors) parts.add("--sensors") - if (container.networkMode != "host") parts.add("--network-mode=${container.networkMode}") + if (container.networkMode != "host") parts.add("--network-mode=${quote(container.networkMode)}")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Android/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.kt` around lines 125 - 126, In buildRestartCommand, wrap the network mode value with the same quote() helper used in buildStartCommand to keep argument quoting consistent: when checking container.networkMode != "host" use parts.add("--network-mode=${quote(container.networkMode)}"); also review the container.enableSensors handling in buildRestartCommand to mirror buildStartCommand's formatting if it had any quoting changes. This targets the buildRestartCommand function and the container.networkMode / container.enableSensors usages and should use the existing quote() function.src/mount.c (1)
120-137: Verifyupdate_gpu_node_permissionsreturn value semantics.The function returns
1if eitherchmodorchownsucceeds. Ifchmodsucceeds butchownfails (or vice versa),updatedwill be1. This is likely intentional (partial success counts as updated), but note that Line 131 setsupdated = 1even whenchmodalready failed at Line 123-127.If the intent is "any change was made," this is correct. If the intent is "all requested changes succeeded," the logic should use
&&instead.💡 Optional: Clarify return semantics
If "all changes must succeed" is desired:
static int update_gpu_node_permissions(const char *path, mode_t mode, gid_t group) { - int updated = 0; - if (chmod(path, mode) == 0) { - updated = 1; - } else { + int chmod_ok = (chmod(path, mode) == 0); + if (!chmod_ok) { ds_warn("Failed to chmod GPU node %s: %s", path, strerror(errno)); } + int chown_ok = 1; if (group != (gid_t)-1) { - if (chown(path, -1, group) == 0) { - updated = 1; - } else { + chown_ok = (chown(path, -1, group) == 0); + if (!chown_ok) { ds_warn("Failed to chown GPU node %s: %s", path, strerror(errno)); } } - return updated; + return chmod_ok && chown_ok; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/mount.c` around lines 120 - 137, The function update_gpu_node_permissions currently treats partial success as a success because it sets updated=1 when either chmod or chown succeeds; decide which semantics you want and implement it explicitly: if you want "any change" keep the current behavior but make it clearer by using two booleans (e.g., chmod_ok and chown_ok) and return (chmod_ok || chown_ok); if you want "all requested changes must succeed" change the logic to return (chmod_ok && (group == (gid_t)-1 || chown_ok)); update the function update_gpu_node_permissions to compute separate success flags for chmod and chown and return the combined result accordingly, and adjust the ds_warn calls to use those flags without overwriting success state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Android/app/build.gradle.kts`:
- Around line 79-106: The nullable handling of targetStoreFile (assigned from
debugConfig.storeFile) uses a mutable var and !! assertions which can break
smart-cast and cause Kotlin type errors; replace this pattern by introducing an
immutable val (e.g., finalStoreFile or resolvedStoreFile) that is computed once
after checking/creating the localKeystore and using safe calls/let to avoid !!,
then use that val inside the getByName("debug") block to set storeFile;
reference targetStoreFile, debugConfig.storeFile, localKeystore, and the
getByName("debug") closure when locating and updating the code.
In
`@Android/app/src/main/java/com/droidspaces/app/ui/screen/EditContainerScreen.kt`:
- Around line 454-463: The hardcoded UI strings in EditContainerScreen (the
Network Mode card Texts and the mode labels used in the radio options) must be
replaced with string resources; add the suggested keys to strings.xml
(network_mode, network_mode_description, network_mode_host, network_mode_nat,
network_mode_macvlan) and update the composable in EditContainerScreen.kt to use
context.getString(R.string.network_mode) and
context.getString(R.string.network_mode_description) for the header/description,
and map each network mode enum/value used by the radio group (e.g., the
identifiers used when rendering the options) to its corresponding R.string id
and call context.getString(...) when building the label so all UI text follows
the existing localization pattern.
In `@Makefile`:
- Around line 53-54: The two shell-command lines inside the makefile's find-cc
recipe currently use space indentation and must be changed to start with a tab
character; locate the lines containing the test for
"$(HOME_VAR)/toolchains/$(1)/bin/$(1)-gcc" and the subsequent echo of
"$(HOME_VAR)/toolchains/$(1)/bin/$(1)-gcc" in the find-cc function and replace
their leading spaces with a single tab so they match the other recipe lines and
satisfy Makefile's tab-required command indentation.
In `@src/main.c`:
- Around line 191-202: The overflow check in the --gpu-group parsing block is
ineffective; replace the single comparison against (unsigned long)(gid_t)-1 with
a round-trip and sentinel check: after parsing val with strtoul, verify that
casting val to gid_t and back to unsigned long preserves the value (i.e.,
(unsigned long)(gid_t)val == val) and also ensure the resulting (gid_t) value is
not the sentinel (gid_t)-1 before assigning to cfg.gpu_group; keep the existing
strtoul error/empty-string checks and return an error if either the round-trip
fails or the sentinel is produced.
In `@src/network.c`:
- Around line 258-262: The call to run_command_quiet(args_move) that moves the
macvlan into the container namespace (args_move / pid_str) has its return value
ignored; update the block in the function that builds args_move to check the
return value from run_command_quiet, and on failure log an error (including the
command and error details) and return/propagate an error code just like the
NAT-mode handling does—i.e., mirror the error handling used for the NAT branch:
check run_command_quiet(...) result, call the same logger/error path, and abort
the setup when it fails.
- Around line 83-101: In generate_random_mac, the call to read(fd, mac, 6) is
unchecked so partial reads can leave mac[] uninitialized; change this to read in
a loop that handles EINTR and accumulates bytes until 6 are read (or until an
unrecoverable error) and if the total bytes read is less than 6 fall back to
filling the remaining bytes with rand() before closing fd; ensure fd is always
closed on error and still apply the mac[0] bit-mangling and snprintf as before.
- Around line 258-295: The Macvlan worker child process spawned by fork() is not
checked for successful exit; after waitpid(worker, NULL, 0) in the macvlan setup
code, inspect the child's status (use int status; waitpid(worker, &status, 0);)
and validate with WIFEXITED(status) and WEXITSTATUS(status)==0 (also handle
WIFSIGNALED to log signal failures); on any failure call ds_error (or an
appropriate error path) and return/propagate an error instead of logging
ds_warn("Macvlan setup complete..."), so functions like setns(), ip commands
invoked via run_command_quiet() failures are detected and reported.
---
Nitpick comments:
In
`@Android/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.kt`:
- Around line 67-69: The networkMode value is added unquoted which is
inconsistent with other dynamic parameters and could risk shell-safety; in
ContainerCommandBuilder (where
parts.add("--network-mode=${container.networkMode}") is used) wrap the
container.networkMode with the existing quote() helper the same way dnsServers
and other params do (e.g.
parts.add("--network-mode=${quote(container.networkMode)}") ), ensuring the
value is quoted before adding to parts.
- Around line 125-126: In buildRestartCommand, wrap the network mode value with
the same quote() helper used in buildStartCommand to keep argument quoting
consistent: when checking container.networkMode != "host" use
parts.add("--network-mode=${quote(container.networkMode)}"); also review the
container.enableSensors handling in buildRestartCommand to mirror
buildStartCommand's formatting if it had any quoting changes. This targets the
buildRestartCommand function and the container.networkMode /
container.enableSensors usages and should use the existing quote() function.
In `@src/mount.c`:
- Around line 120-137: The function update_gpu_node_permissions currently treats
partial success as a success because it sets updated=1 when either chmod or
chown succeeds; decide which semantics you want and implement it explicitly: if
you want "any change" keep the current behavior but make it clearer by using two
booleans (e.g., chmod_ok and chown_ok) and return (chmod_ok || chown_ok); if you
want "all requested changes must succeed" change the logic to return (chmod_ok
&& (group == (gid_t)-1 || chown_ok)); update the function
update_gpu_node_permissions to compute separate success flags for chmod and
chown and return the combined result accordingly, and adjust the ds_warn calls
to use those flags without overwriting success state.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
Android/app/build.gradle.ktsAndroid/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.ktAndroid/app/src/main/java/com/droidspaces/app/ui/screen/EditContainerScreen.ktAndroid/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.ktAndroid/app/src/main/java/com/droidspaces/app/util/ContainerManager.ktAndroid/app/src/main/res/values/strings.xmlMakefilesrc/android.csrc/boot.csrc/cgroup.csrc/check.csrc/container.csrc/droidspace.hsrc/main.csrc/mount.csrc/network.c
🚧 Files skipped from review as they are similar to previous changes (2)
- Android/app/src/main/res/values/strings.xml
- Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt
| var targetStoreFile = debugConfig.storeFile | ||
|
|
||
| if (targetStoreFile == null || !targetStoreFile!!.exists()) { | ||
| val localKeystore = rootProject.file("debug.keystore") | ||
| if (!localKeystore.exists()) { | ||
| println("Generating temporary debug keystore at ${localKeystore.absolutePath}...") | ||
| try { | ||
| exec { | ||
| commandLine("keytool", "-genkey", "-v", | ||
| "-keystore", localKeystore.absolutePath, | ||
| "-storepass", "android", | ||
| "-alias", "androiddebugkey", | ||
| "-keypass", "android", | ||
| "-keyalg", "RSA", | ||
| "-keysize", "2048", | ||
| "-validity", "10000", | ||
| "-dname", "CN=Android Debug,O=Android,C=US") | ||
| } | ||
| } catch (e: Exception) { | ||
| println("Warning: Failed to generate debug keystore: ${e.message}") | ||
| } | ||
| } | ||
| targetStoreFile = localKeystore | ||
| } | ||
|
|
||
| getByName("debug") { | ||
| if (targetStoreFile != null && targetStoreFile!!.exists()) { | ||
| storeFile = targetStoreFile |
There was a problem hiding this comment.
Potential source of Kotlin compilation issues — refactor nullability handling.
The targetStoreFile is a var, which prevents Kotlin smart-casting after null checks. The current code uses !! assertions which are error-prone and may be related to the pipeline's "Kotlin type errors."
Consider using a local val to capture the store file safely:
🔧 Suggested refactor for safer nullability handling
val debugConfig = getByName("debug")
- var targetStoreFile = debugConfig.storeFile
-
- if (targetStoreFile == null || !targetStoreFile!!.exists()) {
+ val configuredStoreFile = debugConfig.storeFile
+ val targetStoreFile: File? = if (configuredStoreFile == null || !configuredStoreFile.exists()) {
val localKeystore = rootProject.file("debug.keystore")
if (!localKeystore.exists()) {
println("Generating temporary debug keystore at ${localKeystore.absolutePath}...")
try {
exec {
commandLine("keytool", "-genkey", "-v",
"-keystore", localKeystore.absolutePath,
"-storepass", "android",
"-alias", "androiddebugkey",
"-keypass", "android",
"-keyalg", "RSA",
"-keysize", "2048",
"-validity", "10000",
"-dname", "CN=Android Debug,O=Android,C=US")
}
} catch (e: Exception) {
println("Warning: Failed to generate debug keystore: ${e.message}")
}
}
- targetStoreFile = localKeystore
- }
+ localKeystore
+ } else {
+ configuredStoreFile
+ }
getByName("debug") {
- if (targetStoreFile != null && targetStoreFile!!.exists()) {
+ if (targetStoreFile != null && targetStoreFile.exists()) {
storeFile = targetStoreFile
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Android/app/build.gradle.kts` around lines 79 - 106, The nullable handling of
targetStoreFile (assigned from debugConfig.storeFile) uses a mutable var and !!
assertions which can break smart-cast and cause Kotlin type errors; replace this
pattern by introducing an immutable val (e.g., finalStoreFile or
resolvedStoreFile) that is computed once after checking/creating the
localKeystore and using safe calls/let to avoid !!, then use that val inside the
getByName("debug") block to set storeFile; reference targetStoreFile,
debugConfig.storeFile, localKeystore, and the getByName("debug") closure when
locating and updating the code.
| Text( | ||
| text = "Network Mode", | ||
| style = MaterialTheme.typography.titleMedium, | ||
| fontWeight = FontWeight.SemiBold | ||
| ) | ||
| Text( | ||
| text = "Choose how the container connects to the network.", | ||
| style = MaterialTheme.typography.bodySmall, | ||
| color = MaterialTheme.colorScheme.onSurfaceVariant | ||
| ) |
There was a problem hiding this comment.
Hardcoded strings should use string resources for i18n support.
The Network Mode card uses hardcoded English strings while the rest of the file consistently uses context.getString(R.string.*). This breaks localization consistency.
♻️ Suggested approach
Add these strings to strings.xml:
<string name="network_mode">Network Mode</string>
<string name="network_mode_description">Choose how the container connects to the network.</string>
<string name="network_mode_host">Host (Shared IP)</string>
<string name="network_mode_nat">NAT (Private IP / Fake MAC)</string>
<string name="network_mode_macvlan">Macvlan (Bridge)</string>Then update the Kotlin code:
Text(
- text = "Network Mode",
+ text = context.getString(R.string.network_mode),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
Text(
- text = "Choose how the container connects to the network.",
+ text = context.getString(R.string.network_mode_description),
style = MaterialTheme.typography.bodySmall,And for the mode labels, use a map to string resource IDs or similar approach.
Also applies to: 469-473, 493-497
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@Android/app/src/main/java/com/droidspaces/app/ui/screen/EditContainerScreen.kt`
around lines 454 - 463, The hardcoded UI strings in EditContainerScreen (the
Network Mode card Texts and the mode labels used in the radio options) must be
replaced with string resources; add the suggested keys to strings.xml
(network_mode, network_mode_description, network_mode_host, network_mode_nat,
network_mode_macvlan) and update the composable in EditContainerScreen.kt to use
context.getString(R.string.network_mode) and
context.getString(R.string.network_mode_description) for the header/description,
and map each network mode enum/value used by the radio group (e.g., the
identifiers used when rendering the options) to its corresponding R.string id
and call context.getString(...) when building the label so all UI text follows
the existing localization pattern.
| elif [ -f "$(HOME_VAR)/toolchains/$(1)/bin/$(1)-gcc" ]; then \ | ||
| echo "$(HOME_VAR)/toolchains/$(1)/bin/$(1)-gcc"; \ |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -A Makefile | sed -n '44,57p'Repository: shedowe19/Droidspaces-OSS
Length of output: 685
Use tab indentation in the find-cc function for lines 53-54.
Lines 53-54 use space indentation (4 and 8 spaces respectively) while all other lines in this function use tabs. Makefile recipes require tab indentation for shell commands; mixing spaces with tabs will cause parsing or execution errors.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Makefile` around lines 53 - 54, The two shell-command lines inside the
makefile's find-cc recipe currently use space indentation and must be changed to
start with a tab character; locate the lines containing the test for
"$(HOME_VAR)/toolchains/$(1)/bin/$(1)-gcc" and the subsequent echo of
"$(HOME_VAR)/toolchains/$(1)/bin/$(1)-gcc" in the find-cc function and replace
their leading spaces with a single tab so they match the other recipe lines and
satisfy Makefile's tab-required command indentation.
src/network.c
Outdated
| static void generate_random_mac(char *buf) { | ||
| /* Locally Administered Address (x2, x6, xA, xE) */ | ||
| /* We use 02:xx:xx:xx:xx:xx */ | ||
| unsigned char mac[6]; | ||
| int fd = open("/dev/urandom", O_RDONLY); | ||
| if (fd >= 0) { | ||
| read(fd, mac, 6); | ||
| close(fd); | ||
| } else { | ||
| /* Fallback pseudo-random */ | ||
| for (int i = 0; i < 6; i++) mac[i] = rand() % 256; | ||
| } | ||
|
|
||
| mac[0] &= 0xFE; /* Unicast */ | ||
| mac[0] |= 0x02; /* Locally Administered */ | ||
|
|
||
| snprintf(buf, 18, "%02x:%02x:%02x:%02x:%02x:%02x", | ||
| mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); | ||
| } |
There was a problem hiding this comment.
Unchecked read() return value.
At line 89, the return value of read(fd, mac, 6) is not checked. While the fallback pseudo-random path handles the fd < 0 case, a partial read could leave uninitialized bytes in mac. Consider verifying the read succeeded fully:
🔧 Suggested fix
int fd = open("/dev/urandom", O_RDONLY);
- if (fd >= 0) {
- read(fd, mac, 6);
+ if (fd >= 0 && read(fd, mac, 6) == 6) {
close(fd);
} else {
/* Fallback pseudo-random */
+ if (fd >= 0) close(fd);
for (int i = 0; i < 6; i++) mac[i] = rand() % 256;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/network.c` around lines 83 - 101, In generate_random_mac, the call to
read(fd, mac, 6) is unchecked so partial reads can leave mac[] uninitialized;
change this to read in a loop that handles EINTR and accumulates bytes until 6
are read (or until an unrecoverable error) and if the total bytes read is less
than 6 fall back to filling the remaining bytes with rand() before closing fd;
ensure fd is always closed on error and still apply the mac[0] bit-mangling and
snprintf as before.
- Android/app/src/main/java/com/droidspaces/app/ui/navigation/DroidspacesNavigation.kt: Update onNext callback for ContainerConfigScreen to include networkMode argument. - Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt: Add missing import for clickable modifier. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/main.c (2)
139-149:⚠️ Potential issue | 🟠 MajorInclude
N:in pass-1 getopt parsing to avoid command mis-detection.Line 139 omits short
-Nargument parsing in pass 1. With-N nat run ...,discovered_cmdcan be resolved incorrectly, which affects strict parsing mode selection.🔧 Proposed fix
- while (getopt_long(argc, argv, "+r:i:n:p:h:d:fHISPvVB:gs", long_options, + while (getopt_long(argc, argv, "+r:i:n:p:h:d:fHISPvVB:gsN:", long_options, NULL) != -1)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.c` around lines 139 - 149, The first getopt_long pass omits the short option 'N' so discovered_cmd can be mis-detected; update the initial getopt_long call to include 'N:' in its optstring (the same short option used later) so that pass-1 parsing recognizes -N arguments and optind/discovered_cmd determination remains correct; adjust the optstring used in the call around getopt_long and ensure discovered_cmd, strict, and temp_optind logic remain unchanged.
39-56:⚠️ Potential issue | 🟡 MinorHelp output is missing
--network-mode.
--network-modeis parsed (Line 118+) but not documented in usage text, making the feature discoverability inconsistent.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.c` around lines 39 - 56, The help/usage output is missing documentation for the parsed --network-mode option; update the usage printing block (the sequence of printf calls that print options in src/main.c) to include a line describing --network-mode (e.g., " --network-mode=MODE Set container network mode (e.g., host, bridge, none)") so the printed help matches the option parsed elsewhere; add the new printf in the same style and ordering as the other long-form options.
♻️ Duplicate comments (4)
src/network.c (2)
87-90:⚠️ Potential issue | 🟡 MinorHandle partial
/dev/urandomreads before usingmac[].At Line 89,
read(fd, mac, 6)is unchecked. A short read can leave bytes uninitialized and generate invalid MAC output.🔧 Proposed fix
int fd = open("/dev/urandom", O_RDONLY); - if (fd >= 0) { - read(fd, mac, 6); - close(fd); - } else { + ssize_t got = 0; + if (fd >= 0) { + while (got < 6) { + ssize_t n = read(fd, mac + got, 6 - got); + if (n < 0 && errno == EINTR) continue; + if (n <= 0) break; + got += n; + } + close(fd); + } + if (got < 6) { /* Fallback pseudo-random */ - for (int i = 0; i < 6; i++) mac[i] = rand() % 256; + for (int i = (int)got; i < 6; i++) mac[i] = rand() % 256; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` around lines 87 - 90, The read from /dev/urandom into mac (int fd = open(...); read(fd, mac, 6);) can return a short read or -1; update the code that fills the mac[] buffer to check the return value, loop until exactly 6 bytes are read (handling EINTR by retrying), decrementing the remaining byte count and advancing the buffer pointer on each partial read, close fd on any terminal error and handle the failure case (e.g., return an error code or fall back to a deterministic/secure alternative) instead of using uninitialized bytes.
258-295:⚠️ Potential issue | 🟡 MinorMacvlan path still ignores move/worker failures.
Line 262 ignores the
ip link set ... netnsresult, and Line 294 waits without validating child exit status. This can log success while setup actually failed.🔧 Proposed fix
char *args_move[] = {"ip", "link", "set", mac_if, "netns", pid_str, NULL}; - run_command_quiet(args_move); + if (run_command_quiet(args_move) != 0) { + ds_error("Failed to move macvlan interface to container namespace"); + return -1; + } ... - waitpid(worker, NULL, 0); + int status; + waitpid(worker, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + ds_error("Failed to configure macvlan interface inside container"); + return -1; + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` around lines 258 - 295, The macvlan setup currently ignores failures from moving the iface and from the worker child; update the block using run_command_quiet(args_move) to check its return and abort with an error log if it fails, handle fork() failure by logging and aborting, ensure the child (worker) checks open(ns_path) and setns(...) and logs exact errors before exiting non‑zero, and in the parent use waitpid(worker, &status, 0) and inspect status with WIFEXITED/WEXITSTATUS (and WIFSIGNALED) to log and propagate a failure instead of always reporting success; reference symbols: run_command_quiet, args_move, fork/worker, open/ns_path, setns, generate_random_mac, waitpid.Android/app/src/main/java/com/droidspaces/app/ui/screen/EditContainerScreen.kt (1)
455-461:⚠️ Potential issue | 🟡 MinorMove Network Mode UI strings to resources.
The new Network Mode texts are still hardcoded English strings; this bypasses localization and is inconsistent with the rest of this screen’s
R.stringusage.Also applies to: 469-473, 493-495
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Android/app/src/main/java/com/droidspaces/app/ui/screen/EditContainerScreen.kt` around lines 455 - 461, The hardcoded "Network Mode" and related texts in EditContainerScreen.kt must be moved to string resources and referenced via stringResource; add entries like network_mode_title, network_mode_description (and equivalents for the other two hardcoded texts noted) to res/values/strings.xml, then replace the literal strings in the Text composables inside the EditContainerScreen composable with stringResource(id = R.string.network_mode_title), stringResource(id = R.string.network_mode_description), etc., and ensure the file imports androidx.compose.ui.res.stringResource; use the same R.string names for the duplicates so all occurrences are localized consistently.src/main.c (1)
191-200:⚠️ Potential issue | 🟡 MinorReject sentinel
gid_tvalue for--gpu-group.The current validation allows values that cast to
(gid_t)-1on some targets, which turnschown(..., -1, group)into a silent “no group change” path.🔧 Proposed fix
- if (errno != 0 || endptr == optarg || *endptr != '\0' || - val > (unsigned long)(gid_t)-1) { + if (errno != 0 || endptr == optarg || *endptr != '\0' || + (unsigned long)(gid_t)val != val || (gid_t)val == (gid_t)-1) { ds_error("Invalid --gpu-group: %s (must be valid GID)", optarg); return 1; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.c` around lines 191 - 200, The parsing for the --gpu-group (case 1002) accepts numeric values that, when cast to gid_t, equal the sentinel (gid_t)-1 which causes chown to skip group changes; update the validation in the strtoul/optarg handling to also reject values that equal (gid_t)-1 after cast (i.e. if (gid_t)val == (gid_t)-1) in addition to the existing errno/endptr/overflow checks, report the error via ds_error("Invalid --gpu-group: %s (must be valid GID)", optarg) and return 1 instead of assigning cfg.gpu_group, so cfg.gpu_group is only set when the parsed value is valid and not the sentinel.
🧹 Nitpick comments (2)
Android/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.kt (1)
67-68: Consider quotingnetworkModefor defensive consistency.While
networkModevalues are likely constrained to "host", "nat", "macvlan", other dynamic values in this builder usequote(). For consistency and defensive coding, consider:parts.add("--network-mode=${quote(container.networkMode)}")This is optional since the current enum-derived values are shell-safe.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Android/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.kt` around lines 67 - 68, In ContainerCommandBuilder, the network mode argument currently interpolates container.networkMode without quoting; update the branch that checks container.networkMode != "host" to wrap the value with the existing quote() helper (i.e., use quote(container.networkMode)) so the constructed flag (--network-mode=...) is consistently shell-escaped like other dynamic values; this change touches the conditional and the parts.add call that builds the network-mode argument.Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt (1)
217-279: Consider extracting a sharedNetworkModeSelectorcomposable.This block duplicates the same UI/logic now present in
EditContainerScreen.kt, which increases drift risk for future behavior/text fixes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt` around lines 217 - 279, Extract the duplicated network selection UI from ContainerConfigScreen into a reusable composable (e.g., NetworkModeSelector) and replace the inline block with a call to it; the new composable should accept the current networkMode and a callback (onModeSelected: (String) -> Unit) or a MutableState<String> so both ContainerConfigScreen and EditContainerScreen can reuse it, move the modes list and the RadioButton/Row layout into NetworkModeSelector, and ensure callers simply pass networkMode and a setter to preserve existing behavior and labels.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt`:
- Around line 238-244: Replace hard-coded English literals in
ContainerConfigScreen composables with Android string resources: create entries
in strings.xml (e.g., network_mode_title, network_mode_description and the other
labels referenced at lines 252-256 and 271-272) and use
stringResource(R.string.<name>) in the Text composables inside
ContainerConfigScreen (where the Text(...) calls render "Network Mode" and the
descriptions). Update all occurrences mentioned (the Text calls around the
title/description and the other label usages) to reference the new R.string keys
to ensure proper localization.
In `@src/container.c`:
- Around line 368-370: ds_configure_network_namespace() is being run after the
monitor unshares CLONE_NEWNET, so it configures networking from inside the
container netns instead of the host; to fix, ensure
ds_configure_network_namespace(...) is called while still in the host network
namespace (move its call to before the monitor unshares CLONE_NEWNET or perform
an explicit setns back to the host netns before invoking it), adjust the logic
around ns_flags/CLONE_NEWNET and the monitor unshare path so host-side
veth/NAT/macvlan wiring happens from the host, and keep
ds_configure_network_namespace, ns_flags, and the monitor unshare sequence
consistent so host configuration runs prior to entering the new netns.
In `@src/network.c`:
- Around line 151-172: The NAT setup currently ignores failures from system
commands; update the block that runs args_host_up, args_nat, args_fwd, and
args_fwd2 to check each run_command_quiet() return value and treat non-zero as a
fatal NAT-setup failure: call ds_error()/ds_warn with the command/context (e.g.,
"ip link set %s up", subnet_cidr, veth_host) and return a non-zero error from
the enclosing function (or propagate an error code) so the caller knows NAT
setup failed; ensure the checks reference the existing symbols args_host_up,
args_host_ip (already checks), args_nat, args_fwd, and args_fwd2 and do not
swallow errors.
- Around line 199-225: The child path currently ignores return values from
run_command_quiet for the sequence of commands (cmd_rename, cmd_mac, cmd_ip,
cmd_up, cmd_lo, cmd_gw) and always calls exit(0); update the child logic to
check each run_command_quiet(...) result and if any call fails return/exit
immediately with a non-zero status (or aggregate an error code) so the parent
can detect failure—ensure the exit(0) is only used when all commands succeed and
propagate or log the failing command name (e.g., cmd_rename, cmd_mac, cmd_ip,
cmd_up, cmd_lo, cmd_gw) to aid diagnosis.
---
Outside diff comments:
In `@src/main.c`:
- Around line 139-149: The first getopt_long pass omits the short option 'N' so
discovered_cmd can be mis-detected; update the initial getopt_long call to
include 'N:' in its optstring (the same short option used later) so that pass-1
parsing recognizes -N arguments and optind/discovered_cmd determination remains
correct; adjust the optstring used in the call around getopt_long and ensure
discovered_cmd, strict, and temp_optind logic remain unchanged.
- Around line 39-56: The help/usage output is missing documentation for the
parsed --network-mode option; update the usage printing block (the sequence of
printf calls that print options in src/main.c) to include a line describing
--network-mode (e.g., " --network-mode=MODE Set container network mode
(e.g., host, bridge, none)") so the printed help matches the option parsed
elsewhere; add the new printf in the same style and ordering as the other
long-form options.
---
Duplicate comments:
In
`@Android/app/src/main/java/com/droidspaces/app/ui/screen/EditContainerScreen.kt`:
- Around line 455-461: The hardcoded "Network Mode" and related texts in
EditContainerScreen.kt must be moved to string resources and referenced via
stringResource; add entries like network_mode_title, network_mode_description
(and equivalents for the other two hardcoded texts noted) to
res/values/strings.xml, then replace the literal strings in the Text composables
inside the EditContainerScreen composable with stringResource(id =
R.string.network_mode_title), stringResource(id =
R.string.network_mode_description), etc., and ensure the file imports
androidx.compose.ui.res.stringResource; use the same R.string names for the
duplicates so all occurrences are localized consistently.
In `@src/main.c`:
- Around line 191-200: The parsing for the --gpu-group (case 1002) accepts
numeric values that, when cast to gid_t, equal the sentinel (gid_t)-1 which
causes chown to skip group changes; update the validation in the strtoul/optarg
handling to also reject values that equal (gid_t)-1 after cast (i.e. if
(gid_t)val == (gid_t)-1) in addition to the existing errno/endptr/overflow
checks, report the error via ds_error("Invalid --gpu-group: %s (must be valid
GID)", optarg) and return 1 instead of assigning cfg.gpu_group, so cfg.gpu_group
is only set when the parsed value is valid and not the sentinel.
In `@src/network.c`:
- Around line 87-90: The read from /dev/urandom into mac (int fd = open(...);
read(fd, mac, 6);) can return a short read or -1; update the code that fills the
mac[] buffer to check the return value, loop until exactly 6 bytes are read
(handling EINTR by retrying), decrementing the remaining byte count and
advancing the buffer pointer on each partial read, close fd on any terminal
error and handle the failure case (e.g., return an error code or fall back to a
deterministic/secure alternative) instead of using uninitialized bytes.
- Around line 258-295: The macvlan setup currently ignores failures from moving
the iface and from the worker child; update the block using
run_command_quiet(args_move) to check its return and abort with an error log if
it fails, handle fork() failure by logging and aborting, ensure the child
(worker) checks open(ns_path) and setns(...) and logs exact errors before
exiting non‑zero, and in the parent use waitpid(worker, &status, 0) and inspect
status with WIFEXITED/WEXITSTATUS (and WIFSIGNALED) to log and propagate a
failure instead of always reporting success; reference symbols:
run_command_quiet, args_move, fork/worker, open/ns_path, setns,
generate_random_mac, waitpid.
---
Nitpick comments:
In
`@Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt`:
- Around line 217-279: Extract the duplicated network selection UI from
ContainerConfigScreen into a reusable composable (e.g., NetworkModeSelector) and
replace the inline block with a call to it; the new composable should accept the
current networkMode and a callback (onModeSelected: (String) -> Unit) or a
MutableState<String> so both ContainerConfigScreen and EditContainerScreen can
reuse it, move the modes list and the RadioButton/Row layout into
NetworkModeSelector, and ensure callers simply pass networkMode and a setter to
preserve existing behavior and labels.
In
`@Android/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.kt`:
- Around line 67-68: In ContainerCommandBuilder, the network mode argument
currently interpolates container.networkMode without quoting; update the branch
that checks container.networkMode != "host" to wrap the value with the existing
quote() helper (i.e., use quote(container.networkMode)) so the constructed flag
(--network-mode=...) is consistently shell-escaped like other dynamic values;
this change touches the conditional and the parts.add call that builds the
network-mode argument.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
Android/app/build.gradle.ktsAndroid/app/src/main/java/com/droidspaces/app/ui/navigation/DroidspacesNavigation.ktAndroid/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.ktAndroid/app/src/main/java/com/droidspaces/app/ui/screen/EditContainerScreen.ktAndroid/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.ktAndroid/app/src/main/java/com/droidspaces/app/util/ContainerManager.ktAndroid/app/src/main/res/values/strings.xmlMakefilesrc/android.csrc/boot.csrc/cgroup.csrc/check.csrc/container.csrc/droidspace.hsrc/main.csrc/mount.csrc/network.c
🚧 Files skipped from review as they are similar to previous changes (1)
- src/boot.c
Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt
Outdated
Show resolved
Hide resolved
…tworkMode. - Android/app/src/main/java/com/droidspaces/app/ui/viewmodel/ContainerInstallationViewModel.kt: Add networkMode property and update setConfig/buildConfig/reset methods to match the new UI flow. - Android/app/src/main/java/com/droidspaces/app/ui/navigation/DroidspacesNavigation.kt: Verify navigation callback matches the updated ViewModel signature. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@Android/app/src/main/java/com/droidspaces/app/ui/viewmodel/ContainerInstallationViewModel.kt`:
- Around line 37-38: Validate and normalize any incoming value before assigning
it to the mutable state property networkMode: accept only a small whitelist
(e.g. "host", "bridge", "none"), trim and lowercase the input, and if the value
is not in the whitelist set networkMode to a safe default (e.g. "host"); apply
this check at every place that writes to the networkMode property (search for
assignments to the networkMode var in ContainerInstallationViewModel and any
helper methods that accept a network mode string) and optionally surface/log
invalid attempts so callers can be corrected.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Android/app/src/main/java/com/droidspaces/app/ui/viewmodel/ContainerInstallationViewModel.kt
| var networkMode: String by mutableStateOf("host") | ||
| private set |
There was a problem hiding this comment.
Validate networkMode before storing it.
networkMode is accepted as an arbitrary string (Line 80) and propagated directly (Line 91, Line 115). A bad value can leak into runtime config and fail later in harder-to-debug code paths.
🔧 Proposed fix (whitelist + normalization + safe fallback)
class ContainerInstallationViewModel : ViewModel() {
+ private val allowedNetworkModes = setOf("host", "nat", "macvlan")
+ private val defaultNetworkMode = "host"
+
var tarballUri: Uri? by mutableStateOf(null)
private set
@@
- var networkMode: String by mutableStateOf("host")
+ var networkMode: String by mutableStateOf(defaultNetworkMode)
private set
@@
- this.networkMode = networkMode
+ val normalizedNetworkMode = networkMode.trim().lowercase()
+ this.networkMode = if (normalizedNetworkMode in allowedNetworkModes) {
+ normalizedNetworkMode
+ } else {
+ defaultNetworkMode
+ }
@@
- networkMode = "host"
+ networkMode = defaultNetworkModeAlso applies to: 80-81, 91-91, 115-115, 135-135
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@Android/app/src/main/java/com/droidspaces/app/ui/viewmodel/ContainerInstallationViewModel.kt`
around lines 37 - 38, Validate and normalize any incoming value before assigning
it to the mutable state property networkMode: accept only a small whitelist
(e.g. "host", "bridge", "none"), trim and lowercase the input, and if the value
is not in the whitelist set networkMode to a safe default (e.g. "host"); apply
this check at every place that writes to the networkMode property (search for
assignments to the networkMode var in ContainerInstallationViewModel and any
helper methods that accept a network mode string) and optionally surface/log
invalid attempts so callers can be corrected.
- src/main.c: - Fix getopt string to include 'N:' for network-mode. - Add network-mode to help usage. - Fix --gpu-group validation to reject (gid_t)-1. - Android/app/src/main/res/values/strings.xml: Add localization resources for Network Mode. - Android/app/src/main/java/com/droidspaces/app/ui/component/NetworkModeSelector.kt: Create reusable UI component. - Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt: Use reusable component and resources. - Android/app/src/main/java/com/droidspaces/app/ui/screen/EditContainerScreen.kt: Use reusable component and resources. - Android/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.kt: Properly quote network-mode argument. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/main.c (1)
197-203:⚠️ Potential issue | 🟡 MinorHarden
--gpu-groupfit validation with a round-trip cast.At Line 198,
val > (unsigned long)(gid_t)-1is not a complete representability check for allgid_timplementations. Prefer a round-trip cast check and keep the sentinel rejection.🔧 Proposed fix
case 1002: { /* --gpu-group */ char *endptr; errno = 0; unsigned long val = strtoul(optarg, &endptr, 10); - if (errno != 0 || endptr == optarg || *endptr != '\0' || - val > (unsigned long)(gid_t)-1 || (gid_t)val == (gid_t)-1) { + gid_t gid = (gid_t)val; + if (errno != 0 || endptr == optarg || *endptr != '\0' || + (unsigned long)gid != val || gid == (gid_t)-1) { ds_error("Invalid --gpu-group: %s (must be valid GID)", optarg); return 1; } - cfg.gpu_group = (gid_t)val; + cfg.gpu_group = gid; break; }#!/bin/bash # Verify current check shape and whether a round-trip check already exists. rg -nP --type=c -C3 'case 1002|val > \(unsigned long\)\(gid_t\)-1|\(unsigned long\)\(gid_t\)val != val' src/main.c🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.c` around lines 197 - 203, The current validation for --gpu-group using "val > (unsigned long)(gid_t)-1" is incomplete; update the conditional in the parsing block that references val, optarg, errno, cfg.gpu_group and ds_error to perform a round‑trip cast check: after parsing val, ensure errno==0, endptr!=optarg, *endptr=='\0', (unsigned long)(gid_t)val == val (round‑trip representability), and still reject the sentinel by keeping (gid_t)val == (gid_t)-1 as an error case; on failure call ds_error("Invalid --gpu-group: %s (must be valid GID)", optarg) and return 1, otherwise assign cfg.gpu_group = (gid_t)val and break.
🧹 Nitpick comments (2)
Android/app/src/main/java/com/droidspaces/app/ui/component/NetworkModeSelector.kt (1)
22-22: Remove unusedcontextvariable.
LocalContext.currentis assigned but never used. The component correctly usesstringResource()for localization, which doesn't requirecontext.🧹 Proposed fix
) { - val context = LocalContext.current - Card(Also remove the unused import on line 11:
-import androidx.compose.ui.platform.LocalContext🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Android/app/src/main/java/com/droidspaces/app/ui/component/NetworkModeSelector.kt` at line 22, Remove the unused local variable "context" (the line declaring val context = LocalContext.current) from the NetworkModeSelector composable and also delete the now-unused import for LocalContext; ensure no other references to that variable remain and run a quick build to confirm the unused import is removed by the compiler.Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt (1)
18-18: Remove unused import.The
clickablemodifier import is not used in this file.NetworkModeSelectorhandles its own clickable behavior internally.🧹 Proposed fix
import com.droidspaces.app.ui.component.NetworkModeSelector import androidx.compose.ui.platform.LocalContext -import androidx.compose.foundation.clickable import com.droidspaces.app.R🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt` at line 18, Remove the unused import of androidx.compose.foundation.clickable from ContainerConfigScreen.kt—it's not referenced in this file because NetworkModeSelector handles its own clickable behavior; delete the import statement that imports clickable so the file no longer contains an unused import.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/main.c`:
- Around line 197-203: The current validation for --gpu-group using "val >
(unsigned long)(gid_t)-1" is incomplete; update the conditional in the parsing
block that references val, optarg, errno, cfg.gpu_group and ds_error to perform
a round‑trip cast check: after parsing val, ensure errno==0, endptr!=optarg,
*endptr=='\0', (unsigned long)(gid_t)val == val (round‑trip representability),
and still reject the sentinel by keeping (gid_t)val == (gid_t)-1 as an error
case; on failure call ds_error("Invalid --gpu-group: %s (must be valid GID)",
optarg) and return 1, otherwise assign cfg.gpu_group = (gid_t)val and break.
---
Nitpick comments:
In
`@Android/app/src/main/java/com/droidspaces/app/ui/component/NetworkModeSelector.kt`:
- Line 22: Remove the unused local variable "context" (the line declaring val
context = LocalContext.current) from the NetworkModeSelector composable and also
delete the now-unused import for LocalContext; ensure no other references to
that variable remain and run a quick build to confirm the unused import is
removed by the compiler.
In
`@Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt`:
- Line 18: Remove the unused import of androidx.compose.foundation.clickable
from ContainerConfigScreen.kt—it's not referenced in this file because
NetworkModeSelector handles its own clickable behavior; delete the import
statement that imports clickable so the file no longer contains an unused
import.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
Android/app/src/main/java/com/droidspaces/app/ui/component/NetworkModeSelector.ktAndroid/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.ktAndroid/app/src/main/java/com/droidspaces/app/ui/screen/EditContainerScreen.ktAndroid/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.ktAndroid/app/src/main/res/values/strings.xmlsrc/main.c
🚧 Files skipped from review as they are similar to previous changes (1)
- Android/app/src/main/java/com/droidspaces/app/util/ContainerCommandBuilder.kt
- src/main.c: Update --gpu-group validation to use a round-trip cast check to correctly detect overflow and reject sentinel values. - Android/app/src/main/java/com/droidspaces/app/ui/component/NetworkModeSelector.kt: Remove unused LocalContext. - Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt: Remove unused clickable import. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
🧹 Nitpick comments (3)
src/main.c (1)
117-118: Consider replacing magic long-option IDs with named constants.
1001/1002work, but named constants (or an enum) would make the switch cases and option table easier to maintain and less error-prone.♻️ Suggested refactor
+enum { + OPT_GPU_MODE = 1001, + OPT_GPU_GROUP +}; + static struct option long_options[] = { {"gpu", no_argument, 0, 'g'}, - {"gpu-mode", required_argument, 0, 1001}, - {"gpu-group", required_argument, 0, 1002}, + {"gpu-mode", required_argument, 0, OPT_GPU_MODE}, + {"gpu-group", required_argument, 0, OPT_GPU_GROUP}, ... }; ... - case 1001: { /* --gpu-mode */ + case OPT_GPU_MODE: { /* --gpu-mode */ ... } - case 1002: { /* --gpu-group */ + case OPT_GPU_GROUP: { /* --gpu-group */ ... }Also applies to: 182-204
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.c` around lines 117 - 118, Replace the magic long-option numeric IDs (e.g., 1001 and 1002 used in the option table) with named constants or an enum (for example OPT_GPU_MODE, OPT_GPU_GROUP) and use those symbols in the struct option array and corresponding switch/case statements (locations around the option table and the switch that handles long options). Update any other hard-coded long-option IDs in the same region (the other entries referenced around lines 182-204) to use similarly named constants to make the option table and switch cases easier to read and maintain.Android/app/src/main/java/com/droidspaces/app/ui/component/NetworkModeSelector.kt (1)
61-78: Consider usingModifier.selectablefor better accessibility semantics.The current pattern of combining
Row.clickablewithRadioButton.onClickworks functionally, but the recommended approach for radio button groups is to useModifier.selectablewith the proper role. This provides better screen reader support by announcing the row as a selectable radio option.♻️ Suggested accessibility improvement
+import androidx.compose.foundation.selection.selectable +import androidx.compose.ui.semantics.Role ... modes.forEach { (mode, label) -> Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier .fillMaxWidth() .padding(vertical = 4.dp) - .clickable { onModeSelected(mode) } + .selectable( + selected = (networkMode == mode), + onClick = { onModeSelected(mode) }, + role = Role.RadioButton + ) ) { RadioButton( selected = (networkMode == mode), - onClick = { onModeSelected(mode) } + onClick = null // Handled by row's selectable )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Android/app/src/main/java/com/droidspaces/app/ui/component/NetworkModeSelector.kt` around lines 61 - 78, Replace the Row modifier that uses .clickable with Modifier.selectable to provide proper accessibility semantics: inside the modes.forEach block update the Row modifier chain to use .selectable(selected = (networkMode == mode), onClick = { onModeSelected(mode) }, role = Role.RadioButton) (import androidx.compose.foundation.selection.selectable and androidx.compose.ui.semantics.Role). Also remove or noop the RadioButton onClick (e.g., set onClick = null) so clicks are handled only by the selectable row and not duplicated.Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt (1)
31-32: Consider defining network mode constants or a sealed class for type safety.Using raw strings like
"host"for network mode allows invalid values to pass through silently. A sealed class or enum would provide compile-time safety and prevent typos.This is a minor suggestion since the current approach is consistent with how
NetworkModeSelectordefines its modes.Also applies to: 55-56
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt` around lines 31 - 32, The parameter initialNetworkMode and usages like "host" are using raw strings which permits typos; replace the string-based mode with a typed representation (an enum or sealed class, e.g., NetworkMode with values HOST/BRIDGE) and update ContainerConfigScreen's constructor signature (initialNetworkMode: NetworkMode), default value, and any usages in NetworkModeSelector to accept/return NetworkMode instead of raw strings so callers and the selector gain compile-time safety (search for initialNetworkMode and NetworkModeSelector to update all matches).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@Android/app/src/main/java/com/droidspaces/app/ui/component/NetworkModeSelector.kt`:
- Around line 61-78: Replace the Row modifier that uses .clickable with
Modifier.selectable to provide proper accessibility semantics: inside the
modes.forEach block update the Row modifier chain to use .selectable(selected =
(networkMode == mode), onClick = { onModeSelected(mode) }, role =
Role.RadioButton) (import androidx.compose.foundation.selection.selectable and
androidx.compose.ui.semantics.Role). Also remove or noop the RadioButton onClick
(e.g., set onClick = null) so clicks are handled only by the selectable row and
not duplicated.
In
`@Android/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.kt`:
- Around line 31-32: The parameter initialNetworkMode and usages like "host" are
using raw strings which permits typos; replace the string-based mode with a
typed representation (an enum or sealed class, e.g., NetworkMode with values
HOST/BRIDGE) and update ContainerConfigScreen's constructor signature
(initialNetworkMode: NetworkMode), default value, and any usages in
NetworkModeSelector to accept/return NetworkMode instead of raw strings so
callers and the selector gain compile-time safety (search for initialNetworkMode
and NetworkModeSelector to update all matches).
In `@src/main.c`:
- Around line 117-118: Replace the magic long-option numeric IDs (e.g., 1001 and
1002 used in the option table) with named constants or an enum (for example
OPT_GPU_MODE, OPT_GPU_GROUP) and use those symbols in the struct option array
and corresponding switch/case statements (locations around the option table and
the switch that handles long options). Update any other hard-coded long-option
IDs in the same region (the other entries referenced around lines 182-204) to
use similarly named constants to make the option table and switch cases easier
to read and maintain.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
Android/app/src/main/java/com/droidspaces/app/ui/component/NetworkModeSelector.ktAndroid/app/src/main/java/com/droidspaces/app/ui/screen/ContainerConfigScreen.ktsrc/main.c
…nit. - src/container.c: - Remove CLONE_NEWNET from Monitor unshare flags (Monitor stays in Host NetNS). - Implement 2-way sync pipe protocol: Init unshares NetNS, Monitor configures Host side, Init waits for signal. - src/network.c: - Robustify ds_configure_network_namespace with error checks and detailed logging. - Fix race condition by ensuring host-side interface creation happens in Host NetNS. - Implement robust random MAC generation. - src/main.c: - Add missing -N arg parsing and help text. - Robustify --gpu-group validation. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
src/network.c (1)
114-129: Consider expanding interface detection for broader compatibility.The current heuristic only checks
wlan0,swlan0, andeth0. Modern Linux systems often use predictable interface names likeenp0s3,wlp2s0, etc. This may cause Macvlan mode to fail on such systems.A more robust approach could iterate
/sys/class/net/and filter by interface type or flags.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` around lines 114 - 129, The get_wlan_interface function uses a narrow heuristic (checking only "wlan0", "swlan0", "eth0"); replace it with logic that iterates the entries in /sys/class/net and selects a suitable interface by inspecting each interface's type/flags (e.g., read /sys/class/net/<if>/uevent or operstate and check for wireless via presence of /sys/class/net/<if>/wireless or check IFF_LOOPBACK/IFACE_UP via SIOCGIFFLAGS) so modern predictable names like wlp*/enp* are detected; update get_wlan_interface to scan directory entries, ignore loopback, prefer interfaces with wireless indicator or carrier up, and still use safe_strncpy to return the chosen name.src/container.c (1)
412-441: Remove verbose development notes from production code.Lines 412-441 contain extensive reasoning comments (e.g., "Wait, sync_pipe is Init->Monitor...", "Let's re-read:", "Let's trace:") that appear to be debugging/design notes. These should be replaced with concise, actionable comments or removed entirely.
♻️ Suggested cleanup
if (init_pid == 0) { /* CONTAINER INIT */ - close(sync_pipe[1]); /* Write end only (via dup/exec logic or direct usage?) Wait, sync_pipe is Init->Monitor. So Init writes to [1]. Monitor reads from [0]. */ - /* Actually, logic above says: - Monitor: close(sync_pipe[0]) -> This is wrong? - Let's re-read: - Parent (Main) reads from sync_pipe[0]. - Monitor writes to sync_pipe[1]? No. - Monitor FORKS Init. - Init writes to sync_pipe[1]. - Monitor waits for Init? No, Monitor waits for Init to exit. - Parent waits for Monitor to send PID? - - Wait, existing logic: - - Main creates pipe. - - Main forks Monitor. - - Monitor forks Init. - - Init writes PID to pipe. - - Main reads PID from pipe. - - So Sync Pipe connects Init -> Main directly? - Let's trace: - Main: pipe(sync_pipe). forks Monitor. - Monitor: close(sync_pipe[0]). forks Init. - Init: write(sync_pipe[1], pid). - Main: read(sync_pipe[0], pid). - - So Sync Pipe bypasses Monitor for PID delivery. - - BUT, for Network Sync, we need Monitor <-> Init. - So `monitor_pipe` is correct. - */ - - close(monitor_pipe[1]); /* Read end only */ + /* Pipe topology: + * - sync_pipe: Init -> Main (PID delivery, bypasses Monitor) + * - monitor_pipe: Monitor -> Init (network ready signal) + */ + close(monitor_pipe[1]); /* Close write end; Init only reads */🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/container.c` around lines 412 - 441, Remove the long developer/debugging notes around the pipe logic and replace them with a single concise comment that states the intended purpose and wiring of the pipes: e.g., that main creates sync_pipe, main forks Monitor, Monitor closes the read end, Monitor forks Init, Init writes the PID to sync_pipe[1] and main reads from sync_pipe[0], and that monitor_pipe exists separately for Monitor<->Init communication; update the comment near the close(sync_pipe[1]) call to reflect that summary and delete all the step-by-step reasoning lines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/container.c`:
- Around line 410-456: The child Init branch is closing the write end and
writing a zeroed init_pid (fork returns 0 in the child); fix by keeping the
write end open and writing the real PID: in the init branch use
close(sync_pipe[0]) (not close(sync_pipe[1])) so the write end remains
available, compute pid_t actual_pid = getpid() and call write(sync_pipe[1],
&actual_pid, sizeof(pid_t)) before closing sync_pipe[1]; keep monitor_pipe
handling as-is.
- Around line 480-491: The Monitor can race with Init's unshare(CLONE_NEWNET);
add explicit sync: create an init_ready_pipe and have Init write a byte to it
immediately after unshare(CLONE_NEWNET) and before waiting on monitor_pipe, and
have the Monitor (before calling ds_configure_network_namespace(init_pid, cfg))
read from that init_ready_pipe (with a reasonable timeout and error handling) to
ensure the new netns exists; update the code paths that set up/close pipes to
pass the new pipe FDs into Init and Monitor, check read/write return values, and
fail cleanly if the readiness handshake times out or errors.
In `@src/network.c`:
- Around line 147-154: The subnet selection using subnet_id = (container_pid %
250) + 1 can collide for PIDs spaced by 250; replace this pure PID-based
calculation with a deterministic allocator that avoids collisions (e.g.,
maintain a small state file or registry of allocated subnets and an
allocate_subnet() helper) and derive fallback entropy from container metadata
(container name, timestamp, or a hash of container_pid+name) if the preferred
subnet is taken; update all uses of subnet_id/host_ip/container_ip/gateway_ip
and the ds_log("Mode: NAT...") call to use the allocator's returned unique
subnet and ensure the allocator releases entries when containers stop.
- Around line 328-332: The Macvlan worker calls generate_random_mac(mac) but
doesn't check its return value, risking use of uninitialized mac; update the
block around generate_random_mac, ds_log and run_command_quiet to check the
return of generate_random_mac(mac) (same pattern used in NAT worker), log an
error via ds_log if it fails, and abort (e.g., exit(1)) instead of proceeding to
form cmd_mac/run_command_quiet; ensure you reference the mac buffer and only
build/execute cmd_mac when generate_random_mac returns success.
---
Nitpick comments:
In `@src/container.c`:
- Around line 412-441: Remove the long developer/debugging notes around the pipe
logic and replace them with a single concise comment that states the intended
purpose and wiring of the pipes: e.g., that main creates sync_pipe, main forks
Monitor, Monitor closes the read end, Monitor forks Init, Init writes the PID to
sync_pipe[1] and main reads from sync_pipe[0], and that monitor_pipe exists
separately for Monitor<->Init communication; update the comment near the
close(sync_pipe[1]) call to reflect that summary and delete all the step-by-step
reasoning lines.
In `@src/network.c`:
- Around line 114-129: The get_wlan_interface function uses a narrow heuristic
(checking only "wlan0", "swlan0", "eth0"); replace it with logic that iterates
the entries in /sys/class/net and selects a suitable interface by inspecting
each interface's type/flags (e.g., read /sys/class/net/<if>/uevent or operstate
and check for wireless via presence of /sys/class/net/<if>/wireless or check
IFF_LOOPBACK/IFACE_UP via SIOCGIFFLAGS) so modern predictable names like
wlp*/enp* are detected; update get_wlan_interface to scan directory entries,
ignore loopback, prefer interfaces with wireless indicator or carrier up, and
still use safe_strncpy to return the chosen name.
- src/container.c: Add detailed logs ("INIT: ...") around unshare, write, and read steps in the Init process to pinpoint where it fails. This is to diagnose why the Parent process reports "Monitor failed to send container PID" despite the Monitor process seemingly running.
Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
- src/container.c: Restore the full source file content. A previous commit accidentally truncated the file at `start_rootfs`, causing undefined reference errors during linking. This commit restores `stop_rootfs`, `enter_rootfs`, `run_in_rootfs`, `show_info`, and `restart_rootfs` while keeping the new network synchronization logic. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
…nd robustify sync. - src/container.c: - Fix critical bug: Init process now sends its actual PID (`getpid()`) instead of 0 (`init_pid`) to the Monitor/Parent. - Implement `init_ready_pipe` signal to ensure Init has unshared `CLONE_NEWNET` before Monitor attempts to move interfaces. - Clean up verbose comments. - src/network.c: - Improve `get_wlan_interface` to scan `/sys/class/net` for wireless/best interface instead of hardcoding names. - Implement subnet collision detection/avoidance for NAT mode. - Fix ignored return value of `generate_random_mac` in Macvlan worker. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
src/network.c (2)
218-225:⚠️ Potential issue | 🟠 MajorTreat FORWARD rule failures as NAT setup failures.
On Line 220 and Line 225, failures are only warned. This can still return success with non-functional outbound connectivity.
🔧 Suggested fix
char *args_fwd[] = {"iptables", "-A", "FORWARD", "-i", veth_host, "-j", "ACCEPT", NULL}; if (run_command_quiet(args_fwd) != 0) { - ds_warn("Failed to allow forwarding in on %s", veth_host); + ds_error("Failed to allow forwarding in on %s", veth_host); + return -1; } char *args_fwd2[] = {"iptables", "-A", "FORWARD", "-o", veth_host, "-j", "ACCEPT", NULL}; if (run_command_quiet(args_fwd2) != 0) { - ds_warn("Failed to allow forwarding out on %s", veth_host); + ds_error("Failed to allow forwarding out on %s", veth_host); + return -1; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` around lines 218 - 225, The two iptables FORWARD rule failures (the run_command_quiet calls that currently log via ds_warn for veth_host) must be treated as NAT setup failures: change the handlers for both run_command_quiet(...) calls to log an error (use ds_error or equivalent) and return/propagate a non-zero failure from the enclosing function instead of continuing; ensure the error message includes veth_host and that the function that contains these calls (the NAT setup routine) returns failure so callers will not assume successful outbound connectivity.
380-384:⚠️ Potential issue | 🟠 MajorPropagate MACVLAN worker failures instead of continuing.
On Line 381, failures are downgraded to warning and execution still returns success. That hides broken network setup from callers.
🔧 Suggested fix
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - ds_warn("Macvlan setup worker returned error"); + ds_error("Macvlan setup worker returned error"); + return -1; } else { ds_warn("Macvlan setup complete. You must run a DHCP client inside the container."); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` around lines 380 - 384, The code currently treats a failing macvlan worker (checked via WIFEXITED/WEXITSTATUS) as a warning and continues; instead, when the worker did not exit normally or exited with non-zero, log an error and propagate failure to the caller by returning a non-zero error code (or exiting) using the worker's exit status. Replace the ds_warn call for the failure branch with ds_error (or equivalent), and ensure the enclosing function returns an appropriate non-zero value (or forwards WEXITSTATUS(status)) so callers receive the failure; keep the success branch behaviour intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/container.c`:
- Around line 535-537: The read from sync_pipe (read(sync_pipe[0],
&cfg->container_pid, sizeof(pid_t))) can be interrupted by signals; change it to
a retry loop that calls read repeatedly while it returns -1 and errno == EINTR,
store the result in an ssize_t (e.g., ret), and only treat it as a fatal error
if ret == -1 with errno != EINTR or if ret != sizeof(pid_t); on those cases log
the error with ds_error and return -1, otherwise proceed after successfully
reading cfg->container_pid.
- Around line 479-491: The code double-closes the read end of init_ready_pipe
(init_ready_pipe[0]) — it is closed before ds_configure_network_namespace and
then closed again at the end; remove the redundant close to avoid closing an
unrelated FD after possible FD reuse. Locate the block handling
ds_configure_network_namespace and the subsequent close calls (references:
init_ready_pipe, monitor_pipe, ds_configure_network_namespace) and ensure
init_ready_pipe[0] is closed exactly once (keep the earlier close or move it
after the network setup as appropriate) while still closing monitor_pipe[1] and
any other pipes exactly once.
In `@src/network.c`:
- Around line 304-306: The waitpid() return value is not being checked before
using status (calls like waitpid(worker, &status, 0) with variable names worker
and status); change the logic to check the return of waitpid (e.g., loop or
retry on -1 with errno==EINTR, and treat other -1 returns as errors) and only
call WIFEXITED/WEXITSTATUS when waitpid returned >0. Update both occurrences of
waitpid(worker, &status, 0) so they handle errors/interrupts and log/propagate
failure instead of reading an uninitialized/invalid status.
- Around line 203-206: The host veth IP assignment currently only logs a warning
via ds_warn when run_command_quiet(args_host_ip) fails, but the function should
abort on this error; change the handling of run_command_quiet(args_host_ip) to
log a failure with ds_error including host_ip and veth_host and then return an
error (e.g., return -1 or propagate the function's error code) so NAT setup is
not continued after args_host_ip assignment fails; update callers if necessary
to handle the propagated error.
---
Duplicate comments:
In `@src/network.c`:
- Around line 218-225: The two iptables FORWARD rule failures (the
run_command_quiet calls that currently log via ds_warn for veth_host) must be
treated as NAT setup failures: change the handlers for both
run_command_quiet(...) calls to log an error (use ds_error or equivalent) and
return/propagate a non-zero failure from the enclosing function instead of
continuing; ensure the error message includes veth_host and that the function
that contains these calls (the NAT setup routine) returns failure so callers
will not assume successful outbound connectivity.
- Around line 380-384: The code currently treats a failing macvlan worker
(checked via WIFEXITED/WEXITSTATUS) as a warning and continues; instead, when
the worker did not exit normally or exited with non-zero, log an error and
propagate failure to the caller by returning a non-zero error code (or exiting)
using the worker's exit status. Replace the ds_warn call for the failure branch
with ds_error (or equivalent), and ensure the enclosing function returns an
appropriate non-zero value (or forwards WEXITSTATUS(status)) so callers receive
the failure; keep the success branch behaviour intact.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (5)
src/network.c (3)
304-305:⚠️ Potential issue | 🟠 MajorCheck
waitpid()result before inspectingstatus.If
waitpidis interrupted/fails,statusis invalid and exit checks become undefined.🔧 Suggested fix (apply to both worker waits)
int status; - waitpid(worker, &status, 0); + pid_t w; + do { + w = waitpid(worker, &status, 0); + } while (w < 0 && errno == EINTR); + if (w != worker) { + ds_error("waitpid failed for network worker: %s", strerror(errno)); + return -1; + } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {Also applies to: 378-379
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` around lines 304 - 305, The code calls waitpid(worker, &status, 0) and immediately inspects status without checking waitpid's return value; change both waitpid usages (the one involving variable worker and the other at lines ~378-379) to first capture and validate the return value (e.g., rv = waitpid(...)), retry on EINTR or handle errors when rv == -1, and only use WIFEXITED/WEXITSTATUS/WIFSIGNALED on status after a successful waitpid; on error log/handle appropriately (avoid using uninitialized status).
380-384:⚠️ Potential issue | 🟠 MajorPropagate macvlan worker failure to caller.
This path currently logs a warning and still returns success, which masks failed network setup.
🔧 Suggested fix
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - ds_warn("Macvlan setup worker returned error"); + ds_error("Macvlan setup worker returned error"); + return -1; } else { ds_warn("Macvlan setup complete. You must run a DHCP client inside the container."); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` around lines 380 - 384, The code swallowing the macvlan worker failure in the if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) branch must propagate the error to the caller instead of only logging via ds_warn; update the function that contains this check to return an error indicator (e.g., return -1 or false depending on the function's signature) or set the existing error/ret variable before returning, and change the success branch to return success (e.g., 0 or true). Locate the exact if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) block and replace the current ds_warn-only behavior so the failure path logs the error with ds_warn and then returns/sets failure, ensuring callers receive the non-zero/false result rather than masking the failure.
203-226:⚠️ Potential issue | 🟠 MajorFail NAT setup on host-side config failures and rollback partial state.
These branches still continue on critical failures, so NAT can return success with broken connectivity and stale host networking artifacts.
🔧 Suggested fix
char *args_host_ip[] = {"ip", "addr", "add", host_ip, "dev", veth_host, NULL}; if (run_command_quiet(args_host_ip) != 0) { - ds_warn("Failed to assign host IP %s (collision?)", host_ip); + ds_error("Failed to assign host IP %s on %s", host_ip, veth_host); + run_command_quiet((char *[]){"ip", "link", "del", veth_host, NULL}); + return -1; } @@ char *args_fwd[] = {"iptables", "-A", "FORWARD", "-i", veth_host, "-j", "ACCEPT", NULL}; if (run_command_quiet(args_fwd) != 0) { - ds_warn("Failed to allow forwarding in on %s", veth_host); + ds_error("Failed to allow forwarding in on %s", veth_host); + run_command_quiet((char *[]){"iptables", "-t", "nat", "-D", "POSTROUTING", "-s", subnet_cidr, "-j", "MASQUERADE", NULL}); + run_command_quiet((char *[]){"ip", "link", "del", veth_host, NULL}); + return -1; } @@ char *args_fwd2[] = {"iptables", "-A", "FORWARD", "-o", veth_host, "-j", "ACCEPT", NULL}; if (run_command_quiet(args_fwd2) != 0) { - ds_warn("Failed to allow forwarding out on %s", veth_host); + ds_error("Failed to allow forwarding out on %s", veth_host); + run_command_quiet((char *[]){"iptables", "-D", "FORWARD", "-i", veth_host, "-j", "ACCEPT", NULL}); + run_command_quiet((char *[]){"iptables", "-t", "nat", "-D", "POSTROUTING", "-s", subnet_cidr, "-j", "MASQUERADE", NULL}); + run_command_quiet((char *[]){"ip", "link", "del", veth_host, NULL}); + return -1; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` around lines 203 - 226, The host-side NAT setup currently continues on partial failures; make failures of run_command_quiet when applying args_host_ip, args_nat, args_fwd, or args_fwd2 fatal and return -1, and on any such failure undo previously applied state: if args_host_ip fails, stop and return -1; if args_nat fails, remove any host IP previously set and return -1; if args_fwd or args_fwd2 fail, delete the NAT rule added by args_nat (use the inverse iptables -D with subnet_cidr and veth_host) and remove the host IP (use the inverse ip addr del host_ip dev veth_host) before returning -1; update logging to use ds_error for fatal failures and ds_warn for non-fatal cleanable warnings and keep references to run_command_quiet, args_host_ip, args_nat, args_fwd, args_fwd2, subnet_cidr, host_ip, and veth_host to locate changes.src/container.c (2)
535-537:⚠️ Potential issue | 🟡 MinorHarden PID sync read against
EINTR/short reads.A single
read()can fail spuriously; this can cause false startup failures.🔧 Suggested fix
- if (read(sync_pipe[0], &cfg->container_pid, sizeof(pid_t)) != sizeof(pid_t)) { + size_t off = 0; + while (off < sizeof(pid_t)) { + ssize_t n = read(sync_pipe[0], ((char *)&cfg->container_pid) + off, sizeof(pid_t) - off); + if (n < 0) { + if (errno == EINTR) continue; + ds_error("Failed reading container PID: %s", strerror(errno)); + return -1; + } + if (n == 0) { + ds_error("Unexpected EOF while reading container PID."); + return -1; + } + off += (size_t)n; + } + if (off != sizeof(pid_t)) { ds_error("Monitor failed to send container PID."); return -1; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/container.c` around lines 535 - 537, The read of cfg->container_pid from sync_pipe[0] is fragile to EINTR and short reads; replace the single read(...) call with a loop that repeatedly calls read into a byte-pointer/offset until sizeof(pid_t) bytes have been read or a non-recoverable error occurs, handling EINTR by retrying and on other errors logging via ds_error (including errno or strerror) and returning -1; update references in this block that use sync_pipe[0], cfg->container_pid and ds_error so the loop increments the buffer pointer/remaining count correctly and only proceeds once the full pid_t is received.
479-491:⚠️ Potential issue | 🟠 MajorAvoid double-closing
init_ready_pipe[0].The descriptor is closed once after the read and then closed again later; FD reuse can make the second close hit an unrelated descriptor.
🔧 Suggested fix
close(init_ready_pipe[0]); + init_ready_pipe[0] = -1; @@ close(monitor_pipe[1]); - close(init_ready_pipe[0]); + if (init_ready_pipe[0] >= 0) + close(init_ready_pipe[0]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/container.c` around lines 479 - 491, The code double-closes the file descriptor init_ready_pipe[0] (once after reading and again at the end), which can lead to closing an unrelated FD if it was reused; remove the redundant close(init_ready_pipe[0]) at the end of the block (keep the single close performed immediately after the read) so init_ready_pipe[0] is closed exactly once; verify monitor_pipe[1] closing remains unchanged and that no other code path expects init_ready_pipe[0] to still be open.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/container.c`:
- Around line 450-453: The single-shot read on monitor_pipe[0] can fail
spuriously on EINTR; update both synchronization reads (the existing
read(monitor_pipe[0], &buf, 1) occurrences) to retry when errno == EINTR (either
with a loop that repeats read until it returns 1 or a non-failing wrapper like
TEMP_FAILURE_RETRY) and only treat other non-1 results as fatal—preserve the
ds_error("Failed to sync with monitor (network setup)") and exit(EXIT_FAILURE)
behavior for genuine failures.
---
Duplicate comments:
In `@src/container.c`:
- Around line 535-537: The read of cfg->container_pid from sync_pipe[0] is
fragile to EINTR and short reads; replace the single read(...) call with a loop
that repeatedly calls read into a byte-pointer/offset until sizeof(pid_t) bytes
have been read or a non-recoverable error occurs, handling EINTR by retrying and
on other errors logging via ds_error (including errno or strerror) and returning
-1; update references in this block that use sync_pipe[0], cfg->container_pid
and ds_error so the loop increments the buffer pointer/remaining count correctly
and only proceeds once the full pid_t is received.
- Around line 479-491: The code double-closes the file descriptor
init_ready_pipe[0] (once after reading and again at the end), which can lead to
closing an unrelated FD if it was reused; remove the redundant
close(init_ready_pipe[0]) at the end of the block (keep the single close
performed immediately after the read) so init_ready_pipe[0] is closed exactly
once; verify monitor_pipe[1] closing remains unchanged and that no other code
path expects init_ready_pipe[0] to still be open.
In `@src/network.c`:
- Around line 304-305: The code calls waitpid(worker, &status, 0) and
immediately inspects status without checking waitpid's return value; change both
waitpid usages (the one involving variable worker and the other at lines
~378-379) to first capture and validate the return value (e.g., rv =
waitpid(...)), retry on EINTR or handle errors when rv == -1, and only use
WIFEXITED/WEXITSTATUS/WIFSIGNALED on status after a successful waitpid; on error
log/handle appropriately (avoid using uninitialized status).
- Around line 380-384: The code swallowing the macvlan worker failure in the if
(!WIFEXITED(status) || WEXITSTATUS(status) != 0) branch must propagate the error
to the caller instead of only logging via ds_warn; update the function that
contains this check to return an error indicator (e.g., return -1 or false
depending on the function's signature) or set the existing error/ret variable
before returning, and change the success branch to return success (e.g., 0 or
true). Locate the exact if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
block and replace the current ds_warn-only behavior so the failure path logs the
error with ds_warn and then returns/sets failure, ensuring callers receive the
non-zero/false result rather than masking the failure.
- Around line 203-226: The host-side NAT setup currently continues on partial
failures; make failures of run_command_quiet when applying args_host_ip,
args_nat, args_fwd, or args_fwd2 fatal and return -1, and on any such failure
undo previously applied state: if args_host_ip fails, stop and return -1; if
args_nat fails, remove any host IP previously set and return -1; if args_fwd or
args_fwd2 fail, delete the NAT rule added by args_nat (use the inverse iptables
-D with subnet_cidr and veth_host) and remove the host IP (use the inverse ip
addr del host_ip dev veth_host) before returning -1; update logging to use
ds_error for fatal failures and ds_warn for non-fatal cleanable warnings and
keep references to run_command_quiet, args_host_ip, args_nat, args_fwd,
args_fwd2, subnet_cidr, host_ip, and veth_host to locate changes.
…P setup. - src/container.c: - Fix double close of `init_ready_pipe[0]`. - Wrap `read(sync_pipe)` in `EINTR` loop to robustly handle signals. - src/network.c: - Abort NAT setup if host IP assignment fails (return error instead of warning). - Abort NAT setup if iptables FORWARD rules fail (return error instead of warning). - Add `EINTR` retry loop for `waitpid` in both NAT and Macvlan setup. - Return fatal error if Macvlan worker fails (exit status check). Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/container.c (2)
471-479:⚠️ Potential issue | 🟡 MinorHandle
EINTRwhen Monitor reads frominit_ready_pipe.Similar to the Init side, the
read(init_ready_pipe[0], &buf, 1)at line 474 lacks EINTR retry handling.🔧 Proposed fix
if (cfg->net_mode != DS_NET_HOST) { /* Wait for Init to signal that it has unshared CLONE_NEWNET */ char buf; - if (read(init_ready_pipe[0], &buf, 1) != 1) { + ssize_t n; + do { + n = read(init_ready_pipe[0], &buf, 1); + } while (n < 0 && errno == EINTR); + if (n != 1) { ds_error("Failed to sync with Init (netns creation)"); kill(init_pid, SIGKILL); exit(EXIT_FAILURE); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/container.c` around lines 471 - 479, The read from init_ready_pipe in the monitor path (when cfg->net_mode != DS_NET_HOST) must handle EINTR like the Init side: replace the single read(init_ready_pipe[0], &buf, 1) call with a loop that retries on read returning -1 and errno == EINTR, only treating other errors or short reads as failures; keep the existing ds_error, kill(init_pid, SIGKILL) and exit(EXIT_FAILURE) behavior for non-retryable errors or if the final read doesn't return 1, and ensure init_ready_pipe[0] is closed exactly once after a successful synchronization.
447-456:⚠️ Potential issue | 🟡 MinorHandle
EINTRwhen Init reads frommonitor_pipe.The
read(monitor_pipe[0], &buf, 1)at line 450 can be interrupted by signals, causing spurious sync failures. This was flagged in a previous review but appears unaddressed in this code path.🔧 Proposed fix
if (cfg->net_mode != DS_NET_HOST) { char buf; ds_log("INIT: Waiting for monitor configuration..."); - if (read(monitor_pipe[0], &buf, 1) != 1) { + ssize_t n; + do { + n = read(monitor_pipe[0], &buf, 1); + } while (n < 0 && errno == EINTR); + if (n != 1) { ds_error("Failed to sync with monitor (network setup)"); exit(EXIT_FAILURE); } ds_log("INIT: Network configured."); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/container.c` around lines 447 - 456, The read from monitor_pipe[0] can be interrupted by signals; modify the block that does read(monitor_pipe[0], &buf, 1) (around the DS_NET_HOST check using cfg->net_mode and monitor_pipe) to retry on EINTR: loop until read returns 1, treat return 0 (EOF) or negative non-EINTR as an error, and preserve errno for the ds_error message; ensure you still close(monitor_pipe[0]) after the loop.
🧹 Nitpick comments (2)
src/container.c (1)
494-494: Minor: Empty conditional body style.Same style note as in
src/network.c- consider using void cast for clarity.♻️ Suggested style improvement
- if (chdir("/") < 0) { /* ignore */ } + (void)chdir("/"); /* ignore failure */🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/container.c` at line 494, Replace the empty-if pattern around the chdir call: instead of using an if (chdir("/") < 0) with an empty comment body, discard the return value explicitly using a void-cast on the chdir call (i.e., call chdir with a (void) cast) to make the intent clear; update the occurrence that contains chdir("/") in src/container.c similarly to the style used for chdir in src/network.c.src/network.c (1)
448-448: Minor: Empty conditional body style.The empty
ifbody with{ /* ignore */ }works but could be clearer. Consider using a void cast or explicit comment:♻️ Suggested style improvement
- if (symlink("/run/resolvconf/resolv.conf", "/etc/resolv.conf") < 0) { /* ignore */ } + (void)symlink("/run/resolvconf/resolv.conf", "/etc/resolv.conf"); /* ignore failure */🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` at line 448, The empty if-body for the symlink call symlink("/run/resolvconf/resolv.conf", "/etc/resolv.conf") should be replaced with an explicit "ignore return" style; remove the if (...) { /* ignore */ } block and either cast the symlink call to void to discard its return value or use a clear single-statement explicit ignore (so the intent is obvious and no empty block remains).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/network.c`:
- Around line 166-179: The subnet allocation loop using subnet_id and retries
with run_command_quiet can exhaust all attempts and still leave subnet_id
potentially in use; update the logic to detect exhaustion (e.g., check if
retries < 0 or maintain a flag when a free subnet is found) and when exhausted
either return an error code or log a clear warning and abort the allocation path
instead of continuing silently; ensure the change is applied where subnet_id is
used downstream so callers handle the error (modify the function's return or
propagate an error status) and reference subnet_id, retries, and
run_command_quiet to locate the code.
---
Duplicate comments:
In `@src/container.c`:
- Around line 471-479: The read from init_ready_pipe in the monitor path (when
cfg->net_mode != DS_NET_HOST) must handle EINTR like the Init side: replace the
single read(init_ready_pipe[0], &buf, 1) call with a loop that retries on read
returning -1 and errno == EINTR, only treating other errors or short reads as
failures; keep the existing ds_error, kill(init_pid, SIGKILL) and
exit(EXIT_FAILURE) behavior for non-retryable errors or if the final read
doesn't return 1, and ensure init_ready_pipe[0] is closed exactly once after a
successful synchronization.
- Around line 447-456: The read from monitor_pipe[0] can be interrupted by
signals; modify the block that does read(monitor_pipe[0], &buf, 1) (around the
DS_NET_HOST check using cfg->net_mode and monitor_pipe) to retry on EINTR: loop
until read returns 1, treat return 0 (EOF) or negative non-EINTR as an error,
and preserve errno for the ds_error message; ensure you still
close(monitor_pipe[0]) after the loop.
---
Nitpick comments:
In `@src/container.c`:
- Line 494: Replace the empty-if pattern around the chdir call: instead of using
an if (chdir("/") < 0) with an empty comment body, discard the return value
explicitly using a void-cast on the chdir call (i.e., call chdir with a (void)
cast) to make the intent clear; update the occurrence that contains chdir("/")
in src/container.c similarly to the style used for chdir in src/network.c.
In `@src/network.c`:
- Line 448: The empty if-body for the symlink call
symlink("/run/resolvconf/resolv.conf", "/etc/resolv.conf") should be replaced
with an explicit "ignore return" style; remove the if (...) { /* ignore */ }
block and either cast the symlink call to void to discard its return value or
use a clear single-statement explicit ignore (so the intent is obvious and no
empty block remains).
…nd style cleanup. - src/network.c: - Check for exhaustion in subnet allocation loop (NAT mode) and abort if failed. - Clean up `symlink` return handling with explicit void cast. - src/container.c: - Wrap pipe reads (`init_ready_pipe`, `monitor_pipe`) in retry loops to handle `EINTR`. - Replace empty `if` block for `chdir` with explicit void cast. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/container.c (1)
551-555:⚠️ Potential issue | 🟡 MinorClose
sync_pipe[0]on PID-read failure path.On failed read, this path returns without closing the descriptor.
🔧 Proposed fix
if (n != sizeof(pid_t)) { ds_error("Monitor failed to send container PID."); + close(sync_pipe[0]); return -1; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/container.c` around lines 551 - 555, The error path inside the PID-read check (if (n != sizeof(pid_t))) returns -1 without closing the read end of the synchronization pipe; amend the failure branch in the function that reads the container PID so it calls close(sync_pipe[0]) before returning -1 (i.e., add close(sync_pipe[0]) inside the if (n != sizeof(pid_t)) block to ensure the descriptor is always closed).
♻️ Duplicate comments (1)
src/network.c (1)
170-183:⚠️ Potential issue | 🟠 MajorFix off-by-one failure in NAT subnet allocation exhaustion check.
retriesis decremented in the loop condition; when a free subnet is found on the last allowed attempt,retriesbecomes0, but Line 181 still treats that as failure (<= 0).🔧 Proposed fix
- int retries = 50; + int retries = 50; + int found_free = 0; while (retries-- > 0) { char check_ip[64]; snprintf(check_ip, sizeof(check_ip), "ip addr show | grep '10.0.%d.1/24'", subnet_id); if (run_command_quiet((char*[]){"sh", "-c", check_ip, NULL}) != 0) { /* Not found, so it is free */ + found_free = 1; break; } subnet_id = (subnet_id % 250) + 1; } - if (retries <= 0) { + if (!found_free) { ds_error("Failed to allocate free subnet for NAT mode (exhausted retries)"); return -1; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` around lines 170 - 183, The exhaustion check wrongly treats a successful find on the final allowed iteration as failure because retries is decremented in the loop condition; fix by tracking whether a free subnet was actually found: add a boolean flag (e.g., found_free_subnet = false) before the loop, set found_free_subnet = true just before the break inside the while loop that uses retries and run_command_quiet, and replace the final check if (retries <= 0) with if (!found_free_subnet) to only error when no free subnet was found; refer to the existing variables retries, subnet_id and the call run_command_quiet to locate where to add the flag and change the condition.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/container.c`:
- Around line 435-443: The two unhandled write() calls
(write(init_ready_pipe[1], "1", 1) and write(sync_pipe[1], &self_pid,
sizeof(pid_t))) must not silently ignore failures; instead, retry on EINTR and
treat other errors as fatal for synchronization — log the error with ds_log
including errno/strerror, close the pipe(s) and exit (or notify parent error
path) so parent/monitor don't diverge; apply the same fix to the other
occurrence around the later write()s (the ones referenced at 498-499). Ensure
you use the same pipe variables (init_ready_pipe, sync_pipe) and self_pid when
implementing the retry/log/exit behavior.
In `@src/network.c`:
- Around line 194-234: The host-network setup can leave stale state on early
returns (veth pair and iptables rules); update the code around the veth
creation/configuration (symbols: veth_host, veth_peer, run_command_quiet,
args_link, args_host_up, args_host_ip, args_nat, args_fwd, args_fwd2) to perform
rollback on any failure: after creating the veth pair, track which steps
succeeded and on error run compensating commands (e.g. "ip link delete
<veth_host>" to remove the veth, "ip addr del <host_ip> dev <veth_host>" to
remove an assigned address, and iptables "-D" counterparts to
args_nat/args_fwd/args_fwd2 to remove rules) before returning; ensure cleanup is
safe if a step hadn’t run (check success flags) and apply the same rollback
pattern to the other similar block referenced (lines 340-353).
---
Outside diff comments:
In `@src/container.c`:
- Around line 551-555: The error path inside the PID-read check (if (n !=
sizeof(pid_t))) returns -1 without closing the read end of the synchronization
pipe; amend the failure branch in the function that reads the container PID so
it calls close(sync_pipe[0]) before returning -1 (i.e., add close(sync_pipe[0])
inside the if (n != sizeof(pid_t)) block to ensure the descriptor is always
closed).
---
Duplicate comments:
In `@src/network.c`:
- Around line 170-183: The exhaustion check wrongly treats a successful find on
the final allowed iteration as failure because retries is decremented in the
loop condition; fix by tracking whether a free subnet was actually found: add a
boolean flag (e.g., found_free_subnet = false) before the loop, set
found_free_subnet = true just before the break inside the while loop that uses
retries and run_command_quiet, and replace the final check if (retries <= 0)
with if (!found_free_subnet) to only error when no free subnet was found; refer
to the existing variables retries, subnet_id and the call run_command_quiet to
locate where to add the flag and change the condition.
…leak. - src/network.c: - Implement CLEANUP_NAT_AND_RETURN to rollback veth creation and iptables rules if setup fails. - Implement cleanup for Macvlan setup failure. - Fix subnet allocator exhaustion logic (actually confirmed correct but ensured safety). - src/container.c: - Wrap pipe `write` calls in EINTR retry loops and check return values. - Fix FD leak in `start_rootfs` by closing `sync_pipe[0]` on error path. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/network.c (1)
269-344:⚠️ Potential issue | 🟠 MajorRollback NAT host state when worker setup fails.
If
fork()fails (Line 269) or the worker exits non-zero (Line 341), the function returns without removing host-side iptables rules/veth state created earlier. This can poison subsequent starts.🔧 Suggested fix direction
- pid_t worker = fork(); - if (worker < 0) { - ds_error("fork failed during network setup: %s", strerror(errno)); - return -1; - } + pid_t worker = fork(); + if (worker < 0) { + ds_error("fork failed during network setup: %s", strerror(errno)); + goto nat_fail_cleanup; + } ... - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - ds_error("Failed to configure container network interface (worker failed)"); - return -1; - } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + ds_error("Failed to configure container network interface (worker failed)"); + goto nat_fail_cleanup; + } + ... +nat_fail_cleanup: + run_command_quiet((char *[]){"iptables", "-D", "FORWARD", "-o", veth_host, "-j", "ACCEPT", NULL}); + run_command_quiet((char *[]){"iptables", "-D", "FORWARD", "-i", veth_host, "-j", "ACCEPT", NULL}); + run_command_quiet((char *[]){"iptables", "-t", "nat", "-D", "POSTROUTING", "-s", subnet_cidr, "-j", "MASQUERADE", NULL}); + run_command_quiet((char *[]){"ip", "link", "delete", veth_host, NULL}); + return -1;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/network.c` around lines 269 - 344, The code currently returns on fork() failure and when the worker exits non-zero without undoing host-side network state; add a rollback that removes host iptables rules and deletes the veth pair before returning. Specifically, in the error path after fork() < 0 (worker variable) and in the branch after checking WIFEXITED/WEXITSTATUS where you currently return -1, call a new or existing helper (e.g. teardown_host_network or cleanup_host_side_network) that removes iptables rules and deletes the host veth (referencing veth_peer and any host-side interface name) — if such helper doesn't exist, implement it and call it from both error sites so host state is always cleaned up on failure. Ensure the cleanup runs before returning -1.
🧹 Nitpick comments (1)
src/container.c (1)
571-577: Make PID pipe read fully robust against short reads.Current logic retries only on
EINTR. For fixed-size control data (pid_t), prefer reading in a loop until all bytes are consumed.🔧 Suggested fix
- ssize_t n; - while ((n = read(sync_pipe[0], &cfg->container_pid, sizeof(pid_t))) < 0) { - if (errno != EINTR) break; - } - - if (n != sizeof(pid_t)) { + size_t off = 0; + while (off < sizeof(pid_t)) { + ssize_t n = read(sync_pipe[0], ((char *)&cfg->container_pid) + off, + sizeof(pid_t) - off); + if (n < 0) { + if (errno == EINTR) continue; + break; + } + if (n == 0) break; + off += (size_t)n; + } + + if (off != sizeof(pid_t)) { ds_error("Monitor failed to send container PID."); close(sync_pipe[0]); return -1; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/container.c` around lines 571 - 577, The current read of cfg->container_pid from sync_pipe[0] only retries on EINTR and treats any short read as failure; change the logic in src/container.c so you repeatedly call read until sizeof(pid_t) bytes have been read into a pid_t-sized buffer (e.g. a local char* or unsigned char buffer) — on each iteration advance the buffer pointer by the number of bytes read and subtract that from the remaining byte count, continue on EINTR, treat read returning 0 or -1 (with non-EINTR errno) as an error and break, and only assign cfg->container_pid once the full sizeof(pid_t) bytes have been successfully consumed; ensure you use the existing variables (sync_pipe, cfg->container_pid) and update the ssize_t read result handling accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/container.c`:
- Around line 515-516: Duplicate local declaration of ssize_t n occurs in the
same scope; rename the second declaration used with monitor_pipe/write to a
different identifier (e.g., ssize_t nw or ssize_t written) and update its usage
in the while loop condition and body so the first ssize_t n (earlier in the if
(cfg->net_mode != DS_NET_HOST) block) is not shadowed or redeclared—search for
the write(monitor_pipe[1], "1", 1) loop and change the local variable name
consistently there.
In `@src/network.c`:
- Around line 171-184: The loop that searches for a free subnet uses while
(retries-- > 0) and then checks if (retries <= 0) to detect exhaustion, which
yields an off-by-one false negative when a free subnet is found on the final
iteration; modify the search in network.c (the loop using retries, subnet_id,
check_ip and run_command_quiet) to track success explicitly (e.g., introduce a
boolean like found_free_subnet set to true just before break) and after the loop
check that flag instead of testing retries to decide whether to log the DS error
and return -1.
---
Duplicate comments:
In `@src/network.c`:
- Around line 269-344: The code currently returns on fork() failure and when the
worker exits non-zero without undoing host-side network state; add a rollback
that removes host iptables rules and deletes the veth pair before returning.
Specifically, in the error path after fork() < 0 (worker variable) and in the
branch after checking WIFEXITED/WEXITSTATUS where you currently return -1, call
a new or existing helper (e.g. teardown_host_network or
cleanup_host_side_network) that removes iptables rules and deletes the host veth
(referencing veth_peer and any host-side interface name) — if such helper
doesn't exist, implement it and call it from both error sites so host state is
always cleaned up on failure. Ensure the cleanup runs before returning -1.
---
Nitpick comments:
In `@src/container.c`:
- Around line 571-577: The current read of cfg->container_pid from sync_pipe[0]
only retries on EINTR and treats any short read as failure; change the logic in
src/container.c so you repeatedly call read until sizeof(pid_t) bytes have been
read into a pid_t-sized buffer (e.g. a local char* or unsigned char buffer) — on
each iteration advance the buffer pointer by the number of bytes read and
subtract that from the remaining byte count, continue on EINTR, treat read
returning 0 or -1 (with non-EINTR errno) as an error and break, and only assign
cfg->container_pid once the full sizeof(pid_t) bytes have been successfully
consumed; ensure you use the existing variables (sync_pipe, cfg->container_pid)
and update the ssize_t read result handling accordingly.
| while (retries-- > 0) { | ||
| char check_ip[64]; | ||
| snprintf(check_ip, sizeof(check_ip), "ip addr show | grep '10.0.%d.1/24'", subnet_id); | ||
| if (run_command_quiet((char*[]){"sh", "-c", check_ip, NULL}) != 0) { | ||
| /* Not found, so it is free */ | ||
| break; | ||
| } | ||
| subnet_id = (subnet_id % 250) + 1; | ||
| } | ||
|
|
||
| if (retries <= 0) { | ||
| ds_error("Failed to allocate free subnet for NAT mode (exhausted retries)"); | ||
| return -1; | ||
| } |
There was a problem hiding this comment.
Fix off-by-one in subnet allocation exhaustion check.
At Line 181, if (retries <= 0) can fail even when a free subnet is found on the last allowed iteration. Track success explicitly.
🔧 Suggested fix
- int retries = 50;
+ int retries = 50;
+ int found_free = 0;
while (retries-- > 0) {
char check_ip[64];
snprintf(check_ip, sizeof(check_ip), "ip addr show | grep '10.0.%d.1/24'", subnet_id);
if (run_command_quiet((char*[]){"sh", "-c", check_ip, NULL}) != 0) {
/* Not found, so it is free */
+ found_free = 1;
break;
}
subnet_id = (subnet_id % 250) + 1;
}
- if (retries <= 0) {
+ if (!found_free) {
ds_error("Failed to allocate free subnet for NAT mode (exhausted retries)");
return -1;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/network.c` around lines 171 - 184, The loop that searches for a free
subnet uses while (retries-- > 0) and then checks if (retries <= 0) to detect
exhaustion, which yields an off-by-one false negative when a free subnet is
found on the final iteration; modify the search in network.c (the loop using
retries, subnet_id, check_ip and run_command_quiet) to track success explicitly
(e.g., introduce a boolean like found_free_subnet set to true just before break)
and after the loop check that flag instead of testing retries to decide whether
to log the DS error and return -1.
- src/container.c: Rename the second usage of `ssize_t n` to `nw` in `start_rootfs` to avoid a redeclaration error. The previous patch introduced two blocks with `ssize_t n` in the same scope. This commit resolves the conflict. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
…c (Retry). - src/container.c: Rename the second usage of `ssize_t n` to `nw` in `start_rootfs` to avoid a redeclaration error. The previous patch failed to apply due to context mismatch, causing the CI failure to persist. This attempt uses the correct context. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/container.c`:
- Around line 337-351: The comment above the sync_pipe declaration is incorrect
about direction; update the comment to reflect that sync_pipe is used for Init
-> Main (Init writes its PID, parent/main reads sync_pipe[0]) instead of Init ->
Monitor. Locate the block that declares int sync_pipe[2] alongside monitor_pipe
and init_ready_pipe and change the inline comment from "Init -> Monitor (sends
PID)" to something like "Init -> Main (sends PID)"; ensure any adjacent
explanatory comment text (the preceding paragraph describing who reads/writes
each pipe) is updated consistently.
- Around line 563-579: The startup error message incorrectly states that
"Monitor failed to send container PID." Update the log to reflect that Init is
the sender: in the read loop that reads into cfg->container_pid from sync_pipe
(the block using read(sync_pipe[0], &cfg->container_pid, sizeof(pid_t)) and
checking n), change the ds_error call to say that Init failed to send the
container PID (or similar wording mentioning Init), and ensure the same
close(sync_pipe[0]) and return -1 behavior remains intact.
- Implemented GPU support for Mali/Genlock devices (Pixel 9 Pro XL target). - Added `--sensors` flag to expose battery/thermal info. - Implemented `--network-mode` (`host`, `nat`, `macvlan`) with `veth` pairs and subnet allocation. - Refactored `src/container.c` to fix synchronization race conditions between Monitor and Init processes. - Updated Android GUI to support new configuration options. - Fixed critical bug where host `iptables` were flushed, causing loss of internet connectivity. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/container.c (1)
450-452: Consider simplifying the comment.The comment mentions "Variable n is already declared in this scope if the previous block ran" but
nwas declared inside the innerifblock (line 435) which ends at line 443. Using a different variable name (nw) is fine, but the comment suggests uncertainty that isn't warranted—you're clearly outside that inner scope here.✏️ Suggested cleaner comment
- /* Variable n is already declared in this scope if the previous block ran, - * but we are inside an if block for net_mode != HOST. - * Let's redeclare safely or assume new block. */ - ssize_t nw; + /* Use nw to distinguish from n used in the network signaling block */ + ssize_t nw;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/container.c` around lines 450 - 452, The comment near the net_mode != HOST branch is overly cautious about variable scope for `n`; simplify it to state that `n` was scoped to the earlier inner block and we deliberately use a different name (`nw`) here to avoid shadowing—update the comment adjacent to the declarations (mentioning `n` and `nw`) to a concise note like "use `nw` here to avoid shadowing the earlier `n` declared in the inner block." Make sure the comment sits immediately above the `nw` declaration in the `net_mode != HOST` branch so reviewers can quickly see the intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/container.c`:
- Around line 450-452: The comment near the net_mode != HOST branch is overly
cautious about variable scope for `n`; simplify it to state that `n` was scoped
to the earlier inner block and we deliberately use a different name (`nw`) here
to avoid shadowing—update the comment adjacent to the declarations (mentioning
`n` and `nw`) to a concise note like "use `nw` here to avoid shadowing the
earlier `n` declared in the inner block." Make sure the comment sits immediately
above the `nw` declaration in the `net_mode != HOST` branch so reviewers can
quickly see the intent.
- Implemented GPU support for Mali/Genlock devices (Pixel 9 Pro XL target). - Added `--sensors` flag to expose battery/thermal info. - Implemented `--network-mode` (`host`, `nat`, `macvlan`) with `veth` pairs and subnet allocation. - Refactored `src/container.c` to fix synchronization race conditions between Monitor and Init processes. - Updated Android GUI to support new configuration options. - Fixed critical bug where host `iptables` were flushed, causing loss of internet connectivity. - Fixed double-close issue in container init sequence. - Removed stray binary `ds-native`. Co-authored-by: shedowe19 <76975113+shedowe19@users.noreply.github.com>
Added
--sensorsflag to expose host battery/thermal info and--gpuflag to enable GPU access with automatic permission fixes for Mali (Pixel 9), Adreno, and DMA heap devices. Included agpu-checkcommand for diagnostics.PR created automatically by Jules for task 13507098297997324078 started by @shedowe19
Summary by CodeRabbit
New Features
Bug Fixes
UI