diff options
author | Luis Ontanon <luis.ontanon@gmail.com> | 2013-06-21 01:27:28 +0000 |
---|---|---|
committer | Luis Ontanon <luis.ontanon@gmail.com> | 2013-06-21 01:27:28 +0000 |
commit | b5c96de50bedd0d944de415c3080e2aa278fc362 (patch) | |
tree | 826cfc10b7625d0c0e144bb1fef7c90d4d6b56be /echld | |
parent | 74f0f96209400931e377c058efa186464dc09fd4 (diff) |
move echld to final dest...
svn path=/trunk/; revision=50102
Diffstat (limited to 'echld')
-rw-r--r-- | echld/echld-int.h | 184 | ||||
-rw-r--r-- | echld/echld.h | 401 | ||||
-rw-r--r-- | echld/echld_child.c | 543 | ||||
-rw-r--r-- | echld/echld_common.c | 797 | ||||
-rw-r--r-- | echld/echld_dispatcher.c | 667 | ||||
-rw-r--r-- | echld/echld_parent.c | 799 |
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,¶m,&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,¶m) ) { + 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,¶m,&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,¶m) ) { + 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); +} + + + |