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 <my_pthread.h>
20 #include <string.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(sizeof(LOGGER_HANDLE), MYF(0))))
135 {
136 my_close(new_log.file, MYF(0));
137 new_log.file= -1;
138 return 0; /* End of memory */
139 }
140 *l_perm= new_log;
141
142 flogger_mutex_init(key_LOCK_logger_service, l_perm,
143 MY_MUTEX_INIT_FAST);
144
145 len= header(&stat_arg, buf, sizeof(buf));
146 my_write(l_perm->file, (uchar *)buf, len, MYF(0));
147
148 return l_perm;
149 }
150
logger_close(LOGGER_HANDLE * log,logger_epilog_func_t footer)151 int logger_close(LOGGER_HANDLE *log, logger_epilog_func_t footer)
152 {
153 int result;
154 File file= log->file;
155 char buf[128];
156 size_t len;
157
158 len= footer(buf, sizeof(buf));
159 my_write(file, (uchar *)buf, len, MYF(0));
160
161 flogger_mutex_destroy(log);
162 my_free(log);
163 if ((result= my_close(file, MYF(0))))
164 errno= my_errno;
165 return result;
166 }
167
168
logger_reopen(LOGGER_HANDLE * log,logger_prolog_func_t header,logger_epilog_func_t footer)169 int logger_reopen(LOGGER_HANDLE *log, logger_prolog_func_t header,
170 logger_epilog_func_t footer)
171 {
172 int result= 0;
173 MY_STAT stat_arg;
174 char buf[128];
175 size_t len;
176
177 flogger_mutex_lock(log);
178
179 len= footer(buf, sizeof(buf));
180 my_write(log->file, (uchar *)buf, len, MYF(0));
181
182 if ((result= my_close(log->file, MYF(0))))
183 {
184 errno= my_errno;
185 goto error;
186 }
187
188 if ((log->file= my_open(log->path, LOG_FLAGS, MYF(0))) < 0)
189 {
190 errno= my_errno;
191 result= 1;
192 goto error;
193 }
194
195 if ((result= my_fstat(log->file, &stat_arg, MYF(0))))
196 {
197 errno= my_errno;
198 goto error;
199 }
200
201 len= header(&stat_arg, buf, sizeof(buf));
202 my_write(log->file, (uchar *)buf, len, MYF(0));
203
204 error:
205 flogger_mutex_unlock(log);
206
207 return result;
208 }
209
210
logname(LOGGER_HANDLE * log,char * buf,unsigned int n_log)211 static char *logname(LOGGER_HANDLE *log, char *buf, unsigned int n_log)
212 {
213 sprintf(buf+log->path_len, ".%0*u", n_dig(log->rotations), n_log);
214 return buf;
215 }
216
217
do_rotate(LOGGER_HANDLE * log)218 static int do_rotate(LOGGER_HANDLE *log)
219 {
220 char namebuf[FN_REFLEN];
221 int result;
222 unsigned int i;
223 char *buf_old, *buf_new, *tmp;
224
225 if (log->rotations == 0)
226 return 0;
227
228 memcpy(namebuf, log->path, log->path_len);
229
230 buf_new= logname(log, namebuf, log->rotations);
231 buf_old= log->path;
232 for (i=log->rotations-1; i>0; i--)
233 {
234 logname(log, buf_old, i);
235 if (!access(buf_old, F_OK) &&
236 (result= my_rename(buf_old, buf_new, MYF(0))))
237 goto exit;
238 tmp= buf_old;
239 buf_old= buf_new;
240 buf_new= tmp;
241 }
242 if ((result= my_close(log->file, MYF(0))))
243 goto exit;
244 namebuf[log->path_len]= 0;
245 result= my_rename(namebuf, logname(log, log->path, 1), MYF(0));
246 log->file= my_open(namebuf, LOG_FLAGS, MYF(0));
247 exit:
248 errno= my_errno;
249 return log->file < 0 || result;
250 }
251
252
logger_write(LOGGER_HANDLE * log,const char * buffer,size_t size,log_record_state_t state)253 int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size,
254 log_record_state_t state)
255 {
256 int result;
257 my_off_t filesize;
258
259 flogger_mutex_lock(log);
260
261 result= my_write(log->file, (uchar *) buffer, size, MYF(0));
262
263 if (state == LOG_RECORD_COMPLETE && log->rotations > 0)
264 {
265 if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 ||
266 ((unsigned long long)filesize >= log->size_limit &&
267 do_rotate(log)))
268 {
269 result= -1;
270 errno= my_errno;
271 goto exit; /* Log rotation needed but failed */
272 }
273 }
274
275 exit:
276 flogger_mutex_unlock(log);
277 return result;
278 }
279
280
logger_init_mutexes()281 void logger_init_mutexes()
282 {
283 #if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) && !defined(FLOGGER_NO_THREADSAFE)
284 mysql_mutex_register(AUDIT_LOG_PSI_CATEGORY,
285 mutex_list, array_elements(mutex_list));
286 #endif /*HAVE_PSI_INTERFACE && !FLOGGER_NO_PSI*/
287 }
288
logger_sync(LOGGER_HANDLE * log)289 int logger_sync(LOGGER_HANDLE *log)
290 {
291 return my_sync(log->file, MYF(0));
292 }
293
logger_set_size_limit(LOGGER_HANDLE * log,unsigned long long size_limit)294 void logger_set_size_limit(LOGGER_HANDLE *log, unsigned long long size_limit)
295 {
296 log->size_limit= size_limit;
297 }
298
logger_set_rotations(LOGGER_HANDLE * log,unsigned int rotations)299 void logger_set_rotations(LOGGER_HANDLE *log, unsigned int rotations)
300 {
301 log->rotations= rotations;
302 }
303