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