forked from osmocom/wireshark
move echld to final dest...
svn path=/trunk/; revision=50102
This commit is contained in:
parent
74f0f96209
commit
b5c96de50b
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue