// Pipe
int pipe[2];
- // FILE Handle
- FILE* f;
+ // Buffer
+ char buffer[64 * 1024];
+ size_t buffered;
// Event Source
sd_event_source* event;
static void pakfire_log_stream_free(struct pakfire_log_stream* stream) {
if (stream->event)
- sd_event_source_unref(stream->event);
- if (stream->f)
- fclose(stream->f);
+ stream->event = sd_event_source_unref(stream->event);
// Close the pipe
if (stream->pipe[0] >= 0)
return NULL;
}
+static int pakfire_log_stream_fill_buffer(struct pakfire_log_stream* stream, int fd) {
+ ssize_t bytes_read;
+
+ // Read into the buffer
+ bytes_read = read(fd, stream->buffer + stream->buffered, sizeof(stream->buffer) - stream->buffered);
+ if (bytes_read < 0)
+ return bytes_read;
+
+ stream->buffered += bytes_read;
+
+ return 0;
+}
+
+static int pakfire_log_stream_drain_buffer(struct pakfire_log_stream* stream) {
+ const char* eol = NULL;
+ size_t length;
+ int r;
+
+ // Log a message if we don't have a callback
+ if (!stream->callback) {
+ CTX_ERROR(stream->ctx, "Log stream has no callback set\n");
+ return -EINVAL;
+ }
+
+ for (;;) {
+ // Find the next line
+ eol = memchr(stream->buffer, '\n', stream->buffered);
+ if (!eol)
+ return 0;
+
+ // Determine the length of the line
+ length = eol - stream->buffer + 1;
+
+ // Call the callback
+ r = stream->callback(stream, stream->buffer, length, stream->data);
+ if (r)
+ return r;
+
+ memmove(stream->buffer, stream->buffer + length, stream->buffered - length);
+ stream->buffered -= length;
+ }
+}
+
static int __pakfire_log_stream(sd_event_source* s, int fd, uint32_t events, void* data) {
struct pakfire_log_stream* stream = data;
-
- char* line = NULL;
- size_t l = 0;
int r = -EINVAL;
- ssize_t bytes_read = 0;
-
- // Read as many lines as possible
if (events & EPOLLIN) {
for (;;) {
- bytes_read = getline(&line, &l, stream->f);
- if (bytes_read < 0)
- break;
-
- // Log a message if we don't have a callback
- if (!stream->callback) {
- CTX_ERROR(stream->ctx, "Log stream has no callback set\n");
- continue;
+ // Fill the buffer
+ r = pakfire_log_stream_fill_buffer(stream, fd);
+ if (r < 0) {
+ switch (-r) {
+ case EAGAIN:
+ return 0;
+
+ default:
+ return r;
+ }
}
- // Call the callback
- r = stream->callback(stream, line, bytes_read, stream->data);
- if (r)
+ // Drain the buffer
+ r = pakfire_log_stream_drain_buffer(stream);
+ if (r < 0)
return r;
}
}
// Handle if the child process has closed the file descriptor
if (events & EPOLLHUP) {
- close(stream->pipe[0]);
- stream->pipe[0] = -1;
+ if (stream->event)
+ stream->event = sd_event_source_unref(stream->event);
+
+ if (stream->pipe[0] >= 0) {
+ close(stream->pipe[0]);
+ stream->pipe[0] = -1;
+ }
}
return r;
return r;
}
- // Create a file handle for easy line buffering
- stream->f = fdopen(stream->pipe[0], "r");
- if (!stream->f) {
- CTX_ERROR(stream->ctx, "Could not open a file handle: %m\n");
- return -errno;
- }
-
return 0;
}