1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
12 #include <sys/socket.h>
17 #include <fmt/format.h>
18 #include <fmt/ostream.h>
20 #include "include/ceph_assert.h"
21 #include "common/LogEntry.h"
22 #include "log/Entry.h"
23 #include "log/SubsystemMap.h"
24 #include "msg/msg_fmt.h"
27 namespace ceph::logging
{
30 const struct sockaddr_un sockaddr
= {
32 "/run/systemd/journal/socket",
35 ssize_t
sendmsg_fd(int transport_fd
, int fd
)
37 constexpr size_t control_len
= CMSG_LEN(sizeof(int));
38 char control
[control_len
];
40 (struct sockaddr
*)&sockaddr
, // msg_name
41 sizeof(sockaddr
), // msg_namelen
44 &control
, // msg_control
45 control_len
, // msg_controllen
47 ceph_assert(transport_fd
>= 0);
49 struct cmsghdr
*cmsg
= CMSG_FIRSTHDR(&mh
);
50 cmsg
->cmsg_level
= SOL_SOCKET
;
51 cmsg
->cmsg_type
= SCM_RIGHTS
;
52 cmsg
->cmsg_len
= CMSG_LEN(sizeof(int));
53 *reinterpret_cast<int *>(CMSG_DATA(cmsg
)) = fd
;
55 return sendmsg(transport_fd
, &mh
, MSG_NOSIGNAL
);
58 char map_prio(short ceph_prio
)
73 class EntryEncoderBase
{
77 {}, {}, {}, { (char *)"\n", 1 },
80 std::string id
= program_invocation_short_name
;
85 static_segment
= "SYSLOG_IDENTIFIER=" + id
+ "\n";
86 m_msg_vec
[0].iov_base
= static_segment
.data();
87 m_msg_vec
[0].iov_len
= static_segment
.size();
90 constexpr struct iovec
*iovec() { return this->m_msg_vec
; }
91 constexpr std::size_t iovec_len()
93 return sizeof(m_msg_vec
) / sizeof(m_msg_vec
[0]);
97 struct iovec m_msg_vec
[4];
98 std::string static_segment
;
101 fmt::memory_buffer meta_buf
;
103 struct iovec
&meta_vec() { return m_msg_vec
[1]; }
104 struct iovec
&msg_vec() { return m_msg_vec
[2]; }
107 class EntryEncoder
: public EntryEncoderBase
{
109 void encode(const Entry
& e
, const SubsystemMap
*s
)
112 fmt::format_to(std::back_inserter(meta_buf
),
121 s
->get_name(e
.m_subsys
),
122 e
.m_stamp
.time_since_epoch().count().count
,
126 uint64_t msg_len
= htole64(e
.size());
127 meta_buf
.resize(meta_buf
.size() + sizeof(msg_len
));
128 *(reinterpret_cast<uint64_t*>(meta_buf
.end()) - 1) = htole64(e
.size());
130 meta_vec().iov_base
= meta_buf
.data();
131 meta_vec().iov_len
= meta_buf
.size();
133 msg_vec().iov_base
= (void *)e
.strv().data();
134 msg_vec().iov_len
= e
.size();
138 class LogEntryEncoder
: public EntryEncoderBase
{
140 void encode(const LogEntry
& le
)
143 fmt::format_to(std::back_inserter(meta_buf
),
152 clog_type_to_syslog_level(le
.prio
),
159 uint64_t msg_len
= htole64(le
.msg
.size());
160 meta_buf
.resize(meta_buf
.size() + sizeof(msg_len
));
161 *(reinterpret_cast<uint64_t*>(meta_buf
.end()) - 1) = htole64(le
.msg
.size());
163 meta_vec().iov_base
= meta_buf
.data();
164 meta_vec().iov_len
= meta_buf
.size();
166 msg_vec().iov_base
= (void *)le
.msg
.data();
167 msg_vec().iov_len
= le
.msg
.size();
171 enum class JournaldClient::MemFileMode
{
177 constexpr const char *mem_file_dir
= "/dev/shm";
179 void JournaldClient::detect_mem_file_mode()
181 int memfd
= memfd_create("ceph-journald", MFD_ALLOW_SEALING
| MFD_CLOEXEC
);
183 mem_file_mode
= MemFileMode::MEMFD_CREATE
;
187 memfd
= open(mem_file_dir
, O_TMPFILE
| O_EXCL
| O_CLOEXEC
, S_IRUSR
| S_IWUSR
);
189 mem_file_mode
= MemFileMode::OPEN_TMPFILE
;
193 mem_file_mode
= MemFileMode::OPEN_UNLINK
;
196 int JournaldClient::open_mem_file()
198 switch (mem_file_mode
) {
199 case MemFileMode::MEMFD_CREATE
:
200 return memfd_create("ceph-journald", MFD_ALLOW_SEALING
| MFD_CLOEXEC
);
201 case MemFileMode::OPEN_TMPFILE
:
202 return open(mem_file_dir
, O_TMPFILE
| O_EXCL
| O_CLOEXEC
, S_IRUSR
| S_IWUSR
);
203 case MemFileMode::OPEN_UNLINK
:
204 char mem_file_template
[] = "/dev/shm/ceph-journald-XXXXXX";
205 int fd
= mkostemp(mem_file_template
, O_CLOEXEC
);
206 unlink(mem_file_template
);
209 ceph_abort("Unexpected mem_file_mode");
212 JournaldClient::JournaldClient() :
214 (struct sockaddr
*)&sockaddr
, // msg_name
215 sizeof(sockaddr
), // msg_namelen
218 fd
= socket(AF_UNIX
, SOCK_DGRAM
| SOCK_CLOEXEC
, 0);
219 ceph_assertf(fd
> 0, "socket creation failed: %s", strerror(errno
));
221 int sendbuf
= 2 * 1024 * 1024;
222 setsockopt(fd
, SOL_SOCKET
, SO_SNDBUF
, &sendbuf
, sizeof(sendbuf
));
224 detect_mem_file_mode();
227 JournaldClient::~JournaldClient()
232 int JournaldClient::send()
234 int ret
= sendmsg(fd
, &m_msghdr
, MSG_NOSIGNAL
);
238 /* Fail silently if the journal is not available */
242 if (errno
!= EMSGSIZE
&& errno
!= ENOBUFS
) {
243 std::cerr
<< "Failed to send log to journald: " << strerror(errno
) << std::endl
;
246 /* Message doesn't fit... Let's dump the data in a memfd and
247 * just pass a file descriptor of it to the other side.
249 int buffer_fd
= open_mem_file();
251 std::cerr
<< "Failed to open buffer_fd while sending log to journald: " << strerror(errno
) << std::endl
;
255 ret
= writev(buffer_fd
, m_msghdr
.msg_iov
, m_msghdr
.msg_iovlen
);
257 std::cerr
<< "Failed to write to buffer_fd while sending log to journald: " << strerror(errno
) << std::endl
;
258 goto err_close_buffer_fd
;
261 if (mem_file_mode
== MemFileMode::MEMFD_CREATE
) {
262 ret
= fcntl(buffer_fd
, F_ADD_SEALS
, F_SEAL_SHRINK
| F_SEAL_GROW
| F_SEAL_WRITE
| F_SEAL_SEAL
);
264 std::cerr
<< "Failed to seal buffer_fd while sending log to journald: " << strerror(errno
) << std::endl
;
265 goto err_close_buffer_fd
;
269 ret
= sendmsg_fd(fd
, buffer_fd
);
271 /* Fail silently if the journal is not available */
273 goto err_close_buffer_fd
;
275 std::cerr
<< "Failed to send fd while sending log to journald: " << strerror(errno
) << std::endl
;
276 goto err_close_buffer_fd
;
286 } // namespace ceph::logging::detail
288 JournaldLogger::JournaldLogger(const SubsystemMap
*s
) :
289 m_entry_encoder(std::make_unique
<detail::EntryEncoder
>()),
292 client
.m_msghdr
.msg_iov
= m_entry_encoder
->iovec();
293 client
.m_msghdr
.msg_iovlen
= m_entry_encoder
->iovec_len();
296 JournaldLogger::~JournaldLogger() = default;
298 int JournaldLogger::log_entry(const Entry
& e
)
300 m_entry_encoder
->encode(e
, m_subs
);
301 return client
.send();
304 JournaldClusterLogger::JournaldClusterLogger() :
305 m_log_entry_encoder(std::make_unique
<detail::LogEntryEncoder
>())
307 client
.m_msghdr
.msg_iov
= m_log_entry_encoder
->iovec();
308 client
.m_msghdr
.msg_iovlen
= m_log_entry_encoder
->iovec_len();
311 JournaldClusterLogger::~JournaldClusterLogger() = default;
313 int JournaldClusterLogger::log_log_entry(const LogEntry
&le
)
315 m_log_entry_encoder
->encode(le
);
316 return client
.send();