fifo_fnc.c

Go to the documentation of this file.
00001 /*
00002  * $Id: fifo_fnc.c 5514 2009-01-26 12:33:24Z henningw $
00003  *
00004  * Copyright (C) 2001-2003 FhG Fokus
00005  * Copyright (C) 2006 Voice Sistem SRL
00006  *
00007  * This file is part of Kamailio, a free SIP server.
00008  *
00009  * Kamailio is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * Kamailio is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00022  *
00023  *
00024  * History:
00025  * ---------
00026  *  2006-09-25  first version (bogdan)
00027  */
00028 
00029 /*!
00030  * \file
00031  * \brief MI Fifo :: Functions
00032  * \ingroup mi
00033  */
00034 
00035 #include <stdlib.h>
00036 #include <stdio.h>
00037 #include <string.h>
00038 #include <sys/types.h>
00039 #include <sys/stat.h>
00040 #include <errno.h>
00041 #include <fcntl.h>
00042 #include <unistd.h>
00043 #include <signal.h>
00044 
00045 #include "../../dprint.h"
00046 #include "../../ut.h"
00047 #include "../../mi/mi.h"
00048 #include "../../mem/mem.h"
00049 #include "../../mem/shm_mem.h"
00050 #include "mi_fifo.h"
00051 #include "fifo_fnc.h"
00052 #include "mi_parser.h"
00053 #include "mi_writer.h"
00054 
00055 static int  mi_fifo_read = 0;
00056 static int  mi_fifo_write = 0;
00057 static char *mi_buf = 0;
00058 static char *reply_fifo_s = 0;
00059 static int  reply_fifo_len = 0;
00060 
00061 
00062 /*! \brief Initialize MI Fifo server */
00063 FILE *mi_init_fifo_server(char *fifo_name, int mi_fifo_mode,
00064                   int mi_fifo_uid, int mi_fifo_gid, char* fifo_reply_dir)
00065 {
00066    FILE *fifo_stream;
00067    long opt;
00068 
00069    /* create FIFO ... */
00070    if ((mkfifo(fifo_name, mi_fifo_mode)<0)) {
00071       LM_ERR("Can't create FIFO: %s (mode=%d)\n", strerror(errno), mi_fifo_mode);
00072       return 0;
00073    }
00074 
00075    LM_DBG("FIFO created @ %s\n", fifo_name );
00076 
00077    if ((chmod(fifo_name, mi_fifo_mode)<0)) {
00078       LM_ERR("Can't chmod FIFO: %s (mode=%d)\n", strerror(errno), mi_fifo_mode);
00079       return 0;
00080    }
00081 
00082    if ((mi_fifo_uid!=-1) || (mi_fifo_gid!=-1)){
00083       if (chown(fifo_name, mi_fifo_uid, mi_fifo_gid)<0){
00084          LM_ERR("Failed to change the owner/group for %s  to %d.%d; %s[%d]\n",
00085             fifo_name, mi_fifo_uid, mi_fifo_gid, strerror(errno), errno);
00086          return 0;
00087       }
00088    }
00089 
00090    LM_DBG("fifo %s opened, mode=%o\n", fifo_name, mi_fifo_mode );
00091 
00092    /* open it non-blocking or else wait here until someone
00093     * opens it for writing */
00094    mi_fifo_read=open(fifo_name, O_RDONLY|O_NONBLOCK, 0);
00095    if (mi_fifo_read<0) {
00096       LM_ERR("Can't open fifo %s for reading - mi_fifo_read did not open: %s\n", fifo_name, strerror(errno));
00097       return 0;
00098    }
00099 
00100    fifo_stream = fdopen(mi_fifo_read, "r");
00101    if (fifo_stream==NULL) {
00102       LM_ERR("fdopen failed on %s: %s\n", fifo_name, strerror(errno));
00103       return 0;
00104    }
00105 
00106    /* make sure the read fifo will not close */
00107    mi_fifo_write=open( fifo_name, O_WRONLY|O_NONBLOCK, 0);
00108    if (mi_fifo_write<0) {
00109       LM_ERR("fifo_write did not open: %s\n", strerror(errno));
00110       return 0;
00111    }
00112    /* set read fifo blocking mode */
00113    if ((opt=fcntl(mi_fifo_read, F_GETFL))==-1){
00114       LM_ERR("fcntl(F_GETFL) failed: %s [%d]\n", strerror(errno), errno);
00115       return 0;
00116    }
00117    if (fcntl(mi_fifo_read, F_SETFL, opt & (~O_NONBLOCK))==-1){
00118       LM_ERR("cntl(F_SETFL) failed: %s [%d]\n", strerror(errno), errno);
00119       return 0;
00120    }
00121 
00122    /* allocate all static buffers */
00123    mi_buf = pkg_malloc(MAX_MI_FIFO_BUFFER);
00124    reply_fifo_s = pkg_malloc(MAX_MI_FILENAME);
00125    if ( mi_buf==NULL|| reply_fifo_s==NULL) {
00126       LM_ERR("no more private memory\n");
00127       return 0;
00128    }
00129 
00130    /* init fifo reply dir buffer */
00131    reply_fifo_len = strlen(fifo_reply_dir);
00132    memcpy( reply_fifo_s, fifo_reply_dir, reply_fifo_len);
00133 
00134    return fifo_stream;
00135 }
00136 
00137 
00138 
00139 /*! \brief reply fifo security checks:
00140  *
00141  * checks if fd is a fifo, is not hardlinked and it's not a softlink
00142  * opened file descriptor + file name (for soft link check)
00143  * \return 0 if ok, <0 if not */
00144 static int mi_fifo_check(int fd, char* fname)
00145 {
00146    struct stat fst;
00147    struct stat lst;
00148    
00149    if (fstat(fd, &fst)<0){
00150       LM_ERR("security: fstat on %s failed: %s\n", fname, strerror(errno));
00151       return -1;
00152    }
00153    /* check if fifo */
00154    if (!S_ISFIFO(fst.st_mode)){
00155       LM_ERR("security: %s is not a fifo\n", fname);
00156       return -1;
00157    }
00158    /* check if hard-linked */
00159    if (fst.st_nlink>1){
00160       LM_ERR("security: fifo_check: %s is hard-linked %d times\n", fname, (unsigned)fst.st_nlink);
00161       return -1;
00162    }
00163 
00164    /* lstat to check for soft links */
00165    if (lstat(fname, &lst)<0){
00166       LM_ERR("security: lstat on %s failed: %s\n", fname, strerror(errno));
00167       return -1;
00168    }
00169    if (S_ISLNK(lst.st_mode)){
00170       LM_ERR("security: fifo_check: %s is a soft link\n", fname);
00171       return -1;
00172    }
00173    /* if this is not a symbolic link, check to see if the inode didn't
00174     * change to avoid possible sym.link, rm sym.link & replace w/ fifo race
00175     */
00176    if ((lst.st_dev!=fst.st_dev)||(lst.st_ino!=fst.st_ino)){
00177       LM_ERR("security: fifo_check: inode/dev number differ: %d %d (%s)\n",
00178          (int)fst.st_ino, (int)lst.st_ino, fname);
00179       return -1;
00180    }
00181    /* success */
00182    return 0;
00183 }
00184 
00185 
00186 
00187 /*! \brief Open reply pipe (filename given in MI command) */
00188 static FILE *mi_open_reply_pipe( char *pipe_name )
00189 {
00190    int fifofd;
00191    FILE *file_handle;
00192    int flags;
00193 
00194    int retries=FIFO_REPLY_RETRIES;
00195 
00196    if (!pipe_name || *pipe_name==0) {
00197       LM_DBG("No file to write to about missing cmd\n");
00198       return 0;
00199    }
00200 
00201 tryagain:
00202    /* open non-blocking to make sure that a broken client will not 
00203     * block the FIFO server forever */
00204    fifofd=open( pipe_name, O_WRONLY | O_NONBLOCK );
00205    if (fifofd==-1) {
00206       /* retry several times if client is not yet ready for getting
00207          feedback via a reply pipe
00208       */
00209       if (errno==ENXIO) {
00210          /* give up on the client - we can't afford server blocking */
00211          if (retries==0) {
00212             LM_ERR("no client at %s\n",pipe_name );
00213             return 0;
00214          }
00215          /* don't be noisy on the very first try */
00216          if (retries != FIFO_REPLY_RETRIES)
00217             LM_DBG("mi_fifo retry countdown: %d\n", retries );
00218          sleep_us( FIFO_REPLY_WAIT );
00219          retries--;
00220          goto tryagain;
00221       }
00222       /* some other opening error */
00223       LM_ERR("open error (%s): %s\n", pipe_name, strerror(errno));
00224       return 0;
00225    }
00226 
00227    /* security checks: is this really a fifo?, is 
00228     * it hardlinked? is it a soft link? */
00229    if (mi_fifo_check(fifofd, pipe_name)<0)
00230       goto error;
00231 
00232    /* we want server blocking for big writes */
00233    if ( (flags=fcntl(fifofd, F_GETFL, 0))<0) {
00234       LM_ERR("pipe (%s): getfl failed: %s\n", pipe_name, strerror(errno));
00235       goto error;
00236    }
00237    flags&=~O_NONBLOCK;
00238    if (fcntl(fifofd, F_SETFL, flags)<0) {
00239       LM_ERR("pipe (%s): setfl cntl failed: %s\n", pipe_name, strerror(errno));
00240       goto error;
00241    }
00242 
00243    /* create an I/O stream */
00244    file_handle=fdopen( fifofd, "w");
00245    if (file_handle==NULL) {
00246       LM_ERR("open error (%s): %s\n",
00247          pipe_name, strerror(errno));
00248       goto error;
00249    }
00250    return file_handle;
00251 error:
00252    close(fifofd);
00253    return 0;
00254 }
00255 
00256 
00257 
00258 /*! \brief Read input on fifo */
00259 int mi_read_line( char *b, int max, FILE *stream, int *read)
00260 {
00261    int retry_cnt;
00262    int len;
00263    retry_cnt=0;
00264 
00265 retry:
00266    if (fgets(b, max, stream)==NULL) {
00267       LM_ERR("fifo_server fgets failed: %s\n", strerror(errno));
00268       /* on Linux, fgets sometimes returns ESPIPE -- give
00269          it few more chances
00270       */
00271       if (errno==ESPIPE) {
00272          retry_cnt++;
00273          if (retry_cnt<4)
00274             goto retry;
00275       }
00276       /* interrupted by signal or ... */
00277       if ((errno==EINTR)||(errno==EAGAIN))
00278          goto retry;
00279       kill(0, SIGTERM);
00280    }
00281    /* if we did not read whole line, our buffer is too small
00282       and we cannot process the request; consume the remainder of 
00283       request
00284    */
00285 
00286    len=strlen(b);
00287    if (len && !(b[len-1]=='\n' || b[len-1]=='\r')) {
00288       LM_ERR("request line too long\n");
00289       return -1;
00290    }
00291    *read = len;
00292 
00293    return 0;
00294 }
00295 
00296 
00297 
00298 /*! \brief Parse out reply file name from MI request */
00299 static inline char *get_reply_filename( char * file, int len )
00300 {
00301    if ( strchr(file,'.') || strchr(file,'/') || strchr(file, '\\') ) {
00302       LM_ERR("Forbidden reply fifo filename: %s\n", file);
00303       return 0;
00304    }
00305 
00306    if (reply_fifo_len + len + 1 > MAX_MI_FILENAME) {
00307       LM_ERR("Reply fifo filename too long %d\n",reply_fifo_len + len);
00308       return 0;
00309    }
00310 
00311    memcpy( reply_fifo_s+reply_fifo_len, file, len );
00312    reply_fifo_s[reply_fifo_len+len]=0;
00313 
00314    return reply_fifo_s;
00315 }
00316 
00317 
00318 static inline void free_async_handler( struct mi_handler *hdl )
00319 {
00320    if (hdl)
00321       shm_free(hdl);
00322 }
00323 
00324 
00325 /*! \brief Open reply fifo, write message and close it */
00326 static void fifo_close_async( struct mi_root *mi_rpl, struct mi_handler *hdl, int done)
00327 {
00328    FILE *reply_stream;
00329    char *name;
00330 
00331    name = (char*)hdl->param;
00332 
00333    if ( mi_rpl!=0 || done ) {
00334       /*open fifo reply*/
00335       reply_stream = mi_open_reply_pipe( name );
00336       if (reply_stream==NULL) {
00337          LM_ERR("Cannot open reply pipe %s\n", name );
00338          return;
00339       }
00340 
00341       if (mi_rpl!=0) {
00342          mi_write_tree( reply_stream, mi_rpl);
00343          free_mi_tree( mi_rpl );
00344       } else {
00345          mi_fifo_reply( reply_stream, "500 command failed\n");
00346       }
00347 
00348       fclose(reply_stream);
00349    }
00350 
00351    if (done)
00352       free_async_handler( hdl );
00353    return;
00354 }
00355 
00356 
00357 static inline struct mi_handler* build_async_handler( char *name, int len)
00358 {
00359    struct mi_handler *hdl;
00360    char *p;
00361 
00362    hdl = (struct mi_handler*)shm_malloc( sizeof(struct mi_handler) + len + 1);
00363    if (hdl==0) {
00364       LM_ERR("no more shared memory\n");
00365       return 0;
00366    }
00367 
00368    p = (char*)(hdl) + sizeof(struct mi_handler);
00369    memcpy( p, name, len+1 );
00370 
00371    hdl->handler_f = fifo_close_async;
00372    hdl->param = (void*)p;
00373 
00374    return hdl;
00375 }
00376 
00377 
00378 #define mi_do_consume() \
00379    do { \
00380       LM_DBG("entered consume\n"); \
00381       /* consume the rest of the fifo request */ \
00382       do { \
00383          mi_read_line(mi_buf,MAX_MI_FIFO_BUFFER,fifo_stream,&line_len); \
00384       } while(line_len>1); \
00385       LM_DBG("**** done consume\n"); \
00386    } while(0)
00387 
00388 
00389 #define mi_open_reply(_name,_file,_err) \
00390    do { \
00391       _file = mi_open_reply_pipe( _name ); \
00392       if (_file==NULL) { \
00393          LM_ERR("cannot open reply pipe %s\n", _name); \
00394          goto _err; \
00395       } \
00396    } while(0)
00397 
00398 
00399 
00400 /*! \brief The actual MI Fifo Server */
00401 void mi_fifo_server(FILE *fifo_stream)
00402 {
00403    struct mi_root *mi_cmd;
00404    struct mi_root *mi_rpl;
00405    struct mi_handler *hdl;
00406    int line_len;
00407    char *file_sep, *command, *file;
00408    struct mi_cmd *f;
00409    FILE *reply_stream;
00410 
00411    while(1) {
00412       reply_stream = NULL;
00413 
00414       /* commands must look this way ':<command>:[filename]' */
00415       if (mi_read_line(mi_buf,MAX_MI_FIFO_BUFFER,fifo_stream, &line_len)) {
00416          LM_ERR("failed to read fifo command\n");
00417          goto consume1;
00418       }
00419 
00420       /* trim from right */
00421       while(line_len) {
00422          if(mi_buf[line_len-1]=='\n' || mi_buf[line_len-1]=='\r'
00423             || mi_buf[line_len-1]==' ' || mi_buf[line_len-1]=='\t' ) {
00424             line_len--;
00425             mi_buf[line_len]=0;
00426          } else break;
00427       } 
00428 
00429       if (line_len==0) {
00430          LM_DBG("fifo command empty\n");
00431          goto consume1;
00432       }
00433       if (line_len<3) {
00434          LM_ERR("fifo command must have at least 3 chars\n");
00435          goto consume1;
00436       }
00437       if (*mi_buf!=MI_CMD_SEPARATOR) {
00438          LM_ERR("fifo command must begin with %c: %.*s\n", MI_CMD_SEPARATOR, line_len, mi_buf );
00439          goto consume1;
00440       }
00441       command = mi_buf+1;
00442       file_sep=strchr(command, MI_CMD_SEPARATOR );
00443       if (file_sep==NULL) {
00444          LM_ERR("file separator missing in fifo command\n");
00445          goto consume1;
00446       }
00447       if (file_sep==command) {
00448          LM_ERR("empty fifo command\n");
00449          goto consume1;
00450       }
00451       if (*(file_sep+1)==0) {
00452          file = NULL;
00453       } else {
00454          file = file_sep+1;
00455          file = get_reply_filename(file, mi_buf+line_len-file);
00456          if (file==NULL) {
00457             LM_ERR("trimming fifo filename\n");
00458             goto consume1;
00459          }
00460       }
00461       /* make command zero-terminated */
00462       *file_sep=0;
00463 
00464       f=lookup_mi_cmd( command, strlen(command) );
00465       if (f==0) {
00466          LM_ERR("fifo command %s is not available\n", command);
00467          mi_open_reply( file, reply_stream, consume1);
00468          mi_fifo_reply( reply_stream, "500 command '%s' not available\n",
00469             command);
00470          goto consume2;
00471       }
00472 
00473       /* if asyncron cmd, build the async handler */
00474       if (f->flags&MI_ASYNC_RPL_FLAG) {
00475          hdl = build_async_handler( file, strlen(file) );
00476          if (hdl==0) {
00477             LM_ERR("failed to build async fifo handler\n");
00478             mi_open_reply( file, reply_stream, consume1);
00479             mi_fifo_reply( reply_stream, "500 Internal server error\n");
00480             goto consume2;
00481          }
00482       } else {
00483          hdl = 0;
00484          mi_open_reply( file, reply_stream, consume1);
00485       }
00486 
00487       if (f->flags&MI_NO_INPUT_FLAG) {
00488          mi_cmd = 0;
00489          mi_do_consume();
00490       } else {
00491          mi_cmd = mi_parse_tree(fifo_stream);
00492          if (mi_cmd==NULL){
00493             LM_ERR("error parsing MI tree\n");
00494             if (!reply_stream)
00495                mi_open_reply( file, reply_stream, consume3);
00496             mi_fifo_reply( reply_stream, "400 parse error in "
00497                "command '%s'\n", command);
00498             goto consume3;
00499          }
00500          mi_cmd->async_hdl = hdl;
00501       }
00502 
00503       LM_DBG("done parsing the mi tree\n");
00504 
00505       if ( (mi_rpl=run_mi_cmd(f, mi_cmd))==0 ){
00506          if (!reply_stream)
00507             mi_open_reply( file, reply_stream, failure);
00508          mi_fifo_reply(reply_stream, "500 command '%s' failed\n", command);
00509          LM_ERR("command (%s) processing failed\n", command );
00510       } else if (mi_rpl!=MI_ROOT_ASYNC_RPL) {
00511          if (!reply_stream)
00512             mi_open_reply( file, reply_stream, failure);
00513          mi_write_tree( reply_stream, mi_rpl);
00514          free_mi_tree( mi_rpl );
00515       } else {
00516          if (mi_cmd)
00517             free_mi_tree( mi_cmd );
00518          continue;
00519       }
00520 
00521       free_async_handler(hdl);
00522       /* close reply fifo */
00523       fclose(reply_stream);
00524       /* destroy request tree */
00525       if (mi_cmd)
00526          free_mi_tree( mi_cmd );
00527       continue;
00528 
00529 failure:
00530       free_async_handler(hdl);
00531       /* destroy request tree */
00532       if (mi_cmd)
00533          free_mi_tree( mi_cmd );
00534       /* destroy the reply tree */
00535       if (mi_rpl)
00536          free_mi_tree(mi_rpl);
00537       continue;
00538 
00539 consume3:
00540       free_async_handler(hdl);
00541       if (reply_stream)
00542 consume2:
00543       fclose(reply_stream);
00544 consume1:
00545       mi_do_consume();
00546    }
00547 }

Generated on Wed May 23 06:00:45 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6