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-1335 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 */
34 static PSI_mutex_key key_LOCK_logger_service;
35 static PSI_mutex_info mutex_list[]=
36 {{ &key_LOCK_logger_service, "logger_service_file_st::lock", PSI_FLAG_GLOBAL}};
37 #endif
38 
39 typedef 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 
n_dig(unsigned int i)51 static unsigned int n_dig(unsigned int i)
52 {
53   return (i == 0) ? 0 : ((i < 10) ? 1 : ((i < 100) ? 2 : 3));
54 }
55 
56 
logger_open(const char * path,unsigned long long size_limit,unsigned int rotations)57 LOGGER_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(PSI_INSTRUMENT_ME,
88                                             sizeof(LOGGER_HANDLE), MYF(0))))
89   {
90     my_close(new_log.file, MYF(0));
91     new_log.file= -1;
92     return 0; /* End of memory */
93   }
94   *l_perm= new_log;
95   flogger_mutex_init(key_LOCK_logger_service, &l_perm->lock,
96                      MY_MUTEX_INIT_FAST);
97   return l_perm;
98 }
99 
logger_close(LOGGER_HANDLE * log)100 int logger_close(LOGGER_HANDLE *log)
101 {
102   int result;
103   File file= log->file;
104   flogger_mutex_destroy(&log->lock);
105   my_free(log);
106   if ((result= my_close(file, MYF(0))))
107     errno= my_errno;
108   return result;
109 }
110 
111 
logname(LOGGER_HANDLE * log,char * buf,unsigned int n_log)112 static char *logname(LOGGER_HANDLE *log, char *buf, unsigned int n_log)
113 {
114   sprintf(buf+log->path_len, ".%0*u", n_dig(log->rotations), n_log);
115   return buf;
116 }
117 
118 
do_rotate(LOGGER_HANDLE * log)119 static int do_rotate(LOGGER_HANDLE *log)
120 {
121   char namebuf[FN_REFLEN];
122   int result;
123   unsigned int i;
124   char *buf_old, *buf_new, *tmp;
125 
126   if (log->rotations == 0)
127     return 0;
128 
129   memcpy(namebuf, log->path, log->path_len);
130 
131   buf_new= logname(log, namebuf, log->rotations);
132   buf_old= log->path;
133   for (i=log->rotations-1; i>0; i--)
134   {
135     logname(log, buf_old, i);
136     if (!access(buf_old, F_OK) &&
137         (result= my_rename(buf_old, buf_new, MYF(0))))
138       goto exit;
139     tmp= buf_old;
140     buf_old= buf_new;
141     buf_new= tmp;
142   }
143   if ((result= my_close(log->file, MYF(0))))
144     goto exit;
145   namebuf[log->path_len]= 0;
146   result= my_rename(namebuf, logname(log, log->path, 1), MYF(0));
147   log->file= my_open(namebuf, LOG_FLAGS, MYF(0));
148 exit:
149   errno= my_errno;
150   return log->file < 0 || result;
151 }
152 
153 
154 /*
155    Return 1 if we should rotate the log
156 */
157 
logger_time_to_rotate(LOGGER_HANDLE * log)158 my_bool logger_time_to_rotate(LOGGER_HANDLE *log)
159 {
160   my_off_t filesize;
161   if (log->rotations > 0 &&
162       (filesize= my_tell(log->file, MYF(0))) != (my_off_t) -1 &&
163       ((ulonglong) filesize >= log->size_limit))
164     return 1;
165   return 0;
166 }
167 
168 
logger_vprintf(LOGGER_HANDLE * log,const char * fmt,va_list ap)169 int logger_vprintf(LOGGER_HANDLE *log, const char* fmt, va_list ap)
170 {
171   int result;
172   char cvtbuf[1024];
173   size_t n_bytes;
174 
175   flogger_mutex_lock(&log->lock);
176   if (logger_time_to_rotate(log) && do_rotate(log))
177   {
178     result= -1;
179     errno= my_errno;
180     goto exit; /* Log rotation needed but failed */
181   }
182 
183   n_bytes= my_vsnprintf(cvtbuf, sizeof(cvtbuf), fmt, ap);
184   if (n_bytes >= sizeof(cvtbuf))
185     n_bytes= sizeof(cvtbuf) - 1;
186 
187   result= (int)my_write(log->file, (uchar *) cvtbuf, n_bytes, MYF(0));
188 
189 exit:
190   flogger_mutex_unlock(&log->lock);
191   return result;
192 }
193 
194 
logger_write_r(LOGGER_HANDLE * log,my_bool allow_rotations,const char * buffer,size_t size)195 static int logger_write_r(LOGGER_HANDLE *log, my_bool allow_rotations,
196                           const char *buffer, size_t size)
197 {
198   int result;
199 
200   flogger_mutex_lock(&log->lock);
201   if (allow_rotations && logger_time_to_rotate(log) && do_rotate(log))
202   {
203     result= -1;
204     errno= my_errno;
205     goto exit; /* Log rotation needed but failed */
206   }
207 
208   result= (int)my_write(log->file, (uchar *) buffer, size, MYF(0));
209 
210 exit:
211   flogger_mutex_unlock(&log->lock);
212   return result;
213 }
214 
215 
logger_write(LOGGER_HANDLE * log,const char * buffer,size_t size)216 int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size)
217 {
218   return logger_write_r(log, TRUE, buffer, size);
219 }
220 
logger_rotate(LOGGER_HANDLE * log)221 int logger_rotate(LOGGER_HANDLE *log)
222 {
223   int result;
224   flogger_mutex_lock(&log->lock);
225   result= do_rotate(log);
226   flogger_mutex_unlock(&log->lock);
227   return result;
228 }
229 
230 
logger_printf(LOGGER_HANDLE * log,const char * fmt,...)231 int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...)
232 {
233   int result;
234   va_list args;
235   va_start(args,fmt);
236   result= logger_vprintf(log, fmt, args);
237   va_end(args);
238   return result;
239 }
240 
logger_init_mutexes()241 void logger_init_mutexes()
242 {
243 #ifdef HAVE_PSI_INTERFACE
244   if (unlikely(PSI_server))
245     PSI_server->register_mutex("sql_logger", mutex_list, 1);
246 #endif
247 }
248 
249