mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
94 lines
2.7 KiB
Go
94 lines
2.7 KiB
Go
package cloud
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
|
||
|
|
"github.com/safedep/dry/log"
|
||
|
|
"github.com/safedep/pmg/config"
|
||
|
|
"github.com/safedep/pmg/internal/audit"
|
||
|
|
"github.com/spf13/cobra"
|
||
|
|
)
|
||
|
|
|
||
|
|
// newSyncBackgroundCommand returns the hidden subcommand invoked by the
|
||
|
|
// detached child that audit.MaybeSpawnBackgroundSync forks. Always exits 0:
|
||
|
|
// the parent has already returned to the user's shell by the time this runs,
|
||
|
|
// so a non-zero exit would only show up to whatever (init / launchd) reaped
|
||
|
|
// the orphaned process.
|
||
|
|
func newSyncBackgroundCommand() *cobra.Command {
|
||
|
|
cmd := &cobra.Command{
|
||
|
|
Use: audit.SyncBackgroundSubcommand,
|
||
|
|
Short: "Internal: drain the cloud sync WAL from a detached child process",
|
||
|
|
Hidden: true,
|
||
|
|
RunE: runSyncBackground,
|
||
|
|
}
|
||
|
|
return cmd
|
||
|
|
}
|
||
|
|
|
||
|
|
func runSyncBackground(cmd *cobra.Command, args []string) error {
|
||
|
|
audit.MarkBackgroundSyncChild()
|
||
|
|
|
||
|
|
cfg := config.Get()
|
||
|
|
|
||
|
|
if !cfg.Config.Cloud.Enabled || !cfg.Config.Cloud.AutoSync.Enabled {
|
||
|
|
log.Debugf("Auto-sync: cloud or auto_sync disabled; exiting")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
lock := audit.NewSyncLock(cfg.CloudSyncLockPath())
|
||
|
|
|
||
|
|
locked, err := lock.TryLock()
|
||
|
|
if err != nil {
|
||
|
|
log.Warnf("Auto-sync: failed to acquire lock: %v", err)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
if !locked {
|
||
|
|
log.Debugf("Auto-sync: another sync is in progress; exiting")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
defer func() {
|
||
|
|
if err := lock.Unlock(); err != nil {
|
||
|
|
log.Warnf("Auto-sync: failed to release lock: %v", err)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
// Re-check cooldown under the lock to close the TOCTOU window between the
|
||
|
|
// parent's pre-fork stat and our acquisition.
|
||
|
|
if !audit.SyncCooldownElapsed(cfg.CloudSyncLastRunPath(), cfg.Config.Cloud.AutoSync.MinInterval) {
|
||
|
|
log.Debugf("Auto-sync: cooldown still in effect under lock; exiting")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(cmd.Context(), cfg.Config.Cloud.AutoSync.Timeout)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
bundle, err := audit.NewSyncClientBundle(cfg)
|
||
|
|
if err != nil {
|
||
|
|
log.Warnf("Auto-sync: failed to initialize sync client: %v", err)
|
||
|
|
recordLastSyncAttempt(cfg)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
defer func() {
|
||
|
|
if err := bundle.Close(); err != nil {
|
||
|
|
log.Warnf("Auto-sync: failed to close sync client: %v", err)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
synced, err := bundle.Sync(ctx)
|
||
|
|
recordLastSyncAttempt(cfg)
|
||
|
|
if err != nil {
|
||
|
|
log.Warnf("Auto-sync: sync failed after %d events: %v", synced, err)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
log.Infof("Auto-sync: drained %d events to SafeDep Cloud", synced)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// recordLastSyncAttempt updates the cooldown timestamp on every attempt so a
|
||
|
|
// failing cloud endpoint does not cause every PMG invocation to retry.
|
||
|
|
func recordLastSyncAttempt(cfg *config.RuntimeConfig) {
|
||
|
|
if err := audit.WriteLastSyncAttempt(cfg.CloudSyncLastRunPath()); err != nil {
|
||
|
|
log.Warnf("failed to update cloud sync lastrun: %v", err)
|
||
|
|
}
|
||
|
|
}
|