Core:Fix:Improve navit_destroy
[navit-package] / navit / log.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2008 Navit Team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * version 2 as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <time.h>
26 #include <sys/time.h>
27 #include <glib.h>
28 #include "file.h"
29 #include "item.h"
30 #include "event.h"
31 #include "callback.h"
32 #include "debug.h"
33 #include "log.h"
34
35 struct log_data {
36         int len;
37         int max_len;
38         char *data;
39 };
40
41 struct log {
42         FILE *f;
43         int overwrite;
44         int empty;
45         int lazy;
46         int mkdir;
47         int flush_size;
48         int flush_time;
49         struct event_timeout *timer;
50         struct callback *timer_callback;
51         struct timeval last_flush;
52         char *filename;
53         char *filename_ex1;
54         char *filename_ex2;
55         struct log_data header;
56         struct log_data data;
57         struct log_data trailer;
58         struct attr **attrs;
59 };
60
61 static void
62 strftime_localtime(char *buffer, int size, char *fmt)
63 {
64         time_t t;
65         struct tm *tm;
66
67         t=time(NULL);
68         tm=localtime(&t);
69         strftime(buffer, 4096, fmt, tm);
70 }
71
72 static void
73 expand_filenames(struct log *this_)
74 {
75         char buffer[4096];
76         int i;
77
78         strftime_localtime(buffer, 4096, this_->filename);
79         this_->filename_ex1=g_strdup(buffer);
80         if (strstr(this_->filename_ex1,"%i")) {
81                 i=0;
82                 do {
83                         g_free(this_->filename_ex2);
84                         this_->filename_ex2=g_strdup_printf(this_->filename_ex1,i++);
85                 } while (file_exists(this_->filename_ex2));
86         } else 
87                 this_->filename_ex2=g_strdup(this_->filename_ex1);
88 }
89
90 static void
91 log_set_last_flush(struct log *this_)
92 {
93         gettimeofday(&this_->last_flush, NULL);
94 }
95
96 static void
97 log_open(struct log *this_)
98 {
99         char *mode;
100         if (this_->overwrite)
101                 mode="w";
102         else
103                 mode="r+";
104         if (this_->mkdir)
105                 file_mkdir(this_->filename_ex2, 2);
106         this_->f=fopen(this_->filename_ex2, mode);
107         if (! this_->f)
108                 this_->f=fopen(this_->filename_ex2, "w");
109         if (! this_->f)
110                 return;
111         if (!this_->overwrite) 
112                 fseek(this_->f, 0, SEEK_END);
113         this_->empty = !ftell(this_->f);
114         log_set_last_flush(this_);
115 }
116
117 static void
118 log_close(struct log *this_)
119 {
120         if (! this_->f)
121                 return;
122         if (this_->trailer.len) 
123                 fwrite(this_->trailer.data, 1, this_->trailer.len, this_->f);
124         fflush(this_->f);
125         fclose(this_->f);
126         this_->f=NULL;
127 }
128
129 static void
130 log_flush(struct log *this_)
131 {
132         long pos;
133         if (this_->lazy && !this_->f) {
134                 if (!this_->data.len)
135                         return;
136                 log_open(this_);
137         }
138         if (! this_->f)
139                 return;
140         if (this_->empty) {
141                 if (this_->header.len) 
142                         fwrite(this_->header.data, 1, this_->header.len, this_->f);
143                 if (this_->header.len || this_->data.len)
144                         this_->empty=0;
145         }
146         fwrite(this_->data.data, 1, this_->data.len, this_->f);
147         if (this_->trailer.len) {
148                 pos=ftell(this_->f);
149                 if (pos > 0) {
150                         fwrite(this_->trailer.data, 1, this_->trailer.len, this_->f);
151                         fseek(this_->f, pos, SEEK_SET); 
152                 }
153         }
154         
155         fflush(this_->f);
156         g_free(this_->data.data);
157         this_->data.data=NULL;
158         this_->data.max_len=this_->data.len=0;
159         log_set_last_flush(this_);
160 }
161
162 static int
163 log_flush_required(struct log *this_)
164 {
165         return this_->data.len > this_->flush_size;
166 }
167
168
169 static void
170 log_change(struct log *this_)
171 {
172         log_flush(this_);
173         log_close(this_);
174         expand_filenames(this_);
175         if (! this_->lazy)
176                 log_open(this_);
177 }
178
179 static int
180 log_change_required(struct log *this_)
181 {
182         char buffer[4096];
183
184         strftime_localtime(buffer, 4096, this_->filename);
185         return (strcmp(this_->filename_ex1, buffer) != 0);
186 }
187
188 static void
189 log_timer(struct log *this_)
190 {
191         struct timeval tv;
192         int delta;
193         gettimeofday(&tv, NULL);
194         delta=(tv.tv_sec-this_->last_flush.tv_sec)*1000+(tv.tv_usec-this_->last_flush.tv_usec)/1000;
195         dbg(1,"delta=%d flush_time=%d\n", delta, this_->flush_time);
196         if (this_->flush_time && delta >= this_->flush_time*1000)
197                 log_flush(this_);
198 }
199
200 int
201 log_get_attr(struct log *this_, enum attr_type type, struct attr *attr, struct attr_iter *iter)
202 {
203         return attr_generic_get_attr(this_->attrs, NULL, type, attr, iter);
204 }
205
206
207 struct log *
208 log_new(struct attr * parent,struct attr **attrs)
209 {
210         struct log *ret=g_new0(struct log, 1);
211         struct attr *data,*overwrite,*lazy,*mkdir,*flush_size,*flush_time;
212         struct file_wordexp *wexp;
213         char *filename, **wexp_data;
214
215         dbg(1,"enter\n");
216         data=attr_search(attrs, NULL, attr_data);
217         if (! data)
218                 return NULL;
219         filename=data->u.str;
220         wexp=file_wordexp_new(filename);
221         if (wexp && file_wordexp_get_count(wexp) > 0) {
222                 wexp_data=file_wordexp_get_array(wexp);
223                 filename=wexp_data[0];
224         }
225         if (filename)
226                 ret->filename=g_strdup(filename);
227         if (wexp)
228                 file_wordexp_destroy(wexp);
229         overwrite=attr_search(attrs, NULL, attr_overwrite);
230         if (overwrite)
231                 ret->overwrite=overwrite->u.num;
232         lazy=attr_search(attrs, NULL, attr_lazy);
233         if (lazy)
234                 ret->lazy=lazy->u.num;
235         mkdir=attr_search(attrs, NULL, attr_mkdir);
236         if (mkdir)
237                 ret->mkdir=mkdir->u.num;
238         flush_size=attr_search(attrs, NULL, attr_flush_size);
239         if (flush_size)
240                 ret->flush_size=flush_size->u.num;
241         flush_time=attr_search(attrs, NULL, attr_flush_time);
242         if (flush_time)
243                 ret->flush_time=flush_time->u.num;
244         if (ret->flush_time) {
245                 dbg(1,"interval %d\n", ret->flush_time*1000);
246                 ret->timer_callback=callback_new_1(callback_cast(log_timer), ret);
247                 ret->timer=event_add_timeout(ret->flush_time*1000, 1, ret->timer_callback);
248         }
249         expand_filenames(ret);
250         if (ret->lazy)
251                 log_set_last_flush(ret);
252         else
253                 log_open(ret);
254         ret->attrs=attr_list_dup(attrs);
255         return ret;
256 }
257
258 void
259 log_set_header(struct log *this_, char *data, int len)
260 {
261         this_->header.data=g_malloc(len);
262         this_->header.max_len=this_->header.len=len;
263         memcpy(this_->header.data, data, len);
264 }
265
266 void
267 log_set_trailer(struct log *this_, char *data, int len)
268 {
269         this_->trailer.data=g_malloc(len);
270         this_->trailer.max_len=this_->trailer.len=len;
271         memcpy(this_->trailer.data, data, len);
272 }
273
274 void
275 log_write(struct log *this_, char *data, int len)
276 {
277         dbg(1,"enter\n");
278         if (log_change_required(this_)) {
279                 dbg(1,"log_change");
280                 log_change(this_);
281         }
282         if (this_->data.len + len > this_->data.max_len) {
283                 dbg(2,"overflow\n");
284                 this_->data.max_len+=16384;
285                 this_->data.data=g_realloc(this_->data.data,this_->data.max_len);
286         }
287         memcpy(this_->data.data+this_->data.len, data, len);
288         this_->data.len+=len;
289         if (log_flush_required(this_))
290                 log_flush(this_);
291 }
292
293 void
294 log_destroy(struct log *this_)
295 {
296         callback_destroy(this_->timer_callback);
297         event_remove_timeout(this_->timer);
298         log_flush(this_);
299         log_close(this_);
300         g_free(this_);
301 }