error reporting from FIFO commands introduced
[sip-router] / fifo_server.c
1 /*
2  * $Id$
3  *
4  * Fifo server is a very powerful tool used to access easily
5  * ser's internals via textual interface, similarly to
6  * how internals of many operating systems are accessible
7  * via the proc file system. This might be used for
8  * making ser do things for you (such as initiating new
9  * transaction from webpages) or inspect server's health.
10  * 
11  * FIFO server allows new functionality to be registered
12  * with it -- thats what register_fifo_cmd is good for.
13  * Remember, the initialization must take place before
14  * forking; best in init_module functions. When a function
15  * is registered, it can be always evoked by sending its
16  * name prefixed by colon to the FIFO.
17  *
18  * There are few commands already implemented in core.
19  * These are 'uptime' for looking at how long the server
20  * is alive and 'print' for debugging purposes.
21  *
22  * Every command sent to FIFO must be sent atomically to
23  * avoid intermixing with other commands and MUST be
24  * terminated by empty line so that the server is to able
25  * to find its end if it does not understand the command.
26  *
27  * File test/transaction.fifo illustrates example of use
28  * of t_uac command (part of TM module).
29  */
30
31 #include <stdlib.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <signal.h>
40 #include <string.h>
41 #include <time.h>
42 #include "dprint.h"
43 #include "ut.h"
44 #include "error.h"
45 #include "config.h"
46 #include "globals.h"
47 #include "fifo_server.h"
48 #include "mem/mem.h"
49 #include "sr_module.h"
50
51 /* FIFO server vars */
52 char *fifo="/tmp/ser_fifo"; /* FIFO name */
53 int fifo_mode=S_IRUSR | S_IWUSR | S_IRGRP | 
54         S_IWGRP | S_IROTH | S_IWOTH;
55 pid_t fifo_pid;
56 /* file descriptors */
57 static int fifo_read=0;
58 static int fifo_write=0;
59 static FILE *fifo_stream;
60
61 /* list of fifo command */
62 static struct fifo_command *cmd_list=0;
63
64 /* up time */
65 static time_t up_since;
66
67 static struct fifo_command *lookup_fifo_cmd( char *name )
68 {
69         struct fifo_command *c;
70         for(c=cmd_list; c; c=c->next) {
71                 if (strcasecmp(c->name, name)==0) return c;
72         }
73         return 0;
74 }
75
76 int register_fifo_cmd(fifo_cmd f, char *cmd_name, void *param)
77 {
78         struct fifo_command *new_cmd;
79
80         if (lookup_fifo_cmd(cmd_name)) {
81                 LOG(L_ERR, "ERROR: register_fifo_cmd: attempt to register synonyms\n");
82                 return E_BUG;
83         }
84         new_cmd=malloc(sizeof(struct fifo_command));
85         if (new_cmd==0) {
86                 LOG(L_ERR, "ERROR: register_fifo_cmd: out of mem\n");
87                 return E_OUT_OF_MEM;
88         }
89         new_cmd->f=f;
90         new_cmd->name=cmd_name;
91         new_cmd->param=param;
92
93         new_cmd->next=cmd_list;
94         cmd_list=new_cmd;
95
96         return 1;
97 }
98
99
100 int read_line( char *b, int max, FILE *stream, int *read )
101 {
102         int len;
103         int retry_cnt;
104
105         retry_cnt=0;
106
107 retry:
108         if (fgets(b, max, stream)==NULL) {
109                 LOG(L_ERR, "ERROR: fifo_server fgets failed: %s\n",
110                         strerror(errno));
111                 /* on Linux, fgets sometimes returns ESPIPE -- give
112                    it few more chances
113                 */
114                 if (errno==ESPIPE) {
115                         retry_cnt++;
116                         if (retry_cnt<4) goto retry;
117                 }
118                 kill(0, SIGTERM);
119         }
120         /* if we did not read whole line, our buffer is too small
121            and we cannot process the request; consume the remainder of 
122            request
123         */
124         len=strlen(b);
125         if (len && !(b[len-1]=='\n' || b[len-1]=='\r')) {
126                 LOG(L_ERR, "ERROR: read_line: request  line too long\n");
127                 return 0;
128         }
129         /* trim from right */
130         while(len) {
131                 if(b[len-1]=='\n' || b[len-1]=='\r'
132                                 || b[len-1]==' ' || b[len-1]=='\t' ) {
133                         len--;
134                         b[len]=0;
135                 } else break;
136         }
137         *read=len;
138         return 1;
139 }
140
141 static void consume_request( FILE *stream )
142 {
143         int len;
144         char buffer[MAX_CONSUME_BUFFER];
145
146         while(!read_line(buffer, MAX_CONSUME_BUFFER, stream, &len));
147
148 #ifdef _OBSOLETED
149         int eol_count;
150
151         eol_count=0;
152
153         /* each request must be terminated by two EoLs */
154         while(eol_count!=2) {
155                 /* read until EoL is encountered */
156                 while(!read_line(buffer, MAX_CONSUME_BUFFER, stream, &len));
157                 eol_count=len==0?eol_count+1:1;
158         }
159 #endif
160 }
161
162 int read_eol( FILE *stream )
163 {
164         int len;
165         char buffer[MAX_CONSUME_BUFFER];
166         if (!read_line(buffer, MAX_CONSUME_BUFFER, stream, &len) || len!=0) {
167                 LOG(L_ERR, "ERROR: read_eol: EOL expected: %.10s...\n",
168                         buffer );
169                 return 0;
170         }
171         return 1;
172 }
173
174 /* read from input until empty line is encountered */   
175 int read_line_set(char *buf, int max_len, FILE *fifo, int *len)
176 {
177         int set_len;
178         char *c;
179         int line_len;
180
181         c=buf;set_len=0;
182         while(1) {
183                 if (!read_line(c,max_len,fifo,&line_len)) {
184                         LOG(L_ERR, "ERROR: fifo_server: line expected\n");
185                         return 0;
186                 }
187                 /* end encountered ... return */
188                 if (line_len==0) {
189                         *len=set_len;
190                         return 1;
191                 }
192                 max_len-=line_len; c+=line_len; set_len+=line_len;
193                 if (max_len<CRLF_LEN) {
194                         LOG(L_ERR, "ERROR: fifo_server: no place for CRLF\n");
195                         return 0;
196                 }
197                 memcpy(c, CRLF, CRLF_LEN);
198                 max_len-=CRLF_LEN; c+=CRLF_LEN; set_len+=CRLF_LEN;
199         }
200 }
201
202 /* read from input until line with only dot in it is encountered */
203 int read_body(char *buf, int max_len, FILE *fifo, int *len)
204 {
205         int set_len;
206         char *c;
207         int line_len;
208
209         c=buf;set_len=0;
210         while(1) {
211                 if (!read_line(c,max_len,fifo,&line_len)) {
212                         LOG(L_ERR, "ERROR: fifo_server: line expected\n");
213                         return 0;
214                 }
215                 /* end encountered ... return */
216                 if (line_len==1 && *c=='.') {
217                         *len=set_len;
218                         return 1;
219                 }
220                 max_len-=line_len; c+=line_len; set_len+=line_len;
221                 if (max_len<CRLF_LEN) {
222                         LOG(L_ERR, "ERROR: fifo_server: no place for CRLF\n");
223                         return 0;
224                 }
225                 memcpy(c, CRLF, CRLF_LEN);
226                 max_len-=CRLF_LEN; c+=CRLF_LEN; set_len+=CRLF_LEN;
227         }
228 }
229
230 static char *trim_filename( char * file )
231 {
232         int prefix_len, fn_len;
233         char *new_fn;
234
235         /* we only allow files in "/tmp" -- any directory
236            changes are not welcome
237         */
238         if (strchr(file,'.') || strchr(file,'/')
239                                 || strchr(file, '\\')) {
240                 LOG(L_ERR, "ERROR: trim_filename: forbidden filename: %s\n"
241                         , file);
242                 return 0;
243         }
244         prefix_len=strlen(FIFO_DIR); fn_len=strlen(file);
245         new_fn=pkg_malloc(prefix_len+fn_len+1);
246         if (new_fn==0) {
247                 LOG(L_ERR, "ERROR: trim_filename: no mem\n");
248                 return 0;
249         }
250
251         memcpy(new_fn, FIFO_DIR, prefix_len);
252         memcpy(new_fn+prefix_len, file, fn_len );
253         new_fn[prefix_len+fn_len]=0;
254
255         return new_fn;
256 }
257
258 /* tell FIFO client what happened via reply pipe */
259 void fifo_reply( char *reply_fifo, char *reply_txt)
260 {
261         FILE *file_handle;
262
263         file_handle=open_reply_pipe(reply_fifo);
264         if (file_handle==0) {
265                 LOG(L_ERR, "ERROR: fifo_reply: no reply pipe %s\n",
266                         fifo);
267                 return;
268         }
269         if (fprintf(file_handle, "%s", reply_txt)<=0) {
270                 LOG(L_ERR, "ERROR: fifo_error: write error (%s): %s\n",
271                         fifo, strerror(errno));
272         }
273         fclose(file_handle);
274 }
275
276 FILE *open_reply_pipe( char *pipe_name )
277 {
278         FILE *file_handle;
279
280         if (!pipe_name) {
281                 DBG("DEBUG: open_reply_pipe: no file to write to about missing cmd\n");
282                 return 0;
283         }
284         file_handle=fopen( pipe_name, "w");
285         if (file_handle==NULL) {
286                 LOG(L_ERR, "ERROR: open_reply_pipe: open error (%s): %s\n",
287                         pipe_name, strerror(errno));
288                 return 0;
289         }
290         return file_handle;
291 }
292
293 static void fifo_server(FILE *fifo_stream)
294 {
295         char buf[MAX_FIFO_COMMAND];
296         int line_len;
297         char *file_sep, *command, *file;
298         struct fifo_command *f;
299         FILE *file_handle;
300
301         file_sep=command=file=0;
302
303         while(1) {
304
305                 /* commands must look this way ':<command>:[filename]' */
306                 if (!read_line(buf, MAX_FIFO_COMMAND, fifo_stream, &line_len)) {
307                         /* line breaking must have failed -- consume the rest
308                            and proceed to a new request
309                         */
310                         LOG(L_ERR, "ERROR: fifo_server: command expected\n");
311                         goto consume;
312                 }
313                 if (line_len==0) {
314                         LOG(L_INFO, "INFO: fifo_server: command empty\n");
315                         continue;
316                 }
317                 if (line_len<3) {
318                         LOG(L_ERR, "ERROR: fifo_server: command must have at least 3 chars\n");
319                         goto consume;
320                 }
321                 if (*buf!=CMD_SEPARATOR) {
322                         LOG(L_ERR, "ERROR: fifo_server: command must begin with %c: %.*s\n", 
323                                 CMD_SEPARATOR, line_len, buf );
324                         goto consume;
325                 }
326                 command=buf+1;
327                 file_sep=strchr(command, CMD_SEPARATOR );
328                 if (file_sep==NULL) {
329                         LOG(L_ERR, "ERROR: fifo_server: file separator missing\n");
330                         goto consume;
331                 }
332                 if (file_sep==command) {
333                         LOG(L_ERR, "ERROR: fifo_server: empty command\n");
334                         goto consume;
335                 }
336                 if (*(file_sep+1)==0) file=NULL; 
337                 else {
338                         file=file_sep+1;
339                         file=trim_filename(file);
340                         if (file==0) {
341                                 LOG(L_ERR, "ERROR: fifo_server: trimming filename\n");
342                                 goto consume;
343                         }
344                 }
345                 /* make command zero-terminated */
346                 *file_sep=0;
347
348                 f=lookup_fifo_cmd( command );
349                 if (f==0) {
350                         LOG(L_ERR, "ERROR: fifo_server: command %s is not available\n",
351                                 command);
352                         file_handle=open_reply_pipe(file);
353                         if (file_handle==0) {
354                                 LOG(L_ERR, "ERROR: fifo_server: no reply pipe\n");
355                                 goto consume;
356                         }
357                         if (fprintf(file_handle, "[%s not available]\n", command)<=0) {
358                                 LOG(L_ERR, "ERROR: fifo_server: write error: %s\n",
359                                         strerror(errno));
360                         }
361                         fclose(file_handle);
362                         goto consume;
363                 }
364                 if (f->f(fifo_stream, file)<0) {
365                         LOG(L_ERR, "ERROR: fifo_server: command (%s) "
366                                 "processing failed\n", command );
367 #ifdef _OBSOLETED
368                         file_handle=open_reply_pipe(file);
369                         if (file_handle==0) {
370                                 LOG(L_ERR, "ERROR: fifo_server: no reply pipe\n");
371                                 goto consume;
372                         }
373                         if (fprintf(file_handle, "[%s failed]\n", command)<=0) {
374                                 LOG(L_ERR, "ERROR: fifo_server: write error: %s\n",
375                                 strerror(errno));
376                         }
377                         fclose(file_handle);
378 #endif
379                         goto consume;
380                 }
381
382 consume:
383                 if (file) { pkg_free(file); file=0;}
384                 consume_request(fifo_stream);
385         }
386 }
387
388 int open_fifo_server()
389 {
390         if (fifo==NULL) {
391                 DBG("TM: open_uac_fifo: no fifo will be opened\n");
392                 /* everything is ok, we just do not want to start */
393                 return 1;
394         }
395         if (strlen(fifo)==0) {
396                 DBG("TM: open_uac_fifo: fifo disabled\n");
397                 return 1;
398         }
399         DBG("TM: open_uac_fifo: opening fifo...\n");
400         if ((mkfifo(fifo, fifo_mode)<0) && (errno!=EEXIST)) {
401                 LOG(L_ERR, "ERROR: open_fifo_server; can't create FIFO: "
402                         "%s (mode=%d)\n",
403                         strerror(errno), fifo_mode);
404                 return -1;
405         } 
406         DBG("DEBUG: fifo %s opened, mode=%d\n", fifo, fifo_mode );
407         time(&up_since);
408         process_no++;
409         fifo_pid=fork();
410         if (fifo_pid<0) {
411                 LOG(L_ERR, "ERROR: open_fifo_server: failure to fork: %s\n",
412                         strerror(errno));
413                 return -1;
414         }
415         if (fifo_pid==0) { /* child == FIFO server */
416                 LOG(L_INFO, "INFO: fifo process starting: %d\n", getpid());
417                 /* call per-child module initialization too -- some
418                    FIFO commands may need it
419                 */
420                 if (init_child(process_no) < 0 ) {
421                         LOG(L_ERR, "ERROR: open_uac_fifo: init_child failed\n");
422                         return -1;
423                 }
424                 fifo_read=open(fifo, O_RDONLY, 0);
425                 if (fifo_read<0) {
426                         LOG(L_ERR, "ERROR: open_uac_fifo: fifo_read did not open: %s\n",
427                                 strerror(errno));
428                         return -1;
429                 }
430                 fifo_stream=fdopen(fifo_read, "r"       );
431                 if (fifo_stream==NULL) {
432                         LOG(L_ERR, "SER: open_uac_fifo: fdopen failed: %s\n",
433                                 strerror(errno));
434                         return -1;
435                 }
436                 LOG(L_INFO, "SER: open_uac_fifo: fifo server up at %s...\n",
437                         fifo);
438                 fifo_server( fifo_stream ); /* never retruns */
439         }
440         /* dad process */
441         pids[process_no]=fifo_pid;
442         /* make sure the read fifo will not close */
443         fifo_write=open(fifo, O_WRONLY, 0);
444         if (fifo_write<0) {
445                 LOG(L_ERR, "SER: open_uac_fifo: fifo_write did not open: %s\n",
446                         strerror(errno));
447                 return -1;
448         }
449         return 1;
450 }
451
452 static int print_version_cmd( FILE *stream, char *response_file )
453 {
454         if (response_file) {
455                 fifo_reply(response_file, SERVER_HDR CRLF );
456 #ifdef _OBSOLETED
457                 file=open( response_file, O_WRONLY );
458                 if (file<0) {
459                         LOG(L_ERR, "ERROR: print_version_cmd: open error (%s): %s\n",
460                                 response_file, strerror(errno));
461                         return -1;
462                 }
463                 if (write(file, SERVER_HDR CRLF, SERVER_HDR_LEN+CRLF_LEN)<0) {
464                         LOG(L_ERR, "ERROR: print_version_cmd: write error: %s\n",
465                                 strerror(errno));
466                         close(file);
467                         return -1;
468                 }
469                 close(file);
470 #endif
471         } else {
472                 LOG(L_ERR, "ERROR: no file for print_version_cmd\n");
473         }
474         return 1;
475 }
476         
477
478 /* diagnostic and hello-world FIFO command */
479 static int print_fifo_cmd( FILE *stream, char *response_file )
480 {
481         char text[MAX_PRINT_TEXT];
482         int text_len;
483         
484         /* expect one line which will be printed out */
485         if (response_file==0 || *response_file==0 ) { 
486                 LOG(L_ERR, "ERROR: print_fifo_cmd: null file\n");
487                 return -1;
488         }
489         if (!read_line(text, MAX_PRINT_TEXT, stream, &text_len)) {
490 #ifdef _OBSOLETED
491                 LOG(L_ERR, "ERROR: print_fifo_cmd: too big text\n");
492 #endif
493                 fifo_reply(response_file, 
494                         "ERROR: print_fifo_cmd: too big text");
495                 return -1;
496         }
497         /* now the work begins */
498         if (response_file) {
499                 fifo_reply(response_file, text );
500 #ifdef _OBSOLETED
501                 file=open( response_file , O_WRONLY);
502                 if (file<0) {
503                         LOG(L_ERR, "ERROR: print_fifo_cmd: open error (%s): %s\n",
504                                 response_file, strerror(errno));
505                         return -1;
506                 }
507                 if (write(file, text,text_len)<0) {
508                         LOG(L_ERR, "ERROR: print_fifo_cmd: write error: %s\n",
509                                  strerror(errno));
510                         close(file);
511                         return 1;
512                 }
513                 close(file);
514 #endif
515         } else {
516                 LOG(L_INFO, "INFO: print_fifo_cmd: %.*s\n", 
517                         text_len, text );
518         }
519         return 1;
520 }
521
522 static int uptime_fifo_cmd( FILE *stream, char *response_file )
523 {
524         FILE *file;
525         time_t now;
526
527         if (response_file==0 || *response_file==0 ) { 
528                 LOG(L_ERR, "ERROR: uptime_fifo_cmd: null file\n");
529                 return -1;
530         }
531         file=fopen(response_file, "w" );
532         if (file==NULL) {
533                 LOG(L_ERR, "ERROR: uptime_fifo_cmd: file %s bad: %s\n",
534                         response_file, strerror(errno) );
535                 return -1;
536         }
537
538         time(&now);
539         fprintf(file, "Now: %s", ctime(&now) );
540         fprintf(file, "Up since: %s", ctime(&up_since) );
541         fprintf(file, "Up time: %.0f [sec]\n", difftime(now, up_since));
542
543         fclose(file);
544         return 1;
545 }
546
547
548 int register_core_fifo()
549 {
550         if (register_fifo_cmd(print_fifo_cmd, FIFO_PRINT, 0)<0) {
551                 LOG(L_CRIT, "unable to register 'print' FIFO cmd\n");
552                 return -1;
553         }
554         if (register_fifo_cmd(uptime_fifo_cmd, FIFO_UPTIME, 0)<0) {
555                 LOG(L_CRIT, "unable to register 'print' FIFO cmd\n");
556                 return -1;
557         }
558         if (register_fifo_cmd(print_version_cmd, FIFO_VERSION, 0)<0) {
559                 LOG(L_CRIT, "unable to register 'version' FIFO cmd\n");
560                 return -1;
561         }
562         return 1;
563 }