Describe what the target actually IS, so a refusal is triageable. * * The config editors collapse nine distinct fail-closed conditions into a * single -1: unsupported structure, an inline comment on a field line, a * non-single-link file, unsafe metadata, lock contention, I/O. `agent=X op=Y * path=Z` alone cannot separate them, and four such failures currently live in * discussion #1560 with
| 8249 | * most of the space — it says the refusal is structural, not a permission or |
| 8250 | * missing-file problem. */ |
| 8251 | static void describe_agent_config_target(const char *path, char *out, size_t out_size) { |
| 8252 | if (!out || out_size == 0) { |
| 8253 | return; |
| 8254 | } |
| 8255 | out[0] = '\0'; |
| 8256 | if (!path || !path[0]) { |
| 8257 | return; |
| 8258 | } |
| 8259 | cbm_path_info_t info; |
| 8260 | if (cbm_path_info_utf8(path, &info) != 0) { |
| 8261 | (void)snprintf(out, out_size, " (target: does not exist or cannot be inspected)"); |
| 8262 | return; |
| 8263 | } |
| 8264 | const char *kind = info.is_symlink ? "symlink" |
| 8265 | : info.is_directory ? "directory" |
| 8266 | : info.is_regular ? "regular file" |
| 8267 | : "special file"; |
| 8268 | /* A refused symlink names the rule that refused it (#1954): the user |
| 8269 | * then knows whether to fix ownership, the target, or the parent. */ |
| 8270 | char refusal[160]; |
| 8271 | if (info.is_symlink && cbm_config_edit_path_refusal(path, refusal, sizeof(refusal))) { |
| 8272 | (void)snprintf(out, out_size, " (target: symlink, %lld bytes; not followed: %s)", |
| 8273 | (long long)info.size, refusal); |
| 8274 | return; |
| 8275 | } |
| 8276 | (void)snprintf(out, out_size, " (target: %s, %lld bytes)", kind, (long long)info.size); |
| 8277 | } |
| 8278 | |
| 8279 | static void record_agent_config_error_with_reason(bool uninstalling, const char *agent, |
| 8280 | const char *operation, const char *path, |
no test coverage detected