summaryrefslogtreecommitdiffstats
path: root/src/libnetdata/log/journal.c
blob: 2182212f6dcfc7bf1bad68cad557b78d3a382789 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// SPDX-License-Identifier: GPL-3.0-or-later

#include "journal.h"

bool is_path_unix_socket(const char *path) {
    // Check if the path is valid
    if(!path || !*path)
        return false;

    struct stat statbuf;

    // Use stat to check if the file exists and is a socket
    if (stat(path, &statbuf) == -1)
        // The file does not exist or cannot be accessed
        return false;

    // Check if the file is a socket
    if (S_ISSOCK(statbuf.st_mode))
        return true;

    return false;
}

bool is_stderr_connected_to_journal(void) {
    const char *journal_stream = getenv("JOURNAL_STREAM");
    if (!journal_stream)
        return false; // JOURNAL_STREAM is not set

    struct stat stderr_stat;
    if (fstat(STDERR_FILENO, &stderr_stat) < 0)
        return false; // Error in getting stderr info

    // Parse device and inode from JOURNAL_STREAM
    char *endptr;
    long journal_dev = strtol(journal_stream, &endptr, 10);
    if (*endptr != ':')
        return false; // Format error in JOURNAL_STREAM

    long journal_ino = strtol(endptr + 1, NULL, 10);

    return (stderr_stat.st_dev == (dev_t)journal_dev) && (stderr_stat.st_ino == (ino_t)journal_ino);
}

int journal_direct_fd(const char *path) {
    if(!path || !*path)
        path = JOURNAL_DIRECT_SOCKET;

    if(!is_path_unix_socket(path))
        return -1;

    int fd = socket(AF_UNIX, SOCK_DGRAM| DEFAULT_SOCKET_FLAGS, 0);
    if (fd < 0) return -1;

    sock_setcloexec(fd);

    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);

    // Connect the socket (optional, but can simplify send operations)
    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        close(fd);
        return -1;
    }

    return fd;
}

static inline bool journal_send_with_memfd(int fd __maybe_unused, const char *msg __maybe_unused, size_t msg_len __maybe_unused) {
#if defined(__NR_memfd_create) && defined(MFD_ALLOW_SEALING) && defined(F_ADD_SEALS) && defined(F_SEAL_SHRINK) && defined(F_SEAL_GROW) && defined(F_SEAL_WRITE)
    // Create a memory file descriptor
    int memfd = (int)syscall(__NR_memfd_create, "journald", MFD_ALLOW_SEALING);
    if (memfd < 0) return false;

    // Write data to the memfd
    if (write(memfd, msg, msg_len) != (ssize_t)msg_len) {
        close(memfd);
        return false;
    }

    // Seal the memfd to make it immutable
    if (fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) < 0) {
        close(memfd);
        return false;
    }

    struct iovec iov = {0};
    struct msghdr msghdr = {0};
    struct cmsghdr *cmsghdr;
    char cmsgbuf[CMSG_SPACE(sizeof(int))];

    msghdr.msg_iov = &iov;
    msghdr.msg_iovlen = 1;
    msghdr.msg_control = cmsgbuf;
    msghdr.msg_controllen = sizeof(cmsgbuf);

    cmsghdr = CMSG_FIRSTHDR(&msghdr);
    if(!cmsghdr) {
        close(memfd);
        return false;
    }

    cmsghdr->cmsg_level = SOL_SOCKET;
    cmsghdr->cmsg_type = SCM_RIGHTS;
    cmsghdr->cmsg_len = CMSG_LEN(sizeof(int));
    memcpy(CMSG_DATA(cmsghdr), &memfd, sizeof(int));

    ssize_t r = sendmsg(fd, &msghdr, 0);

    close(memfd);
    return r >= 0;
#else
    return false;
#endif
}

bool journal_direct_send(int fd, const char *msg, size_t msg_len) {
    // Send the datagram
    if (send(fd, msg, msg_len, 0) < 0) {
        if(errno != EMSGSIZE)
            return false;

        // datagram is too large, fallback to memfd
        if(!journal_send_with_memfd(fd, msg, msg_len))
            return false;
    }

    return true;
}

void journal_construct_path(char *dst, size_t dst_len, const char *host_prefix, const char *namespace_str) {
    if(!host_prefix)
        host_prefix = "";

    if(namespace_str)
        snprintfz(dst, dst_len, "%s/run/systemd/journal.%s/socket",
                  host_prefix, namespace_str);
    else
        snprintfz(dst, dst_len, "%s" JOURNAL_DIRECT_SOCKET,
                  host_prefix);
}