35c81aaa5801e9fbc9abb06c7dc470bd968a7b20
[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 }
52
53 static void logfile_set_should_close(struct log_file *l_file, int on_off)
54 {
55         assert(l_file);
56         if (on_off)
57                 assert(l_file->fd >= 0);
58
59         l_file->should_close = !!on_off;
60 }
61
62 void logfile_set_fd(struct log_file *l_file, int fd, int should_close)
63 {
64         assert(l_file);
65
66         l_file->fd = fd;
67         l_file->isatty = isatty(fd);
68         logfile_set_should_close(l_file, should_close);
69 }
70
71 int logfile_set_path(struct log_file *l_file, const char *path)
72 {
73         assert(l_file);
74
75         char *p = strdup(path);
76         if (!p)
77                 return -errno;
78
79         if (l_file->path)
80                 free(l_file->path);
81         l_file->path = p;
82
83         return 0;
84 }
85
86 static void logfile_close_if_needed(struct log_file *l_file)
87 {
88         assert(l_file);
89
90         if (l_file->fd >= 0 && l_file->should_close) {
91                 close(l_file->fd);
92                 logfile_set_should_close(l_file, 0);
93                 l_file->fd = -1;
94
95         }
96 }
97
98 void logfile_free(struct log_file *l_file)
99 {
100         assert(l_file);
101
102         if (l_file->path) {
103                 free(l_file->path);
104                 l_file->path = NULL;
105         }
106         logfile_close_if_needed(l_file);
107 }
108
109 static int logfile_update_fsize(struct log_file *l_file)
110 {
111         assert(l_file);
112         assert(l_file->fd >= 0);
113
114         struct stat st;
115
116         if (fstat(l_file->fd, &st) < 0)
117                 return -errno;
118
119         l_file->size = st.st_size;
120         return 0;
121 }
122
123 static int logfile_open_internal(struct log_file *l_file)
124 {
125         assert(l_file);
126         assert(l_file->path);
127
128         logfile_close_if_needed(l_file);
129
130         int fd = open(l_file->path, O_CREAT | O_WRONLY | O_APPEND, DEFAULT_LOGFILE_PERM);
131         if (fd < 0)
132                 return -errno;
133
134         logfile_set_fd(l_file, fd, 1);
135         logfile_update_fsize(l_file);
136
137         return 0;
138 }
139
140 void logfile_move(struct log_file *to, struct log_file *from)
141 {
142         *to = *from;
143
144         from->path = NULL;
145         from->fd = -1;
146 }
147
148 /**
149  * @brief Open a log file
150  * @details Open a log file into the passed structure.
151  * @param[in] l_file The file structure to contain the opened file descriptor
152  * @remarks Creates the file if it does not exist, else appends to it.
153  */
154 int logfile_open(struct log_file *l_file)
155 {
156         assert(l_file);
157
158         int r = logfile_open_internal(l_file);
159         if (r < 0)
160                 return r;
161
162         r = logfile_rotate_needed(l_file);
163         if (r > 0)
164                 logfile_do_rotate(l_file);
165
166         return r;
167 }
168
169 /**
170  * @brief Checks if log file should be rotated
171  * @param[in] l_file The file structure to contain the currently opened file
172  * @remarks Updates l_file->size with on-disk file size
173  * @returns 1 if rotate is needed, 0 if not, -errno on error
174  */
175 int logfile_rotate_needed(struct log_file *l_file)
176 {
177         assert(l_file);
178
179         if (l_file->path == NULL || l_file->rotate_size_kbytes <= 0 || l_file->max_rotated <= 0)
180                 return 0;
181
182         logfile_update_fsize(l_file);
183         return BtoKiB(l_file->size) > l_file->rotate_size_kbytes;
184 }
185
186 /**
187  * @brief Rotate log files
188  * @details Opens a new file and moves existing files further back
189  * @param[in] file The file structure to contain the currently opened file
190  */
191 void logfile_do_rotate(struct log_file *file)
192 {
193         assert(file);
194         assert(file->path);
195
196         int i;
197         char path0[PATH_MAX];
198         char path1[PATH_MAX];
199
200         for (i = file->max_rotated ; i > 0 ; i--) {
201                 snprintf(path1, PATH_MAX, "%s.%d", file->path, i);
202                 if (i - 1 == 0)
203                         snprintf(path0, PATH_MAX, "%s",  file->path);
204                 else
205                         snprintf(path0, PATH_MAX, "%s.%d", file->path, i - 1);
206                 if (rename(path0, path1) < 0 && errno != ENOENT)
207                         ERR("while rotating log file: %s", file->path);
208         }
209
210         /* open log file again */
211         logfile_open_internal(file);
212 }
213
214 static void logfile_add_timestamp(struct log_file *file, struct timespec ts)
215 {
216         file->prev_sec = ts.tv_sec;
217         file->prev_nsec = ts.tv_nsec;
218 }
219
220 /**
221  * @brief Write with rotation
222  * @details Writes the entry to given file, automatically handling file rotation
223  * @param[in] e The entry to write
224  * @param[in] file The file to write to
225  * @returns 0 if log was successfully written, else 1
226  */
227 int logfile_write_with_rotation(const dlogutil_entry_s *e, struct log_file *file, dlogutil_sorting_order_e sort_by)
228 {
229         if (file->colors_auto)
230                 file->format.color = file->isatty;
231
232         int written_bytes = 0;
233
234         struct timespec ts;
235         if (dlogutil_entry_get_timestamp(e, sort_by, &ts) != TIZEN_ERROR_NONE)
236                 return 1;
237
238         if (ts.tv_sec < file->prev_sec || (ts.tv_sec == file->prev_sec && ts.tv_nsec < file->prev_nsec)) {
239                 struct dlogutil_entry_with_msg msg;
240                 memcpy(&msg, e, sizeof *e);
241                 const char *tag;
242                 if (dlogutil_entry_get_tag(e, &tag) != TIZEN_ERROR_NONE)
243                         return 1;
244                 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.");
245
246                 if (r < 0) {
247                         ERR("unable to format out-of-order message %m");
248                         written_bytes = 0;
249                 } else {
250                         msg.header.len = r + 1 + sizeof *e;
251
252                         written_bytes += log_print_log_line(file->format, file->fd, &msg.header);
253
254                         if (written_bytes < 0) {
255                                 ERR("unable to write out-of-order message %m");
256                                 written_bytes = 0;
257                         }
258                 }
259         }
260
261         written_bytes += log_print_log_line(file->format, file->fd, e);
262         if (written_bytes <= 0)
263                 return 1;
264         file->size += written_bytes;
265
266         logfile_add_timestamp(file, ts);
267
268         if (logfile_rotate_needed(file))
269                 logfile_do_rotate(file);
270         return 0;
271 }
272
273 /**
274  * @}
275  */