Skip to content

Commit ece3271

Browse files
committed
Fix race condition in isFirstRun using atomic file creation
Use os.OpenFile with os.O_EXCL|os.O_CREATE to atomically create the marker file. This eliminates the check-then-act race condition where multiple concurrent cagent processes could all see the marker file as missing and all proceed with first-run initialization. Fixes #1709 Assisted-By: cagent
1 parent af20cc4 commit ece3271

1 file changed

Lines changed: 13 additions & 12 deletions

File tree

cmd/root/root.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -204,25 +204,26 @@ func (e RuntimeError) Unwrap() error {
204204
return e.Err
205205
}
206206

207-
// isFirstRun checks if this is the first time cagent is being run
208-
// It creates a marker file in the user's config directory
207+
// isFirstRun checks if this is the first time cagent is being run.
208+
// It atomically creates a marker file in the user's config directory
209+
// using os.O_EXCL to avoid a race condition when multiple processes
210+
// start concurrently.
209211
func isFirstRun() bool {
210212
configDir := paths.GetConfigDir()
211213
markerFile := filepath.Join(configDir, ".cagent_first_run")
212214

213-
// Check if marker file exists
214-
if _, err := os.Stat(markerFile); err == nil {
215-
return false // File exists, not first run
216-
}
217-
218-
// Create marker file to indicate this run has happened
215+
// Ensure the config directory exists before trying to create the marker file
219216
if err := os.MkdirAll(configDir, 0o755); err != nil {
220-
return false // Can't create config dir, assume not first run
217+
slog.Warn("Failed to create config directory for first run marker", "error", err)
218+
return false
221219
}
222220

223-
if err := os.WriteFile(markerFile, []byte(""), 0o644); err != nil {
224-
return false // Can't create marker file, assume not first run
221+
// Atomically create the marker file. If it already exists, OpenFile returns an error.
222+
f, err := os.OpenFile(markerFile, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o644)
223+
if err != nil {
224+
return false // File already exists or other error, not first run
225225
}
226+
f.Close()
226227

227-
return true // Successfully created marker, this is first run
228+
return true
228229
}

0 commit comments

Comments
 (0)