package ota import ( "bytes" "context" "os/exec" "time" ) const ( systemUpdatePath = "/userdata/jetkvm/update_system.tar" ) func (s *State) updateSystem(ctx context.Context, systemUpdate *componentUpdateStatus) error { s.mu.Lock() defer s.mu.Unlock() l := s.l.With().Str("path", systemUpdatePath).Logger() if err := s.downloadFile(ctx, systemUpdatePath, systemUpdate.url, &systemUpdate.downloadProgress); err != nil { return s.componentUpdateError("Error downloading system update", err, &l) } downloadFinished := time.Now() systemUpdate.downloadFinishedAt = downloadFinished systemUpdate.downloadProgress = 1 s.onProgressUpdate() if err := s.verifyFile( systemUpdatePath, systemUpdate.hash, &systemUpdate.verificationProgress, ); err != nil { return s.componentUpdateError("Error verifying system update hash", err, &l) } verifyFinished := time.Now() systemUpdate.verifiedAt = verifyFinished systemUpdate.verificationProgress = 1 systemUpdate.updatedAt = verifyFinished systemUpdate.updateProgress = 1 s.onProgressUpdate() l.Info().Msg("System update downloaded") l.Info().Msg("Starting rk_ota command") cmd := exec.Command("rk_ota", "--misc=update", "--tar_path=/userdata/jetkvm/update_system.tar", "--save_dir=/userdata/jetkvm/ota_save", "--partition=all") var b bytes.Buffer cmd.Stdout = &b cmd.Stderr = &b if err := cmd.Start(); err != nil { return s.componentUpdateError("Error starting rk_ota command", err, &l) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func() { ticker := time.NewTicker(1800 * time.Millisecond) defer ticker.Stop() for { select { case <-ticker.C: if systemUpdate.updateProgress >= 0.99 { return } systemUpdate.updateProgress += 0.01 if systemUpdate.updateProgress > 0.99 { systemUpdate.updateProgress = 0.99 } s.onProgressUpdate() case <-ctx.Done(): return } } }() err := cmd.Wait() cancel() rkLogger := s.l.With(). Str("output", b.String()). Int("exitCode", cmd.ProcessState.ExitCode()).Logger() if err != nil { return s.componentUpdateError("Error executing rk_ota command", err, &rkLogger) } rkLogger.Info().Msg("rk_ota success") systemUpdate.updateProgress = 1 systemUpdate.updatedAt = verifyFinished s.onProgressUpdate() return nil } func (s *State) confirmCurrentSystem() { output, err := exec.Command("rk_ota", "--misc=now").CombinedOutput() if err != nil { s.l.Warn().Str("output", string(output)).Msg("failed to set current partition in A/B setup") } s.l.Trace().Str("output", string(output)).Msg("current partition in A/B setup set") }