acquireLockFile loops until it successfully creates a lock file for the given token file path. The lock file is created at tokenPath + ".lock". On each iteration: 1. Try to create the file atomically with O_CREATE|O_EXCL. If that succeeds, write our PID + start time and return nil. 2. If the file a
(tokenPath string, log *zerolog.Logger)
| 109 | // retry the O_EXCL create. No sleep (the atomic create is the |
| 110 | // tiebreaker if multiple processes race to reclaim). |
| 111 | func acquireLockFile(tokenPath string, log *zerolog.Logger) error { |
| 112 | lockPath := tokenPath + ".lock" |
| 113 | deadline := time.Now().Add(lockTimeout) |
| 114 | lastURL := "" |
| 115 | for { |
| 116 | if time.Now().After(deadline) { |
| 117 | return fmt.Errorf("timed out waiting for lock file %s", lockPath) |
| 118 | } |
| 119 | err := tryCreateLockFile(lockPath) |
| 120 | if err == nil { |
| 121 | log.Debug().Str("path", lockPath).Msg("lock file acquired") |
| 122 | return nil |
| 123 | } |
| 124 | if !os.IsExist(err) { |
| 125 | return errors.Wrapf(err, "failed to create lock file %s", lockPath) |
| 126 | } |
| 127 | |
| 128 | // lock file exists, so check if the owner is still alive |
| 129 | stale, content, checkErr := isLockFileStale(lockPath) |
| 130 | if checkErr != nil { |
| 131 | // file may be mid-write by another racer, or was removed |
| 132 | // between our O_EXCL attempt and this read |
| 133 | log.Debug().Err(checkErr).Str("path", lockPath). |
| 134 | Msg("could not read lock file, retrying") |
| 135 | time.Sleep(lockRetryInterval) |
| 136 | continue |
| 137 | } |
| 138 | |
| 139 | if !stale { |
| 140 | // try to display the auth URL so the user can open a browser |
| 141 | // manually if the original window is not visible |
| 142 | if authURL := readAuthURL(tokenPath); authURL != "" && authURL != lastURL { |
| 143 | fmt.Fprintf(os.Stderr, "\nAnother cloudflared process (pid %d) "+ |
| 144 | "is already waiting for authentication.\n\n"+ |
| 145 | "If a browser window did not open, please visit "+ |
| 146 | "the following URL:\n\n%s\n\n", content.PID, authURL) |
| 147 | lastURL = authURL |
| 148 | } |
| 149 | log.Debug().Str("path", lockPath). |
| 150 | Msg("lock file is held by another process, retrying") |
| 151 | time.Sleep(lockRetryInterval) |
| 152 | continue |
| 153 | } |
| 154 | |
| 155 | // stale, so remove and immediately retry |
| 156 | log.Debug().Str("path", lockPath).Int32("stale_pid", content.PID). |
| 157 | Msg("reclaiming stale lock file") |
| 158 | if removeErr := os.Remove(lockPath); removeErr != nil && !os.IsNotExist(removeErr) { |
| 159 | log.Debug().Err(removeErr).Str("path", lockPath). |
| 160 | Msg("could not remove stale lock file, retrying") |
| 161 | time.Sleep(lockRetryInterval) |
| 162 | continue |
| 163 | } |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | // readAuthURL reads the auth URL companion file for the given token path. |
| 168 | // Returns the URL string, or empty string if the file doesn't exist or |
no test coverage detected