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 02111-1301 USA */
15
16
17#ifndef FLOGGER_SKIP_INCLUDES
18#include "my_global.h"
19#include <my_sys.h>
20#include <m_string.h>
21#include <mysql/service_logger.h>
22#include <my_pthread.h>
23#endif /*FLOGGER_SKIP_INCLUDES*/
24
25#ifndef flogger_mutex_init
26#define flogger_mutex_init(A,B,C) mysql_mutex_init(A,B,C)
27#define flogger_mutex_destroy(A) mysql_mutex_destroy(A)
28#define flogger_mutex_lock(A) mysql_mutex_lock(A)
29#define flogger_mutex_unlock(A) mysql_mutex_unlock(A)
30#endif /*flogger_mutex_init*/
31
32#ifdef HAVE_PSI_INTERFACE
33/* These belong to the service initialization */
34static PSI_mutex_key key_LOCK_logger_service;
35static PSI_mutex_info mutex_list[]=
36{{ &key_LOCK_logger_service, "logger_service_file_st::lock", PSI_FLAG_GLOBAL}};
37#endif
38
39typedef struct logger_handle_st {
40 File file;
41 char path[FN_REFLEN];
42 unsigned long long size_limit;
43 unsigned int rotations;
44 size_t path_len;
45 mysql_mutex_t lock;
46} LSFS;
47
48
49#define LOG_FLAGS (O_APPEND | O_CREAT | O_WRONLY)
50
51static unsigned int n_dig(unsigned int i)
52{
53 return (i == 0) ? 0 : ((i < 10) ? 1 : ((i < 100) ? 2 : 3));
54}
55
56
57LOGGER_HANDLE *logger_open(const char *path,
58 unsigned long long size_limit,
59 unsigned int rotations)
60{
61 LOGGER_HANDLE new_log, *l_perm;
62 /*
63 I don't think we ever need more rotations,
64 but if it's so, the rotation procedure should be adapted to it.
65 */
66 if (rotations > 999)
67 return 0;
68
69 new_log.rotations= rotations;
70 new_log.size_limit= size_limit;
71 new_log.path_len= strlen(fn_format(new_log.path, path,
72 mysql_data_home, "", MY_UNPACK_FILENAME));
73
74 if (new_log.path_len+n_dig(rotations)+1 > FN_REFLEN)
75 {
76 errno= ENAMETOOLONG;
77 /* File path too long */
78 return 0;
79 }
80 if ((new_log.file= my_open(new_log.path, LOG_FLAGS, MYF(0))) < 0)
81 {
82 errno= my_errno;
83 /* Check errno for the cause */
84 return 0;
85 }
86
87 if (!(l_perm= (LOGGER_HANDLE *) my_malloc(sizeof(LOGGER_HANDLE), MYF(0))))
88 {
89 my_close(new_log.file, MYF(0));
90 new_log.file= -1;
91 return 0; /* End of memory */
92 }
93 *l_perm= new_log;
94 flogger_mutex_init(key_LOCK_logger_service, &l_perm->lock,
95 MY_MUTEX_INIT_FAST);
96 return l_perm;
97}
98
99int logger_close(LOGGER_HANDLE *log)
100{
101 int result;
102 File file= log->file;
103 flogger_mutex_destroy(&log->lock);
104 my_free(log);
105 if ((result= my_close(file, MYF(0))))
106 errno= my_errno;
107 return result;
108}
109
110
111static char *logname(LOGGER_HANDLE *log, char *buf, unsigned int n_log)
112{
113 sprintf(buf+log->path_len, ".%0*u", n_dig(log->rotations), n_log);
114 return buf;
115}
116
117
118static int do_rotate(LOGGER_HANDLE *log)
119{
120 char namebuf[FN_REFLEN];
121 int result;
122 unsigned int i;
123 char *buf_old, *buf_new, *tmp;
124
125 if (log->rotations == 0)
126 return 0;
127
128 memcpy(namebuf, log->path, log->path_len);
129
130 buf_new= logname(log, namebuf, log->rotations);
131 buf_old= log->path;
132 for (i=log->rotations-1; i>0; i--)
133 {
134 logname(log, buf_old, i);
135 if (!access(buf_old, F_OK) &&
136 (result= my_rename(buf_old, buf_new, MYF(0))))
137 goto exit;
138 tmp= buf_old;
139 buf_old= buf_new;
140 buf_new= tmp;
141 }
142 if ((result= my_close(log->file, MYF(0))))
143 goto exit;
144 namebuf[log->path_len]= 0;
145 result= my_rename(namebuf, logname(log, log->path, 1), MYF(0));
146 log->file= my_open(namebuf, LOG_FLAGS, MYF(0));
147exit:
148 errno= my_errno;
149 return log->file < 0 || result;
150}
151
152
153int logger_vprintf(LOGGER_HANDLE *log, const char* fmt, va_list ap)
154{
155 int result;
156 my_off_t filesize;
157 char cvtbuf[1024];
158 size_t n_bytes;
159
160 flogger_mutex_lock(&log->lock);
161 if (log->rotations > 0)
162 if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 ||
163 ((unsigned long long)filesize >= log->size_limit &&
164 do_rotate(log)))
165 {
166 result= -1;
167 errno= my_errno;
168 goto exit; /* Log rotation needed but failed */
169 }
170
171 n_bytes= my_vsnprintf(cvtbuf, sizeof(cvtbuf), fmt, ap);
172 if (n_bytes >= sizeof(cvtbuf))
173 n_bytes= sizeof(cvtbuf) - 1;
174
175 result= (int)my_write(log->file, (uchar *) cvtbuf, n_bytes, MYF(0));
176
177exit:
178 flogger_mutex_unlock(&log->lock);
179 return result;
180}
181
182
183int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size)
184{
185 int result;
186 my_off_t filesize;
187
188 flogger_mutex_lock(&log->lock);
189 if (log->rotations > 0)
190 if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 ||
191 ((unsigned long long)filesize >= log->size_limit &&
192 do_rotate(log)))
193 {
194 result= -1;
195 errno= my_errno;
196 goto exit; /* Log rotation needed but failed */
197 }
198
199 result= (int)my_write(log->file, (uchar *) buffer, size, MYF(0));
200
201exit:
202 flogger_mutex_unlock(&log->lock);
203 return result;
204}
205
206
207int logger_rotate(LOGGER_HANDLE *log)
208{
209 int result;
210 flogger_mutex_lock(&log->lock);
211 result= do_rotate(log);
212 flogger_mutex_unlock(&log->lock);
213 return result;
214}
215
216
217int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...)
218{
219 int result;
220 va_list args;
221 va_start(args,fmt);
222 result= logger_vprintf(log, fmt, args);
223 va_end(args);
224 return result;
225}
226
227void logger_init_mutexes()
228{
229#ifdef HAVE_PSI_INTERFACE
230 if (unlikely(PSI_server))
231 PSI_server->register_mutex("sql_logger", mutex_list, 1);
232#endif
233}
234
235