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 */
escape(str * dst,char * r,int len,int all)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 */
unescape(str * dst,char * r,int len)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 */
new_chunk_escape(str * src,int escape_all)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 */
new_chunk(str * src)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 */
new_chunk_unescape(str * src)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
free_chunk(struct text_chunk * c)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
free_struct(struct rpc_struct * s)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 */
new_struct(rpc_ctx_t * ctx,str * line)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 */
read_line(char ** b,int * read,struct readline_handle * rh)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 */
trim_filename(char * file)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 */
fifo_check(int fd,char * fname)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 */
open_reply_pipe(char * pipe_name)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 */
fifo_process(char * msg_buf,int size,int * bytes_needed,void * sh,void ** saved_state)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 */
init_fifo_fd(char * fifo,int fifo_mode,int fifo_uid,int fifo_gid,int * fifo_write)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("empty 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
fifo_rpc_init()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 */
destroy_fifo(int read_fd,int w_fd,char * fname)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 */
rpc_fault(rpc_ctx_t * ctx,int code,char * fmt,...)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
safe_write(FILE * f,char * fmt,...)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
build_iovec(rpc_ctx_t * ctx,struct iovec * v,int v_size)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 */
rpc_send(rpc_ctx_t * ctx)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 */
append_chunk(rpc_ctx_t * ctx,struct text_chunk * l)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 */
print_value(rpc_ctx_t * ctx,char fmt,va_list * ap)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 str null_value = str_init("<null string>");
1096
1097 switch(fmt) {
1098 case 'd':
1099 case 't':
1100 str_val.s = int2str(va_arg(*ap, int), &str_val.len);
1101 l = new_chunk(&str_val);
1102 if (!l) {
1103 rpc_fault(ctx, 500, "Internal server error while processing"
1104 " line %d", ctx->line_no);
1105 goto err;
1106 }
1107 break;
1108
1109 case 'f':
1110 str_val.s = buf;
1111 str_val.len = snprintf(buf, 256, "%f", va_arg(*ap, double));
1112 if (str_val.len < 0) {
1113 rpc_fault(ctx, 400, "Error While Converting double");
1114 ERR("Error while converting double\n");
1115 goto err;
1116 }
1117 l = new_chunk(&str_val);
1118 if (!l) {
1119 rpc_fault(ctx, 500, "Internal Server Error, line %d",
1120 ctx->line_no);
1121 goto err;
1122 }
1123 break;
1124
1125 case 'b':
1126 str_val.len = 1;
1127 str_val.s = ((va_arg(*ap, int) == 0) ? "0" : "1");
1128 l = new_chunk(&str_val);
1129 if (!l) {
1130 rpc_fault(ctx, 500, "Internal Server Error, line %d",
1131 ctx->line_no);
1132 goto err;
1133 }
1134 break;
1135
1136 case 's':
1137 str_val.s = va_arg(*ap, char*);
1138 if(str_val.s!=NULL) {
1139 str_val.len = strlen(str_val.s);
1140 } else {
1141 str_val = null_value;
1142 }
1143 l = new_chunk_escape(&str_val, 0);
1144 if (!l) {
1145 rpc_fault(ctx, 500, "Internal Server Error, line %d",
1146 ctx->line_no);
1147 goto err;
1148 }
1149 break;
1150
1151 case 'S':
1152 sp = va_arg(*ap, str*);
1153 if(sp!=NULL && sp->s!=NULL) {
1154 str_val = *sp;
1155 } else {
1156 str_val = null_value;
1157 }
1158 l = new_chunk_escape(&str_val, 0);
1159 if (!l) {
1160 rpc_fault(ctx, 500, "Internal Server Error, line %d",
1161 ctx->line_no);
1162 goto err;
1163 }
1164 break;
1165
1166 default:
1167 rpc_fault(ctx, 500, "Bug In SER (Invalid formatting character %c)", fmt);
1168 ERR("Invalid formatting character\n");
1169 goto err;
1170 }
1171
1172 l->flags |= CHUNK_POSITIONAL;
1173 append_chunk(ctx, l);
1174 return 0;
1175 err:
1176 return -1;
1177 }
1178
1179
rpc_add(rpc_ctx_t * ctx,char * fmt,...)1180 static int rpc_add(rpc_ctx_t* ctx, char* fmt, ...)
1181 {
1182 void** void_ptr;
1183 va_list ap;
1184 str s = {"", 0};
1185 struct text_chunk* l;
1186
1187 va_start(ap, fmt);
1188 while(*fmt) {
1189 if (*fmt == '{' || *fmt == '[') {
1190 void_ptr = va_arg(ap, void**);
1191 l = new_chunk(&s);
1192 if (!l) {
1193 rpc_fault(ctx, 500, "Internal Server Error");
1194 goto err;
1195 }
1196 l->ctx=ctx;
1197 append_chunk(ctx, l);
1198 *void_ptr = l;
1199 } else {
1200 if (print_value(ctx, *fmt, &ap) < 0) goto err;
1201 }
1202 fmt++;
1203 }
1204 va_end(ap);
1205 return 0;
1206 err:
1207 va_end(ap);
1208 return -1;
1209 }
1210
1211 #define RPC_BUF_SIZE 1024
1212
rpc_struct_printf(struct text_chunk * c,char * name,char * fmt,...)1213 static int rpc_struct_printf(struct text_chunk* c, char* name, char* fmt, ...)
1214 {
1215 int n, buf_size;
1216 char* buf;
1217 char* buf0;
1218 va_list ap;
1219 str s, nm;
1220 struct text_chunk* l, *m;
1221 rpc_ctx_t* ctx;
1222
1223 ctx=(rpc_ctx_t*)c->ctx;
1224 buf = (char*)ctl_malloc(RPC_BUF_SIZE);
1225 if (!buf) {
1226 rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
1227 ERR("No memory left\n");
1228 return -1;
1229 }
1230
1231 buf_size = RPC_BUF_SIZE;
1232 while (1) {
1233 /* Try to print in the allocated space. */
1234 va_start(ap, fmt);
1235 n = vsnprintf(buf, buf_size, fmt, ap);
1236 va_end(ap);
1237 /* If that worked, return the string. */
1238 if (n > -1 && n < buf_size) {
1239 nm.s = name;
1240 nm.len = strlen(name);
1241 m = new_chunk_escape(&nm, 1); /* Escape all characters, including : and , */
1242 if (!m) {
1243 rpc_fault(ctx, 500, "Internal Server Error");
1244 goto err;
1245 }
1246
1247 s.s = buf;
1248 s.len = n;
1249 l = new_chunk_escape(&s, 1);
1250 if (!l) {
1251 rpc_fault(ctx, 500, "Internal Server Error");
1252 free_chunk(m);
1253 ERR("Error while creating text_chunk structure");
1254 goto err;
1255 }
1256
1257 l->flags |= CHUNK_MEMBER_VALUE;
1258 l->next = c->next;
1259 c->next = l;
1260 if (c == ctx->last) ctx->last = l;
1261
1262 m->flags |= CHUNK_MEMBER_NAME;
1263 m->next = c->next;
1264 c->next = m;
1265 if (c == ctx->last) ctx->last = m;
1266 return 0;
1267 }
1268 /* Else try again with more space. */
1269 if (n > -1) { /* glibc 2.1 */
1270 buf_size = n + 1; /* precisely what is needed */
1271 } else { /* glibc 2.0 */
1272 buf_size *= 2; /* twice the old size */
1273 }
1274 if ((buf0 = ctl_realloc(buf, buf_size)) == 0) {
1275 rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
1276 ERR("No memory left\n");
1277 goto err;
1278 }
1279 buf = buf0;
1280 }
1281 return 0;
1282 err:
1283 if (buf) ctl_free(buf);
1284 return -1;
1285 }
1286
1287
rpc_rpl_printf(rpc_ctx_t * ctx,char * fmt,...)1288 static int rpc_rpl_printf(rpc_ctx_t* ctx, char* fmt, ...)
1289 {
1290 int n, buf_size;
1291 char* buf;
1292 char* buf0;
1293 va_list ap;
1294 str s;
1295 struct text_chunk* l;
1296
1297 buf = (char*)ctl_malloc(RPC_BUF_SIZE);
1298 if (!buf) {
1299 rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
1300 ERR("No memory left\n");
1301 return -1;
1302 }
1303
1304 buf_size = RPC_BUF_SIZE;
1305 while (1) {
1306 /* Try to print in the allocated space. */
1307 va_start(ap, fmt);
1308 n = vsnprintf(buf, buf_size, fmt, ap);
1309 va_end(ap);
1310 /* If that worked, return the string. */
1311 if (n > -1 && n < buf_size) {
1312 s.s = buf;
1313 s.len = n;
1314 l = new_chunk_escape(&s, 0);
1315 if (!l) {
1316 rpc_fault(ctx, 500, "Internal Server Error");
1317 ERR("Error while creating text_chunk structure");
1318 goto err;
1319 }
1320 append_chunk(ctx, l);
1321 ctl_free(buf);
1322 return 0;
1323 }
1324 /* Else try again with more space. */
1325 if (n > -1) { /* glibc 2.1 */
1326 buf_size = n + 1; /* precisely what is needed */
1327 } else { /* glibc 2.0 */
1328 buf_size *= 2; /* twice the old size */
1329 }
1330 if ((buf0 = ctl_realloc(buf, buf_size)) == 0) {
1331 rpc_fault(ctx, 500, "Internal Server Error (No memory left)");
1332 ERR("No memory left\n");
1333 goto err;
1334 }
1335 buf = buf0;
1336 }
1337 return 0;
1338 err:
1339 if (buf) ctl_free(buf);
1340 return -1;
1341 }
1342
1343
rpc_scan(rpc_ctx_t * ctx,char * fmt,...)1344 static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...)
1345 {
1346 struct text_chunk* l;
1347 struct rpc_struct* s;
1348 int* int_ptr;
1349 char** char_ptr;
1350 str* str_ptr;
1351 double* double_ptr;
1352 void** void_ptr;
1353 int read;
1354 str line;
1355 int nofault;
1356 int modifiers;
1357
1358 va_list ap;
1359 va_start(ap, fmt);
1360
1361 nofault = 0;
1362 read = 0;
1363 modifiers=0;
1364 while(*fmt) {
1365 if (read_line(&line.s, &line.len, &ctx->read_h) < 0) {
1366 va_end(ap);
1367 return read;
1368 }
1369 ctx->line_no++;
1370
1371 switch(*fmt) {
1372 case '*': /* start of optional parameters */
1373 nofault = 1;
1374 modifiers++;
1375 break;
1376 case 'b': /* Bool */
1377 case 't': /* Date and time */
1378 case 'd': /* Integer */
1379 if (!line.len) {
1380 if(nofault==0)
1381 rpc_fault(ctx, 400, "Invalid parameter value on line %d",
1382 ctx->line_no);
1383 goto error;
1384 }
1385 int_ptr = va_arg(ap, int*);
1386 *int_ptr = strtol(line.s, 0, 0);
1387 break;
1388
1389 case 'f': /* double */
1390 if (!line.len) {
1391 if(nofault==0)
1392 rpc_fault(ctx, 400, "Invalid parameter value on line %d",
1393 ctx->line_no);
1394 goto error;
1395 }
1396 double_ptr = va_arg(ap, double*);
1397 *double_ptr = strtod(line.s, 0);
1398 break;
1399
1400 case 's': /* zero terminated string */
1401 case 'S': /* str structure */
1402 l = new_chunk_unescape(&line);
1403 if (!l) {
1404 if(nofault==0) {
1405 rpc_fault(ctx, 500, "Internal Server Error");
1406 ERR("Not enough memory\n");
1407 }
1408 goto error;
1409 }
1410 /* Make sure it gets released at the end */
1411 l->next = ctx->strs;
1412 ctx->strs = l;
1413
1414 if (*fmt == 's') {
1415 char_ptr = va_arg(ap, char**);
1416 *char_ptr = l->s.s;
1417 } else {
1418 str_ptr = va_arg(ap, str*);
1419 *str_ptr = l->s;
1420 }
1421 break;
1422
1423 case '{':
1424 void_ptr = va_arg(ap, void**);
1425 s = new_struct(ctx, &line);
1426 if (!s) goto error;
1427 s->next = ctx->structs;
1428 ctx->structs = s;
1429 *void_ptr = s;
1430 break;
1431
1432 default:
1433 ERR("Invalid parameter type in formatting string: %c\n", *fmt);
1434 rpc_fault(ctx, 500, "Server Internal Error (Invalid Formatting Character '%c')", *fmt);
1435 goto error;
1436 }
1437 fmt++;
1438 read++;
1439 }
1440 va_end(ap);
1441 return read-modifiers;
1442
1443 error:
1444 va_end(ap);
1445 return -(read-modifiers);
1446 }
1447
1448
rpc_struct_add(struct text_chunk * s,char * fmt,...)1449 static int rpc_struct_add(struct text_chunk* s, char* fmt, ...)
1450 {
1451 static char buf[MAX_LINE_BUFFER];
1452 str st, *sp;
1453 void** void_ptr;
1454 va_list ap;
1455 struct text_chunk* m, *c;
1456 rpc_ctx_t* ctx;
1457 str null_value = str_init("<null string>");
1458
1459 ctx=(rpc_ctx_t*)s->ctx;
1460 va_start(ap, fmt);
1461 while(*fmt) {
1462 /* Member name escaped */
1463 st.s = va_arg(ap, char*);
1464 st.len = strlen(st.s);
1465 m = new_chunk_escape(&st, 1); /* Escape all characters, including : and , */
1466 if (!m) {
1467 rpc_fault(ctx, 500, "Internal Server Error");
1468 goto err;
1469 }
1470 m->flags |= CHUNK_MEMBER_NAME;
1471
1472 if(*fmt=='{' || *fmt=='[') {
1473 void_ptr = va_arg(ap, void**);
1474 m->ctx=ctx;
1475 append_chunk(ctx, m);
1476 *void_ptr = m;
1477 } else {
1478 switch(*fmt) {
1479 case 'd':
1480 case 't':
1481 st.s = int2str(va_arg(ap, int), &st.len);
1482 c = new_chunk(&st);
1483 break;
1484
1485 case 'f':
1486 st.s = buf;
1487 st.len = snprintf(buf, 256, "%f", va_arg(ap, double));
1488 if (st.len < 0) {
1489 rpc_fault(ctx, 400, "Error While Converting double");
1490 ERR("Error while converting double\n");
1491 goto err;
1492 }
1493 c = new_chunk(&st);
1494 break;
1495
1496 case 'b':
1497 st.len = 1;
1498 st.s = ((va_arg(ap, int) == 0) ? "0" : "1");
1499 c = new_chunk(&st);
1500 break;
1501
1502 case 's':
1503 st.s = va_arg(ap, char*);
1504 if(st.s==NULL) {
1505 st = null_value;
1506 } else {
1507 st.len = strlen(st.s);
1508 }
1509 c = new_chunk_escape(&st, 1);
1510 break;
1511
1512 case 'S':
1513 sp = va_arg(ap, str*);
1514 if(sp!=NULL && sp->s!=NULL) {
1515 st = *sp;
1516 } else {
1517 st = null_value;
1518 }
1519 c = new_chunk_escape(&st, 1);
1520 break;
1521
1522 default:
1523 rpc_fault(ctx, 500, "Bug In SER (Invalid formatting character %c)",
1524 *fmt);
1525 ERR("Invalid formatting character\n");
1526 goto err;
1527 }
1528
1529 if (!c) {
1530 rpc_fault(ctx, 500, "Internal Server Error");
1531 goto err;
1532 }
1533 c->flags |= CHUNK_MEMBER_VALUE;
1534 c->next = s->next;
1535 s->next = c;
1536 if (s == ctx->last) ctx->last = c;
1537
1538 m->next = s->next;
1539 s->next = m;
1540 if (s == ctx->last) ctx->last = m;
1541 }
1542 fmt++;
1543 }
1544 va_end(ap);
1545 return 0;
1546 err:
1547 if (m) free_chunk(m);
1548 va_end(ap);
1549 return -1;
1550 }
1551
1552
find_member(struct text_chunk ** value,struct rpc_struct * s,str * member_name)1553 static int find_member(struct text_chunk** value, struct rpc_struct* s, str* member_name)
1554 {
1555 struct text_chunk* n, *v;
1556
1557 n = s->names;
1558 v = s->values;
1559 while(n) {
1560 if (member_name->len == n->s.len &&
1561 !strncasecmp(member_name->s, n->s.s, n->s.len)) {
1562 if (n->flags & CHUNK_SEEN) goto skip;
1563 else {
1564 *value = v;
1565 n->flags |= CHUNK_SEEN;
1566 return 0;
1567 }
1568 }
1569
1570 skip:
1571 n = n->next;
1572 v = v->next;
1573 }
1574 return 1;
1575 }
1576
1577
rpc_struct_scan(struct rpc_struct * s,char * fmt,...)1578 static int rpc_struct_scan(struct rpc_struct* s, char* fmt, ...)
1579 {
1580 struct text_chunk* val;
1581 va_list ap;
1582 int* int_ptr;
1583 double* double_ptr;
1584 char** char_ptr;
1585 str* str_ptr;
1586 str member_name;
1587 int ret, read;
1588
1589 read = 0;
1590 va_start(ap, fmt);
1591 while(*fmt) {
1592 member_name.s = va_arg(ap, char*);
1593 member_name.len = strlen(member_name.s);
1594 ret = find_member(&val, s, &member_name);
1595 if (ret > 0) {
1596 va_end(ap);
1597 return read;
1598 }
1599
1600 switch(*fmt) {
1601 case 'b': /* Bool */
1602 case 't': /* Date and time */
1603 case 'd': /* Integer */
1604 int_ptr = va_arg(ap, int*);
1605 if (!val->s.len) {
1606 rpc_fault(s->ctx, 400, "Invalid Parameter Value");
1607 goto error;
1608 }
1609 /* String in text_chunk is always zero terminated */
1610 *int_ptr = strtol(val->s.s, 0, 0);
1611 break;
1612
1613 case 'f': /* double */
1614 double_ptr = va_arg(ap, double*);
1615 if (!val->s.len) {
1616 rpc_fault(s->ctx, 400, "Invalid Parameter Value");
1617 goto error;
1618 }
1619 /* String in text_chunk is always zero terminated */
1620 *double_ptr = strtod(val->s.s, 0);
1621 break;
1622
1623 case 's': /* zero terminated string */
1624 char_ptr = va_arg(ap, char**);
1625 /* String in text_chunk is always zero terminated */
1626 *char_ptr = val->s.s;
1627 break;
1628
1629 case 'S': /* str structure */
1630 str_ptr = va_arg(ap, str*);
1631 *str_ptr = val->s;
1632 break;
1633 default:
1634 rpc_fault(s->ctx, 500, "Invalid character in formatting string '%c'", *fmt);
1635 ERR("Invalid parameter type in formatting string: %c\n", *fmt);
1636 goto error;
1637 }
1638 fmt++;
1639 read++;
1640 }
1641 va_end(ap);
1642 return read;
1643 error:
1644 va_end(ap);
1645 return -read;
1646 }
1647
1648 #endif
1649