3 * Copyright (c) 2015-2020 Samsung Electronics Co., Ltd
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is furnished
10 * to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 #include <linux/limits.h>
30 #include <sys/socket.h>
34 #include <logcommon.h>
36 #include <logconfig.h>
40 static int pipe_fd[LOG_ID_MAX];
41 static char log_pipe_path[LOG_ID_MAX][PATH_MAX];
42 static int wait_pipe_ms = DEFAULT_WAIT_PIPE_MS;
44 static int __write_to_log_pipe(log_id_t log_id, log_priority prio, const char *tag, const char *msg, struct timespec *tp_mono);
46 static pthread_rwlock_t log_pipe_lock = PTHREAD_RWLOCK_INITIALIZER;
49 * @brief Reconnect pipe
50 * @details Gets another pipe for a buffer
51 * @param[in] log_id The buffer whose pipe to replace
52 * @pre `log_pipe_lock` taken as read-only
53 * @post `log_pipe_lock` taken as read-only
54 * @return Boolean whether pipe successfully reconnected
56 static int __reconnect_pipe(log_id_t log_id)
58 int new_fd, old_fd, ret = 0;
60 pthread_rwlock_unlock(&log_pipe_lock);
61 const int wrlock = pthread_rwlock_wrlock(&log_pipe_lock);
62 assert(!wrlock); // we are never supposed to have a read lock at this point so wrlock() can't fail
64 new_fd = connect_pipe(log_pipe_path[log_id], &DLOG_CTRL_REQ_PIPE, wait_pipe_ms);
66 /* if connnecting fails consequently assume
67 * dlog_logger is broken and reduce the timeout to
68 * allow dlog clients to run normally */
70 if (wait_pipe_ms < MIN_WAIT_PIPE_MS)
71 wait_pipe_ms = MIN_WAIT_PIPE_MS;
76 old_fd = pipe_fd[log_id];
78 pipe_fd[log_id] = new_fd;
82 sigset_t all_set, current_set;
84 sigprocmask(SIG_BLOCK, &all_set, ¤t_set);
86 r = dup2(new_fd, old_fd);
89 sigprocmask(SIG_SETMASK, ¤t_set, NULL);
94 ret = (pipe_fd[log_id] >= 0);
97 pthread_rwlock_unlock(&log_pipe_lock);
99 const int rdlock = pthread_rwlock_rdlock(&log_pipe_lock);
100 assert(!rdlock); // we just unlocked so this cannot fail
105 /* @pre `log_pipe_lock` taken as read-only
106 * @post `log_pipe_lock` taken as read-only
108 static int _write_to_log_pipe_critical_section(log_id_t log_id, char *buf)
110 if (pipe_fd[log_id] < 0 && !__reconnect_pipe(log_id))
113 struct pipe_logger_entry *const ple = (struct pipe_logger_entry *)buf;
115 int ret = write(pipe_fd[log_id], buf, ple->len);
116 if (ret < 0 && (errno == EPIPE || errno == EBADF)) {
117 /* Handling EPIPE is obvious, it means other peer disappeared and
118 * we need to reconnect (eg. dlog_logger restarted).
120 * EBADF-handling is a hack - it's needed due to Tizen's launchpad
121 * (application preloader) closing all file descriptors explicitly
122 * (including libdlog's file descriptors) but still trying to log.
124 int was_ebadf = (errno == EBADF);
125 if (!__reconnect_pipe(log_id))
127 ret = write(pipe_fd[log_id], buf, ple->len);
129 create_pipe_message(buf, DLOG_FATAL, "LIBDLOG", "libdlog's internal state has been destroyed! The user application closed libdlog's file descriptor.");
130 set_pipe_message_sent_timestamp((struct pipe_logger_entry *) buf, NULL, NULL);
131 ret = write(pipe_fd[log_id], buf, ple->len);
138 * @brief Write to log
139 * @details Writes a log
140 * @param[in] log_id ID of the buffer to log to. Belongs to (LOG_ID_INVALID, LOG_ID_MAX) non-inclusive
141 * @param[in] prio Priority of the message.
142 * @param[in] tag The message tag, identifies the sender.
143 * @param[in] msg The contents of the message.
144 * @return Number of bytes written, or negative errno
146 static int __write_to_log_pipe(log_id_t log_id, log_priority prio, const char *tag, const char *msg, struct timespec *tp_mono)
149 char buf[LOG_MAX_PAYLOAD_SIZE + sizeof(struct pipe_logger_entry)];
154 if (log_id >= LOG_ID_MAX ||
156 prio < DLOG_VERBOSE ||
157 prio >= DLOG_PRIO_MAX ||
159 1 /* priority */ + strlen(tag) + 1 /* NULL delimiter */ + strlen(msg) + 1 /* NULL terminator */ > LOG_MAX_PAYLOAD_SIZE)
160 return DLOG_ERROR_INVALID_PARAMETER;
162 create_pipe_message(buf, prio, tag, msg);
163 set_pipe_message_sent_timestamp((struct pipe_logger_entry *) buf, tp_mono, NULL);
165 if (!pthread_rwlock_rdlock(&log_pipe_lock)) {
166 ret = _write_to_log_pipe_critical_section(log_id, buf);
167 pthread_rwlock_unlock(&log_pipe_lock);
173 static void __destroy_pipe(void)
175 /* No need for an explicit lock here; this should only be called
176 * by the library destructor which has its own lock */
178 for (size_t i = 0; i < NELEMS(pipe_fd); ++i)
182 wait_pipe_ms = DEFAULT_WAIT_PIPE_MS;
186 * @brief Initialize the backend
187 * @details Prepares the backend for proper and fruitful work
189 void __dlog_init_pipe(const struct log_config *conf)
191 for (size_t i = 0; i < NELEMS(pipe_fd); ++i)
194 for (int i = 0; i < LOG_ID_MAX; ++i) {
195 char conf_key[MAX_CONF_KEY_LEN];
196 snprintf(conf_key, sizeof(conf_key), "%s_write_sock", log_name_by_id(i));
197 const char * conf_val = log_config_get(conf, conf_key);
199 syslog_critical_failure("DLOG CRITICAL FAILURE: DLog config lacks the \"%s\" entry!", conf_key);
203 if (conf_val[0] != '/') {
204 syslog_critical_failure("DLOG CRITICAL FAILURE: path \"%s\" from \"%s\" is not absolute!\n", conf_val, conf_key);
208 const size_t pathlen = strlen(conf_val);
209 static const size_t MAX_PIPE_PATH = sizeof((struct sockaddr_un *)NULL)->sun_path;
210 if (strlen(conf_val) >= MAX_PIPE_PATH) {
211 syslog_critical_failure("DLOG CRITICAL FAILURE: path \"%s\" from \"%s\" is too long! %zu >= %zu", conf_val, conf_key, pathlen, MAX_PIPE_PATH);
215 snprintf(log_pipe_path[i], PATH_MAX, "%s", conf_val);
217 write_to_log = __write_to_log_pipe;
218 destroy_backend = __destroy_pipe;