aboutsummaryrefslogtreecommitdiffstats
path: root/echld
diff options
context:
space:
mode:
authorLuis Ontanon <luis.ontanon@gmail.com>2013-06-21 01:27:28 +0000
committerLuis Ontanon <luis.ontanon@gmail.com>2013-06-21 01:27:28 +0000
commitb5c96de50bedd0d944de415c3080e2aa278fc362 (patch)
tree826cfc10b7625d0c0e144bb1fef7c90d4d6b56be /echld
parent74f0f96209400931e377c058efa186464dc09fd4 (diff)
move echld to final dest...
svn path=/trunk/; revision=50102
Diffstat (limited to 'echld')
-rw-r--r--echld/echld-int.h184
-rw-r--r--echld/echld.h401
-rw-r--r--echld/echld_child.c543
-rw-r--r--echld/echld_common.c797
-rw-r--r--echld/echld_dispatcher.c667
-rw-r--r--echld/echld_parent.c799
6 files changed, 3391 insertions, 0 deletions
diff --git a/echld/echld-int.h b/echld/echld-int.h
new file mode 100644
index 0000000000..245b15d435
--- /dev/null
+++ b/echld/echld-int.h
@@ -0,0 +1,184 @@
+/* echld_child-int.h
+ * epan working child API internals
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Copyright (c) 2013 by Luis Ontanon <luis@ontanon.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef __ECHLD_HDR_INT_
+#define __ECHLD_HDR_INT_
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include "capture_ifinfo.h"
+
+
+#include "echld.h"
+
+/* XXX these shouldn't be needed */
+typedef struct _column_info column_info;
+typedef struct _proto_node proto_tree;
+typedef struct tvbuff tvb_t;
+
+
+struct _hdr {
+ guint32 type_len;
+ guint16 chld_id;
+ guint16 reqh_id;
+};
+
+
+#define ECHLD_HDR_LEN (sizeof(struct _hdr))
+
+typedef union _hdr_t {
+ struct _hdr h;
+ guint8 b[ECHLD_HDR_LEN];
+} hdr_t;
+
+#define HDR_TYPE(H) ((((H)->h.type_len)&0xff000000)>>24)
+#define HDR_LEN(H) ((((H)->h.type_len)&0x00ffffff))
+
+#define GET_HDR_ELEMS(H,T,L,C,R) do { guint32 tl = H->h.type_len; \
+ T=HDR_TYPE(H); L=HDR_LEN(H); C=H->h.chld_id; R=H->h.req_id; } while(0)
+
+
+typedef enum _cst {
+ FREE=0,
+ CREATING,
+ IDLE,
+ READY,
+ READING,
+ CAPTURING,
+ DONE,
+ CLOSED=-1,
+ ERRORED=-2
+} child_state_t;
+
+
+/* these manage the de-framing machine in the receiver side */
+typedef struct _echld_reader {
+ guint8* rp; /* the read pointer*/
+ guint len; /* the used size = (.wp - .rp) */
+
+ guint8* wp; /* the write pointer */
+ int fd; /* the filedesc is serving */
+
+ guint8* data; /* the allocated read buffer */
+ size_t actual_len; /* the actual len of the allocated buffer */
+} echld_reader_t;
+
+
+#define reader_is_empty(R) ( (R)->len == 0 )
+#define reader_has_header(R) ((R)->len >= ECHLD_HDR_LEN)
+#define reader_has_frame(R) ( reader_has_header(R) && ( (HDR_LEN( (hdr_t*)((R)->rp) ) + ECHLD_HDR_LEN) >= ((R)->len )))
+
+#define READER_FD_SET(R,fdset_p) FD_SET(R.fd,&(fdset_p))
+#define READER_FD_ISSET(R,fdset_p) READER_FD_ISSET(R.fd,&(fdset_p))
+#define READER_FD_CLEAR(R,fdset_p) READER_FD_CLEAR(R.fd,&(fdset_p))
+
+void echld_init_reader(echld_reader_t* r, int fd, size_t initial);
+void echld_reset_reader(echld_reader_t* r, int fd, size_t initial);
+
+typedef struct _param {
+ char* name;
+ char* (*get)(char** err );
+ echld_bool_t (*set)(char* val , char** err);
+} param_t;
+
+/* the call_back used by read_frame() */
+typedef int (*read_cb_t)(guint8*, size_t, echld_chld_id_t, echld_msg_type_t, echld_reqh_id_t, void*);
+
+
+typedef struct _child_in {
+ echld_bool_t (*error) (guint8*, size_t, int* , char**);
+ echld_bool_t (*set_param) (guint8*, size_t, char** param, char** value);
+ echld_bool_t (*get_param) (guint8*, size_t, char** param);
+ echld_bool_t (*close_child) (guint8*, size_t, int* mode);
+
+
+ echld_bool_t (*open_file) (guint8*, size_t, char** filename);
+ echld_bool_t (*open_interface) (guint8*, size_t, char** intf_name, char** params);
+ echld_bool_t (*get_sum) (guint8*, size_t, char** range);
+ echld_bool_t (*get_tree) (guint8*, size_t, char** range);
+ echld_bool_t (*add_note) (guint8*, size_t, int* packet_number, char** note);
+ echld_bool_t (*apply_filter) (guint8*, size_t, char** filter);
+ echld_bool_t (*save_file) (guint8*, size_t, char** filename, char** params);
+} child_decoder_t;
+
+typedef struct _child_out {
+ enc_msg_t* (*error) (int , const char*);
+ enc_msg_t* (*child_dead) (const char*);
+ enc_msg_t* (*param) (const char*, const char*);
+ enc_msg_t* (*notify) (const char*); // pre-encoded
+ enc_msg_t* (*packet_sum) (int, const char*); // framenum, sum(pre-encoded)
+ enc_msg_t* (*tree) (int, const char*); // framenum, tree(pre-encoded)
+ enc_msg_t* (*buffer) (int , const char*, const char*, const char*); // totlen,name,range,data
+ enc_msg_t* (*packet_list) (const char*, const char*, const char*); // name, filter, range
+} child_encoder_t;
+
+
+typedef struct _parent_in {
+ echld_bool_t (*error) (enc_msg_t*, int* , char**);
+ echld_bool_t (*child_dead) (enc_msg_t*, char**);
+ echld_bool_t (*param) (enc_msg_t*, char**, char**);
+ echld_bool_t (*notify) (enc_msg_t*, char**); // pre-encoded
+ echld_bool_t (*packet_sum) (enc_msg_t*, int*, char**); // framenum, sum(pre-encoded)
+ echld_bool_t (*packet) (enc_msg_t*, int*, char**); // framenum, tree(pre-encoded)
+ echld_bool_t (*buffer) (enc_msg_t*, int*, char**, char**, char**); // totlen,name,range,data
+ echld_bool_t (*packet_list) (enc_msg_t*, char**, char**, char**); // name, filter, range
+} parent_decoder_t;
+
+void echld_get_all_codecs(child_encoder_t**, child_decoder_t**, echld_parent_encoder_t**, parent_decoder_t**);
+
+void echld_init_reader(echld_reader_t* r, int fd, size_t initial);
+void free_reader(echld_reader_t* r);
+
+int echld_read_frame(echld_reader_t* r, read_cb_t cb, void* cb_data);
+int echld_write_frame(int fd, GByteArray* ba, guint16 chld_id, echld_msg_type_t type, guint16 reqh_id, void* data);
+
+
+void echld_child_initialize(int pipe_from_parent, int pipe_to_parent, int reqh_id);
+int echld_child_loop(void);
+
+/* never returns*/
+void echld_dispatcher_start(int* in_pipe_fds, int* out_pipe_fds);
+
+
+extern void dummy_switch(echld_msg_type_t type);
+
+#define DEBUG_CHILD 5
+#define DEBUG_DISPATCHER 5
+#define DEBUG_PARENT 5
+
+#define BROKEN_PARENT_PIPE 3333
+#define BROKEN_DUMPCAP_PIPE 4444
+#define BROKEN_READFILE 5555
+
+#endif
diff --git a/echld/echld.h b/echld/echld.h
new file mode 100644
index 0000000000..60b97d6f2e
--- /dev/null
+++ b/echld/echld.h
@@ -0,0 +1,401 @@
+/* echld_child.h
+ * epan working child API
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Copyright (c) 2013 by Luis Ontanon <luis@ontanon.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __ECHLD_H
+#define __ECHLD_H
+
+#define ECHLD_VERSION "0.0"
+#define ECHLD_MAJOR_VERSION 0 /* increases when existing things change */
+ /* if this changes an old client may or may not work */
+
+#define ECHLD_MINOR_VERSION 0 /* increases when new things are added */
+ /* if just this one changes an old client will still work */
+
+/*
+ * You should take a look to doc/README.epan_child before reading this
+ */
+
+/* message types */
+typedef enum _echld_msg_type_t echld_msg_type_t;
+
+/* error types */
+typedef enum _echld_error echld_error_t;
+
+/* return codes */
+ /* 0 is ok, everything else is ko, or timeout where applicable. */
+typedef int echld_state_t;
+#define ECHLD_OK 0
+#define ECHLD_TIMEOUT -222
+
+/* id for child working processes, a negative value is an error */
+typedef int echld_chld_id_t;
+
+/* id of requests, a negative value is an error */
+typedef int echld_reqh_id_t;
+
+/* id of message handlers, a negative value is an error */
+typedef int echld_msgh_id_t;
+
+/* sets the codec set by name */
+typedef enum _echld_encoding {
+ ECHLD_ENCODING_TEXT = 'T',
+ ECHLD_ENCODING_XML = 'X',
+ ECHLD_ENCODING_JSON = 'J'
+} echld_encoding_t;
+
+typedef int echld_bool_t;
+
+/* typedef for a timeval so that sys/time.h is not required in the client */
+typedef struct timeval tv_t;
+
+/* will initialize epan registering protocols and taps */
+echld_state_t echld_initialize(echld_encoding_t);
+
+/* cleans up (?) echld and kills the server process(es) */
+echld_state_t echld_terminate(void);
+
+/*
+ * returning ECHLD_NO_ERROR means there has being no error
+ *
+ * errstr_ptr is a ptr to the error message string, will give NULL if no error
+ * usable only after the last API call, doesn't have to be freed.
+ *
+ * for managing asyncronous errors use a msgh for ECHLD_ERROR
+ * the response cb of reqh might be a ECHLD_ERROR message
+ */
+echld_error_t echld_get_error(const char** errstr_ptr);
+
+/*
+ * Children Management Operations
+ */
+
+/* create a new worker process */
+echld_chld_id_t echld_new(void* child_data);
+
+/* will return NULL on error, if NULL is also ok for you use echld_get_error() */
+void* echld_get_data(echld_chld_id_t);
+
+echld_state_t echld_set_data(echld_chld_id_t id, void* child_data);
+
+/* for each child call cb(id,child_data,cb_data) */
+typedef echld_bool_t (*echld_iter_cb_t)(echld_chld_id_t, void* child_data, void* cb_data);
+void echld_foreach_child(echld_iter_cb_t cb, void* cb_data);
+
+/* enc_msg_t is an obscure object for an encoded message */
+typedef struct GByteArray enc_msg_t;
+
+
+/*
+ * prototype of message callbacks passed to echld_reqh() and echld_msgh()
+ *
+ * type: for reqh it might be ECHLD_ERROR, ECHLD_TIMEOUT or what you expect,
+ * in msgh it's always the message for which it was set
+ * msg_buff: the encoded message
+ * cb_data: arbitrary data passed by the user in echld_reqh() or echld_msgh()
+ *
+ * returns TRUE if other potential handlers are to be run, false otherwise
+ */
+typedef echld_bool_t (*echld_msg_cb_t)(echld_msg_type_t type, enc_msg_t* msg_buff, void* cb_data);
+
+
+
+/* encoding and decoding */
+
+
+/*
+ * encoder
+ * the enc_msg_t will be destroyed internally by the req handler
+ * the resulting enc_msg_t can be used in a reqh just once.
+ */
+
+typedef struct _parent_out {
+ enc_msg_t* (*error)(int err, const char* text);
+ enc_msg_t* (*set_param)(const char* param, const char* value);
+ enc_msg_t* (*close_child)(int mode);
+ enc_msg_t* (*open_file)(const char* filename);
+ enc_msg_t* (*open_interface)(const char* intf_name, const char* params);
+ enc_msg_t* (*get_sum)(const char* range);
+ enc_msg_t* (*get_tree)(const char* range);
+ enc_msg_t* (*get_bufer)(const char* name);
+ enc_msg_t* (*add_note)(int packet_number, const char* note);
+ enc_msg_t* (*apply_filter)(const char* filter);
+ enc_msg_t* (*save_file)(const char* filename, const char* params);
+} echld_parent_encoder_t;
+
+echld_parent_encoder_t* echld_get_encoder();
+
+/*
+ * decoder
+ * it returns an allocated string with the decoded response of the message, you free it.
+ * it destroys the enc_msg_t as well.
+ */
+char* echld_decode(echld_msg_type_t, enc_msg_t*);
+
+/*
+ * Request Handlers
+ *
+ */
+
+/* send a request with an optional response handler
+ *
+ * ba is a enc_msg_t that contains the encoded message
+ * resp_cb is the callback and cb_data the data it is going to be passed if executed
+ *
+ * returns the reqh id */
+echld_reqh_id_t echld_reqh(echld_chld_id_t, echld_msg_type_t, int usecs_timeout, enc_msg_t*, echld_msg_cb_t, void*);
+
+/* get callback data for a live request */
+void* echld_reqh_get_data(echld_chld_id_t, echld_reqh_id_t);
+
+/* get the total timeout time for a live request, -1 is err */
+int echld_reqh_get_to(echld_chld_id_t, echld_reqh_id_t);
+
+/* get the remaining timeout time for a live request, -1 is err */
+int echld_reqh_get_remaining_to(echld_chld_id_t, echld_reqh_id_t);
+
+/* get the callback for a live request */
+echld_msg_cb_t echld_reqh_get_cb(echld_chld_id_t, echld_reqh_id_t);
+
+/* set callback data for a live request */
+echld_state_t echld_reqh_set_data(echld_chld_id_t, echld_reqh_id_t, void* );
+
+/* get the callback for a live request */
+echld_state_t echld_reqh_set_cb(echld_chld_id_t, echld_reqh_id_t, echld_msg_cb_t);
+
+/* stop receiving a live request */
+echld_state_t echld_reqh_detach(echld_chld_id_t, echld_reqh_id_t);
+
+
+/*
+ * Message Handlers
+ *
+ */
+
+/* start a message handler */
+echld_msgh_id_t echld_msgh(echld_chld_id_t, echld_msg_type_t, echld_msg_cb_t resp_cb, void* msg_data);
+
+/* stop it */
+echld_state_t echld_msgh_detach(echld_chld_id_t, echld_msgh_id_t);
+
+/* get a msgh's data */
+void* echld_msgh_get_data(echld_chld_id_t, echld_msgh_id_t);
+
+/* get a msgh's cb */
+echld_msg_cb_t echld_msgh_get_cb(echld_chld_id_t, echld_msgh_id_t);
+
+/* get a msgh's type */
+echld_msg_type_t echld_msgh_get_type(echld_chld_id_t, echld_msgh_id_t);
+
+/* get it all from a msgh */
+echld_state_t echld_msgh_get_all(echld_chld_id_t, int msgh_id, echld_msg_type_t*, echld_msg_cb_t*, void**);
+
+/* set a msgh's data */
+echld_state_t echld_msgh_set_data(echld_chld_id_t, int msgh_id, void* );
+
+/* set a msgh's cb */
+echld_state_t echld_msgh_set_cb(echld_chld_id_t, int msgh_id, echld_msg_cb_t);
+
+/* set a msgh's type */
+echld_state_t echld_msgh_set_type(echld_chld_id_t, int msgh_id, echld_msg_type_t);
+
+/* set all elements of a msgh */
+echld_state_t echld_msgh_set_all(echld_chld_id_t, int msgh_id, echld_msg_type_t, echld_msg_cb_t, void*);
+
+
+/*
+ * "Simple" API
+ * these calls require you looping on echld_select() or calling echld_wait() until you get your answer.
+ * see bellow
+ */
+
+typedef void (*echld_ping_cb_t)(int usec, void* data);
+echld_state_t echld_ping(int child_id, echld_ping_cb_t cb, void* cb_data);
+
+typedef void (*echld_list_interface_cb_t)(char* intf_name, char* params, void* cb_data);
+echld_state_t echld_list_interfaces(int child_id, echld_list_interface_cb_t, void* cb_data);
+
+typedef void (*echild_get_packet_summary_cb_t)(char* summary, void* data);
+echld_state_t echld_open_file(int child_id, const char* filename,echild_get_packet_summary_cb_t,void*);
+
+
+echld_state_t echld_open_interface(int child_id, const char* intf_name, const char* params);
+echld_state_t echld_start_capture(int child_id, echild_get_packet_summary_cb_t);
+echld_state_t echld_stop_capture(int child_id);
+
+typedef void (*echild_get_packets_cb)(char* tree_text,void* data);
+typedef void (*echild_get_buffer_cb)(char* buffer_text, void* data);
+echld_state_t echld_get_packets_range(int child_id, const char* range, echild_get_packets_cb, echild_get_buffer_cb, void* data);
+
+
+/*
+ * Server routines
+ */
+
+/*
+ * waits until something gets done
+ *
+ * returns ECHLD_TIMEOUT or ECHLD_OK if something was done
+ */
+echld_state_t echld_wait(tv_t* timeout);
+
+#define ECHLD_WAIT() do { struct timeval tv; int rfds, efds; \
+ echld_select(echld_fdset(&rfds, &efds),&rfds, NULL, &efds, NULL) \
+ && echld_fd_read(&rfds, &efds); } while(0)
+
+/*
+ to be used in place of select() in the main loop of the parent code
+ it will serve the children pipes and return as if select() was called.
+*/
+int echld_select(int nfds, fd_set* rfds, fd_set* wfds, fd_set* efds, tv_t* timeout);
+
+/* or fit these two in your select loop */
+
+/* returns nfds set */
+int echld_fdset(fd_set* rfds, fd_set* efds);
+
+int echld_fd_read(fd_set* rfds, fd_set* efds);
+
+void echld_set_parent_dbg_level(int lvl);
+
+
+#define ECHLD_MAX_CHILDREN 32
+
+enum _echld_msg_type_t {
+ /* in = child to parent */
+ /* out = parent to child */
+
+ ECHLD_ERROR = '!', /* in: an error has occurred,
+ * this can be a response to most messages
+ * some errors are sent asyncronously (some are handled internally, some are then passed)
+ */
+ ECHLD_TIMED_OUT='/', /* in: A reqh has timed out (TO from now on)
+ * this can be a response to some messages
+ * some TOs are sent asyncronously (some are handled internally, some are then passed)
+ */
+
+ ECHLD_NEW_CHILD = '*', /* out: creates a new working child (handled internally) */
+ ECHLD_HELLO = '@', /* in: the working child has being created (handled internally, then passed to msgh) */
+
+ ECHLD_CHILD_DEAD = '#', /* in: a child has dead (handled internally, then passed to msgh) */
+
+ ECHLD_CLOSE_CHILD = 'Q', /* out: close the child */
+ ECHLD_CLOSING = 'q', /* in: the child is closing, error otherwise */
+ /* this handled internally as msgh, if your reqh_cb uses it make sure to return TRUE */
+
+ ECHLD_SET_PARAM = '>', /* out: set a parameter of a child */
+ ECHLD_GET_PARAM = '<', /* out: set a parameter of a child */
+ ECHLD_PARAM = 'p', /* in: the parameter's new/current value, error otherwise */
+
+ /* capture_filter string RO: set at open_capture */
+ /* monitor_mode string RW: use monitor mode if possible, error otherwise */
+ /* inc_pkt_ntfy_timeout number_string RW: timeout in usec after which notification is sent if no maxpackets have arrived yet */
+ /* inc_pkt_ntfy_maxpackets number_string RW: number of packets after which send a notification */
+ /* auto_sum RW: get summaries automatically (without a reqh, as msgh) */
+ /* auto_tree RW: get trees automatically (without a reqh, as msgh) */
+ /* auto_buffer RW: get buffers automatically (without a reqh, as msgh) */
+ /* cwd RW: the current working directory */
+ /* list_files WO: a file listing of the current dir */
+ /* interfaces RO: the interface listing */
+ /* dfilter RW: initial display filter*
+ /* ... */
+
+ ECHLD_PING = '}', /* out: ping the child */
+ ECHLD_PONG = '{', /* out: ping's response, error or TO otherwise */
+
+ ECHLD_CHK_FILTER = 'K', /* out: verify if a (display) filter works */
+ ECHLD_FILTER_CKD = 'k', /* in: yes this filter works, error or TO? otherwise */
+
+ ECHLD_OPEN_FILE = 'O', /* out: open a file */
+ ECHLD_FILE_OPENED = 'o', /* in: the file has being open, error otherwise */
+
+ ECHLD_OPEN_INTERFACE = 'C', /* out: request an interface to be open (get ready for capture) */
+ ECHLD_INTERFACE_OPENED = 'c', /* in: ready to start_capture, error otherwise */
+
+ ECHLD_START_CAPTURE = 'R', /* out: start capturing */
+ ECHLD_CAPTURE_STARTED = 'r', /* in: the capture has started, error otherwise */
+
+ ECHLD_NOTIFY = '%', /* in: many things can be notified by the child:
+ number of packets captured/read
+ other events in the future (?)
+ */
+
+ ECHLD_GET_SUM = 'S', /* out: get the summaries of a range of packets (even before they are notify'd) */
+ ECHLD_PACKET_SUM = 's', /* in: a packet's summary (when it arrives for a reqh) (in msgh if auto_sum )*/
+ /* no timeout, the request hangs until the packets in the range are available */
+ /* error at EOF or CAPTURE_STOPPED if the request is still hanging */
+
+ ECHLD_GET_TREE = 'G', /* out: get the decoded version of the packet */
+ ECHLD_TREE = 't', /* Child -> Parent */
+ /* no timeout, the request hangs until the packets in the range are available */
+ /* error at EOF or CAPTURE_STOPPED if the request is still hanging */
+
+
+ ECHLD_GET_BUFFER = 'B', /* out: get the decoded version of the packet */
+ ECHLD_BUFFER = 'b', /* in: get a buffer (or what we have of it... or the next part... same reqh_id) */
+ /* no timeout, the request hangs until the packets in the range are available */
+ /* error at EOF or CAPTURE_STOPPED if the request is still hanging */
+
+ ECHLD_EOF = 'z', /* in: will be delivered when a file has being read and all pendin ntfy,sums,trees and buffs have being passed
+ or after capture has stopped and all pending stuff is done */
+
+ ECHLD_STOP_CAPTURE = 'X', /* out: stop capturing */
+ ECHLD_CAPTURE_STOPPED = 'x', /* in: capture has stopped, error otherwise */
+
+ ECHLD_ADD_NOTE = 'N', /* out: add a note to the capture */
+ ECHLD_NOTE_ADDED = 'n', /* in: a note has being added */
+
+ ECHLD_APPLY_FILTER = 'A', /* in: apply a filter on the open file/capture */
+ ECHLD_PACKET_LIST = 'l', /* out: a packet list, or error or timeout */
+ /*(or what we have of it... or the next part... same reqh_id) */
+
+ ECHLD_SAVE_FILE = 'W', /* out: save the open file/capture */
+ ECHLD_FILE_SAVED = 'w', /* in: the file was saved */
+
+
+ EC_ACTUAL_ERROR = 0 /* this is not used in the protocol,
+ it is returned for an error in calls returning a message type */
+};
+
+enum _echld_error {
+ ECHLD_NO_ERROR = 0,
+ ECHLD_ERR_UNIMPLEMENTED,
+ ECHLD_ERR_WRONG_MSG,
+ ECHLD_ERR_NO_SUCH_CHILD,
+ ECHLD_ERR_UNKNOWN_PID,
+ ECHLD_ERR_CANNOT_FORK,
+ ECHLD_ERR_SET_FILTER,
+ ECHLD_ERR_CANNOT_OPEN_FILE,
+ ECHLD_ERR_CANNOT_OPEN_INTERFACE,
+ ECHLD_ERR_CANNOT_START_CAPTURE,
+ ECHLD_ERR_CANNOT_LIST_INTERFACES,
+ ECHLD_CANNOT_SET_PARAM,
+ ECHLD_CANNOT_GET_PARAM,
+ ECHLD_ERR_CRASHED_CHILD,
+ ECHLD_ERR_OTHER
+};
+
+
+#endif
diff --git a/echld/echld_child.c b/echld/echld_child.c
new file mode 100644
index 0000000000..e5f165ec21
--- /dev/null
+++ b/echld/echld_child.c
@@ -0,0 +1,543 @@
+/* echld_child.c
+ * epan working child internals
+ * Child process routines and definitions
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Copyright (c) 2013 by Luis Ontanon <luis@ontanon.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include "echld-int.h"
+// echld_
+
+typedef struct _child {
+ child_state_t state;
+
+ int pid;
+ int ppid;
+ int chld_id;
+ int reqh_id;
+ echld_reader_t parent;
+ struct _fds {
+ int pipe_to_parent;
+ int pipe_from_dumpcap;
+ int pipe_to_dumpcap;
+ int file_being_read;
+ } fds;
+
+ struct timeval started;
+ struct timeval now;
+
+ child_encoder_t* enc;
+ child_decoder_t* dec;
+
+ // epan stuff
+} echld_child_t;
+
+
+static echld_child_t child;
+
+
+#define CHILD_RESP(BYTEARR,TYPE) echld_write_frame(child.fds.pipe_to_parent, BYTEARR, child.chld_id, TYPE, child.reqh_id, NULL)
+
+#ifdef DEBUG_CHILD
+static int dbg_level = 0;
+
+void child_debug(int level, char* fmt, ...) {
+ va_list ap;
+ char* str;
+
+ if (dbg_level<level) return;
+
+ va_start(ap, fmt);
+ str = g_strdup_vprintf(fmt,ap);
+ va_end(ap);
+
+ fprintf(stderr, "child[%d]: reqh_id=%d dbg_level=%d message='%s'", child.pid, child.reqh_id, level, str);
+ g_free(str);
+}
+
+#define CHILD_DBG(attrs) ( child_debug attrs )
+#else
+#define CHILD_DBG(attrs)
+#endif
+
+void echld_child_initialize(int pipe_from_parent, int pipe_to_parent, int reqh_id) {
+ child.state = IDLE;
+ child.pid = getpid();
+ child.ppid = getppid();
+ child.chld_id = 0;
+ child.reqh_id = reqh_id;
+ echld_init_reader( &(child.parent), pipe_from_parent,4096);
+ child.fds.pipe_to_parent = pipe_to_parent;
+ child.fds.pipe_from_dumpcap = -1;
+ child.fds.pipe_to_dumpcap = -1;
+ child.fds.file_being_read = -1;
+ gettimeofday(&child.started,NULL);
+ gettimeofday(&child.now,NULL);
+ echld_get_all_codecs(&(child.enc), &(child.dec), NULL, NULL);
+
+ /* epan stuff */
+
+ CHILD_DBG((5,"Child Initialized"));
+}
+
+
+
+void child_err(int e, unsigned reqh_id, const char* fmt, ...) {
+ size_t len= 1024;
+ guint8* b[len];
+ gchar err_str[len];
+ va_list ap;
+ static GByteArray* ba;
+
+ va_start(ap, fmt);
+ g_vsnprintf(err_str,len,fmt,ap);
+ va_end(ap);
+
+ CHILD_DBG((0,"error='%s'",err_str));
+
+ ba = (void*)child.enc->error(e, err_str);
+ echld_write_frame(child.fds.pipe_to_parent, ba, child.chld_id, ECHLD_ERROR, reqh_id, NULL);
+ g_byte_array_free(ba,TRUE);
+}
+
+
+static char* intflist2json(GList* if_list) {
+ /* blatantly stolen from print_machine_readable_interfaces in dumpcap.c */
+#define ADDRSTRLEN 46 /* Covers IPv4 & IPv6 */
+
+ int i;
+ GList *if_entry;
+ if_info_t *if_info;
+ GSList *addr;
+ if_addr_t *if_addr;
+ char addr_str[ADDRSTRLEN];
+ GString *str = g_string_new("={ ");
+ char* s;
+
+ i = 1; /* Interface id number */
+ for (if_entry = g_list_first(if_list); if_entry != NULL;
+ if_entry = g_list_next(if_entry)) {
+ if_info = (if_info_t *)if_entry->data;
+ g_string_append_printf(str,"%d={ intf='%s',", i++, if_info->name);
+
+ /*
+ * Print the contents of the if_entry struct in a parseable format.
+ * Each if_entry element is tab-separated. Addresses are comma-
+ * separated.
+ */
+ /* XXX - Make sure our description doesn't contain a tab */
+ if (if_info->vendor_description != NULL)
+ g_string_append_printf(str," vnd_desc='%s',", if_info->vendor_description);
+
+ /* XXX - Make sure our friendly name doesn't contain a tab */
+ if (if_info->friendly_name != NULL)
+ g_string_append_printf(str," name='%s', addrs=[ ", if_info->friendly_name);
+
+ for (addr = g_slist_nth(if_info->addrs, 0); addr != NULL;
+ addr = g_slist_next(addr)) {
+
+ if_addr = (if_addr_t *)addr->data;
+ switch(if_addr->ifat_type) {
+ case IF_AT_IPv4:
+ if (inet_ntop(AF_INET, &if_addr->addr.ip4_addr, addr_str,
+ ADDRSTRLEN)) {
+ g_string_append_printf(str,"%s", addr_str);
+ } else {
+ g_string_append(str,"<unknown IPv4>");
+ }
+ break;
+ case IF_AT_IPv6:
+ if (inet_ntop(AF_INET6, &if_addr->addr.ip6_addr,
+ addr_str, ADDRSTRLEN)) {
+ g_string_append_printf(str,"%s", addr_str);
+ } else {
+ g_string_append(str,"<unknown IPv6>");
+ }
+ break;
+ default:
+ g_string_append_printf(str,"<type unknown %u>", if_addr->ifat_type);
+ }
+ }
+
+ g_string_append(str," ]"); /* addrs */
+
+
+ if (if_info->loopback)
+ g_string_append(str,", loopback=1");
+ else
+ g_string_append(str,", loopback=0");
+
+ g_string_append(str,"}, ");
+ }
+
+ g_string_truncate(str,str->len - 2); /* the comma and space */
+ g_string_append(str,"}");
+
+ s=str->str;
+ g_string_free(str,FALSE);
+ return s;
+}
+
+static void child_start_interface_listing() {}
+
+static gboolean child_open_file(int chld_id, int reqh_id, char* filename, guint8* buff, int buff_len) {
+ char* reason = "Unimplemented"; // this ain't a good reason to fail!
+ g_snprintf(buff,buff_len,"Cannot open file=%s reason=%s",filename,reason);
+ return FALSE;
+}
+
+static gboolean child_open_interface(int chld_id, int reqh_id, const char* intf_name, const char* params, guint8* err_buf, int errbuff_len) {
+ char* reason = "Unimplemented"; // this ain't a good reason to fail!
+ g_snprintf(err_buf,errbuff_len,"Cannot open interface=%s reason=%s",intf_name,reason);
+ return FALSE;
+}
+
+
+// static void child_list_files() {
+// char* file_info = "{glob='*.cap', file_list={'dummy.cap'={type='pcap-ng', size=20300, npackets=502}}}";
+// // ls
+// // foreach file in cur dir
+// GByteArray* ba = (void*)child.enc->file_info(file_info);
+// CHILD_RESP(ba,ECHLD_FILE_INFO);
+// g_byte_array_free(ba,TRUE);
+// }
+
+
+static char* param_get_cwd(char** err ) {
+ char* pwd = getcwd(NULL, 128);
+
+ if (!pwd) {
+ *err = g_strdup(strerror(errno));
+ }
+ return pwd;
+}
+
+static echld_bool_t param_set_cwd(char* val , char** err ) {
+ /* XXX SANITIZE */
+ if (chdir(val) != 0) {
+ *err = g_strdup_printf("(%d)'%s'",errno,strerror(errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#define COOKIE_SIZE 1024
+static char* cookie = NULL;
+
+static char* param_get_cookie(char** err ) {
+ if (cookie)
+ return g_strdup(cookie);
+
+ *err = g_strdup("cookie not set");
+}
+
+static echld_bool_t param_set_cookie(char* val , char** err ) {
+
+ if (cookie) g_free(cookie);
+
+ cookie = g_strdup(val);
+ return TRUE;
+}
+
+#ifdef DEBUG_CHILD
+static char* param_get_dbg_level(char** err ) {
+ return g_strdup_printf("%d",dbg_level);
+}
+
+static echld_bool_t param_set_dbg_level(char* val , char** err ) {
+ char* p;
+ int lvl = strtol(val, &p, 10);
+
+ if (p<=val) {
+ *err = g_strdup("not an integer");
+ return FALSE;
+ } else if (lvl < 0 || lvl > 5) {
+ *err = g_strdup_printf("invalid level=%d (min=0 max=5)",lvl);
+ return FALSE;
+ }
+
+ dbg_level = lvl;
+ return TRUE;
+}
+#endif
+
+
+static param_t params[] = {
+#ifdef DEBUG_CHILD
+ {"dbg_level", param_get_dbg_level, param_set_dbg_level},
+# endif
+ {"cookie",param_get_cookie,param_set_cookie},
+ {"cwd",param_get_cwd,param_set_cwd},
+ {NULL,NULL,NULL}
+};
+
+static param_t* get_paramset(char* name) {
+ int i;
+ for (i = 0; params[i].name != NULL;i++) {
+ if (strcmp(name,params[i].name) == 0 ) return &(params[i]);
+ }
+ return NULL;
+}
+
+
+static gboolean child_set_filter(char* dflt, GString* err) { return FALSE; }
+static gboolean child_start_capture(GString* msg, GString* err) { return FALSE; }
+static gboolean child_stop_capture(GString* msg, GString* err) { return FALSE; }
+static gboolean child_get_packets(GString* msg, GString* err) { return FALSE; }
+static gboolean child_apply_filter(char* dflt, GString* err) { return FALSE; }
+static gboolean child_add_note(int packet_num, char* note, GString* err) { return FALSE; }
+
+
+static int child_receive(guint8* b, size_t len, echld_chld_id_t chld_id, echld_msg_type_t type, echld_reqh_id_t reqh_id, void* data) {
+ GByteArray* ba = NULL;
+
+ child.chld_id = chld_id;
+ child.reqh_id = reqh_id;
+
+ CHILD_DBG((2,"Message Received type='%c' len='%d'",type,len));
+
+ // gettimeofday(&(child.now), NULL);
+
+ if (child.chld_id != chld_id) {
+ if (child.chld_id == 0) {
+ if ( type == ECHLD_NEW_CHILD) {
+ child.chld_id = chld_id;
+ // more init needed for sure
+ CHILD_DBG((1,"chld_id set, sending HELLO"));
+ CHILD_RESP(ba,ECHLD_HELLO);
+ return 0;
+ } else {
+ child_err(ECHLD_ERR_WRONG_MSG,reqh_id,
+ "not yet initialized: chld_id:%d msg_type='%c'",chld_id,type);
+ return 0;
+ }
+ }
+
+ child_err(ECHLD_ERR_WRONG_MSG,reqh_id,
+ "chld_id: own:%d given:%d msg_type='%c'",child.chld_id,chld_id,type);
+ return 0;
+ }
+
+
+ switch(type) {
+ case ECHLD_PING:
+ CHILD_DBG((1,"PONG"));
+ CHILD_RESP(ba,ECHLD_PONG);
+ break;
+ case ECHLD_SET_PARAM:{
+ char* param;
+ char* value;
+ if ( child.dec->set_param && child.dec->set_param(b,len,&param,&value) ) {
+ param_t* p = get_paramset(param);
+ char* err;
+ if (!p) {
+ child_err(ECHLD_CANNOT_SET_PARAM,reqh_id,"no such param='%s'",param);
+ break;
+ }
+
+ if (!p->set) {
+ child_err(ECHLD_CANNOT_GET_PARAM,reqh_id,"reason='read only'");
+ break;
+ }
+
+ if (! p->set(value,&err) ) {
+ child_err(ECHLD_CANNOT_SET_PARAM,reqh_id,"reason='%s'",err);
+ g_free(err);
+ break;
+ }
+
+ ba = (void*)child.enc->param(param,value);
+ CHILD_RESP(ba,ECHLD_PARAM);
+ g_byte_array_free(ba,TRUE);
+ CHILD_DBG((1,"Set Param: param='%s' value='%s'",param,value));
+
+ break;
+ } else {
+ child_err(ECHLD_CANNOT_SET_PARAM,reqh_id,"reason='decoder error'");
+ break;
+ }
+ }
+ case ECHLD_GET_PARAM: {
+ char* param;
+ if ( child.dec->get_param && child.dec->get_param(b,len,&param) ) {
+ char* err;
+ char* val;
+
+ param_t* p = get_paramset(param);
+
+ if (!p) {
+ child_err(ECHLD_CANNOT_GET_PARAM,reqh_id,"no such param='%s'",param);
+ break;
+ }
+
+ if (!p->get) {
+ child_err(ECHLD_CANNOT_GET_PARAM,reqh_id,"reason='write only'");
+ break;
+ }
+
+ if (!(val = p->get(&err))) {
+ child_err(ECHLD_CANNOT_GET_PARAM,reqh_id,"reason='%s'",err);
+ g_free(err);
+ break;
+ }
+
+ ba = (void*)child.enc->param(param,val);
+ CHILD_RESP(ba,ECHLD_PARAM);
+ g_byte_array_free(ba,TRUE);
+ CHILD_DBG((2,"Get Param: param='%s' value='%s'",param,val));
+ break;
+ } else {
+ child_err(ECHLD_CANNOT_GET_PARAM,reqh_id,"reason='decoder error'");
+ break;
+ }
+ }
+ case ECHLD_CLOSE_CHILD:
+ CHILD_RESP(ba,ECHLD_CLOSING);
+ CHILD_DBG((3,"Closing"));
+
+ // select(0,NULL,NULL,NULL,sleep_time);
+ CHILD_DBG((1,"Bye"));
+ exit(0);
+ break;
+ case ECHLD_OPEN_INTERFACE:
+ case ECHLD_OPEN_FILE:
+ case ECHLD_START_CAPTURE:
+ case ECHLD_STOP_CAPTURE:
+ case ECHLD_GET_SUM:
+ case ECHLD_GET_TREE:
+ case ECHLD_GET_BUFFER:
+ case ECHLD_ADD_NOTE:
+ case ECHLD_APPLY_FILTER:
+ case ECHLD_SAVE_FILE:
+ goto not_implemented;
+ default:
+ child_err(ECHLD_ERR_WRONG_MSG,reqh_id,"chld_id=%d msg_type='%c'",chld_id,type);
+ break;
+ }
+
+ return 0;
+
+ misencoded:
+ // dump the misencoded message (b,blen)
+ child_err(ECHLD_ERR_WRONG_MSG,reqh_id,"misencoded msg msg_type='%c'",type);
+ return 0;
+
+ wrong_state:
+ child_err(ECHLD_ERR_WRONG_MSG,reqh_id,"unexpected message: received in wrong state='%c', msg_type='%c'",child.state,type);
+ return 0;
+
+ not_implemented:
+ child_err(ECHLD_ERR_UNIMPLEMENTED,reqh_id,"unimplemented message: received in wrong state='%c', msg_type='%c'",child.state,type);
+ return 0;
+
+}
+
+static void child_dumpcap_read() {
+ // this folk manages the reading of dumpcap's pipe
+ // it has to read interface descriptions when doing so
+ // and managing capture during capture
+ CHILD_DBG((2,"child_dumpcap_read"));
+}
+
+static void child_read_file() {
+ // this folk manages the reading of the file after open file has opened it
+ CHILD_DBG((2,"child_read_file"));
+}
+
+int echld_child_loop() {
+ int parent_fd = child.fds.pipe_to_parent;
+
+#ifdef DEBUG_CHILD
+ int step = 0;
+#endif
+
+ CHILD_DBG((0,"child_loop()"));
+
+ do {
+ fd_set rfds;
+ fd_set wfds;
+ fd_set efds;
+ struct timeval timeout;
+ struct dispatcher_child* c;
+ int nfds;
+
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+
+ FD_SET(parent_fd,&rfds);
+
+ if (child.fds.pipe_from_dumpcap > 0) {
+ FD_SET(child.fds.pipe_from_dumpcap,&rfds);
+ }
+
+ if (child.fds.file_being_read > 0) {
+ FD_SET(child.fds.file_being_read,&rfds);
+ }
+
+ CHILD_DBG((4,"child_loop: before select() step=%d",step++));
+ nfds = select(nfds, &rfds, &wfds, &efds, &timeout);
+ CHILD_DBG((5,"child_loop: after select() step=%d",step++));
+
+ if ( FD_ISSET(parent_fd,&efds) ) {
+ CHILD_DBG((0,"Broken Parent Pipe step=%d",step++));
+ break;
+ }
+ if (child.fds.pipe_from_dumpcap > 0 && FD_ISSET(child.fds.pipe_from_dumpcap,&efds) ) {
+ CHILD_DBG((0,"Broken Dumpcap Pipe step=%d",step++));
+ break;
+ }
+ if (child.fds.file_being_read > 0 && FD_ISSET(child.fds.file_being_read,&efds) ) {
+ CHILD_DBG((0,"Broken Readfile Pipe step=%d",step++));
+ break;
+ }
+
+ if (FD_ISSET(parent_fd, &rfds)) {
+
+ int st = echld_read_frame(&(child.parent), child_receive, &child);
+
+ if (st < 0) {
+ CHILD_DBG((0,"Read Frame Failed step=%d",step++));
+ return st;
+ }
+ }
+
+ if (child.fds.pipe_from_dumpcap > 0 && FD_ISSET(child.fds.pipe_from_dumpcap,&rfds) ) {
+ child_dumpcap_read();
+ }
+
+ if (child.fds.file_being_read > 0 && FD_ISSET(child.fds.pipe_from_dumpcap,&rfds) ) {
+ child_read_file();
+ }
+ } while(1);
+
+
+ CHILD_RESP(NULL,ECHLD_CLOSING);
+ CHILD_DBG((3,"Closing"));
+ return 222;
+}
+
+
diff --git a/echld/echld_common.c b/echld/echld_common.c
new file mode 100644
index 0000000000..33101e263b
--- /dev/null
+++ b/echld/echld_common.c
@@ -0,0 +1,797 @@
+/* echld_common.h
+ * common functions of ECHLD
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Copyright (c) 2013 by Luis Ontanon <luis@ontanon.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "echld-int.h"
+
+
+
+/**
+ the "epan pipe" protocol
+**/
+
+typedef void (*reader_realloc_t)(echld_reader_t*, size_t);
+
+static void child_realloc_buff(echld_reader_t* r, size_t needed) {
+ size_t a = r->actual_len;
+ size_t s = r->len;
+ int rp_off = r->rp - r->data;
+
+ if ( a < (s + needed) ) {
+ guint8* data = r->data;
+
+ do {
+ a *= 2;
+ } while( a < (s + needed) );
+
+ data = g_realloc(data,a);
+
+ r->actual_len = a;
+ r->len = s;
+ r->data = data;
+ r->wp = data + s;
+ r->rp = data + rp_off;
+ }
+}
+
+static reader_realloc_t reader_realloc_buff = child_realloc_buff;
+
+#ifdef PARENT_THREADS
+static void parent_realloc_buff(echld_reader_t* b, size_t needed) {
+ // parent thread: obtain malloc mutex
+ child_realloc_buff(b,needed);
+ // parent thread: release malloc mutex
+}
+#endif
+
+
+
+void echld_init_reader(echld_reader_t* r, int fd, size_t initial) {
+ r->fd = fd;
+ if (fd >= 0) fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ if (r->data == NULL) {
+ r->actual_len = initial;
+ r->data = g_malloc0(initial);
+ r->wp = r->data;
+ r->rp = NULL;
+ r->len = 0;
+ }
+}
+
+void echld_reset_reader(echld_reader_t* r, int fd, size_t initial) {
+ r->fd = fd;
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ if (r->data == NULL) {
+ r->actual_len = initial;
+ r->data = g_malloc0(initial);
+ r->wp = r->data;
+ r->rp = NULL;
+ r->len = 0;
+ } else {
+ r->wp = r->data;
+ r->rp = NULL;
+ r->len = 0;
+ }
+}
+
+void free_reader(echld_reader_t* r) {
+ free(r->data);
+}
+
+static int reader_readv(echld_reader_t* r, size_t len) {
+ struct iovec iov;
+ int nread;
+
+ if ( (r->actual_len - r->len) < len )
+ reader_realloc_buff(r, len);
+
+ iov.iov_base = r->wp;
+ iov.iov_len = len;
+
+ nread = readv(0, &iov, len);
+
+ if (nread >= 0) {
+ r->wp += nread;
+ r->len += nread;
+ }
+
+ return nread;
+};
+
+
+int echld_read_frame(echld_reader_t* r, read_cb_t cb, void* cb_data) {
+
+ // it will use shared memory instead of inband communication
+ do {
+ hdr_t* h = (hdr_t*)r->rp;
+ int nread;
+ size_t fr_len;
+ size_t missing;
+ int off;
+
+ if ( r->len < ECHLD_HDR_LEN) {
+ /* read the header */
+ goto incomplete_header;
+ } else if ( ! reader_has_frame(r) ) {
+ /* read the (rest of) the frame */
+ goto incomplete_frame;
+ }
+
+ /* we've got a frame! */
+
+ off = (fr_len = HDR_LEN(h)) + ECHLD_HDR_LEN;
+
+ cb( &(r->rp[sizeof(hdr_t)]), HDR_LEN(h), h->h.chld_id, HDR_TYPE(h), h->h.reqh_id, cb_data);
+
+ if ( r->len >= off ) {
+ /* shift the consumed frame */
+ r->len -= off;
+ memcpy(r->rp ,r->rp + off ,r->len);
+ r->wp -= off;
+ r->rp -= off;
+ }
+
+ continue;
+
+ incomplete_header:
+ missing = ECHLD_HDR_LEN - (r->len);
+
+ nread = reader_readv(r,missing);
+
+
+ if (nread < 0) {
+ goto kaput; /*XXX*/
+ } else /* if (nread == 0) {
+ break;
+ } else */ if (nread < missing) {
+ goto again;
+ } else {
+ goto incomplete_frame;
+ }
+
+ incomplete_frame:
+ fr_len = HDR_LEN(h) + ECHLD_HDR_LEN;
+ missing = fr_len - r->len;
+
+ nread = reader_readv(r,missing);
+
+
+ if (nread < 0) {
+ goto kaput; /*XXX*/
+ } else if (nread <= missing) {
+ goto again;
+ }
+
+ } while(1);
+
+ return 0;
+ again: return 1;
+ kaput: return -1;
+}
+
+
+
+
+int echld_write_frame(int fd, GByteArray* ba, guint16 chld_id, echld_msg_type_t type, guint16 reqh_id, void* data) {
+ static guint8* write_buf = NULL;
+ static size_t wb_len = 4096;
+ hdr_t* h;
+ struct iovec iov;
+ int fr_len = ba->len+ECHLD_HDR_LEN;
+
+ data = data; //
+
+ // it will use shared memory instead of inband communication
+
+ if (! write_buf) {
+ // lock if needed
+ write_buf = g_malloc0(wb_len);
+ // unlock if needed
+ }
+
+ if (fr_len > wb_len) {
+ do {
+ wb_len *= 2;
+ } while (fr_len > wb_len);
+
+ // lock if needed
+ write_buf = g_realloc(write_buf,wb_len);
+ // unlock if needed
+ }
+
+ h = (void*)write_buf;
+ h->h.type_len = (type<<24) | (((guint32)ba->len) & 0x00ffffff) ;
+ h->h.chld_id = chld_id;
+ h->h.reqh_id = reqh_id;
+
+ memcpy(write_buf+ECHLD_HDR_LEN,ba->data,ba->len);
+
+ iov.iov_base = write_buf;
+ iov.iov_len = fr_len;
+
+ return (int) writev(fd, &iov, fr_len);
+}
+
+
+
+/* encoders and decoders */
+
+
+
+
+
+/* binary encoders and decoders used for parent->child communication */
+
+static enc_msg_t* str_enc(const char* s) {
+ GByteArray* ba = g_byte_array_new();
+ g_byte_array_append(ba,s,strlen(s)+1);
+ return (enc_msg_t*)ba;
+}
+
+static gboolean str_dec(guint8* b, size_t bs, char** text) {
+ guint8* end = b+bs;
+ b[bs-1] = '\0'; /* null terminate the buffer to avoid strlen running */
+ *text = (char*)b;
+ if (b+(strlen(b)+1) > end) return FALSE;
+ return TRUE;
+}
+
+static gboolean str_deca(enc_msg_t* e, char** text) {
+ GByteArray* ba = (void*)e;
+ return str_dec(ba->data,ba->len,text);
+
+}
+
+static enc_msg_t* int_str_enc(int i, const char* s) {
+ GByteArray* ba = g_byte_array_new();
+ g_array_append(ba,&i,sizeof(int));
+ g_array_append(ba,s,strlen(s)+1);
+ return (enc_msg_t*)ba;
+}
+
+static gboolean int_str_dec(guint8* b, size_t bs, int* ip, char** text) {
+ guint8* end = b+bs;
+ b[bs-1] = '\0'; /* null terminate the buffer to avoid strlen running */
+
+ if ((sizeof(int)) > bs) return FALSE;
+ *ip = *((int*)b);
+ b += (sizeof(int));
+ *text = (char*)b;
+ if ((b += (strlen(b)+1)) > end) return FALSE;
+
+ return TRUE;
+}
+
+static gboolean int_str_deca(enc_msg_t* e, int* ip, char** text) {
+ GByteArray* ba = (void*)e;
+ return int_str_dec(ba->data,ba->len,ip,text);
+}
+
+static enc_msg_t* int_enc(int i) {
+ GByteArray* ba = g_byte_array_new();
+ g_array_append(ba,&i,sizeof(int));
+ return (enc_msg_t*)ba;
+}
+
+static gboolean int_dec(guint8* b, size_t bs, int* ip) {
+ if ((sizeof(int)) > bs) return FALSE;
+ *ip = *((int*)b);
+ return TRUE;
+}
+
+static gboolean int_deca(enc_msg_t* e, int* ip) {
+ GByteArray* ba = (void*)e;
+ return int_dec(ba->data,ba->len,ip);
+}
+
+static enc_msg_t* x2str_enc(const char* s1, const char* s2) {
+ GByteArray* ba = g_byte_array_new();
+ g_array_append(ba,s1,strlen(s1)+1);
+ g_array_append(ba,s2,strlen(s2)+1);
+ return (enc_msg_t*)ba;
+}
+
+static gboolean x2str_dec(guint8* b, size_t blen, char** str1, char** str2) {
+ guint8* end = b+blen;
+ b[blen-1] = '\0'; /* null terminate the buffer to avoid strlen running */
+
+ *str1 = (char*)b;
+ if ((b += (strlen(b)+1)) > end) return FALSE;
+ *str2 = (char*)(b);
+ if ((b += (strlen(b)+1)) > end) return FALSE;
+ return TRUE;
+}
+
+static gboolean x2str_deca(enc_msg_t* e, char** str1, char** str2) {
+ GByteArray* ba = (void*)e;
+ return x2str_dec(ba->data,ba->len,str1,str2);
+}
+
+static gboolean int_3str_dec (guint8* b, size_t len, int* i, char** s1, char** s2, char** s3) {
+ guint8* end = b+len;
+ b[len-1] = '\0';
+
+ if ((sizeof(int)) > len) return FALSE;
+ *i = *((int*)b);
+ b += sizeof(int);
+
+ *s1 = (char*)b;
+ if ((b += (strlen(b)+1)) > end) return FALSE;
+ *s2 = (char*)(b);
+ if ((b += (strlen(b)+1)) > end) return FALSE;
+ *s3 = (char*)b;
+ if ((b += (strlen(b)+1)) > end) return FALSE;
+ return TRUE;
+}
+
+static enc_msg_t* int_3str_enc(int i, const char* s1, const char* s2, const char* s3) {
+ GByteArray* ba = g_byte_array_new();
+ g_array_append(ba,&i,sizeof(int));
+ g_array_append(ba,s1,strlen(s1)+1);
+ g_array_append(ba,s2,strlen(s2)+1);
+ g_array_append(ba,s3,strlen(s3)+1);
+ return (enc_msg_t*)ba;
+}
+
+static gboolean int_3str_deca (enc_msg_t* e, int* i, char** s1, char** s2, char** s3) {
+ GByteArray* ba = (void*)e;
+ return int_3str_dec(ba->data,ba->len,i,s1,s2,s3);
+}
+
+static gboolean x3str_dec (guint8* b, size_t len, char** s1, char** s2, char** s3) {
+ guint8* end = b+len;
+ b[len-1] = '\0';
+
+
+ *s1 = (char*)b;
+ if ((b += (strlen(b)+1)) > end) return FALSE;
+ *s2 = (char*)(b);
+ if ((b += (strlen(b)+1)) > end) return FALSE;
+ *s3 = (char*)b;
+ if ((b += (strlen(b)+1)) > end) return FALSE;
+ return TRUE;
+}
+
+static gboolean x3str_deca (enc_msg_t* e, char** s1, char** s2, char** s3) {
+ GByteArray* ba = (void*)e;
+ return x3str_dec(ba->data,ba->len,s1,s2,s3);
+}
+
+
+static enc_msg_t* x3str_enc(const char* s1, const char* s2, const char* s3) {
+ GByteArray* ba = g_byte_array_new();
+ g_array_append(ba,s1,strlen(s1)+1);
+ g_array_append(ba,s2,strlen(s2)+1);
+ g_array_append(ba,s3,strlen(s3)+1);
+ return (enc_msg_t*)ba;
+}
+
+static echld_parent_encoder_t parent_encoder = {
+ int_str_enc,
+ x2str_enc,
+ int_enc,
+ str_enc,
+ x2str_enc,
+ str_enc,
+ str_enc,
+ str_enc,
+ int_str_enc,
+ str_enc,
+ x2str_enc
+};
+
+echld_parent_encoder_t* echld_get_encoder() {
+ return &parent_encoder;
+}
+
+static child_decoder_t child_decoder = {
+ int_str_dec,
+ x2str_dec,
+ str_dec,
+ int_dec,
+
+
+
+ str_dec,
+ x2str_dec,
+ str_dec,
+ str_dec,
+ int_str_dec,
+ str_dec,
+ x2str_dec
+};
+
+static child_encoder_t child_encoder = {
+ int_str_enc,
+ str_enc,
+ x2str_enc,
+ str_enc,
+ int_str_enc,
+ int_str_enc,
+ int_3str_enc,
+ x3str_enc
+};
+
+static parent_decoder_t parent_decoder = {
+ int_str_deca,
+ str_deca,
+ x2str_deca,
+ str_deca,
+ int_str_deca,
+ int_str_deca,
+ int_3str_deca,
+ x3str_deca
+};
+
+void echld_get_all_codecs( child_encoder_t **e, child_decoder_t **d, echld_parent_encoder_t **pe, parent_decoder_t** pd) {
+ e && (*e = &child_encoder);
+ d && (*d = &child_decoder);
+ pe && (*pe = &parent_encoder);
+ pd && (*pd = &parent_decoder);
+}
+
+
+
+/* output encoders, used in the switch */
+
+
+static char* packet_summary_json(GByteArray* ba) {
+ /* dummy */
+ return g_strdup("{type='packet_summary', packet_summary={}");
+}
+
+static char* tree_json(GByteArray* ba) {
+ /* dummy */
+ return g_strdup("{type='tree', tree={}");
+}
+
+static char* tvb_json(GByteArray* ba, tvb_t* tvb, const char* name) {
+ /* dummy */
+ return g_strdup_printf("{type='buffer', buffer={name='%s', range='0-2', data=[0x12,0xff] }",name);
+}
+
+static char* error_json(GByteArray* ba) {
+ char* s = (char*)(ba->data + sizeof(int));
+ int i = *((int*)s);
+
+ s = g_strdup_printf("{type='error', error={errnum=%d, message='%s'}}",i,s);
+
+ return s;
+}
+
+static char* child_dead_json(GByteArray* ba) {
+ char* s = (char*)(ba->data + sizeof(int));
+ int i = *((int*)s);
+
+ s = g_strdup_printf("{type='child_dead', child_dead={childnum=%d, message='%s'}}",i,s);
+
+ return s;
+}
+
+static char* closing_json(GByteArray* ba) {
+ char* s = (char*)(ba->data);
+ s = g_strdup_printf("{type='closing', closing={reason='%s'}}",s);
+
+ return s;
+}
+
+static char* cwd_json(GByteArray* ba) {
+ char* s = (char*)(ba->data);
+ s = g_strdup_printf("{type='cwd', cwd={dir='%s'}}",s);
+
+ return s;
+}
+
+static char* file_info_json(GByteArray* ba) {
+ char* s1 = (char*)(ba->data);
+ char* s2 = ((char*)(ba->data)) + strlen(s1);
+
+ s1 = g_strdup_printf("{type='file', file={filename='%s' info='%s'}}",s1,s2);
+
+ return s1;
+}
+
+
+static char* note_added_json(GByteArray* ba) {
+ char* s = (char*)(ba->data);
+ s = g_strdup_printf("{ type='note_added', note_added={msg='%s'}}",s);
+
+ return s;
+}
+
+static char* packet_list_json(GByteArray* ba) {
+ return g_strdup("{}");
+}
+
+static char* file_saved_json(GByteArray* ba) {
+ char* s = (char*)(ba->data);
+
+ s = g_strdup_printf("{ type='file_saved', file_saved={msg='%s'}}",s);
+
+ return s;
+}
+
+
+
+static char* param_set_json(GByteArray* ba) {
+ char* s1 = (char*)(ba->data);
+ char* s2 = ((char*)(ba->data)) + strlen(s1);
+
+ s1 = g_strdup_printf("{type='param_set', param_set={param='%s' value='%s'}}",s1,s2);
+
+
+ return s1;
+}
+
+static char* set_param_json(GByteArray* ba) {
+ char* s1 = (char*)(ba->data);
+ char* s2 = ((char*)(ba->data)) + strlen(s1);
+
+ s1 = g_strdup_printf("{type='set_param', set_param={param='%s' value='%s'}}",s1,s2);
+
+
+ return s1;
+}
+
+static char* get_param_json(GByteArray* ba) {
+ char* s1 = (char*)(ba->data);
+
+ s1 = g_strdup_printf("{type='get_param', get_param={param='%s'}}",s1);
+
+
+ return s1;
+}
+
+static char* list_files_json(GByteArray* ba) {
+ char* s1 = (char*)(ba->data);
+
+ s1 = g_strdup_printf("{type='list_files', list_files={glob='%s'}}",s1);
+
+
+ return s1;
+}
+
+
+static char* chk_filter_json(GByteArray* ba) {
+ char* s1 = (char*)(ba->data);
+
+ s1 = g_strdup_printf("{type='chk_filter', chk_filter={filter='%s'}}",s1);
+
+ return s1;
+}
+
+static char* filter_ckd_json(GByteArray* ba) {
+ char* s1 = (char*)(ba->data + sizeof(int));
+ int i = *((int*)ba->data);
+
+ s1 = g_strdup_printf("{type='filter_ckd', filter_ckd={filter='%s',ok=%s}}",ba->data,i?"true":"false");
+
+ return s1;
+}
+
+static char* set_filter_json(GByteArray* ba) {
+ char* s1 = (char*)(ba->data);
+
+ s1 = g_strdup_printf("{type='chk_filter', chk_filter={filter='%s'}}",s1);
+
+ return s1;
+}
+
+static char* filter_set_json(GByteArray* ba) {
+ char* s1 = (char*)(ba->data);
+
+ s1 = g_strdup_printf("{type='filter_set', filter_set={filter='%s'}}",s1);
+
+ return s1;
+}
+
+
+static char* file_opened_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* open_file_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* intf_info_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* open_interface_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+
+static char* interface_opened_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* notify_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* get_tree_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* get_sum_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* get_buffer_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* buffer_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* add_note_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* apply_filter_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+static char* save_file_json(GByteArray* ba) {
+ return g_strdup("");
+}
+
+
+/* this to be used only at the parent */
+static char* decode_json(echld_msg_type_t type, enc_msg_t* m) {
+ GByteArray* ba = (GByteArray*)m;
+
+ switch(type) {
+ case ECHLD_ERROR: return error_json(ba);
+ case ECHLD_TIMED_OUT: return g_strdup("{type='timed_out'}");
+ case ECHLD_NEW_CHILD: return g_strdup("{type='new_child'}");
+ case ECHLD_HELLO: return g_strdup("{type='helo'}");
+ case ECHLD_CHILD_DEAD: return child_dead_json(ba);
+ case ECHLD_CLOSE_CHILD: return g_strdup("{type='close_child'}");
+ case ECHLD_CLOSING: return g_strdup("{type='closing'}");
+ case ECHLD_SET_PARAM: return set_param_json(ba);
+ case ECHLD_GET_PARAM: return get_param_json(ba);
+ case ECHLD_PARAM: return param_set_json(ba);
+ case ECHLD_PING: return g_strdup("{type='ping'}");
+ case ECHLD_PONG: return g_strdup("{type='pong'}");
+ case ECHLD_OPEN_FILE: return open_file_json(ba);
+ case ECHLD_FILE_OPENED: return file_opened_json(ba);
+ case ECHLD_OPEN_INTERFACE: return open_interface_json(ba);
+ case ECHLD_INTERFACE_OPENED: return interface_opened_json(ba);
+ case ECHLD_START_CAPTURE: return g_strdup("{type='start_capture'}");
+ case ECHLD_CAPTURE_STARTED: return g_strdup("{type='capture_started'}");
+ case ECHLD_NOTIFY: return notify_json(ba);
+ case ECHLD_GET_SUM: return get_sum_json(ba);
+ case ECHLD_PACKET_SUM: return packet_summary_json(ba);
+ case ECHLD_GET_TREE: return get_tree_json(ba);
+ case ECHLD_TREE: return tree_json(ba);
+ case ECHLD_GET_BUFFER: return get_buffer_json(ba);
+ case ECHLD_BUFFER: return buffer_json(ba);
+ case ECHLD_EOF: return g_strdup("{type='eof'}");
+ case ECHLD_STOP_CAPTURE: return g_strdup("{type='stop_capture'}");
+ case ECHLD_CAPTURE_STOPPED: return g_strdup("{type='capture_stopped'}");
+ case ECHLD_ADD_NOTE: return add_note_json(ba);
+ case ECHLD_NOTE_ADDED: return note_added_json(ba);
+ case ECHLD_APPLY_FILTER: return apply_filter_json(ba);
+ case ECHLD_PACKET_LIST: return packet_list_json(ba);
+ case ECHLD_SAVE_FILE: return save_file_json(ba);
+ case ECHLD_FILE_SAVED: return g_strdup("{type='file_saved'}");
+ case EC_ACTUAL_ERROR: return g_strdup("{type='actual_error'}");
+ default: break;
+ }
+
+ return NULL;
+}
+char* echld_decode(echld_msg_type_t t, enc_msg_t* m ) {
+ return decode_json(t,m);
+}
+
+
+
+extern void dummy_switch(echld_msg_type_t type) {
+ switch(type) {
+ case ECHLD_ERROR: break; //
+ case ECHLD_TIMED_OUT: break;
+ case ECHLD_NEW_CHILD: break;
+ case ECHLD_HELLO: break;
+ case ECHLD_CHILD_DEAD: break; //S msg
+ case ECHLD_CLOSE_CHILD: break;
+ case ECHLD_CLOSING: break; //
+ case ECHLD_SET_PARAM: break;
+ case ECHLD_GET_PARAM: break;
+ case ECHLD_PARAM: break; //SS param,val
+ case ECHLD_PING: break;
+ case ECHLD_PONG: break; //
+ case ECHLD_OPEN_FILE: break;
+ case ECHLD_FILE_OPENED: break; //
+ case ECHLD_OPEN_INTERFACE: break;
+ case ECHLD_INTERFACE_OPENED: break; //
+ case ECHLD_START_CAPTURE: break;
+ case ECHLD_CAPTURE_STARTED: break; //
+ case ECHLD_NOTIFY: break; //S notification (pre-encoded)
+ case ECHLD_GET_SUM: break;
+ case ECHLD_PACKET_SUM: break; //S (pre-encoded)
+ case ECHLD_GET_TREE: break;
+ case ECHLD_TREE: break; //IS framenum,tree (pre-encoded)
+ case ECHLD_GET_BUFFER: break;
+ case ECHLD_BUFFER: break; //SSIS name,range,totlen,data
+ case ECHLD_EOF: break; //
+ case ECHLD_STOP_CAPTURE: break;
+ case ECHLD_CAPTURE_STOPPED: break; //
+ case ECHLD_ADD_NOTE: break;
+ case ECHLD_NOTE_ADDED: break; //IS
+ case ECHLD_APPLY_FILTER: break;
+ case ECHLD_PACKET_LIST: break; //SS name,range
+ case ECHLD_SAVE_FILE: break;
+ case ECHLD_FILE_SAVED: break;
+ case EC_ACTUAL_ERROR: break;
+ }
+
+ switch(type) {
+ case ECHLD_NEW_CHILD: break;
+ case ECHLD_CLOSE_CHILD: break;
+ case ECHLD_SET_PARAM: break; // set_param(p,v)
+ case ECHLD_GET_PARAM: break; // get_param(p)
+ case ECHLD_PING: break;
+ case ECHLD_OPEN_FILE: break; // open_file(f,mode)
+ case ECHLD_OPEN_INTERFACE: break; // open_interface(if,param)
+ case ECHLD_START_CAPTURE: break;
+ case ECHLD_GET_SUM: break; // get_sum(rng)
+ case ECHLD_GET_TREE: break; // get_tree(rng)
+ case ECHLD_GET_BUFFER: break; // get_buffer(rng)
+ case ECHLD_STOP_CAPTURE: break;
+ case ECHLD_ADD_NOTE: break; // add_note(framenum,note)
+ case ECHLD_APPLY_FILTER: break; // apply_filter(df)
+ case ECHLD_SAVE_FILE: break; // save_file(f,mode)
+
+
+ case ECHLD_ERROR: break; // error(err,reason)
+ case ECHLD_TIMED_OUT: break;
+ case ECHLD_HELLO: break;
+ case ECHLD_CHILD_DEAD: break; // child_dead(msg)
+ case ECHLD_CLOSING: break;
+ case ECHLD_PARAM: break;
+ case ECHLD_PONG: break;
+ case ECHLD_FILE_OPENED: break;
+ case ECHLD_INTERFACE_OPENED: break;
+ case ECHLD_CAPTURE_STARTED: break;
+ case ECHLD_NOTIFY: break; // notify(pre-encoded)
+ case ECHLD_PACKET_SUM: break; // packet_sum(pre-encoded)
+ case ECHLD_TREE: break; //tree(framenum, tree(pre-encoded) )
+ case ECHLD_BUFFER: break; // buffer (name,range,totlen,data)
+ case ECHLD_EOF: break;
+ case ECHLD_CAPTURE_STOPPED: break;
+ case ECHLD_NOTE_ADDED: break;
+ case ECHLD_PACKET_LIST: break; // packet_list(name,filter,range);
+ case ECHLD_FILE_SAVED: break;
+
+ case EC_ACTUAL_ERROR: break;
+ }
+}
diff --git a/echld/echld_dispatcher.c b/echld/echld_dispatcher.c
new file mode 100644
index 0000000000..42edbdd3f9
--- /dev/null
+++ b/echld/echld_dispatcher.c
@@ -0,0 +1,667 @@
+/* echld_dispatcher.c
+ * epan working child API internals
+ * Dispatcher process routines and definitions
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Copyright (c) 2013 by Luis Ontanon <luis@ontanon.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "echld-int.h"
+/**
+ DISPATCHER
+ **/
+
+struct dispatcher_child {
+ unsigned chld_id;
+ child_state_t state;
+ echld_reader_t reader;
+ int write_fd;
+ int pid;
+ gboolean closing;
+};
+
+struct dispatcher {
+ int parent_out;
+ echld_reader_t parent_in;
+ struct dispatcher_child* children;
+ int max_children;
+ int nchildren;
+ int reqh_id;
+ int pid;
+ int ppid;
+ struct _encs {
+ child_encoder_t* to_parent;
+ echld_parent_encoder_t* to_child;
+ } enc;
+ struct _decs {
+ child_decoder_t* from_parent;
+ parent_decoder_t* from_child;
+ } dec;
+
+ gboolean closing;
+};
+
+struct dispatcher* dispatcher;
+
+#define DISP_RESP(B,T) (echld_write_frame( dispatcher->parent_out, (B), 0, (T), dispatcher->reqh_id, NULL))
+
+#ifdef DEBUG_DISPATCHER
+static int dbg_level = 0;
+
+void dispatcher_debug(int level, char* fmt, ...) {
+ va_list ap;
+ char* str;
+
+ if (dbg_level<level) return;
+
+ va_start(ap, fmt);
+ str = g_strdup_vprintf(fmt,ap);
+ va_end(ap);
+
+ fprintf(stderr, "dispatcher[%d]: reqh_id=%d dbg_level=%d message='%s'", dispatcher->pid, dispatcher->reqh_id, level, str);
+ g_free(str);
+}
+
+static char* param_get_dbg_level(char** err ) {
+ return g_strdup_printf("%d",dbg_level);
+}
+
+static echld_bool_t param_set_dbg_level(char* val , char** err ) {
+ char* p;
+ int lvl = strtol(val, &p, 10);
+
+ if (p<=val) {
+ *err = g_strdup("not an integer");
+ return FALSE;
+ } else if (lvl < 0 || lvl > 5) {
+ *err = g_strdup_printf("invalid level=%d (min=0 max=5)",lvl);
+ return FALSE;
+ }
+
+ dbg_level = lvl;
+ return TRUE;
+}
+
+#define DISP_DBG(attrs) ( dispatcher_debug attrs )
+#else
+#define DISP_DBG(attrs)
+#endif
+
+
+/* parameters */
+
+int wait_chldn = 0;
+
+static char* param_get_wait_chldn(char** err ) {
+ return g_strdup_printf("%d",wait_chldn);
+}
+
+static echld_bool_t param_set_wait_chldn(char* val , char** err ) {
+ char* p;
+ int lvl = strtol(val, &p, 10);
+
+ if (p<=val) {
+ *err = g_strdup("not an integer");
+ return FALSE;
+ } else if (lvl < 0 || lvl > 10) {
+ *err = g_strdup_printf("invalid level=%d (min=0 max=5)",lvl);
+ return FALSE;
+ }
+
+ wait_chldn = lvl;
+ return TRUE;
+}
+
+static param_t disp_params[] = {
+#ifdef DEBUG_DISPATCHER
+ {"dbg_level", param_get_dbg_level, param_set_dbg_level},
+# endif
+ {"wait_chldn",param_get_wait_chldn,param_set_wait_chldn},
+ {NULL,NULL,NULL} };
+
+static param_t* get_paramset(char* name) {
+ int i;
+ for (i = 0; disp_params[i].name != NULL;i++) {
+ if (strcmp(name,disp_params[i].name) == 0 ) return &(disp_params[i]);
+ }
+ return NULL;
+}
+
+void dispatcher_err(int err, const char* fmt, ...) {
+ size_t len= 1024;
+ guint8* b[len];
+ char err_str[len];
+ GByteArray* em;
+ va_list ap;
+
+
+ va_start(ap, fmt);
+ g_vsnprintf(err_str,len,fmt,ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s[%d]: error=%d '%s'\n", "dispatcher", dispatcher->pid, err, err_str);
+
+ em = (void*)dispatcher->enc.to_parent->error(err, err_str);
+ echld_write_frame(dispatcher->parent_out, em, 0, ECHLD_ERROR, dispatcher->reqh_id, NULL);
+ g_ptr_array_free((void*)em,TRUE);
+}
+
+static struct dispatcher_child* dispatcher_get_child(struct dispatcher* d, guint16 chld_id) {
+ int i;
+ struct dispatcher_child* cc = d->children;
+ int max_children = d->max_children;
+
+ for(i = 0; i < max_children; i++) {
+ struct dispatcher_child* c = &(c[i]);
+ if (c->chld_id == chld_id) return c;
+ }
+
+ return NULL;
+}
+
+
+static void dispatcher_clear_child(struct dispatcher_child* c) {
+ echld_reset_reader(&(c->reader), -1, 4096);
+ c->chld_id = 0;
+ c->write_fd = 0;
+ c->pid = 0;
+ c->closing = 0;
+}
+
+static void preinit_epan() {
+ /* Here we do initialization of parts of epan that will be the same for every child we fork */
+}
+
+
+static void dispatcher_clear() {
+ /* remove unnecessary stuff for the working child */
+}
+
+void dispatcher_reaper(int sig) {
+ int status;
+ int i;
+ struct dispatcher_child* cc = dispatcher->children;
+ int max_children = dispatcher->max_children;
+ int pid = waitpid(-1, &status, WNOHANG);
+ size_t len= 1024;
+ guint8* b[len];
+ GByteArray* em;
+
+
+ for(i = 0; i < max_children; i++) {
+ struct dispatcher_child* c = &(cc[i]);
+ if ( c->pid == pid ) {
+ if (c->closing || dispatcher->closing) {
+ em = (void*)dispatcher->enc.to_parent->child_dead("OK");
+ } else {
+ /* here we do collect crash data !!! */
+ /*
+ WIFEXITED(status)
+ True if the process terminated normally by a call to _exit(2) or exit(3).
+
+ WIFSIGNALED(status)
+ True if the process terminated due to receipt of a signal.
+
+ WIFSTOPPED(status)
+ True if the process has not terminated, but has stopped and can be restarted. This macro can be true only if the wait call speci-
+ fied the WUNTRACED option or if the child process is being traced (see ptrace(2)).
+
+ Depending on the values of those macros, the following macros produce the remaining status information about the child process:
+
+ WEXITSTATUS(status)
+ If WIFEXITED(status) is true, evaluates to the low-order 8 bits of the argument passed to _exit(2) or exit(3) by the child.
+
+ WTERMSIG(status)
+ If WIFSIGNALED(status) is true, evaluates to the number of the signal that caused the termination of the process.
+
+ WCOREDUMP(status)
+ If WIFSIGNALED(status) is true, evaluates as true if the termination of the process was accompanied by the creation of a core file
+ containing an image of the process when the signal was received.
+
+ WSTOPSIG(status)
+ If WIFSTOPPED(status) is true, evaluates to the number of the signal that caused the process to stop.
+
+*/
+ em = (void*)dispatcher->enc.to_parent->child_dead("Unexpected, probably crashed");
+ dispatcher_err(ECHLD_ERR_CRASHED_CHILD, "Unexpected dead: pid=%d chld_id=%d", pid, c->chld_id);
+ }
+
+ echld_write_frame(dispatcher->parent_out, em, c->chld_id, ECHLD_CHILD_DEAD, 0, NULL);
+ dispatcher_clear_child(c);
+ g_byte_array_free(em,TRUE);
+ return;
+ }
+ }
+
+ dispatcher_err(ECHLD_ERR_UNKNOWN_PID, "Unkown child pid: %d", pid);
+}
+
+
+
+static void dispatcher_destroy() {
+ int i;
+ int max_children = dispatcher->max_children;
+ struct dispatcher_child* cc = dispatcher->children;
+ /* destroy the dispatcher stuff at closing */
+
+ dispatcher->closing = TRUE;
+
+ /* kill all alive children */
+ for(i = 0; i < max_children; i++) {
+ struct dispatcher_child* c = &(cc[i]);
+ if ( c->chld_id ) {
+ kill(c->pid,SIGTERM);
+ DISP_DBG((1,"Killing chld_id=%d pid=%d"));
+ continue;
+ }
+ }
+
+ exit(6666);
+}
+
+/* stuff coming from child going to parent */
+static int dispatch_to_parent(guint8* b, size_t len, echld_chld_id_t chld_id, echld_msg_type_t type, echld_reqh_id_t reqh_id, void* data) {
+ struct dispatcher_child* c = data;
+ dispatcher->reqh_id = reqh_id;
+ /* TODO: timeouts, clear them */
+ /* TODO: keep stats */
+ GByteArray in_ba;
+
+ in_ba.data = b;
+ in_ba.len = len;
+
+ if (chld_id != c->chld_id) {
+ goto misbehabing;
+ }
+
+ switch(type) {
+ case ECHLD_ERROR: break;
+ case ECHLD_TIMED_OUT: break;
+ case ECHLD_HELLO: c->state = IDLE; break;
+ case ECHLD_CLOSING: c->closing = TRUE; c->state = CLOSED; break;
+ case ECHLD_PARAM: break;
+ case ECHLD_PONG: break;
+ case ECHLD_FILE_OPENED: c->state = READING; break;
+ case ECHLD_INTERFACE_OPENED: c->state = READY; break;
+ case ECHLD_CAPTURE_STARTED: c->state = CAPTURING; break;
+ case ECHLD_NOTIFY: break; // notify(pre-encoded)
+ case ECHLD_PACKET_SUM: break; // packet_sum(pre-encoded)
+ case ECHLD_TREE: break; //tree(framenum, tree(pre-encoded) )
+ case ECHLD_BUFFER: break; // buffer (name,range,totlen,data)
+ case ECHLD_EOF: c->state = DONE; break;
+ case ECHLD_CAPTURE_STOPPED: c->state = DONE; break;
+ case ECHLD_NOTE_ADDED: break;
+ case ECHLD_PACKET_LIST: break; // packet_list(name,filter,range);
+ case ECHLD_FILE_SAVED: break;
+
+ default:
+ goto misbehabing;
+ }
+
+ return echld_write_frame(dispatcher->parent_out, &in_ba, chld_id, type, reqh_id, NULL);
+
+misbehabing:
+ c->state = ERRORED;
+ c->closing = TRUE;
+ kill(c->pid,SIGTERM);
+ dispatcher_err(ECHLD_ERR_CRASHED_CHILD,"chld_id=%d",chld_id);
+ return 0;
+
+}
+
+void dispatch_new_child(struct dispatcher* dd) {
+ struct dispatcher_child* c = dispatcher_get_child(dd, 0);
+ int reqh_id = dd->reqh_id;
+ int pid;
+
+ if ( c ) {
+ int parent_pipe_fds[2];
+ int child_pipe_fds[2];
+
+ int pipe_to_parent;
+ int pipe_from_parent;
+ int pipe_to_child;
+ int pipe_from_child;
+
+ if( pipe(parent_pipe_fds) < 0) {
+ dispatcher_err(ECHLD_ERR_CANNOT_FORK,"CANNOT OPEN PARENT PIPE: %s",strerror(errno));
+ return;
+ }
+
+ pipe_from_parent = parent_pipe_fds[0];
+ pipe_to_child = parent_pipe_fds[1];
+
+ if( pipe(child_pipe_fds) < 0) {
+ close(pipe_from_parent);
+ close(pipe_to_child);
+ dispatcher_err(ECHLD_ERR_CANNOT_FORK,"CANNOT OPEN CHILD PIPE: %s",strerror(errno));
+ return;
+ }
+
+ pipe_from_child = child_pipe_fds[0];
+ pipe_to_parent = child_pipe_fds[1];
+
+ switch (( pid = fork() )) {
+ case -1: {
+ close(pipe_to_child);
+ close(pipe_to_parent);
+ close(pipe_from_child);
+ close(pipe_from_parent);
+ dispatcher_err(ECHLD_ERR_CANNOT_FORK,"CANNOT FORK: %s",strerror(errno));
+ return;
+ }
+ case 0: { /* I'm the child */
+ int i;
+ int fdt_len = getdtablesize();
+
+ dispatcher_clear(dd);
+
+ for(i=0;i<fdt_len;i++) {
+ if ( i != pipe_from_parent
+ && i != pipe_to_parent
+ && i != STDERR_FILENO ) {
+ close(i);
+ }
+ }
+
+ echld_child_initialize(pipe_from_parent,pipe_to_parent,reqh_id);
+
+ exit( echld_child_loop() );
+
+ /* it won't */
+ return;
+ }
+ default: {
+ /* I'm the parent */
+ guint8 buf[4];
+ GByteArray out_ba;
+
+ out_ba.data = buf;
+ out_ba.len = 0;
+
+ close(pipe_to_parent);
+ close(pipe_from_parent);
+
+ echld_reset_reader(&(c->reader), pipe_from_child,4096);
+ c->write_fd = pipe_to_child;
+ c->pid = pid;
+ dispatcher->nchildren++;
+
+ /* configure child */
+ echld_write_frame(pipe_to_child, &out_ba, c->chld_id, ECHLD_NEW_CHILD, dispatcher->reqh_id, NULL);
+ return;
+ }
+ }
+ } else {
+ dispatcher_err(ECHLD_ERR_CANNOT_FORK, "MAX CHILDREN REACHED: max_children=%d",dispatcher->max_children);
+ return;
+ }
+}
+
+
+/* process signals sent from parent */
+static int dispatch_to_child(guint8* b, size_t len, echld_chld_id_t chld_id, echld_msg_type_t type, echld_reqh_id_t reqh_id, void* data) {
+ struct dispatcher* disp = data;
+ disp->reqh_id = reqh_id;
+ GByteArray in_ba;
+
+ in_ba.data = b;
+ in_ba.len = len;
+
+ if (chld_id == 0) { /* these are messages to the dispatcher itself */
+ switch(type) {
+ case ECHLD_CLOSE_CHILD:
+ dispatcher_destroy();
+ return 0;
+ case ECHLD_PING:
+ echld_write_frame(disp->parent_out, &in_ba, chld_id, ECHLD_PONG, reqh_id, NULL);
+
+ return 0;
+ case ECHLD_NEW_CHILD:
+ dispatch_new_child(disp);
+ return 0;
+ case ECHLD_SET_PARAM:{
+ char* param;
+ char* value;
+ if ( disp->dec.from_parent->set_param(b,len,&param,&value) ) {
+ GByteArray* ba;
+ param_t* p = get_paramset(param);
+ char* err;
+ if (!p) {
+ dispatcher_err(ECHLD_CANNOT_SET_PARAM,"no such param='%s'",param);
+ return 0;
+ }
+
+ if (! p->set ) {
+ dispatcher_err(ECHLD_CANNOT_SET_PARAM,"reason='read only'");
+ return 0;
+ }
+
+ if (! p->set(value,&err) ) {
+ dispatcher_err(ECHLD_CANNOT_SET_PARAM,"reason='%s'",err);
+ g_free(err);
+ return 0;
+ }
+
+ ba = (void*)disp->enc.to_parent->param(param,value);
+ DISP_RESP(ba,ECHLD_PARAM);
+ g_byte_array_free(ba,TRUE);
+ DISP_DBG((1,"Set Param: param='%s' value='%s'",param,value));
+
+ return 0;
+ } else {
+ dispatcher_err(ECHLD_CANNOT_SET_PARAM,"reason='decoder error'");
+ return 0;
+ }
+ }
+ case ECHLD_GET_PARAM: {
+ GByteArray* ba;
+ char* param;
+ if ( disp->dec.from_parent->get_param(b,len,&param) ) {
+ char* err;
+ char* val;
+
+ param_t* p = get_paramset(param);
+
+ if (!p) {
+ dispatcher_err(ECHLD_CANNOT_GET_PARAM,"no such param='%s'",param);
+ return 0;
+ }
+
+ if (! p->get ) {
+ dispatcher_err(ECHLD_CANNOT_SET_PARAM,"reason='write only'");
+ return 0;
+ }
+
+ if (!(val = p->get(&err))) {
+ dispatcher_err(ECHLD_CANNOT_GET_PARAM,"reason='%s'",err);
+ g_free(err);
+ return 0;
+ }
+
+ ba = (void*)disp->enc.to_parent->param(param,val);
+ DISP_RESP(ba,ECHLD_PARAM);
+ g_byte_array_free(ba,TRUE);
+ DISP_DBG((2,"Get Param: param='%s' value='%s'",param,val));
+ return 0;
+ } else {
+ dispatcher_err(ECHLD_CANNOT_GET_PARAM,"reason='decoder error'");
+ return 0;
+ }
+ }
+ default:
+ dispatcher_err(ECHLD_ERR_WRONG_MSG, "wrong message to dispatcher type='%c'", type);
+ return 0;
+ }
+ } else {
+ struct dispatcher_child* c;
+
+ if (! (c = dispatcher_get_child(dispatcher, chld_id)) ) {
+ dispatcher_err(ECHLD_ERR_NO_SUCH_CHILD, "wrong chld_id %d", chld_id);
+ return 0;
+ }
+
+ switch(type) {
+ case ECHLD_CLOSE_CHILD:
+ c->closing = TRUE;
+ c->state = CLOSED;
+ goto relay_frame;
+
+ case ECHLD_OPEN_FILE:
+ c->state = READING;
+ goto relay_frame;
+
+ case ECHLD_OPEN_INTERFACE:
+ c->state = READY;
+ goto relay_frame;
+
+ case ECHLD_START_CAPTURE:
+ c->state = CAPTURING;
+ goto relay_frame;
+
+ case ECHLD_STOP_CAPTURE:
+ c->state = DONE;
+ goto relay_frame;
+
+ case ECHLD_SAVE_FILE:
+ case ECHLD_APPLY_FILTER:
+ case ECHLD_SET_PARAM:
+ case ECHLD_GET_PARAM:
+ case ECHLD_PING:
+ case ECHLD_GET_SUM:
+ case ECHLD_GET_TREE:
+ case ECHLD_GET_BUFFER:
+ case ECHLD_ADD_NOTE:
+ relay_frame:
+ return echld_write_frame(c->write_fd, &in_ba, chld_id, type, reqh_id, NULL);
+
+ default:
+ dispatcher_err(ECHLD_ERR_WRONG_MSG, "wrong message %d %c", reqh_id, type);
+ return 0;
+ }
+ }
+}
+
+
+int dispatcher_loop() {
+ int parent_out = dispatcher->parent_out;
+ int parent_in = dispatcher->parent_in.fd;
+
+ struct dispatcher_child* children = dispatcher->children;
+
+ do {
+ fd_set rfds;
+ fd_set efds;
+ struct timeval timeout;
+ struct dispatcher_child* c;
+ int nfds;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&efds);
+
+ FD_SET(parent_in,&rfds);
+ FD_SET(parent_in,&efds);
+ FD_SET(parent_out,&efds);
+
+ for (c = children, nfds = 0; c->pid; c++) {
+ if (c->chld_id) {
+ FD_SET(c->reader.fd, &rfds);
+ FD_SET(c->reader.fd, &efds);
+ }
+ nfds++;
+ }
+
+ nfds = select(nfds, &rfds, NULL, &efds, &timeout);
+
+ if ( FD_ISSET(parent_in, &efds) || FD_ISSET(parent_out, &efds) ) {
+ /* XXX deep shit */
+ break;
+ }
+
+ if (FD_ISSET(parent_in, &rfds)) {
+ int st = echld_read_frame(&(dispatcher->parent_in), dispatch_to_child, dispatcher);
+
+ if (st < 0) {
+ /* XXX */
+ continue;
+ }
+ }
+
+ for (c=children; c->pid; c++) {
+ if (c->chld_id) {
+ if ( FD_ISSET(c->reader.fd,&efds) ) {
+ /* XXX cleanup child and report */
+ continue;
+ }
+
+ if (FD_ISSET(c->reader.fd,&rfds)) {
+ int st = echld_read_frame(&(c->reader), dispatch_to_parent, c);
+
+ if (st < 0) {
+ /* XXX cleanup child and report */
+ continue;
+ }
+ continue;
+ }
+ }
+ }
+ } while(1);
+
+ /* won't */
+ return 1;
+}
+
+void echld_dispatcher_start(int* in_pipe_fds, int* out_pipe_fds) {
+ static struct dispatcher d;
+ int fdt_len = getdtablesize();
+ int i;
+
+ preinit_epan();
+
+ signal(SIGCHLD,dispatcher_reaper);
+
+ dispatcher = &d;
+
+ echld_init_reader(&(d.parent_in),in_pipe_fds[0],4096);
+ d.parent_out = out_pipe_fds[1];
+ d.children = g_malloc0(ECHLD_MAX_CHILDREN * sizeof(struct dispatcher_child));
+ d.max_children = ECHLD_MAX_CHILDREN;
+ d.nchildren = 0;
+ d.reqh_id = -1;
+ d.pid = getpid();
+
+ echld_get_all_codecs(&(d.enc.to_parent), &(d.dec.from_parent), &(d.enc.to_child), &(d.dec.from_child));
+
+ dispatcher_clear();
+
+ /* close all fds but those used */
+ for(i=0;i<fdt_len;i++) {
+ if ( i != d.parent_in.fd
+ && i != d.parent_out
+ && i != STDERR_FILENO ) {
+ close(i);
+ }
+ }
+
+ exit(dispatcher_loop());
+}
+
diff --git a/echld/echld_parent.c b/echld/echld_parent.c
new file mode 100644
index 0000000000..b94590444f
--- /dev/null
+++ b/echld/echld_parent.c
@@ -0,0 +1,799 @@
+/* echld_dispatcher.c
+ * epan working child API internals
+ * Parent process routines and definitions ()
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Copyright (c) 2013 by Luis Ontanon <luis@ontanon.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "echld-int.h"
+
+/**
+ PARENT and API
+ **/
+
+#define MAX_PENDING_REQS 16;
+
+typedef struct _req {
+ int reqh_id;
+ echld_msg_cb_t cb;
+ void* cb_data;
+ struct timeval tv;
+} reqh_t;
+
+typedef struct _hdlr {
+ int id;
+ echld_msg_type_t type;
+ echld_msg_cb_t cb;
+ void* cb_data;
+} hdlr_t;
+
+typedef struct _echld_child {
+ int chld_id;
+ void* data;
+ child_state_t state;
+ GArray* handlers;
+ GArray* reqs;
+} echld_t;
+
+struct _echld_parent {
+ echld_t* children;
+ echld_reader_t reader;
+ int dispatcher_fd;
+ int dispatcher_pid;
+ int reqh_id;
+ GByteArray* snd;
+ int closing;
+ echld_parent_encoder_t* enc;
+ parent_decoder_t* dec;
+} parent = {NULL,{NULL,0,NULL,-1,NULL,0},-1,-1,1,NULL,0,NULL,NULL};
+
+#define PARENT_SEND(BYTEARR,CHILDNUM,TYPE) echld_write_frame(parent.dispatcher_fd, BYTEARR, CHILDNUM, TYPE, parent.reqh_id++, NULL)
+
+
+#define PARENT_FATAL(attrs) parent_fatal attrs
+
+#ifdef DEBUG_PARENT
+
+static int dbg_level = 0;
+
+static void parent_dgb(int level, const char* fmt, ...) {
+ va_list ap;
+ char str[1024];
+
+ if (level > dbg_level) return;
+
+ va_start(ap,fmt);
+ g_snprintf(str,1024,fmt,ap);
+ va_end(ap);
+
+ fprintf(stderr,"ParentDebug: level=%d msg='%s'\n",level,str);
+}
+#define PARENT_DBG(attrs) parent_dbg attrs
+#else
+#define PARENT_DBG(attrs)
+#endif
+
+void echld_set_parent_dbg_level(int lvl) {
+ PARENT_DBG((0,"Debug Level Set: %d",dbg_level = lvl));
+}
+
+static void parent_fatal(int exit_code, const char* fmt, ...) {
+ va_list ap;
+ char str[1024];
+
+ va_start(ap,fmt);
+ g_snprintf(str,1024,fmt,ap);
+ va_end(ap);
+
+#ifdef DEBUG_PARENT
+ PARENT_DBG((0,"Fatal error: %s",str));
+#else
+ fprintf(stderr,"Fatal error: %s",str);
+#endif
+
+ exit(exit_code);
+}
+
+static echld_state_t echld_cleanup(void) {
+ int i;
+ char b[4];
+ GByteArray ba;
+
+ ba.data = b;
+ ba.len = 0;
+
+ PARENT_DBG((0,"echld_cleanup starting"));
+ PARENT_SEND(&ba,0,ECHLD_CLOSE_CHILD);
+
+ do ; while(sleep(1)); /* wait a full sec without signals */
+
+ for (i=0;i<ECHLD_MAX_CHILDREN;i++) {
+ g_array_free(parent.children[i].handlers,TRUE);
+ g_array_free(parent.children[i].reqs,TRUE);
+ };
+
+ free(parent.children);
+ g_byte_array_free(parent.snd,TRUE);
+ close(parent.dispatcher_fd);
+ PARENT_DBG((0,"echld_cleanup done"));
+
+}
+
+static int parent_child_cleanup(echld_t* c) {
+
+ PARENT_DBG((2,"cleanup chld_id=%d",c->chld_id));
+ c->chld_id = -1;
+ c->data = NULL;
+ c->state = FREE;
+ g_array_truncate(c->handlers);
+ g_array_truncate(c->reqs);
+}
+
+
+void parent_reaper(int sig) {
+ int pid;
+ int status;
+
+ if (sig != SIGCHLD) {
+ PARENT_FATAL((3333,"Must be SIGCHLD!"));
+ }
+
+ pid = waitpid(-1, &status, WNOHANG);
+ PARENT_DBG((2,"SIGCHLD pid=%d",pid));
+
+ if (pid == parent.dispatcher_pid) {
+
+ if (! parent.closing) {
+ /* crashed */
+ PARENT_FATAL((2222,"Dispatcher process dead"));
+ }
+
+ return;
+ } else {
+ /* XXX: do we care? */
+ return;
+ }
+
+ return;
+}
+
+/* will initialize epan registering protocols and taps */
+echld_state_t echld_initialize(echld_encoding_t enc) {
+ int from_disp[2];
+ int to_disp[2];
+
+ if (enc != ECHLD_ENCODING_JSON) {
+ PARENT_FATAL((1111,"Only JSON implemented"));
+ }
+
+ if ( pipe(to_disp) ) {
+ PARENT_FATAL((1112,"Failed to open dispatcher pipe"));
+ } else if( pipe(from_disp) ) {
+ PARENT_FATAL((1113,"Failed to open dispatcher pipe"));
+ } else {
+ int pid;
+ int i;
+ if (( pid = fork() < 0)) {
+ PARENT_FATAL((1114,"Failed to fork() reason='%s'",strerror(errno)));
+ } else if ( pid == 0) {
+#ifdef PARENT_THREADS
+ reader_realloc_buf = child_realloc_buff;
+#endif
+ /* child code */
+ echld_cleanup();
+
+
+ dispatcher(to_disp,from_disp);
+ PARENT_FATAL((1115,"This shoudln't happen"));
+ }
+
+ /* parent code */
+#ifdef PARENT_THREADS
+ reader_realloc_buf = parent_realloc_buff;
+#endif
+
+ PARENT_DBG((3,"Dispatcher forked"));
+
+ echld_get_all_codecs(NULL, NULL, &parent.enc, &parent.dec);
+ parent.children = g_malloc0(ECHLD_MAX_CHILDREN*sizeof(echld_t));
+ parent.snd = g_byte_array_new();
+ parent.dispatcher_fd = to_disp[0];
+
+ init_reader(&(parent.reader),from_disp[1]);
+
+ for (i=0;i<ECHLD_MAX_CHILDREN;i++) {
+ parent.children[i].chld_id = -1;
+ parent.children[i].data = NULL;
+ parent.children[i].state = FREE;
+ parent.children[i].handlers = g_array_new(TRUE,TRUE,sizeof(hdlr_t));
+ }
+
+ signal(SIGCHLD,parent_reaper);
+ close(to_disp[1]);
+ close(from_disp[0]);
+ PARENT_DBG((3,"Ready"));
+ }
+}
+
+
+echld_state_t echld_terminate(void) {
+ echld_cleanup();
+ return TRUE;
+}
+
+int reqh_id_idx(echld_t* c, int reqh_id) {
+ int i;
+ int imax = c->reqs->len;
+
+ for(i=0; i < imax ; i++) {
+ if (((reqh_t*)&g_array_index (c->reqs, reqh_t, i))->reqh_id == reqh_id) return i;
+ }
+
+ return -1;
+}
+
+
+
+static echld_t* get_child(int id) {
+ int i;
+ for (i=0;i<ECHLD_MAX_CHILDREN;i++) {
+ if (parent.children[i].chld_id == id) return &(parent.children[i]);
+ };
+
+ return NULL;
+}
+
+
+/* send a request */
+
+static int reqh_ids = 1;
+
+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) {
+ reqh_t req;
+
+ if (!c) {
+ PARENT_DBG((1,"REQH_SND: No such child"));
+ return 1;
+ }
+
+ req.reqh_id = reqh_ids++;
+ req.cb = resp_cb;
+ req.cb_data = cb_data;
+ gettimeofday(&(req.tv));
+
+ g_array_append_val(c->reqs,req);
+
+ PARENT_DBG((1,"REQH_SND: type='%c' chld_id=%d reqh_id=%d",t,c->chld_id,req.reqh_id));
+
+ PARENT_SEND(ba,c->chld_id,t);
+
+ if (ba) g_byte_array_free(ba,TRUE);
+
+ return req.reqh_id;
+}
+
+
+echld_reqh_id_t echld_reqh(
+ echld_chld_id_t child_id,
+ echld_msg_type_t t,
+ int usecs_timeout,
+ enc_msg_t* ba,
+ echld_msg_cb_t resp_cb,
+ void* cb_data) {
+ return reqh_snd(get_child(child_id),t,(void*)ba,resp_cb,cb_data);
+}
+
+/* get callback data for a live request */
+void* echld_reqh_get_data(int child_id, int reqh_id) {
+ echld_t* c = get_child(child_id);
+ int idx;
+
+ if (!c) return NULL;
+
+ idx = reqh_id_idx(c,reqh_id);
+
+ if (idx >= 0)
+ return g_array_index(c->reqs, reqh_t, idx).cb_data;
+ else
+ return NULL;
+}
+
+/* get the callback for a live request */
+echld_msg_cb_t echld_reqh_get_cb(int child_id, int reqh_id) {
+ echld_t* c = get_child(child_id);
+ int idx;
+
+ if (!c) return NULL;
+
+ idx = reqh_id_idx(c,reqh_id);
+
+ if (idx >= 0)
+ return g_array_index(c->reqs, reqh_t, idx).cb;
+ else
+ return NULL;
+}
+
+/* set callback data for a live request */
+gboolean echld_reqh_set_data(int child_id, int reqh_id, void* cb_data) {
+ echld_t* c = get_child(child_id);
+ int idx;
+
+ if (!c) return FALSE;
+
+ idx = reqh_id_idx(c,reqh_id);
+
+ if (idx < 0) return FALSE;
+
+ g_array_index(c->reqs, reqh_t, idx).cb_data = cb_data;
+
+ return TRUE;
+}
+
+/* get the callback for a live request */
+gboolean echld_reqh_set_cb(int child_id, int reqh_id, echld_msg_cb_t cb){
+ echld_t* c = get_child(child_id);
+ int idx;
+
+ if (!c) return FALSE;
+
+ idx = reqh_id_idx(c,reqh_id);
+
+ if (idx < 0) return FALSE;
+
+ g_array_index(c->reqs, reqh_t, idx).cb = cb;
+ return TRUE;
+}
+
+
+/* stop receiving a live request */
+gboolean echld_reqh_detach(int child_id, int reqh_id) {
+ echld_t* c = get_child(child_id);
+ int idx;
+
+ if (!c) return FALSE;
+
+ idx = reqh_id_idx(c,reqh_id);
+
+ if (idx < 0) return FALSE;
+
+ g_array_remove_index(c->reqs,idx);
+}
+
+
+static echld_bool_t parent_dead_child(echld_msg_type_t type, enc_msg_t* ba, void* data) {
+ echld_t* c = data;
+ char* str;
+
+ if (type != ECHLD_CHILD_DEAD) {
+ return 1;
+ }
+
+ if ( parent.dec->child_dead((void*)ba,&str) ) {
+ g_string_prepend_printf(str,"Dead Child[%d]: %s",c->chld_id,str);
+ g_free(str);
+ }
+
+ parent_child_cleanup(c);
+ return 0;
+}
+
+static echld_bool_t parent_get_hello(echld_msg_type_t type, enc_msg_t* ba, void* data) {
+ echld_t* c = data;
+
+ switch (type) {
+ case ECHLD_HELLO:
+ c->state = IDLE;
+ return TRUE;
+ case ECHLD_ERROR:
+ case ECHLD_TIMEOUT:
+ default:
+ return FALSE;
+ }
+}
+
+
+
+
+
+int chld_cmp(const void *a, const void *b) {
+ return ((echld_t*)b)->chld_id - ((echld_t*)a)->chld_id;
+}
+
+static int msgh_attach(echld_t* c, echld_msg_type_t t, echld_msg_cb_t resp_cb, void* cb_data);
+
+int echld_new(void* child_data) {
+ int next_chld_id = 1;
+ echld_t* c = get_child(-1);
+
+ if (!c) return -1;
+
+ c->chld_id = next_chld_id++;
+ c->data = child_data;
+ c->state = CREATING;
+ c->handlers = g_array_new(TRUE,TRUE,sizeof(hdlr_t));
+
+ g_byte_array_truncate(parent.snd,0);
+
+ msgh_attach(c,ECHLD_CHILD_DEAD, parent_dead_child , c);
+ reqh_snd(c, ECHLD_NEW_CHILD, parent.snd, parent_get_hello, c);
+
+ qsort(parent.children,ECHLD_MAX_CHILDREN,sizeof(echld_t),chld_cmp);
+
+ return c->chld_id;
+}
+
+
+
+/* XXX these fail silently */
+void* echld_get_data(int child_id) {
+ echld_t* c = get_child(child_id);
+ return c ? c->data : NULL;
+}
+
+echld_state_t echld_set_data(echld_chld_id_t chld_id, void* data) {
+ echld_t* c = get_child(chld_id);
+ if (c) {
+ c->data = data;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int msgh_idx(echld_t* c, int msgh_id) {
+ int i = 0;
+ int imax = c->handlers->len;
+
+ for (i=0;i<imax;i++) {
+ if (((hdlr_t*)(c->handlers->data))[i].id == msgh_id) return i;
+ }
+
+ return -1;
+}
+
+/* start a message handler */
+static int msgh_attach(echld_t* c, echld_msg_type_t t, echld_msg_cb_t resp_cb, void* cb_data) {
+ hdlr_t h;
+ int hdlr_idx;
+ static int msgh_id;
+
+ h.id = msgh_id++;
+ h.type = t;
+ h.cb = resp_cb;
+ h.cb_data = cb_data;
+
+ g_array_append_val(c->handlers,h);
+ return 0;
+}
+
+static int echld_msgh_attach(int child_id, echld_msg_type_t t, echld_msg_cb_t resp_cb, void* cb_data) {
+ echld_t* c = get_child(child_id);
+
+ if (c) return msgh_attach(c,t,resp_cb,cb_data);
+ else return -1;
+}
+
+
+/* stop it */
+static echld_state_t msgh_detach(echld_t* c, int msgh_id) {
+ int idx = msgh_idx(c,msgh_id);
+
+ if (idx < 0) return -1;
+
+ g_array_remove_index(c->handlers,idx);
+
+ return 1;
+}
+
+echld_state_t echld_msgh_detach(int child_id, int msgh_id) {
+ echld_t* c = get_child(child_id);
+ return msgh_detach(c,msgh_id);
+}
+
+/* get a msgh's data */
+
+static void* msgh_get_data(echld_t* c, int msgh_id) {
+ int idx = msgh_idx(c,msgh_id);
+
+ if (idx < 0) return NULL;
+
+ return ((hdlr_t*)(c->handlers->data))[idx].cb_data;
+}
+
+void* echld_msgh_get_data(int child_id, int msgh_id) {
+ echld_t* c = get_child(child_id);
+ return msgh_get_data(c,msgh_id);
+}
+
+/* get a msgh's cb */
+static echld_msg_cb_t msgh_get_cb(echld_t* c, int msgh_id) {
+ int idx = msgh_idx(c,msgh_id);
+
+ if (idx < 0) return NULL;
+
+ return ((hdlr_t*)(c->handlers->data))[idx].cb;
+}
+
+echld_msg_cb_t echld_msgh_get_cb(int child_id, int msgh_id) {
+ echld_t* c = get_child(child_id);
+ return msgh_get_cb(c,msgh_id);
+}
+
+/* get a msgh's type */
+static echld_msg_type_t msgh_get_type(echld_t* c, int msgh_id) {
+ int idx = msgh_idx(c,msgh_id);
+
+ if (idx < 0) return EC_ACTUAL_ERROR;
+
+ return ((hdlr_t*)(c->handlers->data))[idx].type;
+}
+
+echld_msg_type_t echld_msgh_get_type(int child_id, int msgh_id) {
+ echld_t* c = get_child(child_id);
+ return c ? msgh_get_type(c,msgh_id) : EC_ACTUAL_ERROR;
+}
+
+/* get it all from a msgh */
+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) {
+ int idx = msgh_idx(c,msgh_id);
+ hdlr_t* h;
+
+ if (idx < 0) return -1;
+
+ h = &(((hdlr_t*)(c->handlers->data))[idx]);
+
+ t && (*t = h->type);
+ cb && (*cb = h->cb);
+ data && (*data = h->cb_data);
+
+ return 0;
+}
+
+gboolean echld_msgh_get_all(int child_id, int msgh_id, echld_msg_type_t* t, echld_msg_cb_t* cb, void** data) {
+ echld_t* c = get_child(child_id);
+ return c && msgh_get_all(c,msgh_id,t,cb,data);
+}
+
+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) {
+ int idx = msgh_idx(c,msgh_id);
+ hdlr_t* h;
+
+ if (idx < 0) return -1;
+
+ h = &(((hdlr_t*)(c->handlers->data))[idx]);
+
+ h->type = t;
+ h->cb = cb;
+ h->cb_data = data;
+
+ return 0;
+}
+
+gboolean echld_msgh_set_all(int child_id, int msgh_id, echld_msg_type_t t, echld_msg_cb_t cb, void* data) {
+ echld_t* c = get_child(child_id);
+ return c ? msgh_set_all(c,msgh_id,t,cb,data) : FALSE;
+}
+
+/* set a msgh's data */
+static gboolean msgh_set_data(echld_t* c, int msgh_id, void* data) {
+ int idx = msgh_idx(c,msgh_id);
+
+ if (idx < 0) return FALSE;
+
+ ((hdlr_t*)(c->handlers->data))[idx].cb_data = data;
+
+ return TRUE;
+
+}
+
+gboolean echld_msgh_set_data(int child_id, int msgh_id, void* data){
+ echld_t* c = get_child(child_id);
+ return c ? msgh_set_data(c,msgh_id,data) : FALSE;
+}
+
+/* set a msgh's cb */
+gboolean msgh_set_cb(echld_t* c, int msgh_id, echld_msg_cb_t cb) {
+ int idx = msgh_idx(c,msgh_id);
+
+ if (idx < 0) return FALSE;
+
+ ((hdlr_t*)(c->handlers->data))[idx].cb = cb;
+
+ return TRUE;
+}
+
+gboolean echld_msgh_set_cb(int child_id, int msgh_id, echld_msg_cb_t cb) {
+ echld_t* c = get_child(child_id);
+ return c ? msgh_set_cb(c,msgh_id,cb) : FALSE;
+}
+
+/* set a msgh's type */
+
+static gboolean msgh_set_type(echld_t* c, int msgh_id, echld_msg_type_t t) {
+ int idx = msgh_idx(c,msgh_id);
+
+ if (idx < 0) return FALSE;
+
+ ((hdlr_t*)(c->handlers->data))[idx].type = t;
+
+ return TRUE;
+}
+
+gboolean echld_msgh_set_type(int child_id, int msgh_id, echld_msg_type_t t) {
+ echld_t* c = get_child(child_id);
+ return c ? msgh_set_type(c,msgh_id,t) : FALSE;
+}
+
+
+/* call cb(id,child_data,cb_data) for each child*/
+void echld_foreach_child(echld_iter_cb_t cb, void* cb_data) {
+ int i;
+ for(i=0;i<ECHLD_MAX_CHILDREN;i++) {
+ echld_t* c = &(parent.children[i]);
+ cb(c->chld_id,c->data,cb_data);
+ }
+}
+
+static reqh_t* get_req(echld_t* c, int reqh_id) {
+ int idx = reqh_id_idx(c,reqh_id);
+ reqh_t* r;
+ if(idx < 0) return NULL;
+
+ return ((reqh_t*)(c->reqs->data))+idx;
+}
+
+static hdlr_t* get_next_hdlr_for_type(echld_t* c, echld_msg_type_t t, int* cookie) {
+ int imax = c->handlers->len;
+
+ for (;*cookie<imax;(*cookie)++) {
+ if (((hdlr_t*)(c->handlers->data))[*cookie].type == t)
+ return &( ((hdlr_t*)(c->handlers->data))[*cookie] ) ;
+ }
+
+ return NULL;
+}
+
+int parent_read_frame(GByteArray* ba, guint16 chld_id, echld_msg_type_t t, guint16 reqh_id, void* data) {
+ echld_t* c = get_child(chld_id);
+
+ if (ba == NULL) g_byte_array_new();
+
+ if (c) {
+ reqh_t* r = get_req(c, reqh_id);
+ int i = 0;
+ hdlr_t* h;
+ gboolean go_ahead = TRUE;
+
+ if (r) { /* got that reqh_id */
+ go_ahead = r->cb ? r->cb(t,(void*)ba,r->cb_data) : TRUE;
+ }
+
+ while(go_ahead && ( h = get_next_hdlr_for_type(c,t,&i))) {
+ go_ahead = h->cb(t,(void*)ba,r->cb_data);
+ }
+ } else {
+ /* no such child??? */
+ }
+
+ ba && g_byte_array_free(ba,TRUE);
+
+}
+
+int echld_fdset(fd_set* rfds, fd_set* efds) {
+ FD_SET(parent.reader.fd, rfds);
+ FD_SET(parent.reader.fd, efds);
+ FD_SET(parent.dispatcher_fd, efds);
+ return 2;
+}
+
+int echld_fd_read(fd_set* rfds, fd_set* efds) {
+ int r_nfds=0;
+ if (FD_ISSET(parent.reader.fd,efds) || FD_ISSET(parent.dispatcher_fd,efds) ) {
+ /* Handle errored dispatcher */
+ r_nfds--;
+ return;
+ }
+
+ if (FD_ISSET(parent.reader.fd,rfds)) {
+ r_nfds++;
+ read_frame(&(parent.reader),parent_read_frame,&(parent));
+ }
+
+ return r_nfds;
+}
+
+int echld_select(int nfds, fd_set* rfds, fd_set* wfds, fd_set* efds, struct timeval* timeout) {
+ fd_set my_rfds, my_wfds, my_efds;
+ int r_nfds;
+
+ if (rfds == NULL) { rfds = &my_rfds; FD_ZERO(rfds); }
+ if (wfds == NULL) { wfds = &my_wfds; FD_ZERO(wfds); }
+ if (efds == NULL) { efds = &my_efds; FD_ZERO(efds); }
+
+ nfds += echld_fdset(rfds,efds);
+
+ r_nfds = select(nfds, rfds, wfds, efds, timeout);
+
+ r_nfds += echld_fd_read(rfds,efds);
+
+ return r_nfds ;
+}
+
+echld_state_t echld_wait(struct timeval* timeout) {
+ if ( echld_select(0, NULL, NULL, NULL, timeout) < 0) {
+ return -1;
+ } else {
+ return ECHLD_OK;
+ }
+}
+
+
+/* Ping the child */
+struct _ping {
+ struct timeval tv;
+ echld_t* child;
+ echld_ping_cb_t cb;
+ void* cb_data;
+};
+
+static long timevaldiff(struct timeval *starttime, struct timeval *finishtime) {
+ long msec;
+ msec=(finishtime->tv_sec-starttime->tv_sec)*1000;
+ msec+=(finishtime->tv_usec-starttime->tv_usec)/1000;
+ return msec;
+}
+
+static gboolean pong(echld_msg_type_t type, GByteArray* ba, void* data) {
+ struct _ping* p = data;
+ struct timeval t;
+ gettimeofday(&t);
+
+ if (p->cb) p->cb(timevaldiff(&(p->tv),&t), p->cb_data);
+
+ return FALSE;
+}
+
+
+echld_state_t echld_ping(int child_id, echld_ping_cb_t pcb, void* cb_data) {
+ echld_t* c;
+ struct _ping* p;
+ GByteArray* ba;
+
+ if (!(( c = get_child(child_id) )) ) {
+
+ return -1;
+ }
+
+ p = g_malloc0(sizeof(struct _ping));
+ ba = g_byte_array_new();
+
+ p->child = c;
+ p->cb = pcb;
+ p->cb_data = cb_data;
+ gettimeofday(&(p->tv));
+
+ return echld_req(c->chld_id, ECHLD_PING, ba, pong, p);
+}
+
+
+