summaryrefslogtreecommitdiff
path: root/echld/echld_dispatcher.c
diff options
context:
space:
mode:
authorLuis Ontanon <luis.ontanon@gmail.com>2013-06-21 01:27:28 +0000
committerLuis Ontanon <luis.ontanon@gmail.com>2013-06-21 01:27:28 +0000
commitb5c96de50bedd0d944de415c3080e2aa278fc362 (patch)
tree826cfc10b7625d0c0e144bb1fef7c90d4d6b56be /echld/echld_dispatcher.c
parent74f0f96209400931e377c058efa186464dc09fd4 (diff)
downloadwireshark-b5c96de50bedd0d944de415c3080e2aa278fc362.tar.gz
move echld to final dest...
svn path=/trunk/; revision=50102
Diffstat (limited to 'echld/echld_dispatcher.c')
-rw-r--r--echld/echld_dispatcher.c667
1 files changed, 667 insertions, 0 deletions
diff --git a/echld/echld_dispatcher.c b/echld/echld_dispatcher.c
new file mode 100644
index 0000000000..42edbdd3f9
--- /dev/null
+++ b/echld/echld_dispatcher.c
@@ -0,0 +1,667 @@
+/* echld_dispatcher.c
+ * epan working child API internals
+ * Dispatcher process routines and definitions
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Copyright (c) 2013 by Luis Ontanon <luis@ontanon.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "echld-int.h"
+/**
+ DISPATCHER
+ **/
+
+struct dispatcher_child {
+ unsigned chld_id;
+ child_state_t state;
+ echld_reader_t reader;
+ int write_fd;
+ int pid;
+ gboolean closing;
+};
+
+struct dispatcher {
+ int parent_out;
+ echld_reader_t parent_in;
+ struct dispatcher_child* children;
+ int max_children;
+ int nchildren;
+ int reqh_id;
+ int pid;
+ int ppid;
+ struct _encs {
+ child_encoder_t* to_parent;
+ echld_parent_encoder_t* to_child;
+ } enc;
+ struct _decs {
+ child_decoder_t* from_parent;
+ parent_decoder_t* from_child;
+ } dec;
+
+ gboolean closing;
+};
+
+struct dispatcher* dispatcher;
+
+#define DISP_RESP(B,T) (echld_write_frame( dispatcher->parent_out, (B), 0, (T), dispatcher->reqh_id, NULL))
+
+#ifdef DEBUG_DISPATCHER
+static int dbg_level = 0;
+
+void dispatcher_debug(int level, char* fmt, ...) {
+ va_list ap;
+ char* str;
+
+ if (dbg_level<level) return;
+
+ va_start(ap, fmt);
+ str = g_strdup_vprintf(fmt,ap);
+ va_end(ap);
+
+ fprintf(stderr, "dispatcher[%d]: reqh_id=%d dbg_level=%d message='%s'", dispatcher->pid, dispatcher->reqh_id, level, str);
+ g_free(str);
+}
+
+static char* param_get_dbg_level(char** err ) {
+ return g_strdup_printf("%d",dbg_level);
+}
+
+static echld_bool_t param_set_dbg_level(char* val , char** err ) {
+ char* p;
+ int lvl = strtol(val, &p, 10);
+
+ if (p<=val) {
+ *err = g_strdup("not an integer");
+ return FALSE;
+ } else if (lvl < 0 || lvl > 5) {
+ *err = g_strdup_printf("invalid level=%d (min=0 max=5)",lvl);
+ return FALSE;
+ }
+
+ dbg_level = lvl;
+ return TRUE;
+}
+
+#define DISP_DBG(attrs) ( dispatcher_debug attrs )
+#else
+#define DISP_DBG(attrs)
+#endif
+
+
+/* parameters */
+
+int wait_chldn = 0;
+
+static char* param_get_wait_chldn(char** err ) {
+ return g_strdup_printf("%d",wait_chldn);
+}
+
+static echld_bool_t param_set_wait_chldn(char* val , char** err ) {
+ char* p;
+ int lvl = strtol(val, &p, 10);
+
+ if (p<=val) {
+ *err = g_strdup("not an integer");
+ return FALSE;
+ } else if (lvl < 0 || lvl > 10) {
+ *err = g_strdup_printf("invalid level=%d (min=0 max=5)",lvl);
+ return FALSE;
+ }
+
+ wait_chldn = lvl;
+ return TRUE;
+}
+
+static param_t disp_params[] = {
+#ifdef DEBUG_DISPATCHER
+ {"dbg_level", param_get_dbg_level, param_set_dbg_level},
+# endif
+ {"wait_chldn",param_get_wait_chldn,param_set_wait_chldn},
+ {NULL,NULL,NULL} };
+
+static param_t* get_paramset(char* name) {
+ int i;
+ for (i = 0; disp_params[i].name != NULL;i++) {
+ if (strcmp(name,disp_params[i].name) == 0 ) return &(disp_params[i]);
+ }
+ return NULL;
+}
+
+void dispatcher_err(int err, const char* fmt, ...) {
+ size_t len= 1024;
+ guint8* b[len];
+ char err_str[len];
+ GByteArray* em;
+ va_list ap;
+
+
+ va_start(ap, fmt);
+ g_vsnprintf(err_str,len,fmt,ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s[%d]: error=%d '%s'\n", "dispatcher", dispatcher->pid, err, err_str);
+
+ em = (void*)dispatcher->enc.to_parent->error(err, err_str);
+ echld_write_frame(dispatcher->parent_out, em, 0, ECHLD_ERROR, dispatcher->reqh_id, NULL);
+ g_ptr_array_free((void*)em,TRUE);
+}
+
+static struct dispatcher_child* dispatcher_get_child(struct dispatcher* d, guint16 chld_id) {
+ int i;
+ struct dispatcher_child* cc = d->children;
+ int max_children = d->max_children;
+
+ for(i = 0; i < max_children; i++) {
+ struct dispatcher_child* c = &(c[i]);
+ if (c->chld_id == chld_id) return c;
+ }
+
+ return NULL;
+}
+
+
+static void dispatcher_clear_child(struct dispatcher_child* c) {
+ echld_reset_reader(&(c->reader), -1, 4096);
+ c->chld_id = 0;
+ c->write_fd = 0;
+ c->pid = 0;
+ c->closing = 0;
+}
+
+static void preinit_epan() {
+ /* Here we do initialization of parts of epan that will be the same for every child we fork */
+}
+
+
+static void dispatcher_clear() {
+ /* remove unnecessary stuff for the working child */
+}
+
+void dispatcher_reaper(int sig) {
+ int status;
+ int i;
+ struct dispatcher_child* cc = dispatcher->children;
+ int max_children = dispatcher->max_children;
+ int pid = waitpid(-1, &status, WNOHANG);
+ size_t len= 1024;
+ guint8* b[len];
+ GByteArray* em;
+
+
+ for(i = 0; i < max_children; i++) {
+ struct dispatcher_child* c = &(cc[i]);
+ if ( c->pid == pid ) {
+ if (c->closing || dispatcher->closing) {
+ em = (void*)dispatcher->enc.to_parent->child_dead("OK");
+ } else {
+ /* here we do collect crash data !!! */
+ /*
+ WIFEXITED(status)
+ True if the process terminated normally by a call to _exit(2) or exit(3).
+
+ WIFSIGNALED(status)
+ True if the process terminated due to receipt of a signal.
+
+ WIFSTOPPED(status)
+ True if the process has not terminated, but has stopped and can be restarted. This macro can be true only if the wait call speci-
+ fied the WUNTRACED option or if the child process is being traced (see ptrace(2)).
+
+ Depending on the values of those macros, the following macros produce the remaining status information about the child process:
+
+ WEXITSTATUS(status)
+ If WIFEXITED(status) is true, evaluates to the low-order 8 bits of the argument passed to _exit(2) or exit(3) by the child.
+
+ WTERMSIG(status)
+ If WIFSIGNALED(status) is true, evaluates to the number of the signal that caused the termination of the process.
+
+ WCOREDUMP(status)
+ If WIFSIGNALED(status) is true, evaluates as true if the termination of the process was accompanied by the creation of a core file
+ containing an image of the process when the signal was received.
+
+ WSTOPSIG(status)
+ If WIFSTOPPED(status) is true, evaluates to the number of the signal that caused the process to stop.
+
+*/
+ em = (void*)dispatcher->enc.to_parent->child_dead("Unexpected, probably crashed");
+ dispatcher_err(ECHLD_ERR_CRASHED_CHILD, "Unexpected dead: pid=%d chld_id=%d", pid, c->chld_id);
+ }
+
+ echld_write_frame(dispatcher->parent_out, em, c->chld_id, ECHLD_CHILD_DEAD, 0, NULL);
+ dispatcher_clear_child(c);
+ g_byte_array_free(em,TRUE);
+ return;
+ }
+ }
+
+ dispatcher_err(ECHLD_ERR_UNKNOWN_PID, "Unkown child pid: %d", pid);
+}
+
+
+
+static void dispatcher_destroy() {
+ int i;
+ int max_children = dispatcher->max_children;
+ struct dispatcher_child* cc = dispatcher->children;
+ /* destroy the dispatcher stuff at closing */
+
+ dispatcher->closing = TRUE;
+
+ /* kill all alive children */
+ for(i = 0; i < max_children; i++) {
+ struct dispatcher_child* c = &(cc[i]);
+ if ( c->chld_id ) {
+ kill(c->pid,SIGTERM);
+ DISP_DBG((1,"Killing chld_id=%d pid=%d"));
+ continue;
+ }
+ }
+
+ exit(6666);
+}
+
+/* stuff coming from child going to parent */
+static int dispatch_to_parent(guint8* b, size_t len, echld_chld_id_t chld_id, echld_msg_type_t type, echld_reqh_id_t reqh_id, void* data) {
+ struct dispatcher_child* c = data;
+ dispatcher->reqh_id = reqh_id;
+ /* TODO: timeouts, clear them */
+ /* TODO: keep stats */
+ GByteArray in_ba;
+
+ in_ba.data = b;
+ in_ba.len = len;
+
+ if (chld_id != c->chld_id) {
+ goto misbehabing;
+ }
+
+ switch(type) {
+ case ECHLD_ERROR: break;
+ case ECHLD_TIMED_OUT: break;
+ case ECHLD_HELLO: c->state = IDLE; break;
+ case ECHLD_CLOSING: c->closing = TRUE; c->state = CLOSED; break;
+ case ECHLD_PARAM: break;
+ case ECHLD_PONG: break;
+ case ECHLD_FILE_OPENED: c->state = READING; break;
+ case ECHLD_INTERFACE_OPENED: c->state = READY; break;
+ case ECHLD_CAPTURE_STARTED: c->state = CAPTURING; break;
+ case ECHLD_NOTIFY: break; // notify(pre-encoded)
+ case ECHLD_PACKET_SUM: break; // packet_sum(pre-encoded)
+ case ECHLD_TREE: break; //tree(framenum, tree(pre-encoded) )
+ case ECHLD_BUFFER: break; // buffer (name,range,totlen,data)
+ case ECHLD_EOF: c->state = DONE; break;
+ case ECHLD_CAPTURE_STOPPED: c->state = DONE; break;
+ case ECHLD_NOTE_ADDED: break;
+ case ECHLD_PACKET_LIST: break; // packet_list(name,filter,range);
+ case ECHLD_FILE_SAVED: break;
+
+ default:
+ goto misbehabing;
+ }
+
+ return echld_write_frame(dispatcher->parent_out, &in_ba, chld_id, type, reqh_id, NULL);
+
+misbehabing:
+ c->state = ERRORED;
+ c->closing = TRUE;
+ kill(c->pid,SIGTERM);
+ dispatcher_err(ECHLD_ERR_CRASHED_CHILD,"chld_id=%d",chld_id);
+ return 0;
+
+}
+
+void dispatch_new_child(struct dispatcher* dd) {
+ struct dispatcher_child* c = dispatcher_get_child(dd, 0);
+ int reqh_id = dd->reqh_id;
+ int pid;
+
+ if ( c ) {
+ int parent_pipe_fds[2];
+ int child_pipe_fds[2];
+
+ int pipe_to_parent;
+ int pipe_from_parent;
+ int pipe_to_child;
+ int pipe_from_child;
+
+ if( pipe(parent_pipe_fds) < 0) {
+ dispatcher_err(ECHLD_ERR_CANNOT_FORK,"CANNOT OPEN PARENT PIPE: %s",strerror(errno));
+ return;
+ }
+
+ pipe_from_parent = parent_pipe_fds[0];
+ pipe_to_child = parent_pipe_fds[1];
+
+ if( pipe(child_pipe_fds) < 0) {
+ close(pipe_from_parent);
+ close(pipe_to_child);
+ dispatcher_err(ECHLD_ERR_CANNOT_FORK,"CANNOT OPEN CHILD PIPE: %s",strerror(errno));
+ return;
+ }
+
+ pipe_from_child = child_pipe_fds[0];
+ pipe_to_parent = child_pipe_fds[1];
+
+ switch (( pid = fork() )) {
+ case -1: {
+ close(pipe_to_child);
+ close(pipe_to_parent);
+ close(pipe_from_child);
+ close(pipe_from_parent);
+ dispatcher_err(ECHLD_ERR_CANNOT_FORK,"CANNOT FORK: %s",strerror(errno));
+ return;
+ }
+ case 0: { /* I'm the child */
+ int i;
+ int fdt_len = getdtablesize();
+
+ dispatcher_clear(dd);
+
+ for(i=0;i<fdt_len;i++) {
+ if ( i != pipe_from_parent
+ && i != pipe_to_parent
+ && i != STDERR_FILENO ) {
+ close(i);
+ }
+ }
+
+ echld_child_initialize(pipe_from_parent,pipe_to_parent,reqh_id);
+
+ exit( echld_child_loop() );
+
+ /* it won't */
+ return;
+ }
+ default: {
+ /* I'm the parent */
+ guint8 buf[4];
+ GByteArray out_ba;
+
+ out_ba.data = buf;
+ out_ba.len = 0;
+
+ close(pipe_to_parent);
+ close(pipe_from_parent);
+
+ echld_reset_reader(&(c->reader), pipe_from_child,4096);
+ c->write_fd = pipe_to_child;
+ c->pid = pid;
+ dispatcher->nchildren++;
+
+ /* configure child */
+ echld_write_frame(pipe_to_child, &out_ba, c->chld_id, ECHLD_NEW_CHILD, dispatcher->reqh_id, NULL);
+ return;
+ }
+ }
+ } else {
+ dispatcher_err(ECHLD_ERR_CANNOT_FORK, "MAX CHILDREN REACHED: max_children=%d",dispatcher->max_children);
+ return;
+ }
+}
+
+
+/* process signals sent from parent */
+static int dispatch_to_child(guint8* b, size_t len, echld_chld_id_t chld_id, echld_msg_type_t type, echld_reqh_id_t reqh_id, void* data) {
+ struct dispatcher* disp = data;
+ disp->reqh_id = reqh_id;
+ GByteArray in_ba;
+
+ in_ba.data = b;
+ in_ba.len = len;
+
+ if (chld_id == 0) { /* these are messages to the dispatcher itself */
+ switch(type) {
+ case ECHLD_CLOSE_CHILD:
+ dispatcher_destroy();
+ return 0;
+ case ECHLD_PING:
+ echld_write_frame(disp->parent_out, &in_ba, chld_id, ECHLD_PONG, reqh_id, NULL);
+
+ return 0;
+ case ECHLD_NEW_CHILD:
+ dispatch_new_child(disp);
+ return 0;
+ case ECHLD_SET_PARAM:{
+ char* param;
+ char* value;
+ if ( disp->dec.from_parent->set_param(b,len,&param,&value) ) {
+ GByteArray* ba;
+ param_t* p = get_paramset(param);
+ char* err;
+ if (!p) {
+ dispatcher_err(ECHLD_CANNOT_SET_PARAM,"no such param='%s'",param);
+ return 0;
+ }
+
+ if (! p->set ) {
+ dispatcher_err(ECHLD_CANNOT_SET_PARAM,"reason='read only'");
+ return 0;
+ }
+
+ if (! p->set(value,&err) ) {
+ dispatcher_err(ECHLD_CANNOT_SET_PARAM,"reason='%s'",err);
+ g_free(err);
+ return 0;
+ }
+
+ ba = (void*)disp->enc.to_parent->param(param,value);
+ DISP_RESP(ba,ECHLD_PARAM);
+ g_byte_array_free(ba,TRUE);
+ DISP_DBG((1,"Set Param: param='%s' value='%s'",param,value));
+
+ return 0;
+ } else {
+ dispatcher_err(ECHLD_CANNOT_SET_PARAM,"reason='decoder error'");
+ return 0;
+ }
+ }
+ case ECHLD_GET_PARAM: {
+ GByteArray* ba;
+ char* param;
+ if ( disp->dec.from_parent->get_param(b,len,&param) ) {
+ char* err;
+ char* val;
+
+ param_t* p = get_paramset(param);
+
+ if (!p) {
+ dispatcher_err(ECHLD_CANNOT_GET_PARAM,"no such param='%s'",param);
+ return 0;
+ }
+
+ if (! p->get ) {
+ dispatcher_err(ECHLD_CANNOT_SET_PARAM,"reason='write only'");
+ return 0;
+ }
+
+ if (!(val = p->get(&err))) {
+ dispatcher_err(ECHLD_CANNOT_GET_PARAM,"reason='%s'",err);
+ g_free(err);
+ return 0;
+ }
+
+ ba = (void*)disp->enc.to_parent->param(param,val);
+ DISP_RESP(ba,ECHLD_PARAM);
+ g_byte_array_free(ba,TRUE);
+ DISP_DBG((2,"Get Param: param='%s' value='%s'",param,val));
+ return 0;
+ } else {
+ dispatcher_err(ECHLD_CANNOT_GET_PARAM,"reason='decoder error'");
+ return 0;
+ }
+ }
+ default:
+ dispatcher_err(ECHLD_ERR_WRONG_MSG, "wrong message to dispatcher type='%c'", type);
+ return 0;
+ }
+ } else {
+ struct dispatcher_child* c;
+
+ if (! (c = dispatcher_get_child(dispatcher, chld_id)) ) {
+ dispatcher_err(ECHLD_ERR_NO_SUCH_CHILD, "wrong chld_id %d", chld_id);
+ return 0;
+ }
+
+ switch(type) {
+ case ECHLD_CLOSE_CHILD:
+ c->closing = TRUE;
+ c->state = CLOSED;
+ goto relay_frame;
+
+ case ECHLD_OPEN_FILE:
+ c->state = READING;
+ goto relay_frame;
+
+ case ECHLD_OPEN_INTERFACE:
+ c->state = READY;
+ goto relay_frame;
+
+ case ECHLD_START_CAPTURE:
+ c->state = CAPTURING;
+ goto relay_frame;
+
+ case ECHLD_STOP_CAPTURE:
+ c->state = DONE;
+ goto relay_frame;
+
+ case ECHLD_SAVE_FILE:
+ case ECHLD_APPLY_FILTER:
+ case ECHLD_SET_PARAM:
+ case ECHLD_GET_PARAM:
+ case ECHLD_PING:
+ case ECHLD_GET_SUM:
+ case ECHLD_GET_TREE:
+ case ECHLD_GET_BUFFER:
+ case ECHLD_ADD_NOTE:
+ relay_frame:
+ return echld_write_frame(c->write_fd, &in_ba, chld_id, type, reqh_id, NULL);
+
+ default:
+ dispatcher_err(ECHLD_ERR_WRONG_MSG, "wrong message %d %c", reqh_id, type);
+ return 0;
+ }
+ }
+}
+
+
+int dispatcher_loop() {
+ int parent_out = dispatcher->parent_out;
+ int parent_in = dispatcher->parent_in.fd;
+
+ struct dispatcher_child* children = dispatcher->children;
+
+ do {
+ fd_set rfds;
+ fd_set efds;
+ struct timeval timeout;
+ struct dispatcher_child* c;
+ int nfds;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&efds);
+
+ FD_SET(parent_in,&rfds);
+ FD_SET(parent_in,&efds);
+ FD_SET(parent_out,&efds);
+
+ for (c = children, nfds = 0; c->pid; c++) {
+ if (c->chld_id) {
+ FD_SET(c->reader.fd, &rfds);
+ FD_SET(c->reader.fd, &efds);
+ }
+ nfds++;
+ }
+
+ nfds = select(nfds, &rfds, NULL, &efds, &timeout);
+
+ if ( FD_ISSET(parent_in, &efds) || FD_ISSET(parent_out, &efds) ) {
+ /* XXX deep shit */
+ break;
+ }
+
+ if (FD_ISSET(parent_in, &rfds)) {
+ int st = echld_read_frame(&(dispatcher->parent_in), dispatch_to_child, dispatcher);
+
+ if (st < 0) {
+ /* XXX */
+ continue;
+ }
+ }
+
+ for (c=children; c->pid; c++) {
+ if (c->chld_id) {
+ if ( FD_ISSET(c->reader.fd,&efds) ) {
+ /* XXX cleanup child and report */
+ continue;
+ }
+
+ if (FD_ISSET(c->reader.fd,&rfds)) {
+ int st = echld_read_frame(&(c->reader), dispatch_to_parent, c);
+
+ if (st < 0) {
+ /* XXX cleanup child and report */
+ continue;
+ }
+ continue;
+ }
+ }
+ }
+ } while(1);
+
+ /* won't */
+ return 1;
+}
+
+void echld_dispatcher_start(int* in_pipe_fds, int* out_pipe_fds) {
+ static struct dispatcher d;
+ int fdt_len = getdtablesize();
+ int i;
+
+ preinit_epan();
+
+ signal(SIGCHLD,dispatcher_reaper);
+
+ dispatcher = &d;
+
+ echld_init_reader(&(d.parent_in),in_pipe_fds[0],4096);
+ d.parent_out = out_pipe_fds[1];
+ d.children = g_malloc0(ECHLD_MAX_CHILDREN * sizeof(struct dispatcher_child));
+ d.max_children = ECHLD_MAX_CHILDREN;
+ d.nchildren = 0;
+ d.reqh_id = -1;
+ d.pid = getpid();
+
+ echld_get_all_codecs(&(d.enc.to_parent), &(d.dec.from_parent), &(d.enc.to_child), &(d.dec.from_child));
+
+ dispatcher_clear();
+
+ /* close all fds but those used */
+ for(i=0;i<fdt_len;i++) {
+ if ( i != d.parent_in.fd
+ && i != d.parent_out
+ && i != STDERR_FILENO ) {
+ close(i);
+ }
+ }
+
+ exit(dispatcher_loop());
+}
+