Tail one bounded batch from the child log. While the owned tree can still * write, a partial final line remains buffered. Once the tree is quiescent, * final=true delivers that last fragment exactly once. */
| 115 | * write, a partial final line remains buffered. Once the tree is quiescent, |
| 116 | * final=true delivers that last fragment exactly once. */ |
| 117 | static cbm_tail_result_t cbm_tail_log(const char *log_file, long *tail_pos, cbm_proc_log_cb cb, |
| 118 | void *ud, bool final) { |
| 119 | cbm_tail_result_t result = {.status = CBM_TAIL_ERROR, .progressed = false}; |
| 120 | if (!log_file || !tail_pos) { |
| 121 | return result; |
| 122 | } |
| 123 | #ifdef _WIN32 |
| 124 | FILE *lf = cbm_fopen(log_file, "r"); |
| 125 | #else |
| 126 | int open_flags = O_RDONLY | O_NONBLOCK; |
| 127 | #ifdef O_CLOEXEC |
| 128 | open_flags |= O_CLOEXEC; |
| 129 | #endif |
| 130 | #ifdef O_NOFOLLOW |
| 131 | open_flags |= O_NOFOLLOW; |
| 132 | #endif |
| 133 | int log_fd = open(log_file, open_flags); |
| 134 | if (log_fd < 0) { |
| 135 | return result; |
| 136 | } |
| 137 | struct stat log_status; |
| 138 | if (fstat(log_fd, &log_status) != 0 || !S_ISREG(log_status.st_mode)) { |
| 139 | (void)close(log_fd); |
| 140 | return result; |
| 141 | } |
| 142 | FILE *lf = fdopen(log_fd, "r"); |
| 143 | #endif |
| 144 | if (!lf) { |
| 145 | #ifndef _WIN32 |
| 146 | (void)close(log_fd); |
| 147 | #endif |
| 148 | return result; |
| 149 | } |
| 150 | if (fseek(lf, *tail_pos, SEEK_SET) == 0) { |
| 151 | char line[1024]; |
| 152 | size_t delivered_lines = 0; |
| 153 | size_t delivered_bytes = 0; |
| 154 | enum { |
| 155 | CBM_TAIL_MAX_LINES_PER_POLL = 64, |
| 156 | CBM_TAIL_MAX_BYTES_PER_POLL = 64 * 1024, |
| 157 | }; |
| 158 | while (delivered_lines < CBM_TAIL_MAX_LINES_PER_POLL && |
| 159 | delivered_bytes < CBM_TAIL_MAX_BYTES_PER_POLL) { |
| 160 | long before = ftell(lf); |
| 161 | if (!fgets(line, sizeof(line), lf)) { |
| 162 | result.status = ferror(lf) ? CBM_TAIL_ERROR : CBM_TAIL_CAUGHT_UP; |
| 163 | break; |
| 164 | } |
| 165 | size_t l = strlen(line); |
| 166 | delivered_bytes += l; |
| 167 | bool complete = (l > 0 && line[l - 1] == '\n'); |
| 168 | if (complete) { |
| 169 | line[l - 1] = '\0'; |
| 170 | *tail_pos = ftell(lf); |
| 171 | result.progressed = true; |
| 172 | delivered_lines++; |
| 173 | if (line[0] && cb) { |
| 174 | cb(line, ud); |
no test coverage detected