diff --git a/api/ExtlinuxRead.go b/api/ExtlinuxRead.go index 02ad0e0..1dd7fa6 100644 --- a/api/ExtlinuxRead.go +++ b/api/ExtlinuxRead.go @@ -2,6 +2,7 @@ package api import ( "encoding/json" + "fmt" "net/http" "os" "up/utils" @@ -26,7 +27,6 @@ func ExtlinuxRead(w http.ResponseWriter, req *http.Request) { } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(response); err != nil { - // В случае ошибки кодирования уже ничего не сделать, - // можно только залогировать (здесь опущено для краткости) + fmt.Errorf("Encoding error: %v", err) } } diff --git a/api/ExtlinuxWrite.go b/api/ExtlinuxWrite.go index 3b7d5e8..b3be5ae 100644 --- a/api/ExtlinuxWrite.go +++ b/api/ExtlinuxWrite.go @@ -16,7 +16,6 @@ import ( func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) { var MOUNT_POINT = os.Getenv("MOUNT_POINT") var EXTLINUX_FILE = os.Getenv("EXTLINUX_FILE") - // Ограничиваем размер тела, чтобы избежать слишком больших запросов (аналог 4096 в C) r.Body = http.MaxBytesReader(w, r.Body, 4096) body, err := io.ReadAll(r.Body) if err != nil { @@ -24,7 +23,6 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) { return } - // Парсим JSON var requestData struct { Content string `json:"content"` } @@ -35,7 +33,6 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) { return } - // Проверяем наличие поля content if requestData.Content == "" { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) @@ -45,14 +42,12 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) { timestamp := r.URL.Query().Get("timestamp") if timestamp != "" { - // Полное URL-декодирование (в C декодировался только %3A, но здесь используем стандартный способ) decoded, err := url.QueryUnescape(timestamp) if err == nil { timestamp = decoded } } - // Монтируем устройство if utils.MountDevice() < 0 { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) @@ -60,11 +55,9 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) { return } - // Формируем полный путь к файлу filePath := filepath.Join(MOUNT_POINT, EXTLINUX_FILE) fmt.Println(filePath) - // Открываем файл на запись (создаём/перезаписываем) f, err := os.Create(filePath) if err != nil { w.Header().Set("Content-Type", "application/json") @@ -74,7 +67,6 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) { } defer f.Close() - // Записываем содержимое if _, err := f.WriteString(requestData.Content); err != nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) @@ -86,7 +78,6 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) { versionPath := filepath.Join(MOUNT_POINT, EXTLINUX_FILE+".version") vf, err := os.Create(versionPath) if err == nil { - // Форматируем ISO 8601 в человекочитаемый вид (как в C) var readableDate string if len(timestamp) >= 19 { readableDate = timestamp[:10] + " " + timestamp[11:19] // "YYYY-MM-DD HH:MM:SS" @@ -99,10 +90,8 @@ func ExtlinuxWrite(w http.ResponseWriter, r *http.Request) { } } - // Принудительно сбрасываем файловые буферы на диск (аналог sync()) syscall.Sync() - // Успешный ответ w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{"status":"success","message":"extlinux.conf updated"}`)) diff --git a/api/FileDate.go b/api/FileDate.go index 446b4c8..46f5d5c 100644 --- a/api/FileDate.go +++ b/api/FileDate.go @@ -8,33 +8,26 @@ import ( "path/filepath" ) -// handleFiledate обрабатывает HTTP-запрос для получения даты файла (версии) func FileDate(w http.ResponseWriter, r *http.Request) { var MOUNT_POINT = os.Getenv("MOUNT_POINT") - // Получаем параметр "file" из строки запроса filename := r.URL.Query().Get("file") if filename == "" { http.Error(w, `{"error":"Missing file parameter"}`, http.StatusBadRequest) return } - // Формируем путь к файлу версии: /.version versionPath := filepath.Join(MOUNT_POINT, filename+".version") - // Пытаемся прочитать первую строку из файла версии dateStr := "" f, err := os.Open(versionPath) if err == nil { defer f.Close() scanner := bufio.NewScanner(f) if scanner.Scan() { - dateStr = scanner.Text() // считываем строку без символа новой строки + dateStr = scanner.Text() } - // Ошибки сканера игнорируем (как в C) } - // Если файл не существует или другая ошибка — оставляем dateStr пустой - // Формируем JSON-ответ response := map[string]interface{}{ "status": "success", "date": dateStr, diff --git a/api/HandleReboot.go b/api/HandleReboot.go index 8d80caa..8729703 100644 --- a/api/HandleReboot.go +++ b/api/HandleReboot.go @@ -10,13 +10,8 @@ import ( "time" ) -// MOUNT_POINT должен быть определён (например, как глобальная константа) -// const MOUNT_POINT = "/mnt/mydevice" - -// handleReboot обрабатывает HTTP-запрос на перезагрузку системы func HandleReboot(w http.ResponseWriter, r *http.Request) { MOUNT_POINT := os.Getenv("MOUNT_POINT") - // Отправляем JSON-ответ (как в C: mg_http_reply) response := map[string]string{ "status": "success", "message": "System rebooting...", @@ -27,26 +22,19 @@ func HandleReboot(w http.ResponseWriter, r *http.Request) { return } - // Синхронизируем все файловые системы (аналог sync()) syscall.Sync() - // Размонтируем точку монтирования, если она смонтирована fmt.Printf("Unmounting %s...\n", MOUNT_POINT) if err := syscall.Unmount(MOUNT_POINT, 0); err != nil { - // Ошибку не фатализируем, просто логируем fmt.Printf("Unmount error: %v\n", err) } - // Повторная синхронизация syscall.Sync() - // Инициируем мягкую перезагрузку через команду shutdown fmt.Println("Initiating soft reboot via shutdown...") - // Запускаем shutdown в отдельной горутине с небольшой задержкой, - // чтобы ответ гарантированно ушёл клиенту до завершения сервера go func() { - time.Sleep(500 * time.Millisecond) // ждём 0.5 секунды + time.Sleep(500 * time.Millisecond) cmd := exec.Command("shutdown", "-r", "now") if err := cmd.Run(); err != nil { fmt.Printf("Reboot failed: %v\n", err) diff --git a/api/HandleTemperature.go b/api/HandleTemperature.go index f4fa439..e77834c 100644 --- a/api/HandleTemperature.go +++ b/api/HandleTemperature.go @@ -3,8 +3,8 @@ package api import ( "encoding/json" "fmt" - "io/ioutil" "net/http" + "os" "strings" ) @@ -15,44 +15,38 @@ func HandleTemperature(w http.ResponseWriter, r *http.Request) { // Thermal zones 0–3 for i := 0; i < 4 && !success; i++ { tempPath := fmt.Sprintf("/sys/class/thermal/thermal_zone%d/temp", i) - data, err := ioutil.ReadFile(tempPath) + data, err := os.ReadFile(tempPath) if err != nil { continue } - // Читаем тип зоны, если доступен typePath := fmt.Sprintf("/sys/class/thermal/thermal_zone%d/type", i) - typeData, _ := ioutil.ReadFile(typePath) // ошибку игнорируем + typeData, _ := os.ReadFile(typePath) zoneType := strings.TrimSpace(string(typeData)) - // Парсим температуру var tempRaw int if _, err := fmt.Sscanf(string(data), "%d", &tempRaw); err != nil { continue } temp := float64(tempRaw) / 1000.0 - // Проверяем, является ли зона CPU/SOC isCPU := strings.Contains(strings.ToLower(zoneType), "cpu") || strings.Contains(strings.ToLower(zoneType), "soc") temperature = temp success = true if isCPU { - break // предпочитаем CPU + break } - // иначе продолжаем искать CPU } - // Если thermal не дал результата, пробуем hwmon 0–3 for i := 0; i < 4 && !success; i++ { tempPath := fmt.Sprintf("/sys/class/hwmon/hwmon%d/temp1_input", i) - data, err := ioutil.ReadFile(tempPath) + data, err := os.ReadFile(tempPath) if err != nil { continue } - // Читаем имя сенсора namePath := fmt.Sprintf("/sys/class/hwmon/hwmon%d/name", i) - nameData, _ := ioutil.ReadFile(namePath) + nameData, _ := os.ReadFile(namePath) sensorName := strings.TrimSpace(string(nameData)) var tempRaw int @@ -72,7 +66,6 @@ func HandleTemperature(w http.ResponseWriter, r *http.Request) { } } - // Финальный fallback – просто temp1_input для первых 4 hwmon if !success { fallbackPaths := []string{ "/sys/class/hwmon/hwmon0/temp1_input", @@ -81,7 +74,7 @@ func HandleTemperature(w http.ResponseWriter, r *http.Request) { "/sys/class/hwmon/hwmon3/temp1_input", } for _, path := range fallbackPaths { - data, err := ioutil.ReadFile(path) + data, err := os.ReadFile(path) if err != nil { continue } @@ -95,7 +88,6 @@ func HandleTemperature(w http.ResponseWriter, r *http.Request) { } } - // Формируем JSON-ответ response := map[string]interface{}{ "success": success, "temperature": temperature, diff --git a/api/HandleUpload.go b/api/HandleUpload.go index 4d13624..c0f8f6a 100644 --- a/api/HandleUpload.go +++ b/api/HandleUpload.go @@ -14,7 +14,6 @@ import ( "up/utils" ) -// Глобальное состояние загрузки (аналог g_upload в C) type UploadState struct { mu sync.RWMutex Type string @@ -25,7 +24,7 @@ type UploadState struct { var uploadState UploadState -// handleUpload обрабатывает POST /api/upload +// POST /api/upload func HandleUpload(w http.ResponseWriter, r *http.Request) { uboot_max_size_int, err := strconv.Atoi(os.Getenv("UBOOT_MAX_SIZE")) if err != nil { @@ -40,15 +39,13 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { UBOOT_MAX_SIZE := uboot_max_size_int * 1024 * 1024 MAX_FILE_SIZE := max_file_size_int * 1024 * 1024 MOUNT_POINT := os.Getenv("MOUNT_POINT") - // Ограничиваем размер запроса (максимум UBOOT_MAX_SIZE + запас) r.Body = http.MaxBytesReader(w, r.Body, int64(MAX_FILE_SIZE)+1024) if err := r.ParseMultipartForm(32 << 20); err != nil { // 32 MB в памяти http.Error(w, `{"error":"Failed to parse multipart form"}`, http.StatusBadRequest) return } - defer r.MultipartForm.RemoveAll() // очищаем временные файлы + defer r.MultipartForm.RemoveAll() - // Получаем поле "type" typeVals := r.MultipartForm.Value["type"] if len(typeVals) == 0 { http.Error(w, `{"error":"Missing type field"}`, http.StatusBadRequest) @@ -56,7 +53,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { } typ := typeVals[0] - // Получаем файл из поля "file" files := r.MultipartForm.File["file"] if len(files) == 0 { http.Error(w, `{"error":"Missing file field"}`, http.StatusBadRequest) @@ -64,7 +60,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { } fileHeader := files[0] - // Проверка размера файла maxSize := int64(MAX_FILE_SIZE) if typ == "uboot_bin" { maxSize = int64(UBOOT_MAX_SIZE) @@ -74,7 +69,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { return } - // Открываем загруженный файл srcFile, err := fileHeader.Open() if err != nil { http.Error(w, `{"error":"Failed to open uploaded file"}`, http.StatusInternalServerError) @@ -82,13 +76,11 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { } defer srcFile.Close() - // Монтируем устройство if utils.MountDevice() < 0 { http.Error(w, `{"error":"Failed to mount device"}`, http.StatusInternalServerError) return } - // Определяем имя файла назначения filename := utils.GetTargetFilename(typ) if filename == "" { http.Error(w, `{"error":"Invalid upload type"}`, http.StatusBadRequest) @@ -96,7 +88,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { } destPath := filepath.Join(MOUNT_POINT, filename) - // Обновляем состояние загрузки uploadState.mu.Lock() uploadState.Type = typ uploadState.Received = 0 @@ -106,7 +97,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { fmt.Printf("Upload: %s (%d bytes) -> %s\n", typ, fileHeader.Size, destPath) - // Получаем timestamp из query и декодируем timestamp := r.URL.Query().Get("timestamp") if timestamp != "" { decoded, err := url.QueryUnescape(timestamp) @@ -115,7 +105,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { } } - // Специальная обработка для U-Boot if typ == "uboot_bin" { data, err := io.ReadAll(srcFile) if err != nil { @@ -139,7 +128,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { uploadState.Status = "Complete" uploadState.mu.Unlock() } else { - // Обычная запись файла чанками с обновлением прогресса destFile, err := os.Create(destPath) if err != nil { uploadState.mu.Lock() @@ -150,7 +138,7 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { } defer destFile.Close() - buf := make([]byte, 65536) // 64 KB чанки + buf := make([]byte, 65536) // 64 KB chunks var written int64 for { n, err := srcFile.Read(buf) @@ -163,7 +151,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { return } written += int64(n) - // Обновляем прогресс uploadState.mu.Lock() uploadState.Received = written uploadState.Progress = int(written * 100 / fileHeader.Size) @@ -181,19 +168,17 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { } } destFile.Sync() - syscall.Sync() // глобальная синхронизация + syscall.Sync() uploadState.mu.Lock() uploadState.Progress = 100 uploadState.Status = "Complete" uploadState.mu.Unlock() } - // Запись файла версии с timestamp if timestamp != "" { versionPath := filepath.Join(MOUNT_POINT, filename+".version") vf, err := os.Create(versionPath) if err == nil { - // Форматируем ISO 8601 в читаемый вид (как в C) var readableDate string if len(timestamp) >= 19 { readableDate = timestamp[:10] + " " + timestamp[11:19] // "YYYY-MM-DD HH:MM:SS" @@ -208,7 +193,6 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { fmt.Printf("Upload complete: %s\n", destPath) - // Формируем JSON-ответ w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]interface{}{ diff --git a/api/MountStatus.go b/api/MountStatus.go index 0e89fbc..fcfc21a 100644 --- a/api/MountStatus.go +++ b/api/MountStatus.go @@ -3,6 +3,7 @@ package api import ( "bufio" "encoding/json" + "fmt" "net/http" "os" "strings" @@ -13,7 +14,6 @@ func MountStatus(w http.ResponseWriter, req *http.Request) { var DEVICE = os.Getenv("DEVICE") mounted := false - // Проверяем, смонтировано ли устройство, читая /proc/mounts f, err := os.Open("/proc/mounts") if err == nil { defer f.Close() @@ -25,10 +25,8 @@ func MountStatus(w http.ResponseWriter, req *http.Request) { break } } - // Ошибки сканера игнорируем (аналогично C‑версии) } - // Формируем JSON‑ответ response := map[string]interface{}{ "mounted": mounted, "mount_point": MOUNT_POINT, @@ -36,10 +34,7 @@ func MountStatus(w http.ResponseWriter, req *http.Request) { } w.Header().Set("Content-Type", "application/json") - // json.NewEncoder автоматически добавляет символ новой строки в конце, - // что соответствует формату "%s\n" в C. if err := json.NewEncoder(w).Encode(response); err != nil { - // В случае ошибки кодирования уже ничего не сделать, - // можно только залогировать (здесь опущено для краткости) + fmt.Errorf("Encoding error: %v", err) } } diff --git a/utils/WriteUbootToDevices.go b/utils/WriteUbootToDevices.go index eb07a98..1b1e71f 100644 --- a/utils/WriteUbootToDevices.go +++ b/utils/WriteUbootToDevices.go @@ -10,15 +10,11 @@ import ( "up/utils/mtd" ) -// Глобальное состояние загрузки (используется для обновления прогресса) var uploadState struct { sync.RWMutex Progress int - // другие поля могут быть здесь } -// writeUbootToDevices записывает U-Boot в файл, SPI NOR и SD-карту. -// Принимает данные, их длину и путь для сохранения копии в файловой системе. func WriteUbootToDevices(data []byte, filepath string) error { UBOOT_MAX_SIZE, err := strconv.Atoi(os.Getenv("UBOOT_MAX_SIZE")) if err != nil { @@ -35,12 +31,10 @@ func WriteUbootToDevices(data []byte, filepath string) error { fmt.Printf("Writing U-Boot to devices (%d bytes)...\n", dataLen) - // Проверка лимита размера if dataLen > int64(UBOOT_MAX_SIZE*1024*1024) { return fmt.Errorf("U-Boot image too large for SPI NOR (%d > %d bytes)", dataLen, UBOOT_MAX_SIZE) } - // --- 1. Сохраняем в файл (копия) --- f, err := os.Create(filepath) if err != nil { return fmt.Errorf("failed to create file %s: %w", filepath, err) @@ -48,36 +42,29 @@ func WriteUbootToDevices(data []byte, filepath string) error { var written int64 for written < dataLen { - toWrite := dataLen - written - if toWrite > chunkSize { - toWrite = chunkSize - } + toWrite := min(dataLen-written, chunkSize) n, err := f.Write(data[written : written+toWrite]) if err != nil { f.Close() return fmt.Errorf("failed to write to file: %w", err) } written += int64(n) - // Прогресс: 30% за запись в файл uploadState.Lock() uploadState.Progress = int(written * 30 / dataLen) uploadState.Unlock() } f.Close() - syscall.Sync() // синхронизация после записи файла + syscall.Sync() - // --- 2. Запись в SPI NOR (/dev/mtd0) --- fmt.Println("Writing to /dev/mtd0 (SPI NOR)...") mtdFd, err := os.OpenFile("/dev/mtd0", os.O_RDWR|os.O_SYNC, 0660) if err == nil { defer mtdFd.Close() - // Получаем размер MTD-устройства из sysfs mtdSize, err := mtd.ReadMTDSize(0) if err != nil { fmt.Printf("Cannot get MTD size from sysfs, proceeding without erase: %v\n", err) } else { - // Стираем весь чип fmt.Printf("Erasing SPI NOR (size %d bytes)...\n", mtdSize) if err := mtd.EraseMTD(mtdFd, 0, mtdSize); err != nil { fmt.Printf("Warning: erase failed (continuing anyway): %v\n", err) @@ -86,20 +73,15 @@ func WriteUbootToDevices(data []byte, filepath string) error { } } - // Запись в MTD written = 0 for written < dataLen { - toWrite := dataLen - written - if toWrite > chunkSize { - toWrite = chunkSize - } + toWrite := min(dataLen-written, chunkSize) n, err := mtdFd.Write(data[written : written+toWrite]) if err != nil { fmt.Printf("Error writing to /dev/mtd0: %v\n", err) break } written += int64(n) - // Прогресс: 30–65% за SPI NOR uploadState.Lock() uploadState.Progress = 30 + int(written*35/dataLen) uploadState.Unlock() @@ -109,29 +91,23 @@ func WriteUbootToDevices(data []byte, filepath string) error { fmt.Printf("/dev/mtd0 not available: %v (skipping SPI NOR)\n", err) } - // --- 3. Запись в SD-карту (/dev/mmcblk1) со смещением 8 КБ --- fmt.Printf("Writing to /dev/mmcblk1 at offset %d (SD card boot area)...\n", MMC_BOOT_OFFSET) mmcFd, err := os.OpenFile("/dev/mmcblk1", os.O_RDWR|os.O_SYNC, 0660) if err == nil { defer mmcFd.Close() - // Устанавливаем смещение if _, err := mmcFd.Seek(int64(MMC_BOOT_OFFSET), io.SeekStart); err != nil { fmt.Printf("Failed to seek on /dev/mmcblk1: %v\n", err) } else { written = 0 for written < dataLen { - toWrite := dataLen - written - if toWrite > chunkSize { - toWrite = chunkSize - } + toWrite := min(dataLen-written, chunkSize) n, err := mmcFd.Write(data[written : written+toWrite]) if err != nil { fmt.Printf("Error writing to /dev/mmcblk1: %v\n", err) break } written += int64(n) - // Прогресс: 65–100% за MMC uploadState.Lock() uploadState.Progress = 65 + int(written*35/dataLen) uploadState.Unlock() @@ -142,7 +118,6 @@ func WriteUbootToDevices(data []byte, filepath string) error { fmt.Printf("/dev/mmcblk1 not available: %v (skipping SD card)\n", err) } - // Финальная синхронизация syscall.Sync() uploadState.Lock()