fifo_fnc.c

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