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