diff --git a/pkg/autoupdate/findnew.go b/pkg/autoupdate/findnew.go index 34909504e..6fbf65058 100644 --- a/pkg/autoupdate/findnew.go +++ b/pkg/autoupdate/findnew.go @@ -10,6 +10,7 @@ import ( "runtime" "sort" "strings" + "syscall" "time" "github.com/go-kit/kit/log" @@ -368,21 +369,32 @@ func CheckExecutable(ctx context.Context, potentialBinary string, args ...string return nil } - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() + // If we get ETXTBSY error when execing, this could be because this + // binary is freshly downloaded. Retry a small number of times only + // in that circumstance. + // See: https://github.com/golang/go/issues/22315 + for i := 0; i < 3; i += 1 { + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() - cmd := exec.CommandContext(ctx, potentialBinary, args...) + cmd := exec.CommandContext(ctx, potentialBinary, args...) - // Set env, this should prevent launcher for fork-bombing - cmd.Env = append(cmd.Env, "LAUNCHER_SKIP_UPDATES=TRUE") + // Set env, this should prevent launcher for fork-bombing + cmd.Env = append(cmd.Env, "LAUNCHER_SKIP_UPDATES=TRUE") - execErr := cmd.Run() + execErr := cmd.Run() + if execErr != nil && errors.Is(ctx.Err(), syscall.ETXTBSY) { + continue + } + + if ctx.Err() != nil { + return ctx.Err() + } - if ctx.Err() != nil { - return ctx.Err() + return supressRoutineErrors(execErr) } - return supressRoutineErrors(execErr) + return fmt.Errorf("could not exec %s -- text file busy", potentialBinary) } // supressRoutineErrors attempts to tell whether the error was a diff --git a/pkg/osquery/interactive/interactive_test.go b/pkg/osquery/interactive/interactive_test.go index 44281f0bd..f7d1ec610 100644 --- a/pkg/osquery/interactive/interactive_test.go +++ b/pkg/osquery/interactive/interactive_test.go @@ -113,15 +113,12 @@ func downloadOsquery(dir string) error { return fmt.Errorf("error parsing platform: %w, %s", err, runtime.GOOS) } + // Binary is already downloaded to osquerydCacheDir -- create a symlink at outputFile, + // rather than copying the file, to maybe avoid https://github.com/golang/go/issues/22315 outputFile := filepath.Join(dir, "osqueryd") - - path, err := packaging.FetchBinary(context.TODO(), osquerydCacheDir, "osqueryd", target.PlatformBinaryName("osqueryd"), "stable", target) - if err != nil { - return fmt.Errorf("error fetching binary osqueryd binary: %w", err) - } - - if err := fsutil.CopyFile(path, outputFile); err != nil { - return fmt.Errorf("error copying binary osqueryd binary: %w", err) + sourceFile := filepath.Join(osquerydCacheDir, fmt.Sprintf("osqueryd-%s-stable", runtime.GOOS), "osqueryd") + if err := os.Symlink(sourceFile, outputFile); err != nil { + return fmt.Errorf("creating symlink from %s to %s: %w", sourceFile, outputFile, err) } return nil