#include #include #include #include #include #include #include #ifndef MAX_OUTPUT_BYTES #define MAX_OUTPUT_BYTES (2 * 1000 * 1000) // 2 MB #endif static int append_bytes(char **buf, size_t *len, size_t *cap, const char *src, size_t n) { if (*len + n + 1 > *cap) { size_t newcap = (*cap == 0) ? 8192 : *cap; while (*len + n + 1 > newcap) { newcap *= 2; if (newcap > (size_t)(MAX_OUTPUT_BYTES + 65536)) { newcap = (size_t)(MAX_OUTPUT_BYTES + 65536); break; } } char *nbuf = realloc(*buf, newcap); if (!nbuf) return -1; *buf = nbuf; *cap = newcap; } memcpy(*buf + *len, src, n); *len += n; (*buf)[*len] = '\0'; return 0; } static int append_cstr(char **buf, size_t *len, size_t *cap, const char *s) { return append_bytes(buf, len, cap, s, strlen(s)); } static size_t min_size(size_t a, size_t b) { return a < b ? a : b; } static void sanitize_text(char *s, size_t n) { for (size_t i = 0; i < n; i++) if (s[i] == '\0') s[i] = ' '; } static void format_ts(char *out, size_t outsz, uint64_t usec) { time_t sec = (time_t)(usec / 1000000ULL); struct tm tm; localtime_r(&sec, &tm); strftime(out, outsz, "%Y-%m-%d %H:%M:%S", &tm); } static const char* field_value(const void *data, size_t len, const char *key, size_t *vlen) { size_t klen = strlen(key); if (len < klen + 1) return NULL; const char *p = (const char *)data; if (memcmp(p, key, klen) != 0 || p[klen] != '=') return NULL; *vlen = len - (klen + 1); return p + klen + 1; } static int append_entry_line(sd_journal *j, char **buf, size_t *len, size_t *cap) { uint64_t usec = 0; (void)sd_journal_get_realtime_usec(j, &usec); char ts[32]; format_ts(ts, sizeof(ts), usec); const void *data = NULL; size_t dlen = 0; const char *message = NULL; size_t mlen = 0; int r = sd_journal_get_data(j, "MESSAGE", &data, &dlen); if (r >= 0) message = field_value(data, dlen, "MESSAGE", &mlen); const char *ident = NULL; size_t ilen = 0; r = sd_journal_get_data(j, "SYSLOG_IDENTIFIER", &data, &dlen); if (r >= 0) { ident = field_value(data, dlen, "SYSLOG_IDENTIFIER", &ilen); } else if (sd_journal_get_data(j, "_COMM", &data, &dlen) >= 0) { ident = field_value(data, dlen, "_COMM", &ilen); } if (append_cstr(buf, len, cap, "[") < 0) return -1; if (append_cstr(buf, len, cap, ts) < 0) return -1; if (append_cstr(buf, len, cap, "] ") < 0) return -1; if (ident && ilen > 0) { if (append_bytes(buf, len, cap, ident, ilen) < 0) return -1; if (append_cstr(buf, len, cap, ": ") < 0) return -1; } if (message && mlen > 0) { char *tmp = malloc(mlen); if (!tmp) return -1; memcpy(tmp, message, mlen); sanitize_text(tmp, mlen); size_t to_copy = min_size(mlen, (size_t)(MAX_OUTPUT_BYTES > *len ? MAX_OUTPUT_BYTES - *len : 0)); int ok = append_bytes(buf, len, cap, tmp, to_copy); free(tmp); if (ok < 0) return -1; } else { const char *keys[] = {"PRIORITY","SYSLOG_IDENTIFIER","_COMM","_EXE","_CMDLINE","MESSAGE"}; for (size_t i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) { if (sd_journal_get_data(j, keys[i], &data, &dlen) < 0) continue; if (append_cstr(buf, len, cap, (i == 0 ? "" : " ")) < 0) return -1; if (append_bytes(buf, len, cap, (const char*)data, min_size(dlen, (size_t)(MAX_OUTPUT_BYTES - *len))) < 0) return -1; } } if (*len < MAX_OUTPUT_BYTES) { if (append_cstr(buf, len, cap, "\n") < 0) return -1; } return 0; } static char* journal_get_by_pid_impl(int pid) { if (pid <= 0) { char *z = malloc(1); if (z) z[0] = '\0'; return z; } sd_journal *j = NULL; if (sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY) < 0) { char *z = malloc(1); if (z) z[0] = '\0'; return z; } char match[64]; snprintf(match, sizeof(match), "_PID=%d", pid); if (sd_journal_add_match(j, match, 0) < 0) { sd_journal_close(j); char *z = malloc(1); if (z) z[0] = '\0'; return z; } sd_journal_seek_head(j); char *buf = NULL; size_t len = 0, cap = 0; int r; while ((r = sd_journal_next(j)) > 0) { if (len >= MAX_OUTPUT_BYTES) break; if (append_entry_line(j, &buf, &len, &cap) < 0) { free(buf); sd_journal_close(j); return NULL; } } if (len >= MAX_OUTPUT_BYTES) { const char *trunc = "[output truncated]\n"; (void)append_bytes(&buf, &len, &cap, trunc, strlen(trunc)); } if (!buf) { buf = malloc(1); if (!buf) { sd_journal_close(j); return NULL; } buf[0] = '\0'; } sd_journal_close(j); return buf; } #ifdef __GNUC__ __attribute__((visibility("default"))) #endif char* journal_get_by_pid(int pid) { return journal_get_by_pid_impl(pid); } #ifdef __GNUC__ __attribute__((visibility("default"))) #endif void journal_free(char* p) { free(p); } #ifdef BUILD_CLI static int parse_pid(const char *s, int *out) { if (!s || !*s) return -1; char *end = NULL; errno = 0; long v = strtol(s, &end, 10); if (errno != 0 || end == s || *end != '\0' || v <= 0 || v > 0x7fffffffL) return -1; *out = (int)v; return 0; } int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 2; } int pid = 0; if (parse_pid(argv[1], &pid) != 0) { fprintf(stderr, "Invalid pid\n"); return 2; } char *out = journal_get_by_pid_impl(pid); if (!out) { fprintf(stderr, "Out of memory or error\n"); return 1; } fputs(out, stdout); free(out); return 0; } #endif