sipdump: use localtime_r() for a safer multi-thread usage
[sip-router] / src / modules / sipdump / sipdump_write.c
1 /**
2  * Copyright (C) 2017 Daniel-Constantin Mierla (asipto.com)
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  *
12  * This file is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "../../core/dprint.h"
29 #include "../../core/ut.h"
30 #include "../../core/globals.h"
31 #include "../../core/ver.h"
32 #include "../../core/pt.h"
33 #include "../../core/rpc.h"
34 #include "../../core/rpc_lookup.h"
35
36 #include "sipdump_write.h"
37
38 extern int sipdump_rotate;
39
40 static time_t sipdump_last_rotate = 0;
41
42 static sipdump_list_t *_sipdump_list = NULL;
43
44 #define SIPDUMP_FPATH_SIZE 256
45 static char _sipdump_fpath[SIPDUMP_FPATH_SIZE];
46 static str _sipdump_fpath_prefix = {0, 0};
47
48 static FILE *_sipdump_file = NULL;
49
50 /**
51  *
52  */
53 int sipdump_list_init(int en)
54 {
55         if(_sipdump_list!=NULL)
56                 return 0;
57         _sipdump_list = (sipdump_list_t*)shm_malloc(sizeof(sipdump_list_t));
58         if(_sipdump_list==NULL) {
59                 LM_ERR("not enough shared memory\n");
60                 return -1;
61         }
62         memset(_sipdump_list, 0, sizeof(sipdump_list_t));
63         if(lock_init(&_sipdump_list->lock)==NULL) {
64                 shm_free(_sipdump_list);
65                 LM_ERR("failed to init lock\n");
66                 return -1;
67         }
68         _sipdump_list->enable = en;
69         return 0;
70 }
71
72 /**
73  *
74  */
75 int sipdump_enabled(void)
76 {
77         if(_sipdump_list==NULL)
78                 return 0;
79         if(_sipdump_list->enable==0)
80                 return 0;
81         return 1;
82 }
83 /**
84  *
85  */
86 int sipdump_list_destroy(void)
87 {
88         sipdump_data_t *sdd = NULL;
89         sipdump_data_t *sdd0 = NULL;
90         if(_sipdump_list==NULL)
91                 return 0;
92
93         sdd=_sipdump_list->first;
94         while(sdd!=NULL) {
95                 sdd0 = sdd;
96                 sdd=sdd->next;
97                 shm_free(sdd0);
98         }
99         return 0;
100 }
101
102 /**
103  *
104  */
105 int sipdump_list_add(str *data)
106 {
107         sipdump_data_t *sdd = NULL;
108
109         sdd = (sipdump_data_t*)shm_malloc(sizeof(sipdump_data_t)
110                                 + (data->len+1)*sizeof(char));
111         if(sdd==NULL) {
112                 LM_ERR("no more shared memory\n");
113                 return -1;
114         }
115         memset(sdd, 0, sizeof(sipdump_data_t));
116         sdd->data.s = (char*)sdd + sizeof(sipdump_data_t);
117         sdd->data.len = data->len;
118         memcpy(sdd->data.s, data->s, data->len);
119         sdd->data.s[data->len] = '\0';
120         lock_get(&_sipdump_list->lock);
121         if(_sipdump_list->last) {
122                 _sipdump_list->last->next = sdd;
123         } else {
124                 _sipdump_list->first = sdd;
125         }
126         _sipdump_list->last = sdd;
127         lock_release(&_sipdump_list->lock);
128         return 0;
129 }
130
131 /**
132  *
133  */
134 static int sipdump_write_meta(char *fpath)
135 {
136         char mpath[SIPDUMP_FPATH_SIZE];
137         int len;
138         int i;
139         FILE *mfile = NULL;
140         struct tm ti;
141         char t_buf[26] = {0};
142
143         len = strlen(fpath);
144         if(len>=SIPDUMP_FPATH_SIZE-1) {
145                 LM_ERR("file path too long\n");
146                 return -1;
147         }
148         strcpy(mpath, fpath);
149         mpath[len-4] = 'm';
150         mpath[len-3] = 'e';
151         mpath[len-2] = 't';
152         mpath[len-1] = 'a';
153
154         LM_DBG("writing meta to file: %s\n", mpath);
155         mfile = fopen( mpath , "w" );
156         if(mfile==NULL) {
157                 LM_ERR("failed to open meta file %s\n", mpath);
158                 return -1;
159         }
160         localtime_r(&up_since, &ti);
161         fprintf(mfile,
162                         "v: 1.0\n"
163                         "version: %s %s\n"
164                         "start: %s"
165                         "nrprocs: %d\n",
166                         ver_name, ver_version,
167                         asctime_r(&ti, t_buf),
168                         *process_count
169                 );
170         for (i=0; i<*process_count; i++) {
171                 fprintf(mfile,
172                         "process: %d %d %s\n",
173                         i, pt[i].pid, pt[i].desc);
174         }
175
176         fclose(mfile);
177         return 0;
178 }
179
180 /**
181  *
182  */
183 static int sipdump_rotate_file(void)
184 {
185         time_t tv;
186         struct tm ti;
187         int n;
188
189         tv = time(NULL);
190
191         if(_sipdump_file!=NULL
192                         && sipdump_last_rotate>0
193                         && sipdump_last_rotate+sipdump_rotate>tv) {
194                 /* not yet the time for rotation */
195                 return 0;
196         }
197
198         if(_sipdump_file != NULL) {
199                 fclose(_sipdump_file);
200         }
201         localtime_r(&tv, &ti);
202         n = snprintf(_sipdump_fpath+_sipdump_fpath_prefix.len,
203                         SIPDUMP_FPATH_SIZE-_sipdump_fpath_prefix.len,
204                         "%d-%02d-%02d--%02d-%02d-%02d.data",
205                         1900+ti.tm_year, ti.tm_mon, ti.tm_mday,
206                         ti.tm_hour, ti.tm_min, ti.tm_sec);
207         LM_DBG("writing to file: %s (%d)\n", _sipdump_fpath, n);
208         _sipdump_file = fopen( _sipdump_fpath, "w" );
209         if(_sipdump_file==NULL) {
210                 LM_ERR("failed to open file %s\n", _sipdump_fpath);
211                 return -1;
212         }
213         sipdump_write_meta(_sipdump_fpath);
214         sipdump_last_rotate = tv;
215
216         return 0;
217 }
218
219 /**
220  *
221  */
222 int sipdump_file_init(str *folder, str *fprefix)
223 {
224         _sipdump_fpath_prefix.len = snprintf(_sipdump_fpath, SIPDUMP_FPATH_SIZE-64,
225                         "%.*s/%.*s",
226                         folder->len, folder->s,
227                         fprefix->len, fprefix->s);
228         if(_sipdump_fpath_prefix.len<0
229                         || _sipdump_fpath_prefix.len>=SIPDUMP_FPATH_SIZE-64) {
230                 LM_ERR("sipdump file path failed or is too long\n");
231                 return -1;
232         }
233         _sipdump_fpath_prefix.s = _sipdump_fpath;
234         return 0;
235 }
236
237 /**
238  *
239  */
240 void sipdump_timer_exec(unsigned int ticks, void *param)
241 {
242         sipdump_data_t *sdd = NULL;
243         int cnt = 0;
244
245         if(_sipdump_list==NULL || _sipdump_list->first==NULL)
246                 return;
247
248         if(sipdump_rotate_file()<0) {
249                 LM_ERR("sipdump rotate file failed\n");
250                 return;
251         }
252
253         while(1) {
254                 lock_get(&_sipdump_list->lock);
255                 if(_sipdump_list->first==NULL) {
256                         lock_release(&_sipdump_list->lock);
257                         if(_sipdump_file) fflush(_sipdump_file);
258                         return;
259                 }
260                 sdd = _sipdump_list->first;
261                 _sipdump_list->first = sdd->next;
262                 if(sdd->next==NULL) {
263                         _sipdump_list->last = NULL;
264                 }
265                 _sipdump_list->count--;
266                 lock_release(&_sipdump_list->lock);
267                 cnt++;
268                 if(cnt>2000) {
269                         if(sipdump_rotate_file()<0) {
270                                 LM_ERR("sipdump rotate file failed\n");
271                                 return;
272                         }
273                         cnt=0;
274                 }
275                 if(_sipdump_file==NULL) {
276                         LM_ERR("sipdump file is not open\n");
277                         return;
278                 }
279                 /* LM_NOTICE("writing: [[%.*s]] (%d)\n",
280                         sdd->data.len, sdd->data.s, sdd->data.len); */
281                 fwrite(sdd->data.s, 1, sdd->data.len, _sipdump_file);
282                 shm_free(sdd);
283         }
284 }
285
286 static const char *sipdump_rpc_enable_doc[2] = {
287         "Command to control sipdump enable value", 0};
288
289
290 /*
291 * RPC command to control sipdump enable
292 */
293 static void sipdump_rpc_enable(rpc_t *rpc, void *ctx)
294 {
295         int enval = -1;
296         int oval = 0;
297         int nval = 0;
298
299         void *th;
300
301         if(rpc->scan(ctx, "*d", &enval) != 1) {
302                 enval = -1;
303         }
304
305         if(rpc->add(ctx, "{", &th) < 0) {
306                 rpc->fault(ctx, 500, "Internal error root reply");
307                 return;
308         }
309
310         if(_sipdump_list) {
311                 oval = _sipdump_list->enable;
312                 if(enval==0 || enval==1) {
313                         _sipdump_list->enable = enval;
314                         nval = enval;
315                 } else {
316                         nval = oval;
317                 }
318         }
319
320         if(rpc->struct_add(th, "dd", "oldval", oval, "newval", nval) < 0) {
321                 rpc->fault(ctx, 500, "Internal error reply structure");
322                 return;
323         }
324 }
325
326 /* clang-format off */
327 rpc_export_t sipdump_rpc_cmds[] = {
328         {"sipdump.enable", sipdump_rpc_enable,
329                 sipdump_rpc_enable_doc, 0},
330         {0, 0, 0, 0}
331 };
332 /* clang-format on */
333
334 /**
335  * register RPC commands
336  */
337 int sipdump_rpc_init(void)
338 {
339         if(rpc_register_array(sipdump_rpc_cmds) != 0) {
340                 LM_ERR("failed to register RPC commands\n");
341                 return -1;
342         }
343         return 0;
344 }