Replace () with (void) in function prototypes
[platform/core/system/dlog.git] / src / libdlog / log_pipe.c
1 /*  MIT License
2  *
3  * Copyright (c) 2015-2020 Samsung Electronics Co., Ltd
4  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
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
21  * THE SOFTWARE. */
22
23 // C
24 #include <assert.h>
25 #include <stdio.h>
26
27 // POSIX
28 #include <signal.h>
29 #include <linux/limits.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32
33 // DLog
34 #include <logcommon.h>
35 #include <libdlog.h>
36 #include <logconfig.h>
37 #include <logpipe.h>
38 #include <logprint.h>
39
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;
43
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);
45
46 static pthread_rwlock_t log_pipe_lock = PTHREAD_RWLOCK_INITIALIZER;
47
48 /**
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
55  */
56 static int __reconnect_pipe(log_id_t log_id)
57 {
58         int new_fd, old_fd, ret = 0;
59
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
63
64         new_fd = connect_pipe(log_pipe_path[log_id], &DLOG_CTRL_REQ_PIPE, wait_pipe_ms);
65         if (new_fd < 0) {
66                 /* if connnecting fails consequently assume
67                  * dlog_logger is broken and reduce the timeout to
68                  * allow dlog clients to run normally */
69                 wait_pipe_ms /= 2;
70                 if (wait_pipe_ms < MIN_WAIT_PIPE_MS)
71                         wait_pipe_ms = MIN_WAIT_PIPE_MS;
72
73                 goto finish;
74         }
75
76         old_fd = pipe_fd[log_id];
77         if (old_fd < 0)
78                 pipe_fd[log_id] = new_fd;
79         else {
80                 int r;
81 #ifdef __PARANOIC
82                 sigset_t all_set, current_set;
83                 sigfillset(&all_set);
84                 sigprocmask(SIG_BLOCK, &all_set, &current_set);
85 #endif
86                 r = dup2(new_fd, old_fd);
87                 assert(r >= 0);
88 #ifdef __PARANOIC
89                 sigprocmask(SIG_SETMASK, &current_set, NULL);
90 #endif
91                 close(new_fd);
92         }
93
94         ret = (pipe_fd[log_id] >= 0);
95
96 finish:
97         pthread_rwlock_unlock(&log_pipe_lock);
98
99         const int rdlock = pthread_rwlock_rdlock(&log_pipe_lock);
100         assert(!rdlock); // we just unlocked so this cannot fail
101
102         return ret;
103 }
104
105 /* @pre `log_pipe_lock` taken as read-only
106  * @post `log_pipe_lock` taken as read-only
107  */
108 static int _write_to_log_pipe_critical_section(log_id_t log_id, char *buf)
109 {
110         if (pipe_fd[log_id] < 0 && !__reconnect_pipe(log_id))
111                 return -1;
112
113         struct pipe_logger_entry *const ple = (struct pipe_logger_entry *)buf;
114
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).
119                  *
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.
123                  */
124                 int was_ebadf = (errno == EBADF);
125                 if (!__reconnect_pipe(log_id))
126                         return -1;
127                 ret = write(pipe_fd[log_id], buf, ple->len);
128                 if (was_ebadf) {
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);
132                 }
133         }
134         return ret;
135 }
136
137 /**
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
145  */
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)
147 {
148         ssize_t ret = 0;
149         char buf[LOG_MAX_PAYLOAD_SIZE + sizeof(struct pipe_logger_entry)];
150
151         if (!tag)
152                 tag = "";
153
154         if (log_id >= LOG_ID_MAX ||
155             log_id < 0 ||
156             prio < DLOG_VERBOSE ||
157             prio >= DLOG_PRIO_MAX ||
158             !msg ||
159             1 /* priority */ + strlen(tag) + 1 /* NULL delimiter */ + strlen(msg) + 1 /* NULL terminator */ > LOG_MAX_PAYLOAD_SIZE)
160                 return DLOG_ERROR_INVALID_PARAMETER;
161
162         create_pipe_message(buf, prio, tag, msg);
163         set_pipe_message_sent_timestamp((struct pipe_logger_entry *) buf, tp_mono, NULL);
164
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);
168         }
169
170         return ret;
171 }
172
173 static void __destroy_pipe(void)
174 {
175         /* No need for an explicit lock here; this should only be called
176          * by the library destructor which has its own lock */
177
178         for (size_t i = 0; i < NELEMS(pipe_fd); ++i)
179                 if (pipe_fd[i] >= 0)
180                         close(pipe_fd[i]);
181
182         wait_pipe_ms = DEFAULT_WAIT_PIPE_MS;
183 }
184
185 /**
186  * @brief Initialize the backend
187  * @details Prepares the backend for proper and fruitful work
188  */
189 void __dlog_init_pipe(const struct log_config *conf)
190 {
191         for (size_t i = 0; i < NELEMS(pipe_fd); ++i)
192                 pipe_fd[i] = -1;
193
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);
198                 if (!conf_val) {
199                         syslog_critical_failure("DLOG CRITICAL FAILURE: DLog config lacks the \"%s\" entry!", conf_key);
200                         return;
201                 }
202
203                 if (conf_val[0] != '/') {
204                         syslog_critical_failure("DLOG CRITICAL FAILURE: path \"%s\" from \"%s\" is not absolute!\n", conf_val, conf_key);
205                         return;
206                 }
207
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);
212                         return;
213                 }
214
215                 snprintf(log_pipe_path[i], PATH_MAX, "%s", conf_val);
216         }
217         write_to_log = __write_to_log_pipe;
218         destroy_backend = __destroy_pipe;
219 }