static int write_to_syslog( int level, int error, const char *file, int line, const char *func, const char *buffer) { char header_priority[2 + DECIMAL_STR_MAX(int) + 1], header_time[64], header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; struct iovec iovec[5] = {}; struct msghdr msghdr = { .msg_iov = iovec, .msg_iovlen = ELEMENTSOF(iovec), }; time_t t; struct tm *tm; if (syslog_fd < 0) return 0; xsprintf(header_priority, "<%i>", level); t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC); tm = localtime(&t); if (!tm) return -EINVAL; if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) return -EINVAL; xsprintf(header_pid, "["PID_FMT"]: ", getpid()); IOVEC_SET_STRING(iovec[0], header_priority); IOVEC_SET_STRING(iovec[1], header_time); IOVEC_SET_STRING(iovec[2], program_invocation_short_name); IOVEC_SET_STRING(iovec[3], header_pid); IOVEC_SET_STRING(iovec[4], buffer); /* When using syslog via SOCK_STREAM separate the messages by NUL chars */ if (syslog_is_stream) iovec[4].iov_len++; for (;;) { ssize_t n; n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL); if (n < 0) return -errno; if (!syslog_is_stream || (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec))) break; IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n); } return 1; }
_public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) { int r, i, j; va_list ap; struct iovec *iov = NULL; char *f; va_start(ap, format); i = fill_iovec_sprintf(format, ap, 3, &iov); va_end(ap); if (_unlikely_(i < 0)) { r = i; goto finish; } ALLOCA_CODE_FUNC(f, func); IOVEC_SET_STRING(iov[0], file); IOVEC_SET_STRING(iov[1], line); IOVEC_SET_STRING(iov[2], f); r = sd_journal_sendv(iov, i); finish: for (j = 3; j < i; j++) free(iov[j].iov_base); free(iov); return r; }
static int write_to_journal( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, const char *buffer) { char header[LINE_MAX]; struct iovec iovec[4] = {}; struct msghdr mh = {}; if (journal_fd < 0) return 0; log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object); IOVEC_SET_STRING(iovec[0], header); IOVEC_SET_STRING(iovec[1], "MESSAGE="); IOVEC_SET_STRING(iovec[2], buffer); IOVEC_SET_STRING(iovec[3], "\n"); mh.msg_iov = iovec; mh.msg_iovlen = ELEMENTSOF(iovec); if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0) return -errno; return 1; }
static int write_to_console( int level, const char*file, unsigned int line, const char *func, const char *buffer) { struct iovec iovec[5]; unsigned n = 0; if (console_fd < 0) return 0; zero(iovec); IOVEC_SET_STRING(iovec[n++], "dracut-install: "); if (show_location) { char location[64]; if (snprintf(location, sizeof(location), "(%s:%u) ", file, line) <= 0) return -errno; IOVEC_SET_STRING(iovec[n++], location); } IOVEC_SET_STRING(iovec[n++], buffer); IOVEC_SET_STRING(iovec[n++], "\n"); if (writev(console_fd, iovec, n) < 0) return -errno; return 1; }
_public_ int sd_journal_printv(int priority, const char *format, va_list ap) { /* FIXME: Instead of limiting things to LINE_MAX we could do a C99 variable-length array on the stack here in a loop. */ char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1]; struct iovec iov[2]; assert_return(priority >= 0, -EINVAL); assert_return(priority <= 7, -EINVAL); assert_return(format, -EINVAL); xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK); memcpy(buffer, "MESSAGE=", 8); vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); /* Strip trailing whitespace, keep prefix whitespace. */ (void) strstrip(buffer); /* Suppress empty lines */ if (isempty(buffer+8)) return 0; zero(iov); IOVEC_SET_STRING(iov[0], buffer); IOVEC_SET_STRING(iov[1], p); return sd_journal_sendv(iov, 2); }
static int write_to_kmsg( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, const char *buffer) { char header_priority[2 + DECIMAL_STR_MAX(int) + 1], header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; struct iovec iovec[5] = {}; if (kmsg_fd < 0) return 0; xsprintf(header_priority, "<%i>", level); xsprintf(header_pid, "["PID_FMT"]: ", getpid()); IOVEC_SET_STRING(iovec[0], header_priority); IOVEC_SET_STRING(iovec[1], program_invocation_short_name); IOVEC_SET_STRING(iovec[2], header_pid); IOVEC_SET_STRING(iovec[3], buffer); IOVEC_SET_STRING(iovec[4], "\n"); if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0) return -errno; return 1; }
_public_ int sd_journal_perror_with_location( const char *file, const char *line, const char *func, const char *message) { struct iovec iov[6]; char *f; ALLOCA_CODE_FUNC(f, func); IOVEC_SET_STRING(iov[0], file); IOVEC_SET_STRING(iov[1], line); IOVEC_SET_STRING(iov[2], f); return fill_iovec_perror_and_send(message, 3, iov); }
static void do_journal_append(char *file) { struct iovec iovec[5]; int r, f, j = 0; ssize_t n; _cleanup_free_ char *bootchart_file = NULL, *bootchart_message = NULL, *p = NULL; bootchart_file = strappend("BOOTCHART_FILE=", file); if (bootchart_file) IOVEC_SET_STRING(iovec[j++], bootchart_file); IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=9f26aa562cf440c2b16c773d0479b518"); IOVEC_SET_STRING(iovec[j++], "PRIORITY=7"); bootchart_message = strjoin("MESSAGE=Bootchart created: ", file, NULL); if (bootchart_message) IOVEC_SET_STRING(iovec[j++], bootchart_message); p = malloc(9 + BOOTCHART_MAX); if (!p) { r = log_oom(); return; } memcpy(p, "BOOTCHART=", 10); f = open(file, O_RDONLY); if (f < 0) { log_error("Failed to read bootchart data: %m\n"); return; } n = loop_read(f, p + 10, BOOTCHART_MAX, false); if (n < 0) { log_error("Failed to read bootchart data: %s\n", strerror(-n)); close(f); return; } close(f); iovec[j].iov_base = p; iovec[j].iov_len = 10 + n; j++; r = sd_journal_sendv(iovec, j); if (r < 0) log_error("Failed to send bootchart: %s", strerror(-r)); }
static int do_journal_append(char *file) { _cleanup_free_ char *bootchart_message = NULL; _cleanup_free_ char *bootchart_file = NULL; _cleanup_free_ char *p = NULL; _cleanup_close_ int fd = -1; struct iovec iovec[5]; int r, j = 0; ssize_t n; bootchart_file = strappend("BOOTCHART_FILE=", file); if (!bootchart_file) return log_oom(); IOVEC_SET_STRING(iovec[j++], bootchart_file); IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=9f26aa562cf440c2b16c773d0479b518"); IOVEC_SET_STRING(iovec[j++], "PRIORITY=7"); bootchart_message = strjoin("MESSAGE=Bootchart created: ", file, NULL); if (!bootchart_message) return log_oom(); IOVEC_SET_STRING(iovec[j++], bootchart_message); p = malloc(10 + BOOTCHART_MAX); if (!p) return log_oom(); memcpy(p, "BOOTCHART=", 10); fd = open(file, O_RDONLY|O_CLOEXEC); if (fd < 0) return log_error_errno(errno, "Failed to open bootchart data \"%s\": %m", file); n = loop_read(fd, p + 10, BOOTCHART_MAX, false); if (n < 0) return log_error_errno(n, "Failed to read bootchart data: %m"); iovec[j].iov_base = p; iovec[j].iov_len = 10 + n; j++; r = sd_journal_sendv(iovec, j); if (r < 0) log_error_errno(r, "Failed to send bootchart: %m"); return 0; }
void server_forward_kmsg( Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred) { struct iovec iovec[5]; char header_priority[6], header_pid[16]; int n = 0; char *ident_buf = NULL; assert(s); assert(priority >= 0); assert(priority <= 999); assert(message); if (_unlikely_(LOG_PRI(priority) > s->max_level_kmsg)) return; if (_unlikely_(s->dev_kmsg_fd < 0)) return; /* Never allow messages with kernel facility to be written to * kmsg, regardless where the data comes from. */ priority = syslog_fixup_facility(priority); /* First: priority field */ snprintf(header_priority, sizeof(header_priority), "<%i>", priority); char_array_0(header_priority); IOVEC_SET_STRING(iovec[n++], header_priority); /* Second: identifier and PID */ if (ucred) { if (!identifier) { get_process_comm(ucred->pid, &ident_buf); identifier = ident_buf; } snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid); char_array_0(header_pid); if (identifier) IOVEC_SET_STRING(iovec[n++], identifier); IOVEC_SET_STRING(iovec[n++], header_pid); } else if (identifier) { IOVEC_SET_STRING(iovec[n++], identifier); IOVEC_SET_STRING(iovec[n++], ": "); } /* Fourth: message */ IOVEC_SET_STRING(iovec[n++], message); IOVEC_SET_STRING(iovec[n++], "\n"); if (writev(s->dev_kmsg_fd, iovec, n) < 0) log_debug("Failed to write to /dev/kmsg for logging: %m"); free(ident_buf); }
static int write_to_console( int level, int error, const char *file, int line, const char *func, const char *object_field, const char *object, const char *buffer) { char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2]; struct iovec iovec[6] = {}; unsigned n = 0; bool highlight; if (console_fd < 0) return 0; if (log_target == LOG_TARGET_CONSOLE_PREFIXED) { sprintf(prefix, "<%i>", level); IOVEC_SET_STRING(iovec[n++], prefix); } highlight = LOG_PRI(level) <= LOG_ERR && show_color; if (show_location) { snprintf(location, sizeof(location), "(%s:%i) ", file, line); IOVEC_SET_STRING(iovec[n++], location); } if (highlight) IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED); IOVEC_SET_STRING(iovec[n++], buffer); if (highlight) IOVEC_SET_STRING(iovec[n++], ANSI_NORMAL); IOVEC_SET_STRING(iovec[n++], "\n"); if (writev(console_fd, iovec, n) < 0) { if (errno == EIO && getpid() == 1) { /* If somebody tried to kick us from our * console tty (via vhangup() or suchlike), * try to reconnect */ log_close_console(); log_open_console(); if (console_fd < 0) return 0; if (writev(console_fd, iovec, n) < 0) return -errno; } else return -errno; } return 1; }
_public_ int sd_journal_sendv_with_location( const char *file, const char *line, const char *func, const struct iovec *iov, int n) { struct iovec *niov; char *f; assert_return(iov, -EINVAL); assert_return(n > 0, -EINVAL); niov = alloca(sizeof(struct iovec) * (n + 3)); memcpy(niov, iov, sizeof(struct iovec) * n); ALLOCA_CODE_FUNC(f, func); IOVEC_SET_STRING(niov[n++], file); IOVEC_SET_STRING(niov[n++], line); IOVEC_SET_STRING(niov[n++], f); return sd_journal_sendv(niov, n); }
_public_ int sd_journal_printv(int priority, const char *format, va_list ap) { /* FIXME: Instead of limiting things to LINE_MAX we could do a C99 variable-length array on the stack here in a loop. */ char buffer[8 + LINE_MAX], p[11]; struct iovec iov[2]; assert_return(priority >= 0, -EINVAL); assert_return(priority <= 7, -EINVAL); assert_return(format, -EINVAL); snprintf(p, sizeof(p), "PRIORITY=%i", priority & LOG_PRIMASK); char_array_0(p); memcpy(buffer, "MESSAGE=", 8); vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); char_array_0(buffer); zero(iov); IOVEC_SET_STRING(iov[0], buffer); IOVEC_SET_STRING(iov[1], p); return sd_journal_sendv(iov, 2); }
int main(int argc, char* argv[]) { int r, j = 0; _cleanup_free_ char *p = NULL; ssize_t n; pid_t pid; uid_t uid; gid_t gid; struct iovec iovec[14]; _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL, *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *t = NULL; prctl(PR_SET_DUMPABLE, 0); if (argc != _ARG_MAX) { log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_open(); log_error("Invalid number of arguments passed from kernel."); r = -EINVAL; goto finish; } r = parse_pid(argv[ARG_PID], &pid); if (r < 0) { log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_open(); log_error("Failed to parse PID."); goto finish; } if (cg_pid_get_unit(pid, &t) >= 0) { if (streq(t, SPECIAL_JOURNALD_SERVICE)) { /* Make sure we don't make use of the journal, * if it's the journal which is crashing */ log_set_target(LOG_TARGET_KMSG); log_open(); r = divert_coredump(); goto finish; } core_unit = strappend("COREDUMP_UNIT=", t); } else if (cg_pid_get_user_unit(pid, &t) >= 0) core_unit = strappend("COREDUMP_USER_UNIT=", t); if (core_unit) IOVEC_SET_STRING(iovec[j++], core_unit); /* OK, now we know it's not the journal, hence make use of * it */ log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_open(); r = parse_uid(argv[ARG_UID], &uid); if (r < 0) { log_error("Failed to parse UID."); goto finish; } r = parse_gid(argv[ARG_GID], &gid); if (r < 0) { log_error("Failed to parse GID."); goto finish; } core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]); if (core_pid) IOVEC_SET_STRING(iovec[j++], core_pid); core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]); if (core_uid) IOVEC_SET_STRING(iovec[j++], core_uid); core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]); if (core_gid) IOVEC_SET_STRING(iovec[j++], core_gid); core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]); if (core_signal) IOVEC_SET_STRING(iovec[j++], core_signal); core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]); if (core_comm) IOVEC_SET_STRING(iovec[j++], core_comm); #ifdef HAVE_LOGIND if (sd_pid_get_session(pid, &t) >= 0) { core_session = strappend("COREDUMP_SESSION=", t); free(t); if (core_session) IOVEC_SET_STRING(iovec[j++], core_session); } #endif if (get_process_exe(pid, &t) >= 0) { core_exe = strappend("COREDUMP_EXE=", t); free(t); if (core_exe) IOVEC_SET_STRING(iovec[j++], core_exe); } if (get_process_cmdline(pid, 0, false, &t) >= 0) { core_cmdline = strappend("COREDUMP_CMDLINE=", t); free(t); if (core_cmdline) IOVEC_SET_STRING(iovec[j++], core_cmdline); } core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL); if (core_timestamp) IOVEC_SET_STRING(iovec[j++], core_timestamp); IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); IOVEC_SET_STRING(iovec[j++], "PRIORITY=2"); core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL); if (core_message) IOVEC_SET_STRING(iovec[j++], core_message); /* Now, let's drop privileges to become the user who owns the * segfaulted process and allocate the coredump memory under * his uid. This also ensures that the credentials journald * will see are the ones of the coredumping user, thus making * sure the user himself gets access to the core dump. */ if (setresgid(gid, gid, gid) < 0 || setresuid(uid, uid, uid) < 0) { log_error("Failed to drop privileges: %m"); r = -errno; goto finish; } p = malloc(9 + COREDUMP_MAX); if (!p) { r = log_oom(); goto finish; } memcpy(p, "COREDUMP=", 9); n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false); if (n < 0) { log_error("Failed to read core dump data: %s", strerror(-n)); r = (int) n; goto finish; } iovec[j].iov_base = p; iovec[j].iov_len = 9 + n; j++; r = sd_journal_sendv(iovec, j); if (r < 0) log_error("Failed to send coredump: %s", strerror(-r)); finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
static int server_process_entry( Server *s, const void *buffer, size_t *remaining, ClientContext *context, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) { /* Process a single entry from a native message. * Returns 0 if nothing special happened and the message processing should continue, * and a negative or positive value otherwise. * * Note that *remaining is altered on both success and failure. */ struct iovec *iovec = NULL; unsigned n = 0, j, tn = (unsigned) -1; const char *p; size_t m = 0, entry_size = 0; int priority = LOG_INFO; char *identifier = NULL, *message = NULL; pid_t object_pid = 0; int r = 0; p = buffer; while (*remaining > 0) { const char *e, *q; e = memchr(p, '\n', *remaining); if (!e) { /* Trailing noise, let's ignore it, and flush what we collected */ log_debug("Received message with trailing noise, ignoring."); r = 1; /* finish processing of the message */ break; } if (e == p) { /* Entry separator */ *remaining -= 1; break; } if (*p == '.' || *p == '#') { /* Ignore control commands for now, and * comments too. */ *remaining -= (e - p) + 1; p = e + 1; continue; } /* A property follows */ /* n existing properties, 1 new, +1 for _TRANSPORT */ if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) { r = log_oom(); break; } q = memchr(p, '=', e - p); if (q) { if (valid_user_field(p, q - p, false)) { size_t l; l = e - p; /* If the field name starts with an * underscore, skip the variable, * since that indicates a trusted * field */ iovec[n].iov_base = (char*) p; iovec[n].iov_len = l; entry_size += l; n++; server_process_entry_meta(p, l, ucred, &priority, &identifier, &message, &object_pid); } *remaining -= (e - p) + 1; p = e + 1; continue; } else { uint64_t l; char *k; if (*remaining < e - p + 1 + sizeof(uint64_t) + 1) { log_debug("Failed to parse message, ignoring."); break; } l = unaligned_read_le64(e + 1); if (l > DATA_SIZE_MAX) { log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l); break; } if ((uint64_t) *remaining < e - p + 1 + sizeof(uint64_t) + l + 1 || e[1+sizeof(uint64_t)+l] != '\n') { log_debug("Failed to parse message, ignoring."); break; } k = malloc((e - p) + 1 + l); if (!k) { log_oom(); break; } memcpy(k, p, e - p); k[e - p] = '='; memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l); if (valid_user_field(p, e - p, false)) { iovec[n].iov_base = k; iovec[n].iov_len = (e - p) + 1 + l; entry_size += iovec[n].iov_len; n++; server_process_entry_meta(k, (e - p) + 1 + l, ucred, &priority, &identifier, &message, &object_pid); } else free(k); *remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1; p = e + 1 + sizeof(uint64_t) + l + 1; } } if (n <= 0) { r = 1; goto finish; } tn = n++; IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal"); entry_size += strlen("_TRANSPORT=journal"); if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size); goto finish; } if (message) { if (s->forward_to_syslog) server_forward_syslog(s, syslog_fixup_facility(priority), identifier, message, ucred, tv); if (s->forward_to_kmsg) server_forward_kmsg(s, priority, identifier, message, ucred); if (s->forward_to_console) server_forward_console(s, priority, identifier, message, ucred); if (s->forward_to_wall) server_forward_wall(s, priority, identifier, message, ucred); } server_dispatch_message(s, iovec, n, m, context, tv, priority, object_pid); finish: for (j = 0; j < n; j++) { if (j == tn) continue; if (iovec[j].iov_base < buffer || (const char*) iovec[j].iov_base >= p + *remaining) free(iovec[j].iov_base); } free(iovec); free(identifier); free(message); return r; }
int main(int argc, char* argv[]) { /* The small core field we allocate on the stack, to keep things simple */ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL, *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL, *core_slice = NULL; /* The larger ones we allocate on the heap */ _cleanup_free_ char *core_timestamp = NULL, *core_message = NULL, *coredump_data = NULL, *core_owner_uid = NULL, *core_open_fds = NULL, *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL; _cleanup_free_ char *exe = NULL, *comm = NULL, *filename = NULL; const char *info[_INFO_LEN]; _cleanup_close_ int coredump_fd = -1; struct iovec iovec[26]; uint64_t coredump_size; int r, j = 0; uid_t uid, owner_uid; gid_t gid; pid_t pid; char *t; const char *p; /* Make sure we never enter a loop */ prctl(PR_SET_DUMPABLE, 0); /* First, log to a safe place, since we don't know what * crashed and it might be journald which we'd rather not log * to then. */ log_set_target(LOG_TARGET_KMSG); log_open(); if (argc < INFO_COMM + 1) { log_error("Not enough arguments passed from kernel (%d, expected %d).", argc - 1, INFO_COMM + 1 - 1); r = -EINVAL; goto finish; } /* Ignore all parse errors */ parse_config(); log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage)); log_debug("Selected compression %s.", yes_no(arg_compress)); r = parse_uid(argv[INFO_UID + 1], &uid); if (r < 0) { log_error("Failed to parse UID."); goto finish; } r = parse_pid(argv[INFO_PID + 1], &pid); if (r < 0) { log_error("Failed to parse PID."); goto finish; } r = parse_gid(argv[INFO_GID + 1], &gid); if (r < 0) { log_error("Failed to parse GID."); goto finish; } if (get_process_comm(pid, &comm) < 0) { log_warning("Failed to get COMM, falling back to the command line."); comm = strv_join(argv + INFO_COMM + 1, " "); } if (get_process_exe(pid, &exe) < 0) log_warning("Failed to get EXE."); info[INFO_PID] = argv[INFO_PID + 1]; info[INFO_UID] = argv[INFO_UID + 1]; info[INFO_GID] = argv[INFO_GID + 1]; info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1]; info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1]; info[INFO_COMM] = comm; info[INFO_EXE] = exe; if (cg_pid_get_unit(pid, &t) >= 0) { if (streq(t, SPECIAL_JOURNALD_SERVICE)) { free(t); /* If we are journald, we cut things short, * don't write to the journal, but still * create a coredump. */ if (arg_storage != COREDUMP_STORAGE_NONE) arg_storage = COREDUMP_STORAGE_EXTERNAL; r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size); if (r < 0) goto finish; r = maybe_remove_external_coredump(filename, coredump_size); if (r < 0) goto finish; log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename); goto finish; } core_unit = strjoina("COREDUMP_UNIT=", t); free(t); } else if (cg_pid_get_user_unit(pid, &t) >= 0) { core_unit = strjoina("COREDUMP_USER_UNIT=", t); free(t); } if (core_unit) IOVEC_SET_STRING(iovec[j++], core_unit); /* OK, now we know it's not the journal, hence we can make use * of it now. */ log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_open(); core_pid = strjoina("COREDUMP_PID=", info[INFO_PID]); IOVEC_SET_STRING(iovec[j++], core_pid); core_uid = strjoina("COREDUMP_UID=", info[INFO_UID]); IOVEC_SET_STRING(iovec[j++], core_uid); core_gid = strjoina("COREDUMP_GID=", info[INFO_GID]); IOVEC_SET_STRING(iovec[j++], core_gid); core_signal = strjoina("COREDUMP_SIGNAL=", info[INFO_SIGNAL]); IOVEC_SET_STRING(iovec[j++], core_signal); if (sd_pid_get_session(pid, &t) >= 0) { core_session = strjoina("COREDUMP_SESSION=", t); free(t); IOVEC_SET_STRING(iovec[j++], core_session); } if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) { r = asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid); if (r > 0) IOVEC_SET_STRING(iovec[j++], core_owner_uid); } if (sd_pid_get_slice(pid, &t) >= 0) { core_slice = strjoina("COREDUMP_SLICE=", t); free(t); IOVEC_SET_STRING(iovec[j++], core_slice); } if (comm) { core_comm = strjoina("COREDUMP_COMM=", comm); IOVEC_SET_STRING(iovec[j++], core_comm); } if (exe) { core_exe = strjoina("COREDUMP_EXE=", exe); IOVEC_SET_STRING(iovec[j++], core_exe); } if (get_process_cmdline(pid, 0, false, &t) >= 0) { core_cmdline = strjoina("COREDUMP_CMDLINE=", t); free(t); IOVEC_SET_STRING(iovec[j++], core_cmdline); } if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) { core_cgroup = strjoina("COREDUMP_CGROUP=", t); free(t); IOVEC_SET_STRING(iovec[j++], core_cgroup); } if (compose_open_fds(pid, &t) >= 0) { core_open_fds = strappend("COREDUMP_OPEN_FDS=", t); free(t); if (core_open_fds) IOVEC_SET_STRING(iovec[j++], core_open_fds); } p = procfs_file_alloca(pid, "status"); if (read_full_file(p, &t, NULL) >= 0) { core_proc_status = strappend("COREDUMP_PROC_STATUS=", t); free(t); if (core_proc_status) IOVEC_SET_STRING(iovec[j++], core_proc_status); } p = procfs_file_alloca(pid, "maps"); if (read_full_file(p, &t, NULL) >= 0) { core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t); free(t); if (core_proc_maps) IOVEC_SET_STRING(iovec[j++], core_proc_maps); } p = procfs_file_alloca(pid, "limits"); if (read_full_file(p, &t, NULL) >= 0) { core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t); free(t); if (core_proc_limits) IOVEC_SET_STRING(iovec[j++], core_proc_limits); } p = procfs_file_alloca(pid, "cgroup"); if (read_full_file(p, &t, NULL) >=0) { core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t); free(t); if (core_proc_cgroup) IOVEC_SET_STRING(iovec[j++], core_proc_cgroup); } if (get_process_cwd(pid, &t) >= 0) { core_cwd = strjoina("COREDUMP_CWD=", t); free(t); IOVEC_SET_STRING(iovec[j++], core_cwd); } if (get_process_root(pid, &t) >= 0) { core_root = strjoina("COREDUMP_ROOT=", t); free(t); IOVEC_SET_STRING(iovec[j++], core_root); } if (get_process_environ(pid, &t) >= 0) { core_environ = strappend("COREDUMP_ENVIRON=", t); free(t); if (core_environ) IOVEC_SET_STRING(iovec[j++], core_environ); } core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL); if (core_timestamp) IOVEC_SET_STRING(iovec[j++], core_timestamp); IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); IOVEC_SET_STRING(iovec[j++], "PRIORITY=2"); /* Vacuum before we write anything again */ coredump_vacuum(-1, arg_keep_free, arg_max_use); /* Always stream the coredump to disk, if that's possible */ r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size); if (r < 0) /* skip whole core dumping part */ goto log; /* If we don't want to keep the coredump on disk, remove it * now, as later on we will lack the privileges for * it. However, we keep the fd to it, so that we can still * process it and log it. */ r = maybe_remove_external_coredump(filename, coredump_size); if (r < 0) goto finish; if (r == 0) { const char *coredump_filename; coredump_filename = strjoina("COREDUMP_FILENAME=", filename); IOVEC_SET_STRING(iovec[j++], coredump_filename); } /* Vacuum again, but exclude the coredump we just created */ coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use); /* Now, let's drop privileges to become the user who owns the * segfaulted process and allocate the coredump memory under * the user's uid. This also ensures that the credentials * journald will see are the ones of the coredumping user, * thus making sure the user gets access to the core * dump. Let's also get rid of all capabilities, if we run as * root, we won't need them anymore. */ r = drop_privileges(uid, gid, 0); if (r < 0) { log_error_errno(r, "Failed to drop privileges: %m"); goto finish; } #ifdef HAVE_ELFUTILS /* Try to get a strack trace if we can */ if (coredump_size <= arg_process_size_max) { _cleanup_free_ char *stacktrace = NULL; r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace); if (r >= 0) core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL); else if (r == -EINVAL) log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno())); else log_warning_errno(r, "Failed to generate stack trace: %m"); } if (!core_message) #endif log: core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL); if (core_message) IOVEC_SET_STRING(iovec[j++], core_message); /* Optionally store the entire coredump in the journal */ if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) && coredump_size <= arg_journal_size_max) { size_t sz = 0; /* Store the coredump itself in the journal */ r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz); if (r >= 0) { iovec[j].iov_base = coredump_data; iovec[j].iov_len = sz; j++; } } r = sd_journal_sendv(iovec, j); if (r < 0) log_error_errno(r, "Failed to log coredump: %m"); finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
_printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) { PROTECT_ERRNO; int r, n = 0, i = 0, j; struct iovec *iov = NULL; assert(_iov); if (extra > 0) { n = MAX(extra * 2, extra + 4); iov = malloc0(n * sizeof(struct iovec)); if (!iov) { r = -ENOMEM; goto fail; } i = extra; } while (format) { struct iovec *c; char *buffer; va_list aq; if (i >= n) { n = MAX(i*2, 4); c = realloc(iov, n * sizeof(struct iovec)); if (!c) { r = -ENOMEM; goto fail; } iov = c; } va_copy(aq, ap); if (vasprintf(&buffer, format, aq) < 0) { va_end(aq); r = -ENOMEM; goto fail; } va_end(aq); VA_FORMAT_ADVANCE(format, ap); IOVEC_SET_STRING(iov[i++], buffer); format = va_arg(ap, char *); } *_iov = iov; return i; fail: for (j = 0; j < i; j++) free(iov[j].iov_base); free(iov); return r; }
_public_ int sd_journal_sendv(const struct iovec *iov, int n) { PROTECT_ERRNO; int fd; struct iovec *w; uint64_t *l; int i, j = 0; struct sockaddr_un sa = { .sun_family = AF_UNIX, .sun_path = JOURNAL_RUNDIR "/socket", }; struct msghdr mh = { .msg_name = &sa, .msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path), }; ssize_t k; union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(int))]; } control; bool have_syslog_identifier = false; assert_return(iov, -EINVAL); assert_return(n > 0, -EINVAL); w = alloca(sizeof(struct iovec) * n * 5 + 3); l = alloca(sizeof(uint64_t) * n); for (i = 0; i < n; i++) { char *c, *nl; if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1)) return -EINVAL; c = memchr(iov[i].iov_base, '=', iov[i].iov_len); if (_unlikely_(!c || c == iov[i].iov_base)) return -EINVAL; have_syslog_identifier = have_syslog_identifier || (c == (char *) iov[i].iov_base + 17 && startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER")); nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len); if (nl) { if (_unlikely_(nl < c)) return -EINVAL; /* Already includes a newline? Bummer, then * let's write the variable name, then a * newline, then the size (64bit LE), followed * by the data and a final newline */ w[j].iov_base = iov[i].iov_base; w[j].iov_len = c - (char*) iov[i].iov_base; j++; IOVEC_SET_STRING(w[j++], "\n"); l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1); w[j].iov_base = &l[i]; w[j].iov_len = sizeof(uint64_t); j++; w[j].iov_base = c + 1; w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1; j++; } else /* Nothing special? Then just add the line and * append a newline */ w[j++] = iov[i]; IOVEC_SET_STRING(w[j++], "\n"); } if (!have_syslog_identifier) { /* Implicitly add program_invocation_short_name, if it * is not set explicitly. We only do this for * program_invocation_short_name, and nothing else * since everything else is much nicer to retrieve * from the outside. */ IOVEC_SET_STRING(w[j++], "SYSLOG_IDENTIFIER="); IOVEC_SET_STRING(w[j++], program_invocation_short_name); IOVEC_SET_STRING(w[j++], "\n"); } fd = journal_fd(); if (_unlikely_(fd < 0)) return fd; mh.msg_iov = w; mh.msg_iovlen = j; k = sendmsg(fd, &mh, MSG_NOSIGNAL); if (k >= 0) return 0; /* Fail silently if the journal is not available */ if (errno == ENOENT) return 0; if (errno != EMSGSIZE && errno != ENOBUFS) return -errno; return 0; } static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) { PROTECT_ERRNO; size_t n, k; k = isempty(message) ? 0 : strlen(message) + 2; n = 8 + k + 256 + 1; for (;;) { char buffer[n]; char* j; errno = 0; j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k); if (errno == 0) { char error[6 + 10 + 1]; /* for a 32bit value */ if (j != buffer + 8 + k) memmove(buffer + 8 + k, j, strlen(j)+1); memcpy(buffer, "MESSAGE=", 8); if (k > 0) { memcpy(buffer + 8, message, k - 2); memcpy(buffer + 8 + k - 2, ": ", 2); } snprintf(error, sizeof(error), "ERRNO=%u", _saved_errno_); char_array_0(error); IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3"); IOVEC_SET_STRING(iov[skip+1], buffer); IOVEC_SET_STRING(iov[skip+2], error); return sd_journal_sendv(iov, skip + 3); } if (errno != ERANGE) return -errno; n *= 2; } } _public_ int sd_journal_perror(const char *message) { struct iovec iovec[3]; return fill_iovec_perror_and_send(message, 0, iovec); }
void server_forward_console( Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred) { struct iovec iovec[5]; struct timespec ts; char tbuf[sizeof("[] ")-1 + DECIMAL_STR_MAX(ts.tv_sec) + DECIMAL_STR_MAX(ts.tv_nsec)-3 + 1]; char header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t)]; int n = 0, fd; _cleanup_free_ char *ident_buf = NULL; const char *tty; assert(s); assert(message); if (LOG_PRI(priority) > s->max_level_console) return; /* First: timestamp */ if (prefix_timestamp()) { assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); xsprintf(tbuf, "[%5"PRI_TIME".%06ld] ", ts.tv_sec, ts.tv_nsec / 1000); IOVEC_SET_STRING(iovec[n++], tbuf); } /* Second: identifier and PID */ if (ucred) { if (!identifier) { get_process_comm(ucred->pid, &ident_buf); identifier = ident_buf; } xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid); if (identifier) IOVEC_SET_STRING(iovec[n++], identifier); IOVEC_SET_STRING(iovec[n++], header_pid); } else if (identifier) { IOVEC_SET_STRING(iovec[n++], identifier); IOVEC_SET_STRING(iovec[n++], ": "); } /* Fourth: message */ IOVEC_SET_STRING(iovec[n++], message); IOVEC_SET_STRING(iovec[n++], "\n"); tty = s->tty_path ? s->tty_path : "/dev/console"; /* Before you ask: yes, on purpose we open/close the console for each log line we write individually. This is a * good strategy to avoid journald getting killed by the kernel's SAK concept (it doesn't fix this entirely, * but minimizes the time window the kernel might end up killing journald due to SAK). It also makes things * easier for us so that we don't have to recover from hangups and suchlike triggered on the console. */ fd = open_terminal(tty, O_WRONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) { log_debug_errno(fd, "Failed to open %s for logging: %m", tty); return; } if (writev(fd, iovec, n) < 0) log_debug_errno(errno, "Failed to write to %s for logging: %m", tty); safe_close(fd); }
static int write_message(Server *s, const char *buf, struct ucred *ucred) { ssize_t k; char priority[6], pid[16]; struct iovec iovec[5]; unsigned i = 0; char *process = NULL; int r = 0; int prio = LOG_USER | LOG_INFO; assert(s); assert(buf); parse_syslog_priority((char**) &buf, &prio); if (*buf == 0) return 0; if ((prio & LOG_FACMASK) == 0) prio = LOG_USER | LOG_PRI(prio); /* First, set priority field */ snprintf(priority, sizeof(priority), "<%i>", prio); char_array_0(priority); IOVEC_SET_STRING(iovec[i++], priority); /* Second, skip date */ skip_date(&buf); /* Then, add process if set */ if (read_process(&buf, &iovec[i]) > 0) i++; else if (ucred && ucred->pid > 0 && get_process_name(ucred->pid, &process) >= 0) IOVEC_SET_STRING(iovec[i++], process); /* Skip the stored PID if we have a better one */ if (ucred) { snprintf(pid, sizeof(pid), "[%lu]: ", (unsigned long) ucred->pid); char_array_0(pid); IOVEC_SET_STRING(iovec[i++], pid); skip_pid(&buf); if (*buf == ':') buf++; buf += strspn(buf, WHITESPACE); } /* Is the remaining message empty? */ if (*buf) { /* And the rest is the message */ IOVEC_SET_STRING(iovec[i++], buf); IOVEC_SET_STRING(iovec[i++], "\n"); if ((k = writev(s->kmsg_fd, iovec, i)) <= 0) { log_error("Failed to write log message to kmsg: %s", k < 0 ? strerror(errno) : "short write"); r = k < 0 ? -errno : -EIO; } } free(process); return r; }
static void dev_kmsg_record(Server *s, char *p, size_t l) { struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS]; char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL; int priority, r; unsigned n = 0, z = 0, j; unsigned long long usec; char *identifier = NULL, *pid = NULL, *e, *f, *k; uint64_t serial; size_t pl; char *kernel_device = NULL; assert(s); assert(p); if (l <= 0) return; e = memchr(p, ',', l); if (!e) return; *e = 0; r = safe_atoi(p, &priority); if (r < 0 || priority < 0 || priority > 999) return; if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN) return; l -= (e - p) + 1; p = e + 1; e = memchr(p, ',', l); if (!e) return; *e = 0; r = safe_atou64(p, &serial); if (r < 0) return; if (s->kernel_seqnum) { /* We already read this one? */ if (serial < *s->kernel_seqnum) return; /* Did we lose any? */ if (serial > *s->kernel_seqnum) server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED, "Missed %"PRIu64" kernel messages", serial - *s->kernel_seqnum - 1); /* Make sure we never read this one again. Note that * we always store the next message serial we expect * here, simply because this makes handling the first * message with serial 0 easy. */ *s->kernel_seqnum = serial + 1; } l -= (e - p) + 1; p = e + 1; f = memchr(p, ';', l); if (!f) return; /* Kernel 3.6 has the flags field, kernel 3.5 lacks that */ e = memchr(p, ',', l); if (!e || f < e) e = f; *e = 0; r = safe_atollu(p, &usec); if (r < 0) return; l -= (f - p) + 1; p = f + 1; e = memchr(p, '\n', l); if (!e) return; *e = 0; pl = e - p; l -= (e - p) + 1; k = e + 1; for (j = 0; l > 0 && j < N_IOVEC_KERNEL_FIELDS; j++) { char *m; /* Meta data fields attached */ if (*k != ' ') break; k ++, l --; e = memchr(k, '\n', l); if (!e) return; *e = 0; m = cunescape_length_with_prefix(k, e - k, "_KERNEL_"); if (!m) break; if (startswith(m, "_KERNEL_DEVICE=")) kernel_device = m + 15; IOVEC_SET_STRING(iovec[n++], m); z++; l -= (e - k) + 1; k = e + 1; } if (kernel_device) { struct udev_device *ud; ud = udev_device_new_from_device_id(s->udev, kernel_device); if (ud) { const char *g; struct udev_list_entry *ll; char *b; g = udev_device_get_devnode(ud); if (g) { b = strappend("_UDEV_DEVNODE=", g); if (b) { IOVEC_SET_STRING(iovec[n++], b); z++; } } g = udev_device_get_sysname(ud); if (g) { b = strappend("_UDEV_SYSNAME=", g); if (b) { IOVEC_SET_STRING(iovec[n++], b); z++; } } j = 0; ll = udev_device_get_devlinks_list_entry(ud); udev_list_entry_foreach(ll, ll) { if (j > N_IOVEC_UDEV_FIELDS) break; g = udev_list_entry_get_name(ll); if (g) { b = strappend("_UDEV_DEVLINK=", g); if (b) { IOVEC_SET_STRING(iovec[n++], b); z++; } } j++; } udev_device_unref(ud); } } if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu", usec) >= 0) IOVEC_SET_STRING(iovec[n++], source_time); IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=kernel"); if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) IOVEC_SET_STRING(iovec[n++], syslog_priority); if ((priority & LOG_FACMASK) == LOG_KERN) IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=kernel"); else { pl -= syslog_parse_identifier((const char**) &p, &identifier, &pid); /* Avoid any messages we generated ourselves via * log_info() and friends. */ if (pid && is_us(pid)) goto finish; if (identifier) { syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier); if (syslog_identifier) IOVEC_SET_STRING(iovec[n++], syslog_identifier); } if (pid) { syslog_pid = strappend("SYSLOG_PID=", pid); if (syslog_pid) IOVEC_SET_STRING(iovec[n++], syslog_pid); } if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) IOVEC_SET_STRING(iovec[n++], syslog_facility); } message = cunescape_length_with_prefix(p, pl, "MESSAGE="); if (message) IOVEC_SET_STRING(iovec[n++], message); server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority, 0); finish: for (j = 0; j < z; j++) free(iovec[j].iov_base); free(message); free(syslog_priority); free(syslog_identifier); free(syslog_pid); free(syslog_facility); free(source_time); free(identifier); free(pid); }
_public_ int sd_journal_sendv(const struct iovec *iov, int n) { PROTECT_ERRNO; int fd, r; _cleanup_close_ int buffer_fd = -1; struct iovec *w; uint64_t *l; int i, j = 0; static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = "/run/systemd/journal/socket", }; struct msghdr mh = { .msg_name = (struct sockaddr*) &sa.sa, .msg_namelen = SOCKADDR_UN_LEN(sa.un), }; ssize_t k; bool have_syslog_identifier = false; bool seal = true; assert_return(iov, -EINVAL); assert_return(n > 0, -EINVAL); w = newa(struct iovec, n * 5 + 3); l = newa(uint64_t, n); for (i = 0; i < n; i++) { char *c, *nl; if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1)) return -EINVAL; c = memchr(iov[i].iov_base, '=', iov[i].iov_len); if (_unlikely_(!c || c == iov[i].iov_base)) return -EINVAL; have_syslog_identifier = have_syslog_identifier || (c == (char *) iov[i].iov_base + 17 && startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER")); nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len); if (nl) { if (_unlikely_(nl < c)) return -EINVAL; /* Already includes a newline? Bummer, then * let's write the variable name, then a * newline, then the size (64bit LE), followed * by the data and a final newline */ w[j].iov_base = iov[i].iov_base; w[j].iov_len = c - (char*) iov[i].iov_base; j++; IOVEC_SET_STRING(w[j++], "\n"); l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1); w[j].iov_base = &l[i]; w[j].iov_len = sizeof(uint64_t); j++; w[j].iov_base = c + 1; w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1; j++; } else /* Nothing special? Then just add the line and * append a newline */ w[j++] = iov[i]; IOVEC_SET_STRING(w[j++], "\n"); } if (!have_syslog_identifier && string_is_safe(program_invocation_short_name)) { /* Implicitly add program_invocation_short_name, if it * is not set explicitly. We only do this for * program_invocation_short_name, and nothing else * since everything else is much nicer to retrieve * from the outside. */ IOVEC_SET_STRING(w[j++], "SYSLOG_IDENTIFIER="); IOVEC_SET_STRING(w[j++], program_invocation_short_name); IOVEC_SET_STRING(w[j++], "\n"); } fd = journal_fd(); if (_unlikely_(fd < 0)) return fd; mh.msg_iov = w; mh.msg_iovlen = j; k = sendmsg(fd, &mh, MSG_NOSIGNAL); if (k >= 0) return 0; /* Fail silently if the journal is not available */ if (errno == ENOENT) return 0; if (errno != EMSGSIZE && errno != ENOBUFS) return -errno; /* Message doesn't fit... Let's dump the data in a memfd or * temporary file and just pass a file descriptor of it to the * other side. * * For the temporary files we use /dev/shm instead of /tmp * here, since we want this to be a tmpfs, and one that is * available from early boot on and where unprivileged users * can create files. */ buffer_fd = memfd_new(NULL); if (buffer_fd < 0) { if (buffer_fd == -ENOSYS) { buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC); if (buffer_fd < 0) return buffer_fd; seal = false; } else return buffer_fd; } n = writev(buffer_fd, w, j); if (n < 0) return -errno; if (seal) { r = memfd_set_sealed(buffer_fd); if (r < 0) return r; } r = send_one_fd_sa(fd, buffer_fd, mh.msg_name, mh.msg_namelen, 0); if (r == -ENOENT) /* Fail silently if the journal is not available */ return 0; return r; } static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) { PROTECT_ERRNO; size_t n, k; k = isempty(message) ? 0 : strlen(message) + 2; n = 8 + k + 256 + 1; for (;;) { char buffer[n]; char* j; errno = 0; j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k); if (errno == 0) { char error[sizeof("ERRNO=")-1 + DECIMAL_STR_MAX(int) + 1]; if (j != buffer + 8 + k) memmove(buffer + 8 + k, j, strlen(j)+1); memcpy(buffer, "MESSAGE=", 8); if (k > 0) { memcpy(buffer + 8, message, k - 2); memcpy(buffer + 8 + k - 2, ": ", 2); } xsprintf(error, "ERRNO=%i", _saved_errno_); assert_cc(3 == LOG_ERR); IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3"); IOVEC_SET_STRING(iov[skip+1], buffer); IOVEC_SET_STRING(iov[skip+2], error); return sd_journal_sendv(iov, skip + 3); } if (errno != ERANGE) return -errno; n *= 2; } } _public_ int sd_journal_perror(const char *message) { struct iovec iovec[3]; return fill_iovec_perror_and_send(message, 0, iovec); } _public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) { static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = "/run/systemd/journal/stdout", }; _cleanup_close_ int fd = -1; char *header; size_t l; int r; assert_return(priority >= 0, -EINVAL); assert_return(priority <= 7, -EINVAL); fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); if (fd < 0) return -errno; r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return -errno; if (shutdown(fd, SHUT_RD) < 0) return -errno; fd_inc_sndbuf(fd, SNDBUF_SIZE); if (!identifier) identifier = ""; l = strlen(identifier); header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2); memcpy(header, identifier, l); header[l++] = '\n'; header[l++] = '\n'; /* unit id */ header[l++] = '0' + priority; header[l++] = '\n'; header[l++] = '0' + !!level_prefix; header[l++] = '\n'; header[l++] = '0'; header[l++] = '\n'; header[l++] = '0'; header[l++] = '\n'; header[l++] = '0'; header[l++] = '\n'; r = loop_write(fd, header, l, false); if (r < 0) return r; r = fd; fd = -1; return r; } _public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) { int r; va_list ap; va_start(ap, format); r = sd_journal_printv_with_location(priority, file, line, func, format, ap); va_end(ap); return r; } _public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) { char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1]; struct iovec iov[5]; char *f; assert_return(priority >= 0, -EINVAL); assert_return(priority <= 7, -EINVAL); assert_return(format, -EINVAL); xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK); memcpy(buffer, "MESSAGE=", 8); vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); /* Strip trailing whitespace, keep prefixing whitespace */ (void) strstrip(buffer); /* Suppress empty lines */ if (isempty(buffer+8)) return 0; /* func is initialized from __func__ which is not a macro, but * a static const char[], hence cannot easily be prefixed with * CODE_FUNC=, hence let's do it manually here. */ ALLOCA_CODE_FUNC(f, func); zero(iov); IOVEC_SET_STRING(iov[0], buffer); IOVEC_SET_STRING(iov[1], p); IOVEC_SET_STRING(iov[2], file); IOVEC_SET_STRING(iov[3], line); IOVEC_SET_STRING(iov[4], f); return sd_journal_sendv(iov, ELEMENTSOF(iov)); }
void server_forward_console( Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred) { struct iovec iovec[5]; char header_pid[16]; struct timespec ts; char tbuf[4 + DECIMAL_STR_MAX(ts.tv_sec) + DECIMAL_STR_MAX(ts.tv_nsec)-3 + 1]; int n = 0, fd; char *ident_buf = NULL; const char *tty; assert(s); assert(message); if (LOG_PRI(priority) > s->max_level_console) return; /* First: timestamp */ if (prefix_timestamp()) { assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); snprintf(tbuf, sizeof(tbuf), "[%5llu.%06llu] ", (unsigned long long) ts.tv_sec, (unsigned long long) ts.tv_nsec / 1000); IOVEC_SET_STRING(iovec[n++], tbuf); } /* Second: identifier and PID */ if (ucred) { if (!identifier) { get_process_comm(ucred->pid, &ident_buf); identifier = ident_buf; } snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid); char_array_0(header_pid); if (identifier) IOVEC_SET_STRING(iovec[n++], identifier); IOVEC_SET_STRING(iovec[n++], header_pid); } else if (identifier) { IOVEC_SET_STRING(iovec[n++], identifier); IOVEC_SET_STRING(iovec[n++], ": "); } /* Fourth: message */ IOVEC_SET_STRING(iovec[n++], message); IOVEC_SET_STRING(iovec[n++], "\n"); tty = s->tty_path ? s->tty_path : "/dev/console"; fd = open_terminal(tty, O_WRONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) { log_debug("Failed to open %s for logging: %s", tty, strerror(errno)); goto finish; } if (writev(fd, iovec, n) < 0) log_debug("Failed to write to %s for logging: %s", tty, strerror(errno)); safe_close(fd); finish: free(ident_buf); }
void server_forward_console( Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred) { struct iovec iovec[5]; struct timespec ts; char tbuf[sizeof("[] ")-1 + DECIMAL_STR_MAX(ts.tv_sec) + DECIMAL_STR_MAX(ts.tv_nsec)-3 + 1]; char header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t)]; int n = 0, fd; _cleanup_free_ char *ident_buf = NULL; const char *tty; assert(s); assert(message); if (LOG_PRI(priority) > s->max_level_console) return; /* First: timestamp */ if (prefix_timestamp()) { assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); xsprintf(tbuf, "[%5"PRI_TIME".%06ld] ", ts.tv_sec, ts.tv_nsec / 1000); IOVEC_SET_STRING(iovec[n++], tbuf); } /* Second: identifier and PID */ if (ucred) { if (!identifier) { get_process_comm(ucred->pid, &ident_buf); identifier = ident_buf; } xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid); if (identifier) IOVEC_SET_STRING(iovec[n++], identifier); IOVEC_SET_STRING(iovec[n++], header_pid); } else if (identifier) { IOVEC_SET_STRING(iovec[n++], identifier); IOVEC_SET_STRING(iovec[n++], ": "); } /* Fourth: message */ IOVEC_SET_STRING(iovec[n++], message); IOVEC_SET_STRING(iovec[n++], "\n"); tty = s->tty_path ? s->tty_path : "/dev/console"; fd = open_terminal(tty, O_WRONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) { log_debug_errno(errno, "Failed to open %s for logging: %m", tty); return; } if (writev(fd, iovec, n) < 0) log_debug_errno(errno, "Failed to write to %s for logging: %m", tty); safe_close(fd); }
int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) { static const char status_indent[] = " "; /* "[" STATUS "] " */ _cleanup_free_ char *s = NULL; _cleanup_close_ int fd = -1; struct iovec iovec[6] = {}; int n = 0; static bool prev_ephemeral; assert(format); /* This is independent of logging, as status messages are * optional and go exclusively to the console. */ if (vasprintf(&s, format, ap) < 0) return log_oom(); fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) return fd; if (ellipse) { char *e; size_t emax, sl; int c; c = fd_columns(fd); if (c <= 0) c = 80; sl = status ? sizeof(status_indent)-1 : 0; emax = c - sl - 1; if (emax < 3) emax = 3; e = ellipsize(s, emax, 50); if (e) { free(s); s = e; } } if (prev_ephemeral) IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE); prev_ephemeral = ephemeral; if (status) { if (!isempty(status)) { IOVEC_SET_STRING(iovec[n++], "["); IOVEC_SET_STRING(iovec[n++], status); IOVEC_SET_STRING(iovec[n++], "] "); } else IOVEC_SET_STRING(iovec[n++], status_indent); } IOVEC_SET_STRING(iovec[n++], s); if (!ephemeral) IOVEC_SET_STRING(iovec[n++], "\n"); if (writev(fd, iovec, n) < 0) return -errno; return 0; }
void server_process_native_message( Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) { struct iovec *iovec = NULL; unsigned n = 0, j, tn = (unsigned) -1; const char *p; size_t remaining, m = 0, entry_size = 0; int priority = LOG_INFO; char *identifier = NULL, *message = NULL; pid_t object_pid = 0; assert(s); assert(buffer || buffer_size == 0); p = buffer; remaining = buffer_size; while (remaining > 0) { const char *e, *q; e = memchr(p, '\n', remaining); if (!e) { /* Trailing noise, let's ignore it, and flush what we collected */ log_debug("Received message with trailing noise, ignoring."); break; } if (e == p) { /* Entry separator */ if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size); continue; } server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid); n = 0; priority = LOG_INFO; entry_size = 0; p++; remaining--; continue; } if (*p == '.' || *p == '#') { /* Ignore control commands for now, and * comments too. */ remaining -= (e - p) + 1; p = e + 1; continue; } /* A property follows */ /* n existing properties, 1 new, +1 for _TRANSPORT */ if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) { log_oom(); break; } q = memchr(p, '=', e - p); if (q) { if (valid_user_field(p, q - p, false)) { size_t l; l = e - p; /* If the field name starts with an * underscore, skip the variable, * since that indidates a trusted * field */ iovec[n].iov_base = (char*) p; iovec[n].iov_len = l; entry_size += iovec[n].iov_len; n++; /* We need to determine the priority * of this entry for the rate limiting * logic */ if (l == 10 && startswith(p, "PRIORITY=") && p[9] >= '0' && p[9] <= '9') priority = (priority & LOG_FACMASK) | (p[9] - '0'); else if (l == 17 && startswith(p, "SYSLOG_FACILITY=") && p[16] >= '0' && p[16] <= '9') priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3); else if (l == 18 && startswith(p, "SYSLOG_FACILITY=") && p[16] >= '0' && p[16] <= '9' && p[17] >= '0' && p[17] <= '9') priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3); else if (l >= 19 && startswith(p, "SYSLOG_IDENTIFIER=")) { char *t; t = strndup(p + 18, l - 18); if (t) { free(identifier); identifier = t; } } else if (l >= 8 && startswith(p, "MESSAGE=")) { char *t; t = strndup(p + 8, l - 8); if (t) { free(message); message = t; } } else if (l > strlen("OBJECT_PID=") && l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) && startswith(p, "OBJECT_PID=") && allow_object_pid(ucred)) { char buf[DECIMAL_STR_MAX(pid_t)]; memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID=")); buf[l-strlen("OBJECT_PID=")] = '\0'; /* ignore error */ parse_pid(buf, &object_pid); } } remaining -= (e - p) + 1; p = e + 1; continue; } else { le64_t l_le; uint64_t l; char *k; if (remaining < e - p + 1 + sizeof(uint64_t) + 1) { log_debug("Failed to parse message, ignoring."); break; } memcpy(&l_le, e + 1, sizeof(uint64_t)); l = le64toh(l_le); if (l > DATA_SIZE_MAX) { log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l); break; } if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 || e[1+sizeof(uint64_t)+l] != '\n') { log_debug("Failed to parse message, ignoring."); break; } k = malloc((e - p) + 1 + l); if (!k) { log_oom(); break; } memcpy(k, p, e - p); k[e - p] = '='; memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l); if (valid_user_field(p, e - p, false)) { iovec[n].iov_base = k; iovec[n].iov_len = (e - p) + 1 + l; entry_size += iovec[n].iov_len; n++; } else free(k); remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1; p = e + 1 + sizeof(uint64_t) + l + 1; } } if (n <= 0) goto finish; tn = n++; IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal"); entry_size += strlen("_TRANSPORT=journal"); if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size); goto finish; } if (message) { if (s->forward_to_syslog) server_forward_syslog(s, priority, identifier, message, ucred, tv); if (s->forward_to_kmsg) server_forward_kmsg(s, priority, identifier, message, ucred); if (s->forward_to_console) server_forward_console(s, priority, identifier, message, ucred); if (s->forward_to_wall) server_forward_wall(s, priority, identifier, message, ucred); } server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid); finish: for (j = 0; j < n; j++) { if (j == tn) continue; if (iovec[j].iov_base < buffer || (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size) free(iovec[j].iov_base); } free(iovec); free(identifier); free(message); }