all: updated FSF address in GPL text
[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                 return 0;
112         }
113         /* set read fifo blocking mode */
114         if ((opt=fcntl(mi_fifo_read, F_GETFL))==-1){
115                 LM_ERR("fcntl(F_GETFL) failed: %s [%d]\n", strerror(errno), errno);
116                 return 0;
117         }
118         if (fcntl(mi_fifo_read, F_SETFL, opt & (~O_NONBLOCK))==-1){
119                 LM_ERR("cntl(F_SETFL) failed: %s [%d]\n", strerror(errno), errno);
120                 return 0;
121         }
122
123         /* allocate all static buffers */
124         mi_buf = pkg_malloc(MAX_MI_FIFO_BUFFER);
125         reply_fifo_s = pkg_malloc(MAX_MI_FILENAME);
126         if ( mi_buf==NULL|| reply_fifo_s==NULL) {
127                 LM_ERR("no more private memory\n");
128                 return 0;
129         }
130
131         /* init fifo reply dir buffer */
132         reply_fifo_len = strlen(fifo_reply_dir);
133         memcpy( reply_fifo_s, fifo_reply_dir, reply_fifo_len);
134
135         return fifo_stream;
136 }
137
138
139
140 /*! \brief reply fifo security checks:
141  *
142  * checks if fd is a fifo, is not hardlinked and it's not a softlink
143  * opened file descriptor + file name (for soft link check)
144  * \return 0 if ok, <0 if not */
145 static int mi_fifo_check(int fd, char* fname)
146 {
147         struct stat fst;
148         struct stat lst;
149         
150         if (fstat(fd, &fst)<0){
151                 LM_ERR("security: fstat on %s failed: %s\n", fname, strerror(errno));
152                 return -1;
153         }
154         /* check if fifo */
155         if (!S_ISFIFO(fst.st_mode)){
156                 LM_ERR("security: %s is not a fifo\n", fname);
157                 return -1;
158         }
159         /* check if hard-linked */
160         if (fst.st_nlink>1){
161                 LM_ERR("security: fifo_check: %s is hard-linked %d times\n", fname, (unsigned)fst.st_nlink);
162                 return -1;
163         }
164
165         /* lstat to check for soft links */
166         if (lstat(fname, &lst)<0){
167                 LM_ERR("security: lstat on %s failed: %s\n", fname, strerror(errno));
168                 return -1;
169         }
170         if (S_ISLNK(lst.st_mode)){
171                 LM_ERR("security: fifo_check: %s is a soft link\n", fname);
172                 return -1;
173         }
174         /* if this is not a symbolic link, check to see if the inode didn't
175          * change to avoid possible sym.link, rm sym.link & replace w/ fifo race
176          */
177         if ((lst.st_dev!=fst.st_dev)||(lst.st_ino!=fst.st_ino)){
178                 LM_ERR("security: fifo_check: inode/dev number differ: %d %d (%s)\n",
179                         (int)fst.st_ino, (int)lst.st_ino, fname);
180                 return -1;
181         }
182         /* success */
183         return 0;
184 }
185
186
187
188 /*! \brief Open reply pipe (filename given in MI command) */
189 static FILE *mi_open_reply_pipe( char *pipe_name )
190 {
191         int fifofd;
192         FILE *file_handle;
193         int flags;
194
195         int retries=FIFO_REPLY_RETRIES;
196
197         if (!pipe_name || *pipe_name==0) {
198                 LM_DBG("No file to write to about missing cmd\n");
199                 return 0;
200         }
201
202 tryagain:
203         /* open non-blocking to make sure that a broken client will not 
204          * block the FIFO server forever */
205         fifofd=open( pipe_name, O_WRONLY | O_NONBLOCK );
206         if (fifofd==-1) {
207                 /* retry several times if client is not yet ready for getting
208                    feedback via a reply pipe
209                 */
210                 if (errno==ENXIO) {
211                         /* give up on the client - we can't afford server blocking */
212                         if (retries==0) {
213                                 LM_ERR("no client at %s\n",pipe_name );
214                                 return 0;
215                         }
216                         /* don't be noisy on the very first try */
217                         if (retries != FIFO_REPLY_RETRIES)
218                                 LM_DBG("mi_fifo retry countdown: %d\n", retries );
219                         sleep_us( FIFO_REPLY_WAIT );
220                         retries--;
221                         goto tryagain;
222                 }
223                 /* some other opening error */
224                 LM_ERR("open error (%s): %s\n", pipe_name, strerror(errno));
225                 return 0;
226         }
227
228         /* security checks: is this really a fifo?, is 
229          * it hardlinked? is it a soft link? */
230         if (mi_fifo_check(fifofd, pipe_name)<0)
231                 goto error;
232
233         /* we want server blocking for big writes */
234         if ( (flags=fcntl(fifofd, F_GETFL, 0))<0) {
235                 LM_ERR("pipe (%s): getfl failed: %s\n", pipe_name, strerror(errno));
236                 goto error;
237         }
238         flags&=~O_NONBLOCK;
239         if (fcntl(fifofd, F_SETFL, flags)<0) {
240                 LM_ERR("pipe (%s): setfl cntl failed: %s\n", pipe_name, strerror(errno));
241                 goto error;
242         }
243
244         /* create an I/O stream */
245         file_handle=fdopen( fifofd, "w");
246         if (file_handle==NULL) {
247                 LM_ERR("open error (%s): %s\n",
248                         pipe_name, strerror(errno));
249                 goto error;
250         }
251         return file_handle;
252 error:
253         close(fifofd);
254         return 0;
255 }
256
257
258
259 /*! \brief Read input on fifo */
260 int mi_read_line( char *b, int max, FILE *stream, int *read)
261 {
262         int retry_cnt;
263         int len;
264         retry_cnt=0;
265
266 retry:
267         if (fgets(b, max, stream)==NULL) {
268                 LM_ERR("fifo_server fgets failed: %s\n", strerror(errno));
269                 /* on Linux, fgets sometimes returns ESPIPE -- give
270                    it few more chances
271                 */
272                 if (errno==ESPIPE) {
273                         retry_cnt++;
274                         if (retry_cnt<4)
275                                 goto retry;
276                 }
277                 /* interrupted by signal or ... */
278                 if ((errno==EINTR)||(errno==EAGAIN))
279                         goto retry;
280                 kill(0, SIGTERM);
281         }
282         /* if we did not read whole line, our buffer is too small
283            and we cannot process the request; consume the remainder of 
284            request
285         */
286
287         len=strlen(b);
288         if (len && !(b[len-1]=='\n' || b[len-1]=='\r')) {
289                 LM_ERR("request line too long\n");
290                 return -1;
291         }
292         *read = len;
293
294         return 0;
295 }
296
297
298
299 /*! \brief Parse out reply file name from MI request */
300 static inline char *get_reply_filename( char * file, int len )
301 {
302         if ( strchr(file,'.') || strchr(file,'/') || strchr(file, '\\') ) {
303                 LM_ERR("Forbidden reply fifo filename: %s\n", file);
304                 return 0;
305         }
306
307         if (reply_fifo_len + len + 1 > MAX_MI_FILENAME) {
308                 LM_ERR("Reply fifo filename too long %d\n",reply_fifo_len + len);
309                 return 0;
310         }
311
312         memcpy( reply_fifo_s+reply_fifo_len, file, len );
313         reply_fifo_s[reply_fifo_len+len]=0;
314
315         return reply_fifo_s;
316 }
317
318
319 static inline void free_async_handler( struct mi_handler *hdl )
320 {
321         if (hdl)
322                 shm_free(hdl);
323 }
324
325
326 /*! \brief Open reply fifo, write message and close it */
327 static void fifo_close_async( struct mi_root *mi_rpl, struct mi_handler *hdl, int done)
328 {
329         FILE *reply_stream;
330         char *name;
331
332         name = (char*)hdl->param;
333
334         if ( mi_rpl!=0 || done ) {
335                 /*open fifo reply*/
336                 reply_stream = mi_open_reply_pipe( name );
337                 if (reply_stream==NULL) {
338                         LM_ERR("Cannot open reply pipe %s\n", name );
339                         return;
340                 }
341
342                 if (mi_rpl!=0) {
343                         mi_write_tree( reply_stream, mi_rpl);
344                         free_mi_tree( mi_rpl );
345                 } else {
346                         mi_fifo_reply( reply_stream, "500 command failed\n");
347                 }
348
349                 fclose(reply_stream);
350         }
351
352         if (done)
353                 free_async_handler( hdl );
354         return;
355 }
356
357
358 static inline struct mi_handler* build_async_handler( char *name, int len)
359 {
360         struct mi_handler *hdl;
361         char *p;
362
363         hdl = (struct mi_handler*)shm_malloc( sizeof(struct mi_handler) + len + 1);
364         if (hdl==0) {
365                 LM_ERR("no more shared memory\n");
366                 return 0;
367         }
368
369         p = (char*)(hdl) + sizeof(struct mi_handler);
370         memcpy( p, name, len+1 );
371
372         hdl->handler_f = fifo_close_async;
373         hdl->param = (void*)p;
374
375         return hdl;
376 }
377
378
379 #define mi_do_consume() \
380         do { \
381                 LM_DBG("entered consume\n"); \
382                 /* consume the rest of the fifo request */ \
383                 do { \
384                         mi_read_line(mi_buf,MAX_MI_FIFO_BUFFER,fifo_stream,&line_len); \
385                 } while(line_len>1); \
386                 LM_DBG("**** done consume\n"); \
387         } while(0)
388
389
390 #define mi_open_reply(_name,_file,_err) \
391         do { \
392                 _file = mi_open_reply_pipe( _name ); \
393                 if (_file==NULL) { \
394                         LM_ERR("cannot open reply pipe %s\n", _name); \
395                         goto _err; \
396                 } \
397         } while(0)
398
399
400
401 /*! \brief The actual MI Fifo Server */
402 void mi_fifo_server(FILE *fifo_stream)
403 {
404         struct mi_root *mi_cmd;
405         struct mi_root *mi_rpl;
406         struct mi_handler *hdl;
407         int line_len;
408         char *file_sep, *command, *file;
409         struct mi_cmd *f;
410         FILE *reply_stream;
411
412         while(1) {
413                 /* update the local config framework structures */
414                 cfg_update();
415
416                 reply_stream = NULL;
417
418                 /* commands must look this way ':<command>:[filename]' */
419                 if (mi_read_line(mi_buf,MAX_MI_FIFO_BUFFER,fifo_stream, &line_len)) {
420                         LM_ERR("failed to read fifo command\n");
421                         goto consume1;
422                 }
423
424                 /* trim from right */
425                 while(line_len) {
426                         if(mi_buf[line_len-1]=='\n' || mi_buf[line_len-1]=='\r'
427                                 || mi_buf[line_len-1]==' ' || mi_buf[line_len-1]=='\t' ) {
428                                 line_len--;
429                                 mi_buf[line_len]=0;
430                         } else break;
431                 } 
432
433                 if (line_len==0) {
434                         LM_DBG("fifo command empty\n");
435                         goto consume1;
436                 }
437                 if (line_len<3) {
438                         LM_ERR("fifo command must have at least 3 chars\n");
439                         goto consume1;
440                 }
441                 if (*mi_buf!=MI_CMD_SEPARATOR) {
442                         LM_ERR("fifo command must begin with %c: %.*s\n", MI_CMD_SEPARATOR, line_len, mi_buf );
443                         goto consume1;
444                 }
445                 command = mi_buf+1;
446                 file_sep=strchr(command, MI_CMD_SEPARATOR );
447                 if (file_sep==NULL) {
448                         LM_ERR("file separator missing in fifo command\n");
449                         goto consume1;
450                 }
451                 if (file_sep==command) {
452                         LM_ERR("empty fifo command\n");
453                         goto consume1;
454                 }
455                 if (*(file_sep+1)==0) {
456                         file = NULL;
457                 } else {
458                         file = file_sep+1;
459                         file = get_reply_filename(file, mi_buf+line_len-file);
460                         if (file==NULL) {
461                                 LM_ERR("trimming fifo filename\n");
462                                 goto consume1;
463                         }
464                 }
465                 /* make command zero-terminated */
466                 *file_sep=0;
467
468                 f=lookup_mi_cmd( command, strlen(command) );
469                 if (f==0) {
470                         LM_ERR("fifo command %s is not available\n", command);
471                         mi_open_reply( file, reply_stream, consume1);
472                         mi_fifo_reply( reply_stream, "500 command '%s' not available\n",
473                                 command);
474                         goto consume2;
475                 }
476
477                 /* if asyncron cmd, build the async handler */
478                 if (f->flags&MI_ASYNC_RPL_FLAG) {
479                         hdl = build_async_handler( file, strlen(file) );
480                         if (hdl==0) {
481                                 LM_ERR("failed to build async fifo handler\n");
482                                 mi_open_reply( file, reply_stream, consume1);
483                                 mi_fifo_reply( reply_stream, "500 Internal server error\n");
484                                 goto consume2;
485                         }
486                 } else {
487                         hdl = 0;
488                         mi_open_reply( file, reply_stream, consume1);
489                 }
490
491                 if (f->flags&MI_NO_INPUT_FLAG) {
492                         mi_cmd = 0;
493                         mi_do_consume();
494                 } else {
495                         mi_cmd = mi_parse_tree(fifo_stream);
496                         if (mi_cmd==NULL){
497                                 LM_ERR("error parsing MI tree\n");
498                                 if (!reply_stream)
499                                         mi_open_reply( file, reply_stream, consume3);
500                                 mi_fifo_reply( reply_stream, "400 parse error in "
501                                         "command '%s'\n", command);
502                                 goto consume3;
503                         }
504                         mi_cmd->async_hdl = hdl;
505                 }
506
507                 LM_DBG("done parsing the mi tree\n");
508
509                 if ( (mi_rpl=run_mi_cmd(f, mi_cmd))==0 ){
510                         if (!reply_stream)
511                                 mi_open_reply( file, reply_stream, failure);
512                         mi_fifo_reply(reply_stream, "500 command '%s' failed\n", command);
513                         LM_ERR("command (%s) processing failed\n", command );
514                 } else if (mi_rpl!=MI_ROOT_ASYNC_RPL) {
515                         if (!reply_stream)
516                                 mi_open_reply( file, reply_stream, failure);
517                         mi_write_tree( reply_stream, mi_rpl);
518                         free_mi_tree( mi_rpl );
519                 } else {
520                         if (mi_cmd)
521                                 free_mi_tree( mi_cmd );
522                         continue;
523                 }
524
525                 free_async_handler(hdl);
526                 /* close reply fifo */
527                 fclose(reply_stream);
528                 /* destroy request tree */
529                 if (mi_cmd)
530                         free_mi_tree( mi_cmd );
531                 continue;
532
533 failure:
534                 free_async_handler(hdl);
535                 /* destroy request tree */
536                 if (mi_cmd)
537                         free_mi_tree( mi_cmd );
538                 /* destroy the reply tree */
539                 if (mi_rpl)
540                         free_mi_tree(mi_rpl);
541                 continue;
542
543 consume3:
544                 free_async_handler(hdl);
545                 if (reply_stream)
546 consume2:
547                 fclose(reply_stream);
548 consume1:
549                 mi_do_consume();
550         }
551 }