diff --git a/internal/native/cgo/test.patch b/internal/native/cgo/test.patch deleted file mode 100644 index 5f32a357..00000000 --- a/internal/native/cgo/test.patch +++ /dev/null @@ -1,210 +0,0 @@ -diff --git a/internal/native/cgo/video.c b/internal/native/cgo/video.c -index 2a4a034..760621a 100644 ---- a/internal/native/cgo/video.c -+++ b/internal/native/cgo/video.c -@@ -354,6 +354,10 @@ bool detected_signal = false, streaming_flag = false, streaming_stopped = true; - pthread_t *streaming_thread = NULL; - pthread_mutex_t streaming_mutex = PTHREAD_MUTEX_INITIALIZER; - -+// Diagnostic tracking for validation -+static uint64_t last_close_time = 0; -+static int consecutive_failures = 0; -+ - bool get_streaming_flag() - { - log_info("getting streaming flag"); -@@ -395,6 +399,12 @@ void *run_video_stream(void *arg) - continue; - } - -+ // Log attempt to open with timing info -+ RK_U64 time_since_close = last_close_time > 0 ? (get_us() - last_close_time) : 0; -+ log_info("[DIAG] Attempting to open %s (time_since_last_close=%llu us)", -+ VIDEO_DEV, time_since_close); -+ -+ RK_U64 open_start_time = get_us(); - int video_dev_fd = open(VIDEO_DEV, O_RDWR); - if (video_dev_fd < 0) - { -@@ -402,7 +412,9 @@ void *run_video_stream(void *arg) - usleep(1000000); - continue; - } -- log_info("opened video capture device %s", VIDEO_DEV); -+ RK_U64 open_end_time = get_us(); -+ log_info("[DIAG] opened video capture device %s in %llu us", -+ VIDEO_DEV, open_end_time - open_start_time); - - uint32_t width = detected_width; - uint32_t height = detected_height; -@@ -414,14 +426,45 @@ void *run_video_stream(void *arg) - fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUYV; - fmt.fmt.pix_mp.field = V4L2_FIELD_ANY; - -+ // Probe device state before attempting format set -+ struct v4l2_format query_fmt; -+ memset(&query_fmt, 0, sizeof(query_fmt)); -+ query_fmt.type = type; -+ int query_ret = ioctl(video_dev_fd, VIDIOC_G_FMT, &query_fmt); -+ log_info("[DIAG] VIDIOC_G_FMT probe: ret=%d, errno=%d (%s)", -+ query_ret, query_ret < 0 ? errno : 0, -+ query_ret < 0 ? strerror(errno) : "OK"); -+ -+ RK_U64 set_fmt_start_time = get_us(); -+ log_info("[DIAG] Attempting VIDIOC_S_FMT: %ux%u, time_since_open=%llu us", -+ width, height, set_fmt_start_time - open_end_time); -+ - if (ioctl(video_dev_fd, VIDIOC_S_FMT, &fmt) < 0) - { -- log_error("Set format fail: %s", strerror(errno)); -+ RK_U64 failure_time = get_us(); -+ int saved_errno = errno; -+ consecutive_failures++; -+ -+ log_error("[DIAG] Set format fail: errno=%d (%s)", saved_errno, strerror(saved_errno)); -+ log_error("[DIAG] Failure context: consecutive_failures=%d, time_since_open=%llu us, " -+ "time_since_last_close=%llu us, resolution=%ux%u, streaming_flag=%d", -+ consecutive_failures, -+ failure_time - open_end_time, -+ last_close_time > 0 ? (open_start_time - last_close_time) : 0, -+ width, height, -+ streaming_flag); -+ - usleep(100000); // Sleep for 100 milliseconds - close(video_dev_fd); -+ last_close_time = get_us(); -+ log_info("[DIAG] Closed device after format failure at %llu us", last_close_time); - continue; - } - -+ // Success - reset failure counter -+ log_info("[DIAG] VIDIOC_S_FMT succeeded (previous consecutive failures: %d)", consecutive_failures); -+ consecutive_failures = 0; -+ - struct v4l2_buffer buf; - - struct v4l2_requestbuffers req; -@@ -601,9 +644,46 @@ void *run_video_stream(void *arg) - } - cleanup: - log_info("cleaning up video capture device %s", VIDEO_DEV); -- if (ioctl(video_dev_fd, VIDIOC_STREAMOFF, &type) < 0) -+ -+ RK_U64 streamoff_start = get_us(); -+ log_info("[DIAG] Attempting VIDIOC_STREAMOFF"); -+ -+ int streamoff_ret = ioctl(video_dev_fd, VIDIOC_STREAMOFF, &type); -+ RK_U64 streamoff_end = get_us(); -+ -+ if (streamoff_ret < 0) -+ { -+ log_error("[DIAG] VIDIOC_STREAMOFF failed: errno=%d (%s), duration=%llu us", -+ errno, strerror(errno), streamoff_end - streamoff_start); -+ } -+ else -+ { -+ log_info("[DIAG] VIDIOC_STREAMOFF succeeded in %llu us", -+ streamoff_end - streamoff_start); -+ } -+ -+ // VALIDATION TEST: Explicitly free V4L2 buffer queue -+ struct v4l2_requestbuffers req_free; -+ memset(&req_free, 0, sizeof(req_free)); -+ req_free.count = 0; // Tell driver to free all buffers -+ req_free.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; -+ req_free.memory = V4L2_MEMORY_DMABUF; -+ -+ RK_U64 reqbufs_start = get_us(); -+ log_info("[DIAG] VALIDATION: Calling VIDIOC_REQBUFS(count=0) to free buffer queue"); -+ -+ int reqbufs_ret = ioctl(video_dev_fd, VIDIOC_REQBUFS, &req_free); -+ RK_U64 reqbufs_end = get_us(); -+ -+ if (reqbufs_ret < 0) -+ { -+ log_error("[DIAG] VALIDATION: REQBUFS(0) FAILED - errno=%d (%s), duration=%llu us", -+ errno, strerror(errno), reqbufs_end - reqbufs_start); -+ } -+ else - { -- log_error("VIDIOC_STREAMOFF failed: %s", strerror(errno)); -+ log_info("[DIAG] VALIDATION: REQBUFS(0) SUCCEEDED - freed buffers in %llu us", -+ reqbufs_end - reqbufs_start); - } - - venc_stop(); -@@ -617,9 +697,13 @@ void *run_video_stream(void *arg) - } - - log_info("closing video capture device %s", VIDEO_DEV); -+ RK_U64 close_start = get_us(); - close(video_dev_fd); -+ last_close_time = get_us(); -+ log_info("[DIAG] Device closed, took %llu us, timestamp=%llu", -+ last_close_time - close_start, last_close_time); - } -- -+ - log_info("video stream thread exiting"); - - streaming_stopped = true; -@@ -648,7 +732,7 @@ void video_shutdown() - RK_MPI_MB_DestroyPool(memPool); - } - log_info("Destroyed memory pool"); -- -+ - pthread_mutex_destroy(&streaming_mutex); - log_info("Destroyed streaming mutex"); - } -@@ -665,14 +749,14 @@ void video_start_streaming() - log_warn("video streaming already started"); - return; - } -- -+ - pthread_t *new_thread = malloc(sizeof(pthread_t)); - if (new_thread == NULL) - { - log_error("Failed to allocate memory for streaming thread"); - return; - } -- -+ - set_streaming_flag(true); - int result = pthread_create(new_thread, NULL, run_video_stream, NULL); - if (result != 0) -@@ -682,7 +766,7 @@ void video_start_streaming() - free(new_thread); - return; - } -- -+ - // Only set streaming_thread after successful creation - streaming_thread = new_thread; - } -@@ -693,7 +777,7 @@ void video_stop_streaming() - log_info("video streaming already stopped"); - return; - } -- -+ - log_info("stopping video streaming"); - set_streaming_flag(false); - -@@ -711,7 +795,7 @@ void video_stop_streaming() - free(streaming_thread); - streaming_thread = NULL; - -- log_info("video streaming stopped"); -+ log_info("video streaming stopped"); - } - - void video_restart_streaming() -@@ -818,4 +902,4 @@ void video_set_quality_factor(float factor) - - float video_get_quality_factor() { - return quality_factor; --} -\ No newline at end of file -+} diff --git a/network.go b/network.go index 00dd45fa..b808d6fe 100644 --- a/network.go +++ b/network.go @@ -2,10 +2,8 @@ package kvm import ( "fmt" - "reflect" "github.com/jetkvm/kvm/internal/network" - "github.com/jetkvm/kvm/internal/ota" "github.com/jetkvm/kvm/internal/udhcpc" ) @@ -84,79 +82,21 @@ func initNetwork() error { } }, }) -} -func setHostname(nm *nmlite.NetworkManager, hostname, domain string) error { - if nm == nil { - return nil - } - - if hostname == "" { - hostname = GetDefaultHostname() - } - - return nm.SetHostname(hostname, domain) -} - -func shouldRebootForNetworkChange(oldConfig, newConfig *types.NetworkConfig) (rebootRequired bool, postRebootAction *ota.PostRebootAction) { - oldDhcpClient := oldConfig.DHCPClient.String - - l := networkLogger.With(). - Interface("old", oldConfig). - Interface("new", newConfig). - Logger() - - // DHCP client change always requires reboot - if newConfig.DHCPClient.String != oldDhcpClient { - rebootRequired = true - l.Info().Msg("DHCP client changed, reboot required") - return rebootRequired, postRebootAction - } - - oldIPv4Mode := oldConfig.IPv4Mode.String - newIPv4Mode := newConfig.IPv4Mode.String - - // IPv4 mode change requires reboot - if newIPv4Mode != oldIPv4Mode { - rebootRequired = true - l.Info().Msg("IPv4 mode changed with udhcpc, reboot required") - - if newIPv4Mode == "static" && oldIPv4Mode != "static" { - postRebootAction = &ota.PostRebootAction{ - HealthCheck: fmt.Sprintf("//%s/device/status", newConfig.IPv4Static.Address.String), - RedirectTo: fmt.Sprintf("//%s", newConfig.IPv4Static.Address.String), - } - l.Info().Interface("postRebootAction", postRebootAction).Msg("IPv4 mode changed to static, reboot required") + if state == nil { + if err == nil { + return fmt.Errorf("failed to create NetworkInterfaceState") } - - return rebootRequired, postRebootAction + return err } - // IPv4 static config changes require reboot - if !reflect.DeepEqual(oldConfig.IPv4Static, newConfig.IPv4Static) { - rebootRequired = true - - // Handle IP change for redirect (only if both are not nil and IP changed) - if newConfig.IPv4Static != nil && oldConfig.IPv4Static != nil && - newConfig.IPv4Static.Address.String != oldConfig.IPv4Static.Address.String { - postRebootAction = &ota.PostRebootAction{ - HealthCheck: fmt.Sprintf("//%s/device/status", newConfig.IPv4Static.Address.String), - RedirectTo: fmt.Sprintf("//%s", newConfig.IPv4Static.Address.String), - } - - l.Info().Interface("postRebootAction", postRebootAction).Msg("IPv4 static config changed, reboot required") - } - - return rebootRequired, postRebootAction + if err := state.Run(); err != nil { + return err } - // IPv6 mode change requires reboot when using udhcpc - if newConfig.IPv6Mode.String != oldConfig.IPv6Mode.String && oldDhcpClient == "udhcpc" { - rebootRequired = true - l.Info().Msg("IPv6 mode changed with udhcpc, reboot required") - } + networkState = state - return rebootRequired, postRebootAction + return nil } func rpcGetNetworkState() network.RpcNetworkState { diff --git a/scripts/release.sh b/scripts/release.sh deleted file mode 100644 index b6afef91..00000000 --- a/scripts/release.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/bash -set -eE -set -o pipefail - -SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))") -source ${SCRIPT_PATH}/build_utils.sh - -# Function to display help message -show_help() { - echo "Usage: $0 [options] -v " - echo - echo "Required:" - echo " --app-version App version to release" - echo " --system-version System version to release" - echo - echo "Optional:" - echo " -u, --user Remote username (default: root)" - echo " --run-go-tests Run go tests" - echo " --run-go-tests-only Run go tests and exit" - echo " --skip-ui-build Skip frontend/UI build" - echo " --skip-native-build Skip native build" - echo " --disable-docker Disable docker build" - echo " -i, --install Build for release and install the app" - echo " --help Display this help message" - echo - echo "Example:" - echo " $0 --system-version 0.2.6" -} - - -BUILD_VERSION=$1 -R2_PATH="r2://jetkvm-update/system" -PACK_BIN_PATH="./tools/linux/Linux_Pack_Firmware" -UNPACK_BIN="${PACK_BIN_PATH}/mk-update_unpack.sh" - -# Create temporary directory for downloads -TEMP_DIR=$(mktemp -d) -msg_ok "Created temporary directory: $TEMP_DIR" - -# Cleanup function -cleanup() { - if [ -d "$TEMP_DIR" ]; then - msg_info "Cleaning up temporary directory: $TEMP_DIR" - rm -rf "$TEMP_DIR" - fi -} - -# Set trap to cleanup on exit -# trap cleanup EXIT - -mkdir -p ${TEMP_DIR}/extracted-update -${UNPACK_BIN} -i update.img -o ${TEMP_DIR}/extracted-update - -exit 0 -# Check if the version already exists -if rclone lsf $R2_PATH/$BUILD_VERSION/ | grep -q .; then - msg_err "Error: Version $BUILD_VERSION already exists in the remote storage." - exit 1 -fi - -# Check if the version exists in the github -RELEASE_URL="https://api.github.com/repos/jetkvm/rv1106-system/releases/tags/v$BUILD_VERSION" - -# Download the release JSON -RELEASE_JSON=$(curl -s $RELEASE_URL) - -# Check if the release has assets we need -if echo $RELEASE_JSON | jq -e '.assets | length == 0' > /dev/null; then - msg_err "Error: Version $BUILD_VERSION does not have assets we need." - exit 1 -fi - -function get_file_by_name() { - local file_name=$1 - local file_url=$(echo $RELEASE_JSON | jq -r ".assets[] | select(.name == \"$file_name\") | .browser_download_url") - if [ -z "$file_url" ]; then - msg_err "Error: File $file_name not found in the release." - exit 1 - fi - local digest=$(echo $RELEASE_JSON | jq -r ".assets[] | select(.name == \"$file_name\") | .digest") - local temp_file_path="$TEMP_DIR/$file_name" - - msg_info "Downloading $file_name: $file_url" - - # Download the file to temporary directory - curl -L -o "$temp_file_path" "$file_url" - - # Verify digest if available - if [ "$digest" != "null" ] && [ -n "$digest" ]; then - msg_info "Verifying digest for $file_name ..." - local calculated_digest=$(sha256sum "$temp_file_path" | cut -d' ' -f1) - # Strip "sha256:" prefix if present - local expected_digest=$(echo "$digest" | sed 's/^sha256://') - if [ "$calculated_digest" != "$expected_digest" ]; then - msg_err "🙅 Digest verification failed for $file_name" - msg_info "Expected: $expected_digest" - msg_info "Calculated: $calculated_digest" - exit 1 - fi - else - msg_warn "Warning: No digest available for $file_name, skipping verification" - fi - - msg_ok "✅ $file_name downloaded and verified." -} - -get_file_by_name "update_ota.tar" -get_file_by_name "update.img" - -strings -d bin/jetkvm_app | grep -x '0.4.8' - -# Ask for confirmation -msg_info "Do you want to continue with the release? (y/n)" -read -n 1 -s -r -p "Press y to continue, any other key to exit" -echo -ne "\n" -if [ "$REPLY" != "y" ]; then - msg_err "🙅 Release cancelled." - exit 1 -fi - -msg_info "Releasing $BUILD_VERSION..." - -sha256sum $TEMP_DIR/update_ota.tar | awk '{print $1}' > $TEMP_DIR/update_ota.tar.sha256 -sha256sum $TEMP_DIR/update.img | awk '{print $1}' > $TEMP_DIR/update.img.sha256 - -# Check if the version already exists -msg_info "Copying to $R2_PATH/$BUILD_VERSION/" - -rclone copyto --progress $TEMP_DIR/update_ota.tar $R2_PATH/$BUILD_VERSION/system.tar -rclone copyto --progress $TEMP_DIR/update_ota.tar.sha256 $R2_PATH/$BUILD_VERSION/system.tar.sha256 -rclone copyto --progress $TEMP_DIR/update.img $R2_PATH/$BUILD_VERSION/update.img -rclone copyto --progress $TEMP_DIR/update.img.sha256 $R2_PATH/$BUILD_VERSION/update.img.sha256 - -msg_ok "✅ $BUILD_VERSION released." \ No newline at end of file