1 /* Copyright (C) 2012 Monty Program Ab
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
15
16 #include <my_global.h>
17 #include <mysql/plugin.h>
18 #include <my_sys.h>
19 #include <string.h>
20 #include <my_thread_local.h>
21
22 #include "logger.h"
23 #include "audit_log.h"
24
25 extern char *mysql_data_home;
26
27 #ifndef FLOGGER_NO_PSI
28 #define flogger_mutex_init(A,B,C) \
29 if ((B)->thread_safe) \
30 mysql_mutex_init(A,&((B)->lock),C)
31
32 #define flogger_mutex_destroy(A) \
33 if ((A)->thread_safe) \
34 mysql_mutex_destroy(&((A)->lock))
35
36 #define flogger_mutex_lock(A) \
37 if ((A)->thread_safe) \
38 mysql_mutex_lock(&((A)->lock))
39
40 #define flogger_mutex_unlock(A) \
41 if ((A)->thread_safe) \
42 mysql_mutex_unlock(&((A)->lock))
43 #else
44 #define flogger_mutex_init(A,B,C) \
45 if ((B)->thread_safe) \
46 pthread_mutex_init(&((B)->lock.m_mutex), C)
47
48 #define flogger_mutex_destroy(A) \
49 if ((A)->thread_safe) \
50 pthread_mutex_destroy(&((A)->lock.m_mutex))
51
52 #define flogger_mutex_lock(A) \
53 if ((A)->thread_safe) \
54 pthread_mutex_lock(&((A)->lock.m_mutex))
55
56 #define flogger_mutex_unlock(A) \
57 if ((A)->thread_safe) \
58 pthread_mutex_unlock(&((A)->lock.m_mutex))
59 #endif /*!FLOGGER_NO_PSI*/
60
61 #if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI)
62 /* These belong to the service initialization */
63 static PSI_mutex_key key_LOCK_logger_service;
64 static PSI_mutex_info mutex_list[]=
65 {{ &key_LOCK_logger_service, "file_logger::lock", PSI_FLAG_GLOBAL}};
66 #else
67 #define key_LOCK_logger_service 0
68 #endif /*HAVE_PSI_INTERFACE && !FLOGGER_NO_PSI*/
69
70 typedef struct logger_handle_st {
71 File file;
72 char path[FN_REFLEN];
73 unsigned long long size_limit;
74 unsigned int rotations;
75 size_t path_len;
76 mysql_mutex_t lock;
77 int thread_safe;
78 } LSFS;
79
80
81 #define LOG_FLAGS (O_APPEND | O_CREAT | O_WRONLY)
82
n_dig(unsigned int i)83 static unsigned int n_dig(unsigned int i)
84 {
85 return (i == 0) ? 0 : ((i < 10) ? 1 : ((i < 100) ? 2 : 3));
86 }
87
logger_open(const char * path,unsigned long long size_limit,unsigned int rotations,int thread_safe,logger_prolog_func_t header)88 LOGGER_HANDLE *logger_open(const char *path,
89 unsigned long long size_limit,
90 unsigned int rotations,
91 int thread_safe,
92 logger_prolog_func_t header)
93 {
94 LOGGER_HANDLE new_log, *l_perm;
95 MY_STAT stat_arg;
96 char buf[128];
97 size_t len;
98
99 /*
100 I don't think we ever need more rotations,
101 but if it's so, the rotation procedure should be adapted to it.
102 */
103 if (rotations > 999)
104 return 0;
105
106 new_log.rotations= rotations;
107 new_log.size_limit= size_limit;
108 new_log.path_len= strlen(fn_format(new_log.path, path,
109 mysql_data_home, "", MY_UNPACK_FILENAME));
110 new_log.thread_safe= thread_safe;
111
112 if (new_log.path_len+n_dig(rotations)+1 > FN_REFLEN)
113 {
114 errno= ENAMETOOLONG;
115 /* File path too long */
116 return 0;
117 }
118
119 if ((new_log.file= my_open(new_log.path, LOG_FLAGS, 0666)) < 0)
120 {
121 errno= my_errno();
122 /* Check errno for the cause */
123 return 0;
124 }
125
126 if (my_fstat(new_log.file, &stat_arg, MYF(0)))
127 {
128 errno= my_errno();
129 my_close(new_log.file, MYF(0));
130 new_log.file= -1;
131 return 0;
132 }
133
134 if (!(l_perm= (LOGGER_HANDLE *) my_malloc(key_memory_audit_log_logger_handle,
135 sizeof(LOGGER_HANDLE), MYF(0))))
136 {
137 my_close(new_log.file, MYF(0));
138 new_log.file= -1;
139 return 0; /* End of memory */
140 }
141 *l_perm= new_log;
142
143 flogger_mutex_init(key_LOCK_logger_service, l_perm,
144 MY_MUTEX_INIT_FAST);
145
146 len= header(&stat_arg, buf, sizeof(buf));
147 my_write(l_perm->file, (uchar *)buf, len, MYF(0));
148
149 return l_perm;
150 }
151
logger_close(LOGGER_HANDLE * log,logger_epilog_func_t footer)152 int logger_close(LOGGER_HANDLE *log, logger_epilog_func_t footer)
153 {
154 int result;
155 File file= log->file;
156 char buf[128];
157 size_t len;
158
159 len= footer(buf, sizeof(buf));
160 my_write(file, (uchar *)buf, len, MYF(0));
161
162 flogger_mutex_destroy(log);
163 my_free(log);
164 if ((result= my_close(file, MYF(0))))
165 errno= my_errno();
166 return result;
167 }
168
169
logger_reopen(LOGGER_HANDLE * log,logger_prolog_func_t header,logger_epilog_func_t footer)170 int logger_reopen(LOGGER_HANDLE *log, logger_prolog_func_t header,
171 logger_epilog_func_t footer)
172 {
173 int result= 0;
174 MY_STAT stat_arg;
175 char buf[128];
176 size_t len;
177
178 flogger_mutex_lock(log);
179
180 len= footer(buf, sizeof(buf));
181 my_write(log->file, (uchar *)buf, len, MYF(0));
182
183 if ((result= my_close(log->file, MYF(0))))
184 {
185 errno= my_errno();
186 goto error;
187 }
188
189 if ((log->file= my_open(log->path, LOG_FLAGS, MYF(0))) < 0)
190 {
191 errno= my_errno();
192 result= 1;
193 goto error;
194 }
195
196 if ((result= my_fstat(log->file, &stat_arg, MYF(0))))
197 {
198 errno= my_errno();
199 goto error;
200 }
201
202 len= header(&stat_arg, buf, sizeof(buf));
203 my_write(log->file, (uchar *)buf, len, MYF(0));
204
205 error:
206 flogger_mutex_unlock(log);
207
208 return result;
209 }
210
211
logname(LOGGER_HANDLE * log,char * buf,size_t buf_len,unsigned int n_log)212 static char *logname(LOGGER_HANDLE *log, char *buf, size_t buf_len,
213 unsigned int n_log)
214 {
215 snprintf(buf+log->path_len, buf_len, ".%0*u", n_dig(log->rotations),
216 n_log);
217 return buf;
218 }
219
220
do_rotate(LOGGER_HANDLE * log)221 static int do_rotate(LOGGER_HANDLE *log)
222 {
223 char namebuf[FN_REFLEN];
224 int result;
225 unsigned int i;
226 char *buf_old, *buf_new, *tmp;
227
228 if (log->rotations == 0)
229 return 0;
230
231 memcpy(namebuf, log->path, log->path_len);
232
233 buf_new= logname(log, namebuf, sizeof(namebuf), log->rotations);
234 buf_old= log->path;
235 for (i=log->rotations-1; i>0; i--)
236 {
237 logname(log, buf_old, FN_REFLEN, i);
238 if (!access(buf_old, F_OK) &&
239 (result= my_rename(buf_old, buf_new, MYF(0))))
240 goto exit;
241 tmp= buf_old;
242 buf_old= buf_new;
243 buf_new= tmp;
244 }
245 if ((result= my_close(log->file, MYF(0))))
246 goto exit;
247 namebuf[log->path_len]= 0;
248 result= my_rename(namebuf, logname(log, log->path, FN_REFLEN, 1), MYF(0));
249 log->file= my_open(namebuf, LOG_FLAGS, MYF(0));
250 exit:
251 errno= my_errno();
252 return log->file < 0 || result;
253 }
254
255
logger_write(LOGGER_HANDLE * log,const char * buffer,size_t size,log_record_state_t state)256 int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size,
257 log_record_state_t state)
258 {
259 int result;
260 my_off_t filesize;
261
262 flogger_mutex_lock(log);
263
264 result= my_write(log->file, (uchar *) buffer, size, MYF(0));
265
266 if (state == LOG_RECORD_COMPLETE && log->rotations > 0)
267 {
268 if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 ||
269 ((unsigned long long)filesize >= log->size_limit &&
270 do_rotate(log)))
271 {
272 result= -1;
273 errno= my_errno();
274 goto exit; /* Log rotation needed but failed */
275 }
276 }
277
278 exit:
279 flogger_mutex_unlock(log);
280 return result;
281 }
282
283
logger_init_mutexes()284 void logger_init_mutexes()
285 {
286 #if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) && !defined(FLOGGER_NO_THREADSAFE)
287 mysql_mutex_register(AUDIT_LOG_PSI_CATEGORY,
288 mutex_list, array_elements(mutex_list));
289 #endif /*HAVE_PSI_INTERFACE && !FLOGGER_NO_PSI*/
290 }
291
logger_sync(LOGGER_HANDLE * log)292 int logger_sync(LOGGER_HANDLE *log)
293 {
294 return my_sync(log->file, MYF(0));
295 }
296
logger_set_size_limit(LOGGER_HANDLE * log,unsigned long long size_limit)297 void logger_set_size_limit(LOGGER_HANDLE *log, unsigned long long size_limit)
298 {
299 log->size_limit= size_limit;
300 }
301
logger_set_rotations(LOGGER_HANDLE * log,unsigned int rotations)302 void logger_set_rotations(LOGGER_HANDLE *log, unsigned int rotations)
303 {
304 log->rotations= rotations;
305 }
306