witness fix for new tvb_get_unicode_string() interface using wmem
[metze/wireshark/wip.git] / echld / parent.c
1 /* parent.c
2  *  epan working child API internals
3  *  Parent process routines and definitions
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * Copyright (c) 2013 by Luis Ontanon <luis@ontanon.org>
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 #include "config.h"
29
30 #include "echld-int.h"
31
32 /**
33  PARENT and API
34  **/
35
36 #define MAX_PENDING_REQS 16;
37
38 typedef struct _req {
39         int reqh_id;
40         echld_msg_cb_t cb;
41         void* cb_data;
42         struct timeval tv;
43 } reqh_t;
44
45 typedef struct _hdlr {
46         int id;
47         echld_msg_type_t type;
48         echld_msg_cb_t cb;
49         void* cb_data;
50 } hdlr_t;
51
52 typedef struct _echld_child {
53         int chld_id;
54         void* data;
55         echld_new_cb_t cb;
56         child_state_t state;
57         GArray* handlers;
58         GArray* reqs;
59 } echld_t;
60
61 struct _echld_parent {
62         echld_t* children;
63         echld_reader_t reader;
64         int dispatcher_fd;
65         int dispatcher_pid;
66         int reqh_id;
67         GByteArray* snd;
68         int closing;
69         echld_parent_encoder_t* enc;
70         parent_decoder_t* dec;
71 } parent  = {NULL,{NULL,0,NULL,-1,NULL,0},-1,-1,1,NULL,0,NULL,NULL};
72
73
74 static int reqh_ids = 1;
75
76
77 #ifdef DEBUG_PARENT
78
79 static int dbg_level = DEBUG_PARENT;
80
81 static void parent_dbg(int level, const char* fmt, ...) {
82         va_list ap;
83         char str[1024];
84
85         if (level > dbg_level) return;
86
87         va_start(ap,fmt);
88         g_vsnprintf(str,1024,fmt,ap);
89         va_end(ap);
90
91         fprintf(stderr,"ParentDebug: level=%d msg='%s'\n",level,str);
92         fflush(stderr);
93 }
94
95 #define PARENT_DBG(attrs) parent_dbg attrs
96 #define PARENT_SEND(BYTEARR,CHILDNUM,TYPE,R_ID) do {  long st = echld_write_frame(parent.dispatcher_fd, BYTEARR, CHILDNUM, TYPE, R_ID, NULL); PARENT_DBG((1,"SEND type='%s' chld_id=%d reqh_id=%d err_msg='%s'",TY(TYPE),CHILDNUM,R_ID, ( st >= 8 ? "ok" : ((st<0)?strerror(errno):"?") )  )); } while(0)
97
98 #else
99 #define PARENT_DBG(attrs)
100 #define PARENT_SEND(BYTEARR,CHILDNUM,TYPE,R_ID) echld_write_frame(parent.dispatcher_fd, BYTEARR, CHILDNUM, TYPE, R_ID, NULL)
101 #endif
102
103 extern void echld_set_parent_dbg_level(int lvl) {
104         (dbg_level = lvl);
105         if (lvl > 6) {
106                 echld_common_set_dbg(lvl,stderr,"parent");
107         }
108         PARENT_DBG((0,"Debug Level Set: %d",lvl));
109 }
110
111
112 #define PARENT_FATAL(attrs) parent_fatal attrs
113
114 static void parent_fatal(int exit_code, const char* fmt, ...) {
115         va_list ap;
116         char str[1024];
117
118         va_start(ap,fmt);
119         g_vsnprintf(str,1024,fmt,ap);
120         va_end(ap);
121
122 #ifdef DEBUG_PARENT
123         PARENT_DBG((0,"Fatal error: exit_code=%d str=%s",exit_code,str));
124 #else
125         fprintf(stderr,"Fatal error: exit_code=%d str=%s",exit_code,str);
126 #endif
127
128         kill(parent.dispatcher_pid,SIGTERM);
129         exit(exit_code);
130 }
131
132 static void echld_cleanup(void) {
133         // int i;
134
135         PARENT_DBG((4,"echld_cleanup starting"));
136
137         // for (i=0;i<ECHLD_MAX_CHILDREN;i++) {
138         //      if ( parent.children[i].handlers ) g_array_free(parent.children[i].handlers,TRUE);
139         //      if ( parent.children[i].reqs ) g_array_free(parent.children[i].reqs,TRUE);
140         // };
141
142         // g_free(parent.children);
143
144         // g_byte_array_free(parent.snd,TRUE);
145
146         PARENT_DBG((3,"echld_cleanup done"));
147
148 }
149
150 static void parent_child_cleanup(echld_t* c) {
151
152         PARENT_DBG((2,"cleanup chld_id=%d",c->chld_id));
153         c->chld_id = -1;
154         c->data = NULL;
155         c->state = FREE;
156         g_array_set_size(c->handlers,0);
157         g_array_set_size(c->reqs,0);
158 }
159
160
161 void parent_reaper(int sig) {
162         int pid;
163         int status;
164
165         if (sig == SIGCHLD) {
166                 PARENT_FATAL((3333,"Must be SIGCHLD!"));
167         }
168
169         pid =  waitpid(-1, &status, WNOHANG);
170         PARENT_DBG((2,"SIGCHLD pid=%d",pid));
171
172         if (pid == parent.dispatcher_pid) {
173
174                 if (! parent.closing) {
175                         /* crashed */
176                         sleep(120);
177                         PARENT_FATAL((DISPATCHER_DEAD,"Dispatcher process dead"));
178                 }
179
180                 return;
181         } else {
182                 /* XXX: do we care? */
183                 return;
184         }
185
186         return;
187 }
188
189 static echld_bool_t hello_cb(echld_msg_type_t type, enc_msg_t* msg_buff, void* ud) {
190         echld_init_t* init = (echld_init_t*)ud;
191         char* err = NULL;
192         int errnum = 0;
193
194         if (init && init->dispatcher_hello_cb) {
195                 switch(type) {
196                         case ECHLD_ERROR:
197                                 parent.dec->error(msg_buff, &errnum ,&err);
198                                 break;
199                         case ECHLD_TIMED_OUT:
200                                 err = g_strdup("timedout");
201                                 break;
202                         default:
203                                 err = g_strdup_printf("Wrong MSG 'HELLO' expected, got '%s",TY(type));
204                                 break;
205                         case ECHLD_HELLO:
206                                 break;
207                 }
208
209                 init->dispatcher_hello_cb(ud,err);
210                 if (err) g_free(err);
211         }
212
213         return TRUE;
214 }
215
216
217
218 /* will initialize epan registering protocols and taps */
219 void echld_initialize(echld_init_t* init) {
220         int from_disp[2];
221         int to_disp[2];
222         PARENT_DBG((1,"Echld Starting"));
223
224         if (!init) {
225                 PARENT_FATAL((NO_INITIALIZER,"Missing Initializer"));
226         }
227
228         if (init->encoding != ECHLD_ENCODING_JSON) {
229                 PARENT_FATAL((UNIMPLEMENTED,"Only JSON implemented"));
230         }
231
232         if ( pipe(to_disp) ) {
233                 PARENT_FATAL((DISPATCHER_PIPE_FAILED,"Failed to open pipe to dispatcher"));
234         } else if( pipe(from_disp) )  {
235                 PARENT_FATAL((DISPATCHER_PIPE_FAILED,"Failed to open pipe from dispatcher"));
236         } else {
237                 int pid;
238                 int i;
239
240                 PARENT_DBG((3,"Pipes Opened fr[0]=%d fr[1]=%d to[0]=%d to[1]=%d",from_disp[0],from_disp[1],to_disp[0],to_disp[1]));
241
242                 pid = fork();
243
244                 if ( pid < 0 ) {
245                         PARENT_FATAL((CANNOT_FORK,"Failed to fork() reason='%s'",strerror(errno)));
246                 } else if ( pid == 0) {
247 #ifdef PARENT_THREADS
248                         reader_realloc_buf =  child_realloc_buff;
249 #endif
250                         /* child code */
251                         echld_cleanup();
252
253                         if (init->after_fork_cb)
254                                 init->after_fork_cb(init->after_fork_cb_data);
255
256                         echld_dispatcher_start(to_disp,from_disp,init->argv0,init->main);
257
258                         PARENT_FATAL((SHOULD_HAVE_EXITED_BEFORE,"This shouldn't happen"));
259                 } else {
260                         /* parent code */
261         #ifdef PARENT_THREADS
262                         reader_realloc_buf =  parent_realloc_buff;
263         #endif
264
265                         /* echld_common_set_dbg(9,stderr,"parent"); */
266
267                         PARENT_DBG((3,"Dispatcher forked"));
268
269                         echld_get_all_codecs(NULL, NULL, &parent.enc, &parent.dec);
270                         parent.children = g_new0(echld_t,ECHLD_MAX_CHILDREN);
271                         parent.snd = g_byte_array_new();
272                         parent.dispatcher_fd = to_disp[1];
273                         parent.dispatcher_pid = pid;
274
275                         echld_init_reader(&(parent.reader),from_disp[0],4096);
276
277
278                         for (i=0;i<ECHLD_MAX_CHILDREN;i++) {
279                                 parent.children[i].chld_id = -1;
280                                 parent.children[i].data = NULL;
281                                 parent.children[i].state = FREE;
282                                 parent.children[i].handlers = g_array_new(TRUE,TRUE,sizeof(hdlr_t));
283                                 parent.children[i].reqs = g_array_new(TRUE,TRUE,sizeof(reqh_t));
284                         }
285
286                         parent.children[0].chld_id = 0;
287                         parent.children[0].state = IDLE;
288
289                         signal(SIGCHLD,parent_reaper);
290                         //close(to_disp[0]);
291                         //close(from_disp[1]);
292                         if (init->dispatcher_hello_cb) echld_msgh(0, ECHLD_HELLO, hello_cb, init);
293
294                         PARENT_DBG((3,"Ready"));
295                 }
296         }
297 }
298
299
300 extern echld_state_t echld_terminate(void) {
301
302         parent.closing = TRUE;
303         PARENT_SEND(NULL,0,ECHLD_CLOSE_CHILD,++reqh_ids);
304
305         do {;} while(sleep(1)); /* wait a full sec without signals */
306
307         echld_cleanup();
308         close(parent.dispatcher_fd);
309         kill(parent.dispatcher_pid,SIGTERM);
310         return TRUE;
311 }
312
313
314
315
316 static echld_t* get_child(int id) {
317         int i;
318         for (i=0;i<ECHLD_MAX_CHILDREN;i++) {
319                 if (parent.children[i].chld_id == id) return &(parent.children[i]);
320         };
321
322         return NULL;
323 }
324
325
326 /* send a request */
327
328 int reqh_id_idx(echld_t* c, int reqh_id) {
329         int i;
330         int imax = c->reqs->len;
331         reqh_t* rr = (reqh_t*)c->reqs->data;
332
333         for(i=0; i < imax ; i++) {
334                 if (rr[i].reqh_id == reqh_id)
335                         return i;
336         }
337
338         return -1;
339 }
340
341
342 static echld_state_t reqh_snd(echld_t* c, echld_msg_type_t t, GByteArray* ba, echld_msg_cb_t resp_cb, void* cb_data) {
343         int idx;
344         reqh_t* r;
345         int reqh_id = reqh_ids++;
346
347         if (!c) {
348                 PARENT_DBG((1,"REQH_SND: No such child"));
349                 return 1;
350         }
351
352         idx = reqh_id_idx(c,-1);
353         if (idx < 0) {
354                 reqh_t req;
355                 idx = c->reqs->len;
356                 g_array_append_val(c->reqs,req);
357         }
358
359         r = &(((reqh_t*)c->reqs->data)[idx]);
360
361         r->reqh_id = reqh_id;
362         r->cb = resp_cb;
363         r->cb_data = cb_data;
364
365         gettimeofday(&(r->tv),NULL);
366
367         PARENT_DBG((4,"reqh_add: idx='%d'",idx));
368
369         PARENT_DBG((3,"REQH_SND: type='%s' chld_id=%d reqh_id=%d",TY(t), c->chld_id,reqh_id));
370
371         PARENT_SEND(ba,c->chld_id,t,reqh_id);
372
373         if (ba) g_byte_array_free(ba,TRUE); /* do we? */
374
375         return reqh_id;
376 }
377
378
379 extern echld_reqh_id_t echld_reqh(
380                 echld_chld_id_t child_id,
381                 echld_msg_type_t t,
382                 int usecs_timeout _U_,
383                 enc_msg_t* ba,
384                 echld_msg_cb_t resp_cb,
385                 void* cb_data) {
386         return reqh_snd(get_child(child_id),t,ba,resp_cb,cb_data);
387 }
388
389 /* get callback data for a live request */
390 extern void* echld_reqh_get_data(int child_id, int reqh_id) {
391         echld_t* c = get_child(child_id);
392         int idx;
393
394         if (!c) return NULL;
395
396         idx = reqh_id_idx(c,reqh_id);
397
398         if (idx >= 0)
399                 return g_array_index(c->reqs, reqh_t, idx).cb_data;
400         else
401                 return NULL;
402 }
403
404 /* get the callback for a live request */
405 extern echld_msg_cb_t echld_reqh_get_cb(int child_id, int reqh_id) {
406         echld_t* c = get_child(child_id);
407         int idx;
408
409         if (!c) return NULL;
410
411         idx = reqh_id_idx(c,reqh_id);
412
413         if (idx >= 0)
414                 return g_array_index(c->reqs, reqh_t, idx).cb;
415         else
416                 return NULL;
417 }
418
419 /* set callback data for a live request */
420 extern gboolean echld_reqh_set_data(int child_id, int reqh_id, void* cb_data) {
421         echld_t* c = get_child(child_id);
422         int idx;
423
424         if (!c) return FALSE;
425
426         idx = reqh_id_idx(c,reqh_id);
427
428         if (idx < 0) return FALSE;
429
430         g_array_index(c->reqs, reqh_t, idx).cb_data = cb_data;
431
432         return TRUE;
433 }
434
435 /* get the callback for a live request */
436 extern gboolean echld_reqh_set_cb(int child_id, int reqh_id, echld_msg_cb_t cb){
437         echld_t* c = get_child(child_id);
438         int idx;
439
440         if (!c) return FALSE;
441
442         idx = reqh_id_idx(c,reqh_id);
443
444         if (idx < 0) return FALSE;
445
446         g_array_index(c->reqs, reqh_t, idx).cb = cb;
447         return TRUE;
448 }
449
450
451 /* stop receiving a live request */
452 extern gboolean echld_reqh_detach(int child_id, int reqh_id) {
453         echld_t* c = get_child(child_id);
454         int idx;
455
456         if (!c) return FALSE;
457
458         idx = reqh_id_idx(c,reqh_id);
459
460         if (idx < 0) return FALSE;
461
462         g_array_remove_index(c->reqs,idx);
463
464         return TRUE;
465 }
466
467
468 static echld_bool_t parent_dead_child(echld_msg_type_t type, enc_msg_t* ba, void* data) {
469         echld_t* c = (echld_t*)data;
470         char* s;
471
472         if (type !=  ECHLD_CHILD_DEAD) {
473                 PARENT_DBG((1, "Must Be ECHLD_CHILD_DEAD"));
474                 return 1;
475         }
476
477         if ( parent.dec->child_dead(ba,&s) ) {
478                 PARENT_DBG((1,"Dead Child[%d]: %s",c->chld_id,s));
479                 g_free(s);
480         }
481
482         parent_child_cleanup(c);
483         return 0;
484 }
485
486 static echld_bool_t parent_get_hello(echld_msg_type_t type, enc_msg_t* ba, void* data) {
487         echld_t* c = (echld_t*)data;
488         int err_id;
489         char* err = NULL;
490
491         switch (type) {
492                 case  ECHLD_HELLO:
493                         PARENT_DBG((1,"Child[%d]: =>IDLE",c->chld_id));
494                         c->state = IDLE;
495                         break;
496                 case ECHLD_ERROR:
497                         parent.dec->error(ba,&err_id,&err);
498                         break;
499                 case ECHLD_TIMED_OUT:
500                         err = g_strdup("timedout");
501                         break;
502                 default:
503                         err = g_strdup_printf("Wrong MSG 'HELLO' expected, got '%s",TY(type));
504                         break;
505         }
506
507         if (c->cb)
508                 c->cb(c->data,err);
509
510         if (err) g_free(err);
511
512         return TRUE;
513 }
514
515
516
517
518
519 int chld_cmp(const void *a, const void *b) {
520         return ((echld_t*)b)->chld_id - ((echld_t*)a)->chld_id;
521 }
522
523 static int msgh_attach(echld_t* c, echld_msg_type_t t, echld_msg_cb_t resp_cb, void* cb_data);
524
525 static int next_chld_id = 1;
526
527 extern int echld_new(enc_msg_t* new_child_em, echld_new_cb_t cb, void* child_data) {
528         echld_t* c = get_child(-1);
529
530         if (!c) return -1;
531
532         c->chld_id = (next_chld_id++);
533         c->data = child_data;
534         c->state = CREATING;
535         c->cb = cb;
536
537         PARENT_DBG((1,"Child[%d]: =>CREATING",c->chld_id));
538
539         msgh_attach(c,ECHLD_CHILD_DEAD, parent_dead_child , c);
540     reqh_snd(c, ECHLD_NEW_CHILD, (GByteArray*)new_child_em, parent_get_hello, c);
541
542         return c->chld_id;
543 }
544
545
546
547 /* XXX these fail silently */
548 extern void* echld_get_data(int child_id) {
549         echld_t* c = get_child(child_id);
550         return c ? c->data : NULL;
551 }
552
553 extern echld_state_t echld_set_data(echld_chld_id_t chld_id, void* data) {
554         echld_t* c = get_child(chld_id);
555         if (c) {
556                 c->data = data;
557                 return TRUE;
558         }
559
560         return FALSE;
561 }
562
563 static int msgh_idx(echld_t* c, int msgh_id) {
564         int i  = 0;
565         int imax = c->handlers->len;
566
567         for (i=0;i<imax;i++) {
568                 if (((hdlr_t*)(c->handlers->data))[i].id == msgh_id) return i;
569         }
570
571         return -1;
572 }
573
574 /* start a message handler */
575 static int msgh_attach(echld_t* c, echld_msg_type_t t, echld_msg_cb_t resp_cb, void* cb_data) {
576         hdlr_t h;
577         static int msgh_id = 1;
578
579         h.id = msgh_id++;
580         h.type = t;
581         h.cb = resp_cb;
582         h.cb_data = cb_data;
583
584         g_array_append_val(c->handlers,h);
585         return 0;
586 }
587
588 extern int echld_msgh(int child_id, echld_msg_type_t t, echld_msg_cb_t resp_cb, void* cb_data) {
589         echld_t* c = get_child(child_id);
590
591         if (c) return msgh_attach(c,t,resp_cb,cb_data);
592         else return -1;
593 }
594
595
596 /* stop it */
597 static echld_state_t msgh_detach(echld_t* c, int msgh_id) {
598         int idx = msgh_idx(c,msgh_id);
599
600         if (idx < 0) return -1;
601
602         g_array_remove_index(c->handlers,idx);
603
604         return 1;
605 }
606
607 extern echld_state_t echld_msgh_detach(int child_id, int msgh_id) {
608         echld_t* c = get_child(child_id);
609         return msgh_detach(c,msgh_id);
610 }
611
612 /* get a msgh's data */
613
614 static void* msgh_get_data(echld_t* c, int msgh_id) {
615         int idx = msgh_idx(c,msgh_id);
616
617         if (idx < 0) return NULL;
618
619         return ((hdlr_t*)(c->handlers->data))[idx].cb_data;
620 }
621
622 extern void* echld_msgh_get_data(int child_id, int msgh_id) {
623         echld_t* c = get_child(child_id);
624         return msgh_get_data(c,msgh_id);
625 }
626
627 /* get a msgh's cb */
628 static echld_msg_cb_t msgh_get_cb(echld_t* c, int msgh_id) {
629         int idx = msgh_idx(c,msgh_id);
630
631         if (idx < 0) return NULL;
632
633         return ((hdlr_t*)(c->handlers->data))[idx].cb;
634 }
635
636 extern echld_msg_cb_t echld_msgh_get_cb(int child_id, int msgh_id) {
637         echld_t* c = get_child(child_id);
638         return msgh_get_cb(c,msgh_id);
639 }
640
641 /* get a msgh's type */
642 static echld_msg_type_t msgh_get_type(echld_t* c, int msgh_id) {
643         int idx = msgh_idx(c,msgh_id);
644
645         if (idx < 0) return EC_ACTUAL_ERROR;
646
647         return ((hdlr_t*)(c->handlers->data))[idx].type;
648 }
649
650 extern echld_msg_type_t echld_msgh_get_type(int child_id, int msgh_id) {
651         echld_t* c = get_child(child_id);
652         return c ? msgh_get_type(c,msgh_id) : EC_ACTUAL_ERROR;
653 }
654
655 /* get it all from a msgh */
656 static echld_state_t msgh_get_all(echld_t* c, int msgh_id, echld_msg_type_t* t, echld_msg_cb_t* cb, void** data) {
657         int idx = msgh_idx(c,msgh_id);
658         hdlr_t* h;
659
660         if (idx < 0) return -1;
661
662         h = &(((hdlr_t*)(c->handlers->data))[idx]);
663
664         if (t) *t = h->type;
665         if (cb) *cb = h->cb;
666         if (data) *data = h->cb_data;
667
668         return 0;
669 }
670
671 extern gboolean echld_msgh_get_all(int child_id, int msgh_id, echld_msg_type_t* t, echld_msg_cb_t* cb, void** data) {
672         echld_t* c = get_child(child_id);
673         return c && msgh_get_all(c,msgh_id,t,cb,data);
674 }
675
676 static echld_state_t msgh_set_all(echld_t* c, int msgh_id, echld_msg_type_t t, echld_msg_cb_t cb, void* data) {
677         int idx = msgh_idx(c,msgh_id);
678         hdlr_t* h;
679
680         if (idx < 0) return -1;
681
682         h = &(((hdlr_t*)(c->handlers->data))[idx]);
683
684         h->type = t;
685         h->cb = cb;
686         h->cb_data = data;
687
688         return 0;
689 }
690
691 extern gboolean echld_msgh_set_all(int child_id, int msgh_id, echld_msg_type_t t, echld_msg_cb_t cb, void* data) {
692         echld_t* c = get_child(child_id);
693         return c ? msgh_set_all(c,msgh_id,t,cb,data) : FALSE;
694 }
695
696 /* set a msgh's data */
697 static gboolean msgh_set_data(echld_t* c, int msgh_id, void* data) {
698         int idx = msgh_idx(c,msgh_id);
699
700         if (idx < 0) return FALSE;
701
702         ((hdlr_t*)(c->handlers->data))[idx].cb_data = data;
703
704         return TRUE;
705
706 }
707
708 extern gboolean echld_msgh_set_data(int child_id, int msgh_id, void* data){
709         echld_t* c = get_child(child_id);
710         return c ? msgh_set_data(c,msgh_id,data) : FALSE;
711 }
712
713 /* set a msgh's cb */
714 extern gboolean msgh_set_cb(echld_t* c, int msgh_id, echld_msg_cb_t cb) {
715         int idx = msgh_idx(c,msgh_id);
716
717         if (idx < 0) return FALSE;
718
719         ((hdlr_t*)(c->handlers->data))[idx].cb = cb;
720
721         return TRUE;
722 }
723
724 extern gboolean echld_msgh_set_cb(int child_id, int msgh_id, echld_msg_cb_t cb) {
725         echld_t* c = get_child(child_id);
726         return c ? msgh_set_cb(c,msgh_id,cb) : FALSE;
727 }
728
729 /* set a msgh's type */
730
731 static gboolean msgh_set_type(echld_t* c, int msgh_id, echld_msg_type_t t) {
732         int idx = msgh_idx(c,msgh_id);
733
734         if (idx < 0) return FALSE;
735
736         ((hdlr_t*)(c->handlers->data))[idx].type = t;
737
738         return TRUE;
739 }
740
741 extern gboolean echld_msgh_set_type(int child_id, int msgh_id, echld_msg_type_t t) {
742         echld_t* c = get_child(child_id);
743         return c ? msgh_set_type(c,msgh_id,t) : FALSE;
744 }
745
746
747 /* call cb(id,child_data,cb_data) for each child*/
748 extern void echld_foreach_child(echld_iter_cb_t cb, void* cb_data) {
749         int i;
750         for(i=0;i<ECHLD_MAX_CHILDREN;i++) {
751                 echld_t* c = &(parent.children[i]);
752                 cb(c->chld_id,c->data,cb_data);
753         }
754 }
755
756 static reqh_t* get_req(echld_t* c, int reqh_id) {
757         int idx = reqh_id_idx(c,reqh_id);
758         if(idx < 0) return NULL;
759
760         return ((reqh_t*)(c->reqs->data))+idx;
761 }
762
763 static hdlr_t* get_next_hdlr_for_type(echld_t* c, echld_msg_type_t t, int* cookie) {
764         int imax = c->handlers->len;
765         hdlr_t* r = NULL;
766
767         for (;(*cookie)<imax;(*cookie)++) {
768                 if (((hdlr_t*)(c->handlers->data))[*cookie].type == t) {
769                         r =  &( ((hdlr_t*)(c->handlers->data))[*cookie] );
770                         (*cookie)++;
771                         break;
772                 }
773         }
774
775         return r;
776 }
777
778 static long parent_read_frame(guint8* b, size_t len, echld_chld_id_t chld_id, echld_msg_type_t t, echld_reqh_id_t reqh_id, void* data _U_) {
779         echld_t* c = get_child(chld_id);
780         GByteArray* ba = g_byte_array_new();
781
782         PARENT_DBG((1,"MSG_IN<- ch=%d t='%s' rh=%d",chld_id,TY(t),reqh_id));
783         g_byte_array_append(ba,b, (guint)len);
784
785         if (c) {
786                 reqh_t* r = get_req(c, reqh_id);
787                 int i;
788                 hdlr_t* h;
789                 gboolean go_ahead = TRUE;
790
791                 if (r) { /* got that reqh_id */
792                         if (r->cb)  {
793                                 go_ahead = r->cb(t,ba,r->cb_data);
794                         }
795
796                         r->reqh_id = -1;
797                         r->cb = NULL;
798                         r->cb_data = 0;
799                         r->tv.tv_sec = 0;
800                         r->tv.tv_usec = 0;
801
802                         PARENT_DBG((2,"handled by reqh_id=%d msg='%s'",reqh_id,go_ahead?"retrying":"done"));
803                 }
804
805                 i=0;
806                 while(go_ahead && ( h = get_next_hdlr_for_type(c,t,&i))) {
807                                 if (h->cb)
808                                         go_ahead = h->cb(t,ba,h->cb_data);
809
810                                 PARENT_DBG((2,"handled by t='%s' msgh_id=%d msg='%s'",TY(h->type), h->id,go_ahead?"retrying":"done"));
811                 }
812         } else {
813                 PARENT_DBG((1,"parent_read_frame: No such child"));
814         }
815
816         g_byte_array_free(ba,TRUE);
817         return 1;
818 }
819
820 extern int echld_fdset(fd_set* rfds, fd_set* efds) {
821         FD_SET(parent.reader.fd, rfds);
822         FD_SET(parent.reader.fd, efds);
823         FD_SET(parent.dispatcher_fd, efds);
824         return 2;
825 }
826
827 extern int echld_fd_read(fd_set* rfds, fd_set* efds) {
828         int r_nfds=0;
829         if (FD_ISSET(parent.reader.fd,efds) || FD_ISSET(parent.dispatcher_fd,efds) ) {
830                 /* Handle errored dispatcher */
831                 PARENT_DBG((1,"parent errored"));
832                 return -1;
833         }
834
835         if (FD_ISSET(parent.reader.fd,rfds)) {
836                 PARENT_DBG((3,"reading from dispatcher"));
837                 echld_read_frame(&(parent.reader),parent_read_frame,&(parent));
838         }
839
840         return r_nfds;
841 }
842
843 extern int echld_select(int nfds _U_, fd_set* rfds, fd_set* wfds, fd_set* efds, struct timeval* timeout) {
844         fd_set my_rfds, my_wfds, my_efds;
845         int r_nfds;
846
847
848         if (rfds == NULL) { rfds = &my_rfds; FD_ZERO(rfds); }
849         if (wfds == NULL) { wfds = &my_wfds; FD_ZERO(wfds); }
850         if (efds == NULL) { efds = &my_efds; FD_ZERO(efds); }
851
852         echld_fdset(rfds,efds);
853
854         PARENT_DBG((5,"Select()"));
855         r_nfds = select(FD_SETSIZE, rfds, wfds, efds, timeout);
856
857         echld_fd_read(rfds,efds);
858
859         return r_nfds ;
860 }
861
862 extern echld_state_t echld_wait(struct timeval* timeout) {
863         if ( echld_select(0, NULL, NULL, NULL, timeout) < 0) {
864                 return -1;
865         } else {
866                 return ECHLD_OK;
867         }
868 }
869
870 enc_msg_t* echld_new_child_params(void) {
871         return (enc_msg_t*)g_byte_array_new();
872 }
873
874 enc_msg_t* echld_new_child_params_merge(enc_msg_t* em1, enc_msg_t* em2) {
875         GByteArray* ba = g_byte_array_new();
876         GByteArray* b1 = (GByteArray*)em1;
877         GByteArray* b2 = (GByteArray*)em2;
878
879         g_byte_array_append(ba,b1->data,b1->len);
880         g_byte_array_append(ba,b2->data,b2->len);
881
882         return (enc_msg_t*)ba;
883 }
884
885 char* echld_new_child_params_str(enc_msg_t* em, const char* prefix, const char* postfix, int trunc_n, const char* fmt) {
886         GByteArray* ba = (GByteArray*)em;
887         GString* str = g_string_new(prefix);
888         char* p = (char*) ba->data;
889         int tot_len = ba->len;
890         long rem = tot_len;
891         p[rem-1] = '\0'; /* make sure last char is null */
892
893         while(rem > 2) {
894                 char* param = p;
895                 long param_len = strlen(param)+1;
896                 char* value = p + param_len;
897                 long value_len;
898
899                 rem -= param_len;
900
901                 if (rem < 0) {
902                         g_string_free(str,TRUE);
903                         return NULL;
904                 }
905
906                 value_len = strlen(value)+1;
907
908                 rem -= value_len;
909                 p = value + value_len;
910
911                 if (rem < 0) {
912                         g_string_free(str,TRUE);
913                         return NULL;
914                 }
915
916                 g_string_append_printf(str,fmt,param,value);
917         }
918         g_string_truncate(str, str->len - trunc_n);
919         g_string_append(str,postfix);
920         p = str->str;
921         g_string_free(str,FALSE);
922         return p;
923 }
924
925 void echld_new_child_params_add_params(enc_msg_t* em, ...) {
926         GByteArray* ba = (GByteArray*) em;
927         va_list ap;
928
929         va_start(ap,em);
930         do {
931                 char* param_str = va_arg(ap, char*);
932
933                 if (param_str) {
934                         char* val_str = va_arg(ap, char*);
935
936                         g_byte_array_append(ba, (guint8*) param_str, (guint)strlen(param_str)+1);
937                         g_byte_array_append(ba, (guint8*) val_str, (guint)strlen(val_str)+1);
938                         continue;
939                 }
940
941                 break;
942         } while(1);
943         va_end(ap);
944
945 }
946
947