mi_fifo: close fifo stream in case of errors
[sip-router] / modules / mi_fifo / fifo_fnc.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2001-2003 FhG Fokus
5  * Copyright (C) 2006 Voice Sistem SRL
6  *
7  * This file is part of Kamailio, a free SIP server.
8  *
9  * Kamailio is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Kamailio is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22  *
23  *
24  * History:
25  * ---------
26  *  2006-09-25  first version (bogdan)
27  */
28
29 /*!
30  * \file
31  * \brief MI Fifo :: Functions
32  * \ingroup mi
33  */
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <signal.h>
44
45 #include "../../dprint.h"
46 #include "../../ut.h"
47 #include "../../lib/kmi/mi.h"
48 #include "../../mem/mem.h"
49 #include "../../mem/shm_mem.h"
50 #include "../../cfg/cfg_struct.h"
51 #include "mi_fifo.h"
52 #include "fifo_fnc.h"
53 #include "mi_parser.h"
54 #include "mi_writer.h"
55
56 static int  mi_fifo_read = 0;
57 static int  mi_fifo_write = 0;
58 static char *mi_buf = 0;
59 static char *reply_fifo_s = 0;
60 static int  reply_fifo_len = 0;
61
62
63 /*! \brief Initialize MI Fifo server */
64 FILE *mi_init_fifo_server(char *fifo_name, int mi_fifo_mode,
65                                                 int mi_fifo_uid, int mi_fifo_gid, char* fifo_reply_dir)
66 {
67         FILE *fifo_stream;
68         long opt;
69
70         /* create FIFO ... */
71         if ((mkfifo(fifo_name, mi_fifo_mode)<0)) {
72                 LM_ERR("Can't create FIFO: %s (mode=%d)\n", strerror(errno), mi_fifo_mode);
73                 return 0;
74         }
75
76         LM_DBG("FIFO created @ %s\n", fifo_name );
77
78         if ((chmod(fifo_name, mi_fifo_mode)<0)) {
79                 LM_ERR("Can't chmod FIFO: %s (mode=%d)\n", strerror(errno), mi_fifo_mode);
80                 return 0;
81         }
82
83         if ((mi_fifo_uid!=-1) || (mi_fifo_gid!=-1)){
84                 if (chown(fifo_name, mi_fifo_uid, mi_fifo_gid)<0){
85                         LM_ERR("Failed to change the owner/group for %s  to %d.%d; %s[%d]\n",
86                                 fifo_name, mi_fifo_uid, mi_fifo_gid, strerror(errno), errno);
87                         return 0;
88                 }
89         }
90
91         LM_DBG("fifo %s opened, mode=%o\n", fifo_name, mi_fifo_mode );
92
93         /* open it non-blocking or else wait here until someone
94          * opens it for writing */
95         mi_fifo_read=open(fifo_name, O_RDONLY|O_NONBLOCK, 0);
96         if (mi_fifo_read<0) {
97                 LM_ERR("Can't open fifo %s for reading - mi_fifo_read did not open: %s\n", fifo_name, strerror(errno));
98                 return 0;
99         }
100
101         fifo_stream = fdopen(mi_fifo_read, "r");
102         if (fifo_stream==NULL) {
103                 LM_ERR("fdopen failed on %s: %s\n", fifo_name, strerror(errno));
104                 return 0;
105         }
106
107         /* make sure the read fifo will not close */
108         mi_fifo_write=open( fifo_name, O_WRONLY|O_NONBLOCK, 0);
109         if (mi_fifo_write<0) {
110                 LM_ERR("fifo_write did not open: %s\n", strerror(errno));
111                 fclose(fifo_stream);
112                 return 0;
113         }
114         /* set read fifo blocking mode */
115         if ((opt=fcntl(mi_fifo_read, F_GETFL))==-1){
116                 LM_ERR("fcntl(F_GETFL) failed: %s [%d]\n", strerror(errno), errno);
117                 fclose(fifo_stream);
118                 return 0;
119         }
120         if (fcntl(mi_fifo_read, F_SETFL, opt & (~O_NONBLOCK))==-1){
121                 LM_ERR("cntl(F_SETFL) failed: %s [%d]\n", strerror(errno), errno);
122                 fclose(fifo_stream);
123                 return 0;
124         }
125
126         /* allocate all static buffers */
127         mi_buf = pkg_malloc(MAX_MI_FIFO_BUFFER);
128         reply_fifo_s = pkg_malloc(MAX_MI_FILENAME);
129         if ( mi_buf==NULL|| reply_fifo_s==NULL) {
130                 LM_ERR("no more private memory\n");
131                 fclose(fifo_stream);
132                 return 0;
133         }
134
135         /* init fifo reply dir buffer */
136         reply_fifo_len = strlen(fifo_reply_dir);
137         memcpy( reply_fifo_s, fifo_reply_dir, reply_fifo_len);
138
139         return fifo_stream;
140 }
141
142
143
144 /*! \brief reply fifo security checks:
145  *
146  * checks if fd is a fifo, is not hardlinked and it's not a softlink
147  * opened file descriptor + file name (for soft link check)
148  * \return 0 if ok, <0 if not */
149 static int mi_fifo_check(int fd, char* fname)
150 {
151         struct stat fst;
152         struct stat lst;
153
154         if (fstat(fd, &fst)<0){
155                 LM_ERR("security: fstat on %s failed: %s\n", fname, strerror(errno));
156                 return -1;
157         }
158         /* check if fifo */
159         if (!S_ISFIFO(fst.st_mode)){
160                 LM_ERR("security: %s is not a fifo\n", fname);
161                 return -1;
162         }
163         /* check if hard-linked */
164         if (fst.st_nlink>1){
165                 LM_ERR("security: fifo_check: %s is hard-linked %d times\n", fname, (unsigned)fst.st_nlink);
166                 return -1;
167         }
168
169         /* lstat to check for soft links */
170         if (lstat(fname, &lst)<0){
171                 LM_ERR("security: lstat on %s failed: %s\n", fname, strerror(errno));
172                 return -1;
173         }
174         if (S_ISLNK(lst.st_mode)){
175                 LM_ERR("security: fifo_check: %s is a soft link\n", fname);
176                 return -1;
177         }
178         /* if this is not a symbolic link, check to see if the inode didn't
179          * change to avoid possible sym.link, rm sym.link & replace w/ fifo race
180          */
181         if ((lst.st_dev!=fst.st_dev)||(lst.st_ino!=fst.st_ino)){
182                 LM_ERR("security: fifo_check: inode/dev number differ: %d %d (%s)\n",
183                         (int)fst.st_ino, (int)lst.st_ino, fname);
184                 return -1;
185         }
186         /* success */
187         return 0;
188 }
189
190
191
192 /*! \brief Open reply pipe (filename given in MI command) */
193 static FILE *mi_open_reply_pipe( char *pipe_name )
194 {
195         int fifofd;
196         FILE *file_handle;
197         int flags;
198
199         int retries=FIFO_REPLY_RETRIES;
200
201         if (!pipe_name || *pipe_name==0) {
202                 LM_DBG("No file to write to about missing cmd\n");
203                 return 0;
204         }
205
206 tryagain:
207         /* open non-blocking to make sure that a broken client will not
208          * block the FIFO server forever */
209         fifofd=open( pipe_name, O_WRONLY | O_NONBLOCK );
210         if (fifofd==-1) {
211                 /* retry several times if client is not yet ready for getting
212                    feedback via a reply pipe
213                 */
214                 if (errno==ENXIO) {
215                         /* give up on the client - we can't afford server blocking */
216                         if (retries==0) {
217                                 LM_ERR("no client at %s\n",pipe_name );
218                                 return 0;
219                         }
220                         /* don't be noisy on the very first try */
221                         if (retries != FIFO_REPLY_RETRIES)
222                                 LM_DBG("mi_fifo retry countdown: %d\n", retries );
223                         sleep_us( FIFO_REPLY_WAIT );
224                         retries--;
225                         goto tryagain;
226                 }
227                 /* some other opening error */
228                 LM_ERR("open error (%s): %s\n", pipe_name, strerror(errno));
229                 return 0;
230         }
231
232         /* security checks: is this really a fifo?, is
233          * it hardlinked? is it a soft link? */
234         if (mi_fifo_check(fifofd, pipe_name)<0)
235                 goto error;
236
237         /* we want server blocking for big writes */
238         if ( (flags=fcntl(fifofd, F_GETFL, 0))<0) {
239                 LM_ERR("pipe (%s): getfl failed: %s\n", pipe_name, strerror(errno));
240                 goto error;
241         }
242         flags&=~O_NONBLOCK;
243         if (fcntl(fifofd, F_SETFL, flags)<0) {
244                 LM_ERR("pipe (%s): setfl cntl failed: %s\n", pipe_name, strerror(errno));
245                 goto error;
246         }
247
248         /* create an I/O stream */
249         file_handle=fdopen( fifofd, "w");
250         if (file_handle==NULL) {
251                 LM_ERR("open error (%s): %s\n",
252                         pipe_name, strerror(errno));
253                 goto error;
254         }
255         return file_handle;
256 error:
257         close(fifofd);
258         return 0;
259 }
260
261
262
263 /*! \brief Read input on fifo */
264 int mi_read_line( char *b, int max, FILE *stream, int *read)
265 {
266         int retry_cnt;
267         int len;
268         retry_cnt=0;
269
270 retry:
271         if (fgets(b, max, stream)==NULL) {
272                 LM_ERR("fifo_server fgets failed: %s\n", strerror(errno));
273                 /* on Linux, fgets sometimes returns ESPIPE -- give
274                    it few more chances
275                 */
276                 if (errno==ESPIPE) {
277                         retry_cnt++;
278                         if (retry_cnt<4)
279                                 goto retry;
280                 }
281                 /* interrupted by signal or ... */
282                 if ((errno==EINTR)||(errno==EAGAIN))
283                         goto retry;
284                 kill(0, SIGTERM);
285         }
286         /* if we did not read whole line, our buffer is too small
287            and we cannot process the request; consume the remainder of
288            request
289         */
290
291         len=strlen(b);
292         if (len && !(b[len-1]=='\n' || b[len-1]=='\r')) {
293                 LM_ERR("request line too long\n");
294                 return -1;
295         }
296         *read = len;
297
298         return 0;
299 }
300
301
302
303 /*! \brief Parse out reply file name from MI request */
304 static inline char *get_reply_filename( char * file, int len )
305 {
306         if ( strchr(file,'.') || strchr(file,'/') || strchr(file, '\\') ) {
307                 LM_ERR("Forbidden reply fifo filename: %s\n", file);
308                 return 0;
309         }
310
311         if (reply_fifo_len + len + 1 > MAX_MI_FILENAME) {
312                 LM_ERR("Reply fifo filename too long %d\n",reply_fifo_len + len);
313                 return 0;
314         }
315
316         memcpy( reply_fifo_s+reply_fifo_len, file, len );
317         reply_fifo_s[reply_fifo_len+len]=0;
318
319         return reply_fifo_s;
320 }
321
322
323 static inline void free_async_handler( struct mi_handler *hdl )
324 {
325         if (hdl)
326                 shm_free(hdl);
327 }
328
329
330 /*! \brief Open reply fifo, write message and close it */
331 static void fifo_close_async( struct mi_root *mi_rpl, struct mi_handler *hdl, int done)
332 {
333         FILE *reply_stream;
334         char *name;
335
336         name = (char*)hdl->param;
337
338         if ( mi_rpl!=0 || done ) {
339                 /*open fifo reply*/
340                 reply_stream = mi_open_reply_pipe( name );
341                 if (reply_stream==NULL) {
342                         LM_ERR("Cannot open reply pipe %s\n", name );
343                         return;
344                 }
345
346                 if (mi_rpl!=0) {
347                         mi_write_tree( reply_stream, mi_rpl);
348                         free_mi_tree( mi_rpl );
349                 } else {
350                         mi_fifo_reply( reply_stream, "500 command failed\n");
351                 }
352
353                 fclose(reply_stream);
354         }
355
356         if (done)
357                 free_async_handler( hdl );
358         return;
359 }
360
361
362 static inline struct mi_handler* build_async_handler( char *name, int len)
363 {
364         struct mi_handler *hdl;
365         char *p;
366
367         hdl = (struct mi_handler*)shm_malloc( sizeof(struct mi_handler) + len + 1);
368         if (hdl==0) {
369                 LM_ERR("no more shared memory\n");
370                 return 0;
371         }
372
373         p = (char*)(hdl) + sizeof(struct mi_handler);
374         memcpy( p, name, len+1 );
375
376         hdl->handler_f = fifo_close_async;
377         hdl->param = (void*)p;
378
379         return hdl;
380 }
381
382
383 #define mi_do_consume() \
384         do { \
385                 LM_DBG("entered consume\n"); \
386                 /* consume the rest of the fifo request */ \
387                 do { \
388                         mi_read_line(mi_buf,MAX_MI_FIFO_BUFFER,fifo_stream,&line_len); \
389                 } while(line_len>1); \
390                 LM_DBG("**** done consume\n"); \
391         } while(0)
392
393
394 #define mi_open_reply(_name,_file,_err) \
395         do { \
396                 _file = mi_open_reply_pipe( _name ); \
397                 if (_file==NULL) { \
398                         LM_ERR("cannot open reply pipe %s\n", _name); \
399                         goto _err; \
400                 } \
401         } while(0)
402
403
404
405 /*! \brief The actual MI Fifo Server */
406 void mi_fifo_server(FILE *fifo_stream)
407 {
408         struct mi_root *mi_cmd;
409         struct mi_root *mi_rpl;
410         struct mi_handler *hdl;
411         int line_len;
412         char *file_sep, *command, *file;
413         struct mi_cmd *f;
414         FILE *reply_stream = NULL;
415
416         while(1) {
417                 /* update the local config framework structures */
418                 cfg_update();
419
420
421                 if(reply_stream) {
422                         fclose(reply_stream);
423                         reply_stream = NULL;
424                 }
425
426                 /* commands must look this way ':<command>:[filename]' */
427                 if (mi_read_line(mi_buf,MAX_MI_FIFO_BUFFER,fifo_stream, &line_len)) {
428                         LM_ERR("failed to read fifo command\n");
429                         goto consume1;
430                 }
431
432                 /* trim from right */
433                 while(line_len) {
434                         if(mi_buf[line_len-1]=='\n' || mi_buf[line_len-1]=='\r'
435                                 || mi_buf[line_len-1]==' ' || mi_buf[line_len-1]=='\t' ) {
436                                 line_len--;
437                                 mi_buf[line_len]=0;
438                         } else break;
439                 }
440
441                 if (line_len==0) {
442                         LM_DBG("fifo command empty\n");
443                         goto consume1;
444                 }
445                 if (line_len<3) {
446                         LM_ERR("fifo command must have at least 3 chars\n");
447                         goto consume1;
448                 }
449                 if (*mi_buf!=MI_CMD_SEPARATOR) {
450                         LM_ERR("fifo command must begin with %c: %.*s\n", MI_CMD_SEPARATOR, line_len, mi_buf );
451                         goto consume1;
452                 }
453                 command = mi_buf+1;
454                 file_sep=strchr(command, MI_CMD_SEPARATOR );
455                 if (file_sep==NULL) {
456                         LM_ERR("file separator missing in fifo command\n");
457                         goto consume1;
458                 }
459                 if (file_sep==command) {
460                         LM_ERR("empty fifo command\n");
461                         goto consume1;
462                 }
463                 if (*(file_sep+1)==0) {
464                         file = NULL;
465                 } else {
466                         file = file_sep+1;
467                         file = get_reply_filename(file, mi_buf+line_len-file);
468                         if (file==NULL) {
469                                 LM_ERR("trimming fifo filename\n");
470                                 goto consume1;
471                         }
472                 }
473                 /* make command zero-terminated */
474                 *file_sep=0;
475
476                 f=lookup_mi_cmd( command, strlen(command) );
477                 if (f==0) {
478                         LM_ERR("fifo command %s is not available\n", command);
479                         mi_open_reply( file, reply_stream, consume1);
480                         mi_fifo_reply( reply_stream, "500 command '%s' not available\n",
481                                 command);
482                         goto consume2;
483                 }
484
485                 /* if asyncron cmd, build the async handler */
486                 if (f->flags&MI_ASYNC_RPL_FLAG) {
487                         hdl = build_async_handler( file, strlen(file) );
488                         if (hdl==0) {
489                                 LM_ERR("failed to build async fifo handler\n");
490                                 mi_open_reply( file, reply_stream, consume1);
491                                 mi_fifo_reply( reply_stream, "500 Internal server error\n");
492                                 goto consume2;
493                         }
494                 } else {
495                         hdl = 0;
496                         mi_open_reply( file, reply_stream, consume1);
497                 }
498
499                 if (f->flags&MI_NO_INPUT_FLAG) {
500                         mi_cmd = 0;
501                         mi_do_consume();
502                 } else {
503                         mi_cmd = mi_parse_tree(fifo_stream);
504                         if (mi_cmd==NULL){
505                                 LM_ERR("error parsing MI tree\n");
506                                 if (!reply_stream)
507                                         mi_open_reply( file, reply_stream, consume3);
508                                 mi_fifo_reply( reply_stream, "400 parse error in "
509                                         "command '%s'\n", command);
510                                 goto consume3;
511                         }
512                         mi_cmd->async_hdl = hdl;
513                 }
514
515                 LM_DBG("done parsing the mi tree\n");
516
517                 if ( (mi_rpl=run_mi_cmd(f, mi_cmd))==0 ){
518                         if (!reply_stream)
519                                 mi_open_reply( file, reply_stream, failure);
520                         mi_fifo_reply(reply_stream, "500 command '%s' failed\n", command);
521                         LM_ERR("command (%s) processing failed\n", command );
522                 } else if (mi_rpl!=MI_ROOT_ASYNC_RPL) {
523                         if (!reply_stream)
524                                 mi_open_reply( file, reply_stream, failure);
525                         mi_write_tree( reply_stream, mi_rpl);
526                         free_mi_tree( mi_rpl );
527                 } else {
528                         if (mi_cmd)
529                                 free_mi_tree( mi_cmd );
530                         continue;
531                 }
532
533                 free_async_handler(hdl);
534                 /* close reply fifo */
535                 fclose(reply_stream);
536                 reply_stream = NULL;
537                 /* destroy request tree */
538                 if (mi_cmd)
539                         free_mi_tree( mi_cmd );
540                 continue;
541
542 failure:
543                 free_async_handler(hdl);
544                 /* destroy request tree */
545                 if (mi_cmd)
546                         free_mi_tree( mi_cmd );
547                 /* destroy the reply tree */
548                 if (mi_rpl)
549                         free_mi_tree(mi_rpl);
550                 continue;
551
552 consume3:
553                 free_async_handler(hdl);
554 consume2:
555                 if (reply_stream) {
556                         fclose(reply_stream);
557                         reply_stream = NULL;
558                 }
559 consume1:
560                 mi_do_consume();
561         }
562 }