a84073a802d2c26b75f4b2365d6afd4e73015ff5
[sip-router] / src / modules / ctl / fifo_server.c
1 /*
2  * Copyright (C) 2001-2003 FhG Fokus
3  * Copyright (C) 2005 iptelorg GmbH
4  *
5  * This file is part of Kamailio, a free SIP server.
6  *
7  * Kamailio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version
11  *
12  * Kamailio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License 
18  * along with this program; if not, write to the Free Software 
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 /*! \file
24  * \brief ctl module
25  * \ingroup ctl
26  *
27  */
28
29 /*! \defgroup ctl Control binrpc socket
30  *
31  * Fifo server is a very powerful tool used to access easily
32  * ser's internals via textual interface, similarly to
33  * how internals of many operating systems are accessible
34  * via the proc file system. This might be used for
35  * making ser do things for you (such as initiating new
36  * transaction from webpages) or inspect server's health.
37  * 
38  * FIFO server allows new functionality to be registered
39  * with it -- thats what register_fifo_cmd is good for.
40  * Remember, the initialization must take place before
41  * forking; best in init_module functions. When a function
42  * is registered, it can be always evoked by sending its
43  * name prefixed by colon to the FIFO.
44  *
45  * There are few commands already implemented in core.
46  * These are 'uptime' for looking at how long the server
47  * is alive and 'print' for debugging purposes.
48  *
49  * Every command sent to FIFO must be sent atomically to
50  * avoid intermixing with other commands and MUST be
51  * terminated by empty line so that the server is to able
52  * to find its end if it does not understand the command.
53  *
54  * File test/transaction.fifo illustrates example of use
55  * of t_uac command (part of TM module).
56  *
57  */
58
59 #ifdef USE_FIFO
60
61 #include <limits.h>
62 #include <stdlib.h>
63 #include <sys/types.h>
64 #include <unistd.h>
65 #include <stdio.h>
66 #include <errno.h>
67 #include <sys/stat.h>
68 #include <fcntl.h>
69 #include <signal.h>
70 #include <string.h>
71 #include <time.h>
72 #include <stdarg.h>
73 #ifdef USE_TCP
74 #include <sys/socket.h>
75 #endif
76
77 #include "../../core/dprint.h"
78 #include "../../core/ut.h"
79 #include "../../core/error.h"
80 #include "../../core/config.h"
81 #include "../../core/globals.h"
82 #include "../../core/mem/mem.h"
83 #include "../../core/mem/shm_mem.h"
84 #include "../../core/sr_module.h"
85 #include "../../core/pt.h"
86 #include "../../core/rpc.h"
87 #include "../../core/tsend.h"
88 #include "fifo_server.h"
89 #include "io_listener.h"
90 #include "ctl.h"
91
92
93 #define MAX_FIFO_COMMAND        128    /* Maximum length of a FIFO server command */
94 #define MAX_CONSUME_BUFFER     1024    /* Buffer dimensions for FIFO server */
95 #define MAX_LINE_BUFFER        2048    /* Maximum parameter line length */
96 #define DEFAULT_REPLY_RETRIES     4    /* Default number of reply write attempts */
97 #define DEFAULT_REPLY_WAIT    80000    /* How long we should wait for the client, in micro seconds */
98 #define DEFAULT_FIFO_DIR    "/tmp/"    /* Where reply pipes may be opened */
99 #define MAX_MSG_CHUNKS        1024     /* maximum message pieces */
100 #define FIFO_TX_TIMEOUT        200     /* maximum time to block writing to
101                                           the fifo */
102
103 /* readline from a buffer helper */
104 struct readline_handle{ 
105         char* s;    /* buffer start */
106         char* end;  /* end */
107         char* crt;  /* crt. pos */
108 };
109
110 enum text_flags {
111         CHUNK_SEEN         = (1 << 0),
112         CHUNK_POSITIONAL   = (1 << 1), /* Positinal parameter, should be followed by \n */
113         CHUNK_MEMBER_NAME  = (1 << 2), /* Struct member name, should be followed by : */
114         CHUNK_MEMBER_VALUE = (1 << 3)  /* Struct member value, should be followed by , if
115                                         * there is another member name and \n if not */
116 };
117         
118
119 /*
120  * Generit text chunk. Flags attribute contains arbitrary flags
121  */
122 struct text_chunk {
123         unsigned char flags;
124         str s;
125         struct text_chunk* next;
126         void *ctx; /* context, which must be passed along */
127 };
128
129
130 /* 
131  * This is the parameter if rpc_struct_add 
132  */
133 struct rpc_struct_out {
134         struct rpc_context* ctx;
135         struct text_chunk* line;
136 };
137
138
139 struct rpc_struct {
140         struct rpc_context* ctx;
141         struct text_chunk* names;  /* Names of elements */
142         struct text_chunk* values; /* Element values as strings */
143         struct rpc_struct* next;
144 };
145
146
147 /*
148  * Context structure containing state of processing
149  */
150 typedef struct rpc_context {
151         char* method;               /* Request method name */
152         char* reply_file;           /* Full path and name to the reply FIFO file */
153         int reply_sent;             /* This flag ensures that we do not send a reply twice */
154         int code;                   /* Reply code */
155         char* reason;               /* Reason phrase */
156         struct text_chunk* body;    /* First line to be appended as reply body */
157         struct text_chunk* last;    /* Last body line */
158         struct text_chunk* strs;    /* Strings to be collected at the end of processing */
159         struct rpc_struct* structs; /* Structures to be collected at the end of processing */
160         struct readline_handle read_h;
161         struct send_handle* send_h;
162         int line_no;
163 } rpc_ctx_t;
164
165
166
167
168 char* fifo_dir           = DEFAULT_FIFO_DIR;       /* dir where reply fifos are
169                                                                                                           allowed */
170 int   fifo_reply_retries = DEFAULT_REPLY_RETRIES;
171 int   fifo_reply_wait    = DEFAULT_REPLY_WAIT;
172
173
174 static rpc_t     func_param;        /* Pointers to implementation of RPC funtions */
175
176 static int  rpc_send         (rpc_ctx_t* ctx);                                 /* Send the reply to the client */
177 static void rpc_fault        (rpc_ctx_t* ctx,       int code, char* fmt, ...); /* Signal a failure to the client */
178 static int  rpc_add          (rpc_ctx_t* ctx,       char* fmt, ...);           /* Add a new piece of data to the result */
179 static int  rpc_scan         (rpc_ctx_t* ctx,       char* fmt, ...);           /* Retrieve request parameters */
180 static int  rpc_rpl_printf   (rpc_ctx_t* ctx,       char* fmt, ...);           /* Add printf-like formated data to the result set */
181 static int  rpc_struct_add   (struct text_chunk* s, char* fmt, ...);           /* Create a new structure */
182 static int  rpc_struct_scan  (struct rpc_struct* s, char* fmt, ...);           /* Scan attributes of a structure */
183 static int  rpc_struct_printf(struct text_chunk* s, char* name, char* fmt, ...);
184
185
186 /*
187  * Escape string in buffer 'r' of length len. Write
188  * the escaped string in buffer dst. The destination
189  * buffer must exist and must be twice as big as the
190  * input buffer.
191  *
192  * Parameter all controls the set of characters to be
193  * escaped. If set to 1 then all characters, including
194  * structure delimiters, will be escaped. If set to
195  * 0 then only line delimiters, tab and zero will be
196  * escaped.
197  */
198 static void escape(str* dst, char* r, int len, int all)
199 {
200         int i;
201         char* w;
202         if (!len) { 
203                 dst->len = 0;
204                 return;
205         }
206
207         w = dst->s;
208         for(i = 0; i < len; i++) {
209                 switch(r[i]) {
210                 case '\n': *w++ = '\\'; *w++ = 'n';  break;
211                 case '\r': *w++ = '\\'; *w++ = 'r';  break;
212                 case '\t': *w++ = '\\'; *w++ = 't';  break;
213                 case '\\': *w++ = '\\'; *w++ = '\\'; break;
214                 case '\0': *w++ = '\\'; *w++ = '0';  break;
215                 case ':': 
216                         if (all) {
217                                 *w++ = '\\';
218                                 *w++ = 'o';
219                         } else *w++ = r[i];
220                         break;
221                         
222                 case ',':
223                         if (all) {
224                                 *w++ = '\\';
225                                 *w++ = 'c';
226                         } else *w++ = r[i];
227                         break;
228
229                 default:
230                         *w++ = r[i];
231                         break;
232                 }
233         }
234         dst->len = w - dst->s;
235 }
236
237
238 /*
239  * Unescape the string in buffer 'r' of length len.
240  * The resulting string will be stored in buffer dst
241  * which must exist and must be at least as big as
242  * the source buffer. The function will update dst->len
243  * to the length of the resulting string.
244  *
245  * Return value 0 indicates success, -1 indicates
246  * formatting error.
247  */
248 static int unescape(str* dst, char* r, int len)
249 {
250         char* w;
251         int i;
252
253         if (!len) {
254                 dst->len = 0;
255                 return 0;
256         }
257
258         w = dst->s;
259         for(i = 0; i < len; i++) {
260                 switch(*r) {
261                 case '\\':
262                         r++;
263                         i++;
264                         switch(*r++) {
265                         case '\\': *w++ = '\\'; break;
266                         case 'n':  *w++ = '\n'; break;
267                         case 'r':  *w++ = '\r'; break;
268                         case 't':  *w++ = '\t'; break;
269                         case '0':  *w++ = '\0'; break;
270                         case 'c':  *w++ = ':';  break; /* Structure delimiter */
271                         case 'o':  *w++ = ',';  break; /* Structure delimiter */
272                         default:   return -1;
273                         }
274                         break;
275
276                 default: *w++ = *r++; break;
277                 }
278         }
279         dst->len = w - dst->s;
280         return 0;
281 }
282
283
284 /*
285  * Create a new text chunk, the input text will
286  * be escaped.
287  */
288 struct text_chunk* new_chunk_escape(str* src, int escape_all)
289 {
290         struct text_chunk* l;
291         if (!src) return 0;
292
293         l = ctl_malloc(sizeof(struct text_chunk));
294         if (!l) {
295                 ERR("No Memory Left\n");
296                 return 0;
297         }
298         l->s.s = ctl_malloc(src->len * 2 + 1);
299         if (!l->s.s) {
300                 ERR("No Memory Left\n");
301                 ctl_free(l);
302                 return 0;
303         }
304         l->next = 0;
305         l->flags = 0;
306         escape(&l->s, src->s, src->len, escape_all);
307         l->s.s[l->s.len] = '\0';
308         return l;
309 }
310
311 /*
312  * Create a new text chunk, the input text
313  * will not be escaped. The function returns
314  * 0 on an error
315  */
316 struct text_chunk* new_chunk(str* src)
317 {
318         struct text_chunk* l;
319         if (!src) return 0;
320
321         l = ctl_malloc(sizeof(struct text_chunk));
322         if (!l) {
323                 ERR("No Memory Left\n");
324                 return 0;
325         }
326         l->s.s = ctl_malloc(src->len + 1);
327         if (!l->s.s) {
328                 ERR("No Memory Left\n");
329                 ctl_free(l);
330                 return 0;
331         }
332         l->next = 0;
333         l->flags = 0;
334         memcpy(l->s.s, src->s, src->len);
335         l->s.len = src->len;
336         l->s.s[l->s.len] = '\0';
337         return l;
338 }
339
340
341 /*
342  * Create a new text chunk, the input text
343  * will be unescaped first.
344  */
345 struct text_chunk* new_chunk_unescape(str* src)
346 {
347         struct text_chunk* l;
348         if (!src) return 0;
349
350         l = ctl_malloc(sizeof(struct text_chunk));
351         if (!l) {
352                 ERR("No Memory Left\n");
353                 return 0;
354         }
355         l->s.s = ctl_malloc(src->len + 1);
356         if (!l->s.s) {
357                 ERR("No Memory Left\n");
358                 ctl_free(l);
359                 return 0;
360         }
361         l->next = 0;
362         l->flags = 0;
363         if (unescape(&l->s, src->s, src->len) < 0) {
364                 ctl_free(l->s.s);
365                 ctl_free(l);
366                 return 0;
367         }
368         l->s.s[l->s.len] = '\0';
369         return l;
370 }
371
372
373 static void free_chunk(struct text_chunk* c)
374 {
375         if (c && c->s.s) ctl_free(c->s.s);
376         if (c) ctl_free(c);
377 }
378
379
380 static void free_struct(struct rpc_struct* s)
381 {
382         struct text_chunk* c;
383
384         if (!s) return;
385         while(s->names) {
386                 c = s->names;
387                 s->names = s->names->next;
388                 free_chunk(c);
389         }
390
391         while(s->values) {
392                 c = s->values;
393                 s->values = s->values->next;
394                 free_chunk(c);
395         }
396
397         ctl_free(s);
398 }
399
400
401 /*
402  * Parse a structure
403  */
404 static struct rpc_struct* new_struct(rpc_ctx_t* ctx, str* line)
405 {
406         char* comma, *colon;
407         struct rpc_struct* s;
408         str left, right = STR_NULL, name, value;
409         struct text_chunk* n, *v;
410
411         if (!line->len) {
412                 rpc_fault(ctx, 400, "Line %d Empty - Structure Expected", 
413                                         ctx->line_no);
414                 return 0;
415         }
416
417         s = (struct rpc_struct*)ctl_malloc(sizeof(struct rpc_struct));
418         if (!s) {
419                 rpc_fault(ctx, 500, "Internal Server Error (No Memory Left)");
420                 return 0;
421         }
422         memset(s, 0, sizeof(struct rpc_struct));
423         s->ctx = ctx;
424         
425         left = *line;
426         do {
427                 comma = q_memchr(left.s, ',', left.len);
428                 if (comma) {
429                         right.s = comma + 1;
430                         right.len = left.len - (comma - left.s) - 1;
431                         left.len = comma - left.s;
432                 }
433                 
434                      /* Split the record to name and value */
435                 colon = q_memchr(left.s, ':', left.len);
436                 if (!colon) {
437                         rpc_fault(ctx, 400, "Colon missing in struct on line %d",
438                                                         ctx->line_no);
439                         goto err;;
440                 }
441                 name.s = left.s;
442                 name.len = colon - name.s;
443                 value.s = colon + 1;
444                 value.len = left.len - (colon - left.s) - 1;
445                 
446                      /* Create name chunk */
447                 n = new_chunk_unescape(&name);
448                 if (!n) {
449                         rpc_fault(ctx, 400, "Error while processing struct member '%.*s' "
450                                                 "on line %d", name.len, ZSW(name.s), ctx->line_no);
451                         goto err;
452                 }
453                 n->next = s->names;
454                 s->names = n;
455
456                      /* Create value chunk */
457                 v = new_chunk_unescape(&value);
458                 if (!v) {
459                         rpc_fault(ctx, 400, "Error while processing struct membeer '%.*s'"
460                                                 " on line %d", name.len, ZSW(name.s), ctx->line_no);
461                         goto err;
462                 }
463                 v->next = s->values;
464                 s->values = v;
465
466                 left = right;
467         } while(comma);
468
469         return s;
470  err:
471         if (s) free_struct(s);
472         return 0;
473 }
474
475
476 /*
477  * Read a line from FIFO file and store a pointer to the data in buffer 'b'
478  * and the legnth of the line in variable 'read'
479  *
480  * Returns -1 on error, 0 on success
481  */
482 static int read_line(char** b, int* read, struct readline_handle* rh)
483 {
484         char* eol;
485         char* trim;
486         
487         if (rh->crt>=rh->end){
488                 /* end, nothing more to read */
489                 return -1;
490         }
491         for(eol=rh->crt; (eol<rh->end) && (*eol!='\n'); eol++);
492         *eol=0;
493         trim=eol;
494         /* trim spaces at the end */
495         for(trim=eol;(trim>rh->crt) && 
496                         ((*trim=='\r')||(*trim==' ')||(*trim=='\t')); trim--){
497                 *trim=0;
498         }
499         *b=rh->crt;
500         *read = (int)(trim-rh->crt);
501         rh->crt=eol+1;
502         return 0;
503 }
504
505
506 /*
507  * Remove directory path from filename and replace it
508  * with the path configured through a module parameter.
509  * 
510  * The result is allocated using ctl_malloc and thus
511  * has to be freed using ctl_free
512  */
513 static char *trim_filename(char * file)
514 {
515         int prefix_len, fn_len;
516         char *new_fn;
517         
518         /* we only allow files in "/tmp" -- any directory
519          * changes are not welcome
520          */
521         if (strchr(file, '.') || strchr(file, '/')
522             || strchr(file, '\\')) {
523                 ERR("Forbidden filename: %s\n"
524                     , file);
525                 return 0;
526         }
527         prefix_len = strlen(fifo_dir); fn_len = strlen(file);
528         new_fn = ctl_malloc(prefix_len + fn_len + 1);
529         if (new_fn == 0) {
530                 ERR("No memory left\n");
531                 return 0;
532         }
533
534         memcpy(new_fn, fifo_dir, prefix_len);
535         memcpy(new_fn + prefix_len, file, fn_len);
536         new_fn[prefix_len + fn_len] = 0;
537         return new_fn;
538 }
539
540
541
542 /* reply fifo security checks:
543  * checks if fd is a fifo, is not hardlinked and it's not a softlink
544  * opened file descriptor + file name (for soft link check)
545  * returns 0 if ok, <0 if not 
546  */
547 static int fifo_check(int fd, char* fname)
548 {
549         struct stat fst;
550         struct stat lst;
551         
552         if (fstat(fd, &fst) < 0) {
553                 ERR("fstat failed: %s\n",
554                     strerror(errno));
555                 return -1;
556         }
557              /* check if fifo */
558         if (!S_ISFIFO(fst.st_mode)){
559                 ERR("%s is not a fifo\n", fname);
560                 return -1;
561         }
562              /* check if hard-linked */
563         if (fst.st_nlink > 1) {
564                 ERR("%s is hard-linked %d times\n",
565                     fname, (unsigned)fst.st_nlink);
566                 return -1;
567         }
568         
569              /* lstat to check for soft links */
570         if (lstat(fname, &lst) < 0) {
571                 ERR("lstat failed: %s\n",
572                     strerror(errno));
573                 return -1;
574         }
575         if (S_ISLNK(lst.st_mode)) {
576                 ERR("%s is a soft link\n", fname);
577                 return -1;
578         }
579              /* if this is not a symbolic link, check to see if the inode didn't
580               * change to avoid possible sym.link, rm sym.link & replace w/ fifo race
581               */
582         if ((lst.st_dev != fst.st_dev) || (lst.st_ino != fst.st_ino)) {
583                 ERR("inode/dev number differ : %d %d (%s)\n",
584                     (int)fst.st_ino, (int)lst.st_ino, fname);
585                 return -1;
586         }
587              /* success */
588         return 0;
589 }
590
591
592 /*
593  * Open the FIFO reply file
594  * returns fs no on success, -1 on error
595  */
596 static int open_reply_pipe(char *pipe_name)
597 {
598         
599         int fifofd;
600         int flags;
601         
602         int retries = fifo_reply_retries;
603         
604         fifofd=-1;
605         if (!pipe_name || *pipe_name == 0) {
606                 DBG("No file to write to about missing cmd\n");
607                 goto error;
608         }
609         
610  tryagain:
611              /* open non-blocking to make sure that a broken client will not 
612               * block the FIFO server forever */
613         fifofd = open(pipe_name, O_WRONLY | O_NONBLOCK);
614         if (fifofd == -1) {
615                      /* retry several times if client is not yet ready for getting
616                       * feedback via a reply pipe
617                       */
618                 if (errno == ENXIO) {
619                              /* give up on the client - we can't afford server blocking */
620                         if (retries == 0) {
621                                 ERR("No client at %s\n", pipe_name);
622                                 goto error;
623                         }
624                              /* don't be noisy on the very first try */
625                         if (retries != fifo_reply_retries) {
626                                 DBG("Retry countdown: %d\n", retries);
627                         }
628                         sleep_us(fifo_reply_wait);
629                         retries--;
630                         goto tryagain;
631                 }
632                      /* some other opening error */
633                 ERR("Open error (%s): %s\n",
634                     pipe_name, strerror(errno));
635                 goto error;
636         }
637              /* security checks: is this really a fifo?, is 
638               * it hardlinked? is it a soft link? */
639         if (fifo_check(fifofd, pipe_name) < 0) goto error;
640         
641              /* we want server blocking for big writes */
642         if ((flags = fcntl(fifofd, F_GETFL, 0)) < 0) {
643                 ERR("(%s): getfl failed: %s\n", pipe_name, strerror(errno));
644                 goto error;
645         }
646         flags &= ~O_NONBLOCK;
647         if (fcntl(fifofd, F_SETFL, flags) < 0) {
648                 ERR("(%s): setfl cntl failed: %s\n",
649                     pipe_name, strerror(errno));
650                 goto error;
651         }
652         
653         return fifofd;
654  error:
655         if (fifofd!=-1) close(fifofd);
656         return -1;
657 }
658
659
660
661
662 /*
663  * Man FIFO routine running in the FIFO
664  * processes requests received
665  * through the FIFO file repeatedly
666  */
667 int fifo_process(char* msg_buf, int size, int* bytes_needed, void *sh,
668                                         void** saved_state)
669 {
670         rpc_export_t* exp;
671         char* buf;
672         int line_len;
673         char *file_sep;
674         struct text_chunk* p;
675         struct rpc_struct* s;
676         int r;
677         int req_size;
678         static rpc_ctx_t context; 
679
680         DBG("process_fifo: called with %d bytes, offset %d: %.*s\n",
681                         size, (int)(long)*saved_state, size, msg_buf);
682         /* search for the end of the request (\n\r) */
683         if (size < 6){ /* min fifo request */
684                 *bytes_needed=6-size;
685                 return 0; /* we want more bytes, nothing processed */
686         }
687         for (r=1+(int)(long)*saved_state;r<size;r++){
688                 if ((msg_buf[r]=='\n' || msg_buf[r]=='\r') &&
689                         (msg_buf[r-1]=='\n'|| msg_buf[r-1]=='\r')){
690                         /* found double cr, or double lf => end of request */
691                         req_size=r;
692                         goto process;
693                 }
694         }
695         /* no end of request found => ask for more bytes */
696         *bytes_needed=1;
697         /* save current offset, to optimize search */
698         *saved_state=(void*)(long)(r-1);
699         return 0; /* we want again the whole buffer */
700 process:
701         
702         DBG("process_fifo  %d bytes request: %.*s\n", 
703                         req_size, req_size, msg_buf);
704         file_sep = 0;
705         context.method = 0;
706         context.reply_file = 0;
707         context.body = 0;
708         context.code = 200;
709         context.reason = "OK";
710         context.reply_sent = 0;
711         context.last = 0;
712         context.line_no = 0;
713         context.read_h.s=msg_buf;
714         context.read_h.end=msg_buf+size;
715         context.read_h.crt=msg_buf;
716         context.send_h=(struct send_handle*)sh;
717                      /* commands must look this way ':<command>:[filename]' */
718                 if (read_line(&buf, &line_len, &context.read_h) < 0) {
719                              /* line breaking must have failed -- consume the rest
720                               * and proceed to a new request
721                               */
722                         ERR("Command expected\n");
723                         goto consume;
724                 }
725                 context.line_no++;
726                 if (line_len == 0) {
727                         DBG("Empty command received\n");
728                         goto consume;
729                 }
730                 if (line_len < 3) {
731                         ERR("Command must have at least 3 chars\n");
732                         goto consume;
733                 }
734                 if (*buf != CMD_SEPARATOR) {
735                         ERR("Command must begin with %c: %.*s\n", 
736                             CMD_SEPARATOR, line_len, buf);
737                         goto consume;
738                 }
739
740                 context.method = buf + 1;
741                 file_sep = strchr(context.method, CMD_SEPARATOR);
742                 if (file_sep == NULL) {
743                         ERR("File separator missing\n");
744                         goto consume;
745                 }
746                 if (file_sep == context.method) {
747                         ERR("Empty command\n");
748                         goto consume;
749                 }
750                 if (*(file_sep + 1) == 0) context.reply_file = NULL; 
751                 else {
752                         context.reply_file = file_sep + 1;
753                         context.reply_file = trim_filename(context.reply_file);
754                         if (context.reply_file == 0) {
755                                 ERR("Trimming filename\n");
756                                 goto consume;
757                         }
758                 }
759                      /* make command zero-terminated */
760                 *file_sep = 0;
761                 
762                 exp = find_rpc_export(context.method, 0);
763                 if (!exp || !exp->function) {
764                         DBG("Command %s not found\n", context.method);
765                         rpc_fault(&context, 500, "Command '%s' not found", context.method);
766                         goto consume;
767                 }
768
769                 exp->function(&func_param, &context);
770
771         consume:
772                 if (!context.reply_sent) {
773                         rpc_send(&context);
774                 }
775
776                 if (context.reply_file) { 
777                         ctl_free(context.reply_file); 
778                         context.reply_file = 0; 
779                 }
780                 
781                      /* Collect garbage (unescaped strings and structures) */
782                 while(context.strs) {
783                         p = context.strs;
784                         context.strs = context.strs->next;
785                         free_chunk(p);
786                 }
787
788                 while(context.structs) {
789                         s = context.structs;
790                         context.structs = context.structs->next;
791                         free_struct(s);
792                 }
793
794                 *bytes_needed=0;
795                 DBG("Command consumed\n");
796                 DBG("process_fifo: returning %d, bytes_needed 0\n", req_size+1);
797                 return req_size+1; /* all was processed (including terminating \n)*/
798
799 }
800
801
802 /*
803  * Initialze the FIFO fd
804  * Make sure that we can create and open the FIFO file and
805  * make it secure. This function must be executed from mod_init.
806  * This ensures that it has sufficient privileges.
807  */
808 int init_fifo_fd(char* fifo, int fifo_mode, int fifo_uid, int fifo_gid,
809                                         int* fifo_write)
810 {
811         struct stat filestat;
812         int n;
813         long opt;
814         int fifo_read = -1;
815         
816         if (fifo == NULL) {
817                 ERR("null fifo: no fifo will be opened\n");
818                 /* error null fifo */
819                 return -1;
820         }
821         if (strlen(fifo) == 0) {
822                 ERR("emtpy fifo: fifo disabled\n");
823                 return -1;
824         }
825         
826         DBG("Opening fifo...\n");
827         n = stat(fifo, &filestat);
828         if (n == 0) {
829                 /* FIFO exist, delete it (safer) */
830                 if (unlink(fifo) < 0) {
831                         ERR("Cannot delete old fifo (%s):"
832                             " %s\n", fifo, strerror(errno));
833                         return -1;
834                 }
835         } else if (n < 0 && errno != ENOENT) {
836                 ERR("FIFO stat failed: %s\n",
837                     strerror(errno));
838         }
839         /* create FIFO ... */
840         if ((mkfifo(fifo, fifo_mode) < 0)) {
841                 ERR("Can't create FIFO: "
842                     "%s (mode=%d)\n",
843                     strerror(errno), fifo_mode);
844                 return -1;
845         } 
846         DBG("FIFO created @ %s\n", fifo );
847         if ((chmod(fifo, fifo_mode) < 0)) {
848                 ERR("Can't chmod FIFO: %s (mode=%d)\n",
849                     strerror(errno), fifo_mode);
850                 return -1;
851         }
852         if ((fifo_uid != -1) || (fifo_gid != -1)) {
853                 if (chown(fifo, fifo_uid, fifo_gid) < 0) {
854                         ERR("Failed to change the owner/group for %s  to %d.%d; %s[%d]\n",
855                             fifo, fifo_uid, fifo_gid, strerror(errno), errno);
856                         return -1;
857                 }
858         }
859         
860         DBG("fifo %s opened, mode=%d\n", fifo, fifo_mode);
861         
862         fifo_read = open(fifo, O_RDONLY | O_NONBLOCK, 0);
863         if (fifo_read < 0) {
864                 ERR("fifo_read did not open: %s\n", strerror(errno));
865                 return -1;
866         }
867         /* make sure the read fifo will not close */
868         *fifo_write = open(fifo, O_WRONLY | O_NONBLOCK, 0);
869         if (*fifo_write < 0) {
870                 ERR("fifo_write did not open: %s\n", strerror(errno));
871                 close(fifo_read);
872                 return -1;
873         }
874         /* set read fifo blocking mode */
875         if ((opt = fcntl(fifo_read, F_GETFL)) == -1) {
876                 ERR("fcntl(F_GETFL) failed: %s [%d]\n", strerror(errno), errno);
877                 close(fifo_read);
878                 close(*fifo_write);
879                 *fifo_write = -1;
880                 return -1;
881         }
882         if (fcntl(fifo_read, F_SETFL, opt & (~O_NONBLOCK)) == -1) {
883                 ERR("fcntl(F_SETFL) failed: %s [%d]\n", strerror(errno), errno);
884                 close(fifo_read);
885                 close(*fifo_write);
886                 *fifo_write = -1;
887                 return -1;
888         }
889         return fifo_read;
890 }
891
892
893
894 int fifo_rpc_init()
895 {
896         memset(&func_param, 0, sizeof(func_param));
897         func_param.send = (rpc_send_f)rpc_send;
898         func_param.fault = (rpc_fault_f)rpc_fault;
899         func_param.add = (rpc_add_f)rpc_add;
900         func_param.scan = (rpc_scan_f)rpc_scan;
901         func_param.rpl_printf = (rpc_rpl_printf_f)rpc_rpl_printf;
902         func_param.struct_add = (rpc_struct_add_f)rpc_struct_add;
903         /* use rpc_struct_add for array_add */
904         func_param.array_add = (rpc_array_add_f)rpc_struct_add;
905         func_param.struct_scan = (rpc_struct_scan_f)rpc_struct_scan;    
906         func_param.struct_printf = (rpc_struct_printf_f)rpc_struct_printf;
907         return 0;
908 }
909
910
911
912 /*
913  * Close and unlink the FIFO file
914  */
915 void destroy_fifo(int read_fd, int w_fd, char* fname)
916 {
917         if (read_fd!=-1)
918                 close(read_fd);
919         if(w_fd!=-1)
920                 close(w_fd);
921         /* if  FIFO was created, delete it */
922         if (fname && strlen(fname)) {
923                 if (unlink(fname) < 0) {
924                         WARN("Cannot delete fifo (%s):"
925                              " %s\n", fname, strerror(errno));
926                 }
927         }
928 }
929
930
931
932 #define REASON_BUF_LEN 1024
933
934
935 /*
936  * An error occurred, signal it to the client
937  */
938 static void rpc_fault(rpc_ctx_t* ctx, int code, char* fmt, ...)
939 {
940         static char buf[REASON_BUF_LEN];
941         va_list ap;
942         ctx->code = code;
943         va_start(ap, fmt);
944         vsnprintf(buf, REASON_BUF_LEN, fmt, ap);
945         va_end(ap);
946         ctx->reason = buf;
947 }
948
949
950 static inline int safe_write(FILE* f, char* fmt, ...)
951 {
952         va_list ap;
953         
954         if (!*fmt) return 0;
955         va_start(ap, fmt);
956
957  retry:
958              /* First line containing code and reason phrase */
959         if (vfprintf(f, fmt, ap) <= 0) {
960                 ERR("fifo write error: %s\n", strerror(errno));
961                 if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) {
962                         goto retry;
963                 }
964                 va_end(ap);
965                 return -1;
966         }
967         va_end(ap);
968         return 0;
969 }
970
971
972
973 inline static int build_iovec(rpc_ctx_t* ctx, struct iovec* v, int v_size) 
974 {
975         struct text_chunk* p;
976         int r_c_len;
977         int r;
978         
979         
980         
981         /* reason code */
982         v[0].iov_base=int2str(ctx->code, &r_c_len);
983         v[0].iov_len=r_c_len;
984         v[1].iov_base=" ";
985         v[1].iov_len=1;
986         /* reason txt */
987         v[2].iov_base=ctx->reason;
988         v[2].iov_len=strlen(ctx->reason);
989         v[3].iov_base="\n";
990         v[3].iov_len=1;
991         r=4;
992         /* Send the body */
993         while(ctx->body) {
994                 p = ctx->body;
995                 ctx->body = ctx->body->next;
996                 if (p->s.len){
997                         if (r>=v_size) goto error_overflow;
998                         v[r].iov_base=p->s.s;
999                         v[r].iov_len=p->s.len;
1000                         r++;
1001                 }
1002                 if (p->flags & CHUNK_POSITIONAL) {
1003                         if (r>=v_size) goto error_overflow;
1004                         v[r].iov_base="\n";
1005                         v[r].iov_len=1;
1006                         r++;
1007                 } else if (p->flags & CHUNK_MEMBER_NAME) {
1008                         if (r>=v_size) goto error_overflow;
1009                         v[r].iov_base=":";
1010                         v[r].iov_len=1;
1011                         r++;
1012                 } else if (p->flags & CHUNK_MEMBER_VALUE) {
1013                         if (p->next && p->next->flags & CHUNK_MEMBER_NAME) {
1014                                 if (r>=MAX_MSG_CHUNKS) goto error_overflow;
1015                                 v[r].iov_base=",";
1016                                 v[r].iov_len=1;
1017                                 r++;
1018                         } else {
1019                                 if (r>=v_size) goto error_overflow;
1020                                 v[r].iov_base="\n";
1021                                 v[r].iov_len=1;
1022                                 r++;
1023                         }
1024                 }
1025                 free_chunk(p);
1026         }
1027         return r;
1028 error_overflow:
1029         ERR("too many message chunks, iovec buffer overflow: %d/%d\n", r,
1030                         MAX_MSG_CHUNKS);
1031         return -1;
1032 }
1033
1034
1035
1036 /*
1037  * Send a reply, either positive or negative, to the client
1038  */
1039 static int rpc_send(rpc_ctx_t* ctx) 
1040 {
1041         struct iovec v[MAX_MSG_CHUNKS];
1042         int f;
1043         int n;
1044         int ret;
1045         /* Send the reply only once */
1046         if (ctx->reply_sent) return 1;
1047         else ctx->reply_sent = 1;
1048         
1049         if ((n=build_iovec(ctx, v, MAX_MSG_CHUNKS))<0)
1050                 goto error;
1051         if (ctx->send_h->type==S_FIFO){
1052         /* Open the reply file */
1053                 f = open_reply_pipe(ctx->reply_file);
1054                 if (f == -1) {
1055                         ERR("No reply pipe %s\n", ctx->reply_file);
1056                         return -1;
1057                 }
1058                 ret=tsend_dgram_ev(f, v, n, FIFO_TX_TIMEOUT);
1059                 close(f);
1060         }else{
1061                 ret=sock_send_v(ctx->send_h, v, n);
1062         }
1063         return (ret>=0)?0:-1;
1064 error:
1065         ERR("rpc_send fifo error\n");
1066         return -1;
1067 }
1068
1069
1070
1071 /*
1072  * Add a chunk to reply
1073  */
1074 static void append_chunk(rpc_ctx_t* ctx, struct text_chunk* l)
1075 {
1076         if (!ctx->last) {
1077                 ctx->body = l;
1078                 ctx->last = l;
1079         } else {
1080                 ctx->last->next = l;
1081                 ctx->last = l;
1082         }
1083 }
1084
1085
1086 /*
1087  * Convert a value to data chunk and add it to reply
1088  */
1089 static int print_value(rpc_ctx_t* ctx, char fmt, va_list* ap)
1090 {
1091         struct text_chunk* l;
1092         str str_val;
1093         str* sp;
1094         char buf[256];
1095
1096         switch(fmt) {
1097         case 'd':
1098         case 't':
1099                 str_val.s = int2str(va_arg(*ap, int), &str_val.len);
1100                 l = new_chunk(&str_val);
1101                 if (!l) {
1102                         rpc_fault(ctx, 500, "Internal server error while processing"
1103                                         " line %d", ctx->line_no);
1104                         goto err;
1105                 }
1106                 break;
1107                 
1108         case 'f':
1109                 str_val.s = buf;
1110                 str_val.len = snprintf(buf, 256, "%f", va_arg(*ap, double));
1111                 if (str_val.len < 0) {
1112                         rpc_fault(ctx, 400, "Error While Converting double");
1113                         ERR("Error while converting double\n");
1114                         goto err;
1115                 }
1116                 l = new_chunk(&str_val);
1117                 if (!l) {
1118                         rpc_fault(ctx, 500, "Internal Server Error, line %d",
1119                                                 ctx->line_no);
1120                         goto err;
1121                 }
1122                 break;
1123                 
1124         case 'b':
1125                 str_val.len = 1;
1126                 str_val.s = ((va_arg(*ap, int) == 0) ? "0" : "1");
1127                 l = new_chunk(&str_val);
1128                 if (!l) {
1129                         rpc_fault(ctx, 500, "Internal Server Error, line %d", 
1130                                                 ctx->line_no);
1131                         goto err;
1132                 }
1133                 break;
1134                                 
1135         case 's':
1136                 str_val.s = va_arg(*ap, char*);
1137                 str_val.len = strlen(str_val.s);
1138                 l = new_chunk_escape(&str_val, 0);
1139                 if (!l) {
1140                         rpc_fault(ctx, 500, "Internal Server Error, line %d", 
1141                                                 ctx->line_no);
1142                         goto err;
1143                 }
1144                 break;
1145                 
1146         case 'S':
1147                 sp = va_arg(*ap, str*);
1148                 l = new_chunk_escape(sp, 0);
1149                 if (!l) {
1150                         rpc_fault(ctx, 500, "Internal Server Error, line %d", 
1151                                                         ctx->line_no);
1152                         goto err;
1153                 }
1154                 break;
1155                 
1156         default:
1157                 rpc_fault(ctx, 500, "Bug In SER (Invalid formatting character %c)", fmt);
1158                 ERR("Invalid formatting character\n");
1159                 goto err;
1160         }
1161
1162         l->flags |= CHUNK_POSITIONAL;
1163         append_chunk(ctx, l);
1164         return 0;
1165  err:
1166         return -1;
1167 }
1168
1169
1170 static int rpc_add(rpc_ctx_t* ctx, char* fmt, ...)
1171 {
1172         void** void_ptr;
1173         va_list ap;
1174         str s = {"", 0};
1175         struct text_chunk* l;
1176
1177         va_start(ap, fmt);
1178         while(*fmt) {
1179                 if (*fmt == '{' || *fmt == '[') {
1180                         void_ptr = va_arg(ap, void**);
1181                         l = new_chunk(&s);
1182                         if (!l) {
1183                                 rpc_fault(ctx, 500, "Internal Server Error");
1184                                 goto err;
1185                         }
1186                         l->ctx=ctx;
1187                         append_chunk(ctx, l);
1188                         *void_ptr = l;
1189                 } else {
1190                         if (print_value(ctx, *fmt, &ap) < 0) goto err;
1191                 }
1192                 fmt++;
1193         }
1194         va_end(ap);
1195         return 0;
1196  err:
1197         va_end(ap);
1198         return -1;
1199 }
1200
1201 #define RPC_BUF_SIZE 1024
1202
1203 static int rpc_struct_printf(struct text_chunk* c, char* name, char* fmt, ...)
1204 {
1205         int n, buf_size;
1206         char* buf;
1207         va_list ap;
1208         str s, nm;
1209         struct text_chunk* l, *m;
1210         rpc_ctx_t* ctx;
1211         
1212         ctx=(rpc_ctx_t*)c->ctx;
1213         buf = (char*)ctl_malloc(RPC_BUF_SIZE);
1214         if (!buf) {
1215                 rpc_fault(ctx,  500, "Internal Server Error (No memory left)");
1216                 ERR("No memory left\n");
1217                 return -1;
1218         }
1219         
1220         buf_size = RPC_BUF_SIZE;
1221         while (1) {
1222                      /* Try to print in the allocated space. */
1223                 va_start(ap, fmt);
1224                 n = vsnprintf(buf, buf_size, fmt, ap);
1225                 va_end(ap);
1226                      /* If that worked, return the string. */
1227                 if (n > -1 && n < buf_size) {
1228                         nm.s = name;
1229                         nm.len = strlen(name);
1230                         m = new_chunk_escape(&nm, 1); /* Escape all characters, including : and , */
1231                         if (!m) {
1232                                 rpc_fault(ctx, 500, "Internal Server Error");
1233                                 goto err;
1234                         }
1235
1236                         s.s = buf;
1237                         s.len = n;
1238                         l = new_chunk_escape(&s, 1);
1239                         if (!l) {
1240                                 rpc_fault(ctx, 500, "Internal Server Error");
1241                                 free_chunk(m);
1242                                 ERR("Error while creating text_chunk structure");
1243                                 goto err;
1244                         }
1245                         
1246                         l->flags |= CHUNK_MEMBER_VALUE;
1247                         l->next = c->next;
1248                         c->next = l;
1249                         if (c == ctx->last) ctx->last = l;
1250
1251                         m->flags |= CHUNK_MEMBER_NAME;
1252                         m->next = c->next;
1253                         c->next = m;
1254                         if (c == ctx->last) ctx->last = m;
1255                         return 0;
1256                 }
1257                      /* Else try again with more space. */
1258                 if (n > -1) {   /* glibc 2.1 */
1259                         buf_size = n + 1; /* precisely what is needed */
1260                 } else {          /* glibc 2.0 */
1261                         buf_size *= 2;  /* twice the old size */
1262                 }
1263                 if ((buf = ctl_realloc(buf, buf_size)) == 0) {
1264                         rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
1265                         ERR("No memory left\n");
1266                         goto err;
1267                 }
1268         }
1269         return 0;
1270  err:
1271         if (buf) ctl_free(buf);
1272         return -1;
1273 }
1274
1275
1276 static int rpc_rpl_printf(rpc_ctx_t* ctx, char* fmt, ...)
1277 {
1278         int n, buf_size;
1279         char* buf;
1280         va_list ap;
1281         str s;
1282         struct text_chunk* l;
1283
1284         buf = (char*)ctl_malloc(RPC_BUF_SIZE);
1285         if (!buf) {
1286                 rpc_fault(ctx,  500, "Internal Server Error (No memory left)");
1287                 ERR("No memory left\n");
1288                 return -1;
1289         }
1290         
1291         buf_size = RPC_BUF_SIZE;
1292         while (1) {
1293                      /* Try to print in the allocated space. */
1294                 va_start(ap, fmt);
1295                 n = vsnprintf(buf, buf_size, fmt, ap);
1296                 va_end(ap);
1297                      /* If that worked, return the string. */
1298                 if (n > -1 && n < buf_size) {
1299                         s.s = buf;
1300                         s.len = n;
1301                         l = new_chunk_escape(&s, 0);
1302                         if (!l) {
1303                                 rpc_fault(ctx, 500, "Internal Server Error");
1304                                 ERR("Error while creating text_chunk structure");
1305                                 goto err;
1306                         }
1307                         append_chunk(ctx, l);
1308                         ctl_free(buf);
1309                         return 0;
1310                 }
1311                      /* Else try again with more space. */
1312                 if (n > -1) {   /* glibc 2.1 */
1313                         buf_size = n + 1; /* precisely what is needed */
1314                 } else {          /* glibc 2.0 */
1315                         buf_size *= 2;  /* twice the old size */
1316                 }
1317                 if ((buf = ctl_realloc(buf, buf_size)) == 0) {
1318                         rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
1319                         ERR("No memory left\n");
1320                         goto err;
1321                 }
1322         }
1323         return 0;
1324  err:
1325         if (buf) ctl_free(buf);
1326         return -1;
1327 }
1328
1329
1330 static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
1331 {
1332         struct text_chunk* l;
1333         struct rpc_struct* s;
1334         int* int_ptr;
1335         char** char_ptr;
1336         str* str_ptr;
1337         double* double_ptr;
1338         void** void_ptr;
1339         int read;
1340         str line;
1341         int nofault;
1342         int modifiers;
1343
1344         va_list ap;
1345         va_start(ap, fmt);
1346
1347         nofault = 0;
1348         read = 0;
1349         modifiers=0;
1350         while(*fmt) {
1351                 if (read_line(&line.s, &line.len, &ctx->read_h) < 0) {
1352                         va_end(ap);
1353                         return read;
1354                 }
1355                 ctx->line_no++;
1356
1357                 switch(*fmt) {
1358                 case '*': /* start of optional parameters */
1359                         nofault = 1;
1360                         modifiers++;
1361                         break;
1362                 case 'b': /* Bool */
1363                 case 't': /* Date and time */
1364                 case 'd': /* Integer */
1365                         if (!line.len) {
1366                                 if(nofault==0)
1367                                         rpc_fault(ctx, 400, "Invalid parameter value on line %d", 
1368                                                                         ctx->line_no);
1369                                 goto error;
1370                         }
1371                         int_ptr = va_arg(ap, int*);
1372                         *int_ptr = strtol(line.s, 0, 0);
1373                         break;
1374
1375                 case 'f': /* double */
1376                         if (!line.len) {
1377                                 if(nofault==0)
1378                                         rpc_fault(ctx, 400, "Invalid parameter value on line %d", 
1379                                                                 ctx->line_no);
1380                                 goto error;
1381                         }
1382                         double_ptr = va_arg(ap, double*);
1383                         *double_ptr = strtod(line.s, 0);
1384                         break;
1385                         
1386                 case 's': /* zero terminated string */
1387                 case 'S': /* str structure */
1388                         l = new_chunk_unescape(&line);
1389                         if (!l) {
1390                                 if(nofault==0) {
1391                                         rpc_fault(ctx, 500, "Internal Server Error");
1392                                         ERR("Not enough memory\n");
1393                                 }
1394                                 goto error;
1395                         }
1396                              /* Make sure it gets released at the end */
1397                         l->next = ctx->strs;
1398                         ctx->strs = l;
1399
1400                         if (*fmt == 's') {
1401                                 char_ptr = va_arg(ap, char**);
1402                                 *char_ptr = l->s.s;
1403                         } else {
1404                                 str_ptr = va_arg(ap, str*);
1405                                 *str_ptr = l->s;
1406                         }
1407                         break;
1408
1409                 case '{':
1410                         void_ptr = va_arg(ap, void**);
1411                         s = new_struct(ctx, &line);
1412                         if (!s) goto error;
1413                         s->next = ctx->structs;
1414                         ctx->structs = s;
1415                         *void_ptr = s;
1416                         break;
1417
1418                 default:
1419                         ERR("Invalid parameter type in formatting string: %c\n", *fmt);
1420                         rpc_fault(ctx, 500, "Server Internal Error (Invalid Formatting Character '%c')", *fmt);
1421                         goto error;
1422                 }
1423                 fmt++;
1424                 read++;
1425         }
1426         va_end(ap);
1427         return read-modifiers;
1428
1429  error:
1430         va_end(ap);
1431         return -(read-modifiers);
1432 }
1433
1434
1435 static int rpc_struct_add(struct text_chunk* s, char* fmt, ...)
1436 {
1437         static char buf[MAX_LINE_BUFFER];
1438         str st, *sp;
1439         void** void_ptr;
1440         va_list ap;
1441         struct text_chunk* m, *c;
1442         rpc_ctx_t* ctx;
1443
1444         ctx=(rpc_ctx_t*)s->ctx;
1445         va_start(ap, fmt);
1446         while(*fmt) {
1447                      /* Member name escaped */
1448                 st.s = va_arg(ap, char*);
1449                 st.len = strlen(st.s);
1450                 m = new_chunk_escape(&st, 1); /* Escape all characters, including : and , */
1451                 if (!m) {
1452                         rpc_fault(ctx, 500, "Internal Server Error");
1453                         goto err;
1454                 }
1455                 m->flags |= CHUNK_MEMBER_NAME;
1456                 
1457                 if(*fmt=='{' || *fmt=='[') {
1458                         void_ptr = va_arg(ap, void**);
1459                         m->ctx=ctx;
1460                         append_chunk(ctx, m);
1461                         *void_ptr = m;
1462                 } else {
1463                         switch(*fmt) {
1464                         case 'd':
1465                         case 't':
1466                                 st.s = int2str(va_arg(ap, int), &st.len);
1467                                 c = new_chunk(&st);
1468                                 break;
1469
1470                         case 'f':
1471                                 st.s = buf;
1472                                 st.len = snprintf(buf, 256, "%f", va_arg(ap, double));
1473                                 if (st.len < 0) {
1474                                         rpc_fault(ctx, 400, "Error While Converting double");
1475                                         ERR("Error while converting double\n");
1476                                         goto err;
1477                                 }
1478                                 c = new_chunk(&st);
1479                                 break;
1480
1481                                 case 'b':
1482                                         st.len = 1;
1483                                         st.s = ((va_arg(ap, int) == 0) ? "0" : "1");
1484                                         c = new_chunk(&st);
1485                                 break;
1486
1487                         case 's':
1488                                 st.s = va_arg(ap, char*);
1489                                 st.len = strlen(st.s);
1490                                 c = new_chunk_escape(&st, 1);
1491                                 break;
1492
1493                         case 'S':
1494                                 sp = va_arg(ap, str*);
1495                                 c = new_chunk_escape(sp, 1);
1496                                 break;
1497
1498                         default:
1499                                 rpc_fault(ctx, 500, "Bug In SER (Invalid formatting character %c)",
1500                                                 *fmt);
1501                                 ERR("Invalid formatting character\n");
1502                                 goto err;
1503                         }
1504
1505                         if (!c) {
1506                                 rpc_fault(ctx, 500, "Internal Server Error");
1507                                 goto err;
1508                         }
1509                         c->flags |= CHUNK_MEMBER_VALUE;
1510                         c->next = s->next;
1511                         s->next = c;
1512                         if (s == ctx->last) ctx->last = c;
1513
1514                         m->next = s->next;
1515                         s->next = m;
1516                         if (s == ctx->last) ctx->last = m;
1517                 }
1518                 fmt++;
1519         }
1520         va_end(ap);
1521         return 0;
1522  err:
1523         if (m) free_chunk(m);
1524         va_end(ap);
1525         return -1;
1526 }
1527
1528
1529 static int find_member(struct text_chunk** value, struct rpc_struct* s, str* member_name)
1530 {
1531         struct text_chunk* n, *v;
1532
1533         n = s->names;
1534         v = s->values;
1535         while(n) {
1536                 if (member_name->len == n->s.len &&
1537                     !strncasecmp(member_name->s, n->s.s, n->s.len)) {
1538                         if (n->flags & CHUNK_SEEN) goto skip;
1539                         else {
1540                                 *value = v;
1541                                 n->flags |= CHUNK_SEEN;
1542                                 return 0;
1543                         }
1544                 }
1545
1546         skip:
1547                 n = n->next;
1548                 v = v->next;
1549         }
1550         return 1;
1551 }
1552
1553
1554 static int rpc_struct_scan(struct rpc_struct* s, char* fmt, ...)
1555 {
1556         struct text_chunk* val;
1557         va_list ap;
1558         int* int_ptr;
1559         double* double_ptr;
1560         char** char_ptr;
1561         str* str_ptr;
1562         str member_name;
1563         int ret, read;
1564
1565         read = 0;
1566         va_start(ap, fmt);
1567         while(*fmt) {
1568                 member_name.s = va_arg(ap, char*);
1569                 member_name.len = strlen(member_name.s);
1570                 ret = find_member(&val, s, &member_name);
1571                 if (ret > 0) {
1572                         va_end(ap);
1573                         return read;
1574                 }
1575                 
1576                 switch(*fmt) {
1577                 case 'b': /* Bool */
1578                 case 't': /* Date and time */
1579                 case 'd': /* Integer */
1580                         int_ptr = va_arg(ap, int*);
1581                         if (!val->s.len) {
1582                                 rpc_fault(s->ctx, 400, "Invalid Parameter Value");
1583                                 goto error;
1584                         }
1585                              /* String in text_chunk is always zero terminated */
1586                         *int_ptr = strtol(val->s.s, 0, 0);
1587                         break;
1588
1589                 case 'f': /* double */
1590                         double_ptr = va_arg(ap, double*);
1591                         if (!val->s.len) {
1592                                 rpc_fault(s->ctx, 400, "Invalid Parameter Value");
1593                                 goto error;
1594                         }
1595                              /* String in text_chunk is always zero terminated */
1596                         *double_ptr = strtod(val->s.s, 0);
1597                         break;
1598                         
1599                 case 's': /* zero terminated string */
1600                         char_ptr = va_arg(ap, char**);
1601                              /* String in text_chunk is always zero terminated */
1602                         *char_ptr = val->s.s;
1603                         break;
1604
1605                 case 'S': /* str structure */
1606                         str_ptr = va_arg(ap, str*);
1607                         str_ptr->len = strlen(str_ptr->s);
1608                         *str_ptr = val->s;
1609                         break;
1610                 default:
1611                         rpc_fault(s->ctx, 500, "Invalid character in formatting string '%c'", *fmt);
1612                         ERR("Invalid parameter type in formatting string: %c\n", *fmt);
1613                         goto error;
1614                 }
1615                 fmt++;
1616                 read++;
1617         }
1618         va_end(ap);
1619         return read;
1620  error:
1621         va_end(ap);
1622         return -read;
1623 }
1624
1625 #endif