move echld to final dest...

svn path=/trunk/; revision=50102
This commit is contained in:
Luis Ontanon 2013-06-21 01:27:28 +00:00
parent 74f0f96209
commit b5c96de50b
6 changed files with 3391 additions and 0 deletions

184
echld/echld-int.h Normal file
View File

@ -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

401
echld/echld.h Normal file
View File

@ -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

543
echld/echld_child.c Normal file
View File

@ -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;
}

797
echld/echld_common.c Normal file
View File

@ -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;
}
}

667
echld/echld_dispatcher.c Normal file
View File

@ -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());
}

799
echld/echld_parent.c Normal file
View File

@ -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);
}