Introduce log write buffering
[platform/core/system/dlog.git] / src / shared / log_file.c
1 /*
2  * DLOG
3  * Copyright (c) 2005-2008, The Android Open Source Project
4  * Copyright (c) 2012-2015 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <log_file.h>
20 #include <queued_entry_timestamp.h>
21
22 #include <fcntl.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <limits.h>
28 #include <assert.h>
29
30 #define DEFAULT_LOGFILE_PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
31
32 /**
33  * @addtogroup SHARED_FUNCTIONS
34  * @{
35  */
36
37 void logfile_init(struct log_file *l_file)
38 {
39         assert(l_file);
40
41         memset(l_file, 0, sizeof(struct log_file));
42
43         l_file->fd = -1;
44         l_file->rotate_size_kbytes = DEFAULT_ROTATE_SIZE_KB;
45         l_file->max_rotated = DEFAULT_ROTATE_NUM_FILES;
46         l_file->format.format = FORMAT_BRIEF;
47         l_file->isatty = false;
48         l_file->colors_auto = true;
49         l_file->prev_sec = INT_MIN;
50         l_file->prev_nsec = INT_MIN;
51         l_file->buffer = (struct log_write_buffer) { };
52 }
53
54 bool logfile_init_buffer(struct log_file *l_file, size_t buf_size)
55 {
56         assert(l_file);
57         assert(l_file->buffer.size == 0);
58
59         if (buf_size <= 0)
60                 return true;
61
62         l_file->buffer.data = malloc(buf_size);
63         if (!l_file->buffer.data) {
64                 l_file->buffer = (struct log_write_buffer) { };
65                 return false;
66         }
67
68         l_file->buffer.position = 0;
69         l_file->buffer.size = buf_size;
70
71         return true;
72 }
73
74 static void logfile_set_should_close(struct log_file *l_file, int on_off)
75 {
76         assert(l_file);
77         if (on_off)
78                 assert(l_file->fd >= 0);
79
80         l_file->should_close = !!on_off;
81 }
82
83 void logfile_set_fd(struct log_file *l_file, int fd, int should_close)
84 {
85         assert(l_file);
86
87         l_file->fd = fd;
88         l_file->isatty = isatty(fd);
89         logfile_set_should_close(l_file, should_close);
90 }
91
92 int logfile_set_path(struct log_file *l_file, const char *path)
93 {
94         assert(l_file);
95
96         char *p = strdup(path);
97         if (!p)
98                 return -errno;
99
100         if (l_file->path)
101                 free(l_file->path);
102         l_file->path = p;
103
104         return 0;
105 }
106
107 static void logfile_close_if_needed(struct log_file *l_file)
108 {
109         assert(l_file);
110
111         if (l_file->fd >= 0 && l_file->should_close) {
112                 close(l_file->fd);
113                 logfile_set_should_close(l_file, 0);
114                 l_file->fd = -1;
115
116         }
117 }
118
119 void logfile_free(struct log_file *l_file)
120 {
121         assert(l_file);
122
123         if (l_file->path) {
124                 free(l_file->path);
125                 l_file->path = NULL;
126         }
127         if (l_file->buffer.data) {
128                 logfile_flush(l_file);
129                 free(l_file->buffer.data);
130                 l_file->buffer.data = NULL;
131                 l_file->buffer.position = 0;
132                 l_file->buffer.size = 0;
133         }
134         logfile_close_if_needed(l_file);
135 }
136
137 static int logfile_update_fsize(struct log_file *l_file)
138 {
139         assert(l_file);
140         assert(l_file->fd >= 0);
141
142         struct stat st;
143
144         if (fstat(l_file->fd, &st) < 0)
145                 return -errno;
146
147         l_file->size = st.st_size;
148         return 0;
149 }
150
151 static int logfile_open_internal(struct log_file *l_file)
152 {
153         assert(l_file);
154         assert(l_file->path);
155
156         logfile_close_if_needed(l_file);
157
158         int fd = open(l_file->path, O_CREAT | O_WRONLY | O_APPEND, DEFAULT_LOGFILE_PERM);
159         if (fd < 0)
160                 return -errno;
161
162         logfile_set_fd(l_file, fd, 1);
163         logfile_update_fsize(l_file);
164
165         return 0;
166 }
167
168 void logfile_move(struct log_file *to, struct log_file *from)
169 {
170         *to = *from;
171
172         from->path = NULL;
173         from->fd = -1;
174         from->buffer = (struct log_write_buffer) { };
175 }
176
177 /**
178  * @brief Open a log file
179  * @details Open a log file into the passed structure.
180  * @param[in] l_file The file structure to contain the opened file descriptor
181  * @remarks Creates the file if it does not exist, else appends to it.
182  */
183 int logfile_open(struct log_file *l_file)
184 {
185         assert(l_file);
186
187         int r = logfile_open_internal(l_file);
188         if (r < 0)
189                 return r;
190
191         r = logfile_rotate_needed(l_file);
192         if (r > 0)
193                 logfile_do_rotate(l_file);
194
195         return r;
196 }
197
198 /**
199  * @brief Checks if log file should be rotated
200  * @param[in] l_file The file structure to contain the currently opened file
201  * @remarks Updates l_file->size with on-disk file size
202  * @returns 1 if rotate is needed, 0 if not, -errno on error
203  */
204 int logfile_rotate_needed(struct log_file *l_file)
205 {
206         assert(l_file);
207
208         if (l_file->path == NULL || l_file->rotate_size_kbytes <= 0 || l_file->max_rotated <= 0)
209                 return 0;
210
211         logfile_update_fsize(l_file);
212
213         size_t size = l_file->size;
214         if (l_file->buffer.data)
215                 size += l_file->buffer.position;
216
217         return BtoKiB(size) > l_file->rotate_size_kbytes;
218 }
219
220 /**
221  * @brief Rotate log files
222  * @details Opens a new file and moves existing files further back
223  * @param[in] file The file structure to contain the currently opened file
224  */
225 void logfile_do_rotate(struct log_file *file)
226 {
227         assert(file);
228         assert(file->path);
229
230         int i;
231         char path0[PATH_MAX];
232         char path1[PATH_MAX];
233
234         if ((errno = -logfile_flush(file)) > 0)
235                 ERR("while flushing log file");
236
237         for (i = file->max_rotated ; i > 0 ; i--) {
238                 snprintf(path1, PATH_MAX, "%s.%d", file->path, i);
239                 if (i - 1 == 0)
240                         snprintf(path0, PATH_MAX, "%s",  file->path);
241                 else
242                         snprintf(path0, PATH_MAX, "%s.%d", file->path, i - 1);
243                 if (rename(path0, path1) < 0 && errno != ENOENT)
244                         ERR("while rotating log file: %s", file->path);
245         }
246
247         /* open log file again */
248         logfile_open_internal(file);
249 }
250
251 static void logfile_add_timestamp(struct log_file *file, struct timespec ts)
252 {
253         file->prev_sec = ts.tv_sec;
254         file->prev_nsec = ts.tv_nsec;
255 }
256
257 int logfile_flush(struct log_file *file)
258 {
259         assert(file);
260
261         if (!file->buffer.data)
262                 return 0;
263
264         int written = write(file->fd, file->buffer.data, file->buffer.position);
265         if (write > 0)
266                 file->buffer.position = 0; // TODO: data loss possibility here
267         return written;
268 }
269
270 /**
271  * @brief Write with rotation
272  * @details Writes the entry to given file, automatically handling file rotation
273  * @param[in] e The entry to write
274  * @param[in] file The file to write to
275  * @returns 0 if log was successfully written, else 1
276  */
277 int logfile_write_with_rotation(const dlogutil_entry_s *e, struct log_file *file, dlogutil_sorting_order_e sort_by)
278 {
279         if (file->colors_auto)
280                 file->format.color = file->isatty;
281
282         int written_bytes = 0;
283
284         struct timespec ts;
285         if (dlogutil_entry_get_timestamp(e, sort_by, &ts) != TIZEN_ERROR_NONE)
286                 return 1;
287
288         if (ts.tv_sec < file->prev_sec || (ts.tv_sec == file->prev_sec && ts.tv_nsec < file->prev_nsec)) {
289                 struct dlogutil_entry_with_msg msg;
290                 memcpy(&msg, e, sizeof *e);
291                 const char *tag;
292                 if (dlogutil_entry_get_tag(e, &tag) != TIZEN_ERROR_NONE)
293                         return 1;
294                 int r = snprintf(msg.msg, sizeof msg.msg, "%s%c%s", tag, '\0', "INFO: Following log entry could not be sorted and is out of order.");
295
296                 if (r < 0) {
297                         ERR("unable to format out-of-order message %m");
298                         written_bytes = 0;
299                 } else {
300                         msg.header.len = r + 1 + sizeof *e;
301
302                         written_bytes += log_print_log_line(file->format, file->fd, &msg.header, &file->buffer);
303
304                         if (written_bytes < 0) {
305                                 ERR("unable to write out-of-order message %m");
306                                 written_bytes = 0;
307                         }
308                 }
309         }
310
311         written_bytes += log_print_log_line(file->format, file->fd, e, &file->buffer);
312         if (written_bytes <= 0)
313                 return 1;
314         file->size += written_bytes;
315
316         logfile_add_timestamp(file, ts);
317
318         if (logfile_rotate_needed(file))
319                 logfile_do_rotate(file);
320         return 0;
321 }
322
323 /**
324  * @}
325  */