journal-send.c revision 15a5e95075a7f6007dd97b2a165c8ed16fe683df
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2011 Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is free software; you can redistribute it and/or modify it
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering under the terms of the GNU Lesser General Public License as published by
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering (at your option) any later version.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is distributed in the hope that it will be useful, but
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Lesser General Public License for more details.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering You should have received a copy of the GNU Lesser General Public License
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering char **_f = &(f); \
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering } while(false)
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering/* We open a single fd, and we'll share it with the current process,
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering * all its threads, and all its subprocesses. This means we need to
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering * initialize it atomically, and need to operate on it atomically
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering * never assuming we are the only user */
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poetteringstatic int journal_fd(void) {
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering_public_ int sd_journal_print(int priority, const char *format, ...) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering r = sd_journal_printv(priority, format, ap);
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering_public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering /* FIXME: Instead of limiting things to LINE_MAX we could do a
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering C99 variable-length array on the stack here in a loop. */
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1];
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen_printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering int r, n = 0, i = 0, j;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering c = realloc(iov, n * sizeof(struct iovec));
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering if (vasprintf(&buffer, format, aq) < 0) {
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering for (j = 0; j < i; j++)
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering_public_ int sd_journal_send(const char *format, ...) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering i = fill_iovec_sprintf(format, ap, 0, &iov);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering for (j = 0; j < i; j++)
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering_public_ int sd_journal_sendv(const struct iovec *iov, int n) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering .sun_path = "/run/systemd/journal/socket",
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering .msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path),
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen for (i = 0; i < n; i++) {
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1))
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen have_syslog_identifier = have_syslog_identifier ||
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER"));
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen /* Already includes a newline? Bummer, then
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen * let's write the variable name, then a
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen * newline, then the size (64bit LE), followed
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen * by the data and a final newline */
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1;
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering /* Nothing special? Then just add the line and
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen * append a newline */
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen w[j++] = iov[i];
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen string_is_safe(program_invocation_short_name)) {
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen /* Implicitly add program_invocation_short_name, if it
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen * is not set explicitly. We only do this for
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen * program_invocation_short_name, and nothing else
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering * since everything else is much nicer to retrieve
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering * from the outside. */
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering IOVEC_SET_STRING(w[j++], "SYSLOG_IDENTIFIER=");
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering IOVEC_SET_STRING(w[j++], program_invocation_short_name);
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen /* Fail silently if the journal is not available */
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen /* Message doesn't fit... Let's dump the data in a memfd or
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen * temporary file and just pass a file descriptor of it to the
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen * other side.
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen * For the temporary files we use /dev/shm instead of /tmp
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen * here, since we want this to be a tmpfs, and one that is
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen * available from early boot on and where unprivileged users
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen * can create files. */
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen buffer_fd = open_tmpfile("/dev/shm", O_RDWR | O_CLOEXEC);
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersenstatic int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) {
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering k = isempty(message) ? 0 : strlen(message) + 2;
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k);
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering char error[sizeof("ERRNO=")-1 + DECIMAL_STR_MAX(int) + 1];
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering xsprintf(error, "ERRNO=%i", _saved_errno_);
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3");
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering_public_ int sd_journal_perror(const char *message) {
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering return fill_iovec_perror_and_send(message, 0, iovec);
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt .un.sun_path = "/run/systemd/journal/stdout",
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering r = connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) {
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering r = sd_journal_printv_with_location(priority, file, line, func, format, ap);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering_public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering char buffer[8 + LINE_MAX], p[sizeof("PRIORITY=")-1 + DECIMAL_STR_MAX(int) + 1];
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering /* func is initialized from __func__ which is not a macro, but
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering * a static const char[], hence cannot easily be prefixed with
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering * CODE_FUNC=, hence let's do it manually here. */
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return sd_journal_sendv(iov, ELEMENTSOF(iov));
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering_public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering i = fill_iovec_sprintf(format, ap, 3, &iov);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering for (j = 3; j < i; j++)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering_public_ int sd_journal_sendv_with_location(
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering niov = alloca(sizeof(struct iovec) * (n + 3));
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering memcpy(niov, iov, sizeof(struct iovec) * n);
const char *message) {