/*===========================================================================* * * * wtpman.c - WTP manager WSX agent * * * * Copyright (c) 1991-2003 iMatix Corporation * * * * ------------------ GPL Licensed Source Code ------------------ * * iMatix makes this software available under the GNU General * * Public License (GPL) license for open source projects. For * * details of the GPL license please see www.gnu.org or read the * * file license.gpl provided in this package. * * * * 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 in the file 'license.gpl'; if * * not, write to the Free Software Foundation, Inc., 59 Temple * * Place - Suite 330, Boston, MA 02111-1307, USA. * * * * You can also license this software under iMatix's General Terms * * of Business (GTB) for commercial projects. If you have not * * explicitly licensed this software under the iMatix GTB you may * * only use it under the terms of the GNU General Public License. * * * * For more information, send an email to info@imatix.com. * * -------------------------------------------------------------- * *===========================================================================*/ #include "sfl.h" /* SFL library header file */ #include "smtlib.h" /* SMT kernel functions */ #include "smtdefn.h" /* SMT definitions */ #include "smthttpl.h" /* SMT HTTP definitions */ #include "smtmsg.h" /* SMT message API */ #include "wtplib.h" /* WTP definitions */ #include "wtpmsg.h" /* WTP message functions */ /*- Definitions -------------------------------------------------------------*/ #define AGENT_NAME "wtpman" /* Our public name */ #define STACK_MAX 32 /* Max depth of program stack */ #define FORM_MAX 48000L /* Max size of form output data */ #define APPLIC_STOPPED 0 /* Application is off-line */ #define APPLIC_RUNNING 1 /* Application is on-line */ #define APPLIC_LOCKED 2 /* Application is on-line, locked */ /* Structure of server table; one entry per server process */ /* This table is allocated when the application starts-up. */ /* Current implementation always creates max. processes at start. */ typedef struct { int atp; /* Application ATP number, 1..n */ THREAD *thread; /* Server thread for this process */ Bool ready; /* Is server process ready? */ PROCESS pid; /* Server process ID, or zero */ time_t started; /* Time process was started */ long callback; /* Callback key */ qbyte signature; /* Signature CRC-32 */ } SERVER; /* This structures define tables that are shared between multiple threads; */ /* the last thread to leave the room switches-off the light. */ typedef struct { SYMTAB *table; /* Symbol table,... */ int users; /* Number of threads using it */ } SHRSYM; typedef struct { SERVER *table; /* Server table,... */ int users; /* Number of threads using it */ } SHRSRV; typedef struct { /* Shared application info */ qbyte cur_sessions; /* Current session count */ qbyte peak_sessions; /* Highest session count */ qbyte trans_count; /* Number transactions */ qbyte trans_cost; /* And total time, 1/100s */ qbyte fatal_errors; /* Fatal error count */ int users; /* Number of threads using it */ } SHRINF; typedef struct _TCB { /* Thread context block: */ /* Used by all threads */ event_t thread_type; /* Thread type indicator */ sock_t handle; /* Handle for i/o, if used */ char *config_file; /* Thread config file, if any */ SYMTAB *config_table; /* Thread config table if any */ /* Used by MASTER and APPLIC threads */ QID http_qid; /* Incoming HTTP thread QID */ WSXREQ *request; /* Decoded WTP request */ char *http_data; /* HTTP post data */ /* Used only by APPLIC threads */ int applic_port; /* Callback port */ int applic_state; /* Application state */ qbyte session_count; /* Number of sessions created */ long start_time; /* Application starting time */ long start_date; /* Application starting time */ /* Used by APPLIC and SERVER and SESSION threads */ char *applic_uri; /* Application URI */ char *applic_name; /* Application name */ QUEUE *do_queue; /* Queued Do requests */ SHRSYM *programs; /* Program look-up table */ SHRSRV *servers; /* Server table */ SHRINF *info; /* Application info table */ int nbr_servers; /* Size of server table */ /* Used only by SERVER threads */ char *server_msg; /* Message data from server */ char *server_name; /* Server executable name */ qbyte server_msgsize; /* Size of this message */ int server_index; /* Index into servers table */ Bool server_ready; /* Idle, waiting for work */ QID applic_qid; /* Application thread QID */ QID session_qid; /* Client session thread QID */ byte message_type; /* Last incoming message type */ long entry_time; /* Time message sent to server */ /* Used only by SESSION threads */ int stack_ptr; /* Size of call stack */ char **call_stack; /* Program call stack */ dbyte session_step; /* Sequential session step */ char *session_program; /* Current active program */ byte *session_request; /* WTP message, encoded */ int session_reqsize; /* Size of encoded wtp message */ qbyte session_envsize; /* Session environment size */ byte *session_envdata; /* Session environment data */ SYMTAB *session_context; /* Session context blocks */ char *session_screen; /* Last HTML screen we showed */ } TCB; #define TCONFIG(s,d) sym_get_value (tcb-> config_table, (s), (d)) #define TCONFIG_SET(s) *(sym_get_value (tcb-> config_table, (s), "1")) == '1' /*- Function prototypes -----------------------------------------------------*/ static THREAD *new_thread (char *name, char *method, event_t type); static int atp_max_value (int atp_nbr); static char *atp_name_value (int atp_nbr); static long get_callback_key (int index); static void use_applic_values (TCB *applic_tcb, TCB *child_tcb); static void create_new_session (THREAD *thread, char *arguments); static char *get_session_id (char *arguments); static dbyte get_session_step (char *arguments); static void http_reply_html (THREAD *thread, char *header, char *msg); static void wtp_send_msg_ok (THREAD *thread); static void wtp_send_msg_error (THREAD *thread, dbyte code); static void alloc_programs_table (TCB *tcb); static void alloc_servers_table (TCB *tcb); static void alloc_info_table (TCB *tcb); static void start_required_atps (TCB *tcb); static void start_atp (SERVER *server, char *file, char *workdir, char *user, char *group, char **environment, int port, char *protocol, qbyte callback); static void unuse_programs_table (TCB *tcb); static void unuse_servers_table (TCB *tcb); static void unuse_info_table (TCB *tcb); static char *session_root_program (TCB *tcb); static int session_set_program (TCB *tcb, char *program); static char *session_uri (THREAD *thread); static Bool program_is_active (TCB *tcb, char *program); static void put_context (int local_block, qbyte size, byte *data); static void get_context (qbyte *global_size, byte **global_data, qbyte *local_size, byte **local_data); /*- Global variables used in this source file only --------------------------*/ static AGENT *this_agent; /* Handle to ourselves... */ static TCB *tcb; /* Address thread context block */ static QID operq, /* Operator console event queue */ logq, /* Server log queue */ sockq, /* Socket agent event queue */ timeq, /* Timer agent event queue */ tranq; /* Transfer agent event queue */ #include "wtpman.d" /* Include dialog data */ /******************** INITIALISE AGENT - ENTRY POINT *********************/ int wtpman_init (void) { AGENT *agent; /* Handle for our agent */ THREAD *thread; /* Handle to various threads */ # include "wtpman.i" /* Include dialog interpreter */ /* Method name Event value Priority */ /* Shutdown event comes from Kernel */ method_declare (agent, "SHUTDOWN", shutdown_event, SMT_PRIORITY_MAX); /* Public methods supported by this agent */ method_declare (agent, "WSX_REQUEST", request_event, 0); /* Reply events from socket agent */ method_declare (agent, "SOCK_INPUT_OK", ok_event, 0); method_declare (agent, "SOCK_OUTPUT_OK", ok_event, 0); method_declare (agent, "SOCK_READ_OK", ok_event, 0); method_declare (agent, "SOCK_WRITE_OK", SMT_NULL_EVENT, 0); method_declare (agent, "SOCK_CLOSED", sock_closed_event, 0); method_declare (agent, "SOCK_ERROR", sock_error_event, 0); method_declare (agent, "SOCK_TIMEOUT", sock_error_event, 0); /* Reply events from transfer agent */ method_declare (agent, "TRAN_PUTH_OK", SMT_NULL_EVENT, 0); method_declare (agent, "TRAN_GETH_OK", ok_event, 0); method_declare (agent, "TRAN_CLOSED", sock_closed_event, 0); method_declare (agent, "TRAN_ERROR", sock_error_event, 0); /* Reply events from timer agent */ method_declare (agent, "TIME_ALARM", timeout_event, 0); /* Private methods used to pass initial thread events */ method_declare (agent, "_MASTER", master_event, 0); method_declare (agent, "_APPLIC", applic_event, 0); method_declare (agent, "_SESSION", session_event, 0); method_declare (agent, "_SERVER", server_event, 0); /* Private methods used between various threads */ method_declare (agent, "_CONTROL", control_event, 0); method_declare (agent, "_REQUEST", request_event, 0); method_declare (agent, "_CONTINUE", continue_event, 0); method_declare (agent, "_REFRESH", refresh_event, 0); /* These are reply events from server to session thread */ method_declare (agent, "_SESSION_SHOW", session_show_event, 0); method_declare (agent, "_SESSION_CALL", session_call_event, 0); method_declare (agent, "_SESSION_RETURN", session_return_event, 0); method_declare (agent, "_SESSION_EXIT", session_exit_event, 0); method_declare (agent, "_SESSION_ERROR", session_error_event, 0); method_declare (agent, "_SERVER_TIMEOUT", server_timeout_event, 0); method_declare (agent, "_SERVER_ABORT", server_abort_event, 0); /* Ensure that logging agent is running, else start it up */ smtlog_init (); if ((thread = thread_create (SMT_LOGGING, "")) != NULL) logq = thread-> queue-> qid; else return (-1); /* Ensure that operator console is running, else start it up */ smtoper_init (); if ((thread = thread_lookup (SMT_OPERATOR, "")) != NULL) operq = thread-> queue-> qid; else return (-1); /* Ensure that socket i/o agent is running, else start it up */ smtsock_init (); if ((thread = thread_lookup (SMT_SOCKET, "")) != NULL) sockq = thread-> queue-> qid; else return (-1); /* Ensure that timer agent is running, else start it up */ smttime_init (); if ((thread = thread_lookup (SMT_TIMER, "")) != NULL) timeq = thread-> queue-> qid; else return (-1); /* Ensure that transfer agent is running, else start it up */ smttran_init (); if ((thread = thread_lookup (SMT_TRANSFER, "")) != NULL) tranq = thread-> queue-> qid; else return (-1); /* Create initial thread to manage master port */ new_thread ("main", "_MASTER", master_event); this_agent = agent; /* Handle to ourselves... */ /* Signal okay to caller that we initialised okay */ return (0); } static THREAD * new_thread (char *name, char *method, event_t thread_type) { static QID null_qid = { 0, 0 }; THREAD *thread; /* Handle to new thread */ TCB *tcb; /* Thread context block */ if ((thread = thread_create (AGENT_NAME, name)) != NULL) { if (method) SEND (&thread-> queue-> qid, method, ""); tcb = thread-> tcb; /* Initialise all fields */ tcb-> thread_type = thread_type; tcb-> handle = 0; tcb-> config_file = NULL; tcb-> config_table = NULL; tcb-> http_qid = null_qid; tcb-> request = NULL; tcb-> applic_port = 0; tcb-> applic_state = 0; tcb-> start_time = 0; tcb-> start_date = 0; tcb-> session_count = 0; tcb-> applic_uri = NULL; tcb-> applic_name = NULL; tcb-> nbr_servers = 0; tcb-> do_queue = NULL; tcb-> servers = NULL; tcb-> programs = NULL; tcb-> info = NULL; tcb-> server_msg = NULL; tcb-> server_name = NULL; tcb-> server_msgsize = 0; tcb-> server_index = 0; tcb-> server_ready = FALSE; tcb-> applic_qid = null_qid; tcb-> session_qid = null_qid; tcb-> message_type = 0; tcb-> entry_time = 0; tcb-> stack_ptr = 0; tcb-> call_stack = NULL; tcb-> session_step = 0; tcb-> session_program = NULL; tcb-> session_request = NULL; tcb-> session_reqsize = 0; tcb-> session_envdata = NULL; tcb-> session_context = NULL; tcb-> session_screen = NULL; } return (thread); } /************************* INITIALISE THE THREAD *************************/ MODULE initialise_the_thread (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ /* We don't set the_next_event because we expect an argument event */ /* to supply the initial event for the dialog state machine. */ } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - M A S T E R T H R E A D - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /************************ LOAD MASTER CONFIG FILE ************************/ MODULE load_master_config_file (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ /* Load configuration data, if any, into the config_table */ tcb-> config_table = ini_dyn_load (NULL, "wtpcfg.ini"); } /************************** OPEN MASTER LOG FILE *************************/ MODULE open_master_log_file (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ sendfmt (&logq, "APPEND", TCONFIG ("general:logfile", "wtpman.log")); } /************************* CLOSE MANAGER LOG FILE ************************/ MODULE close_manager_log_file (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ SEND (&logq, "CLOSE", ""); } /************************* CREATE APPLIC THREADS *************************/ MODULE create_applic_threads (THREAD *thread) { int applic_nbr; /* Cardinal applic, 1..n */ char applic_key [30], /* Applic number as string */ *uri, /* Applic URI string */ *config_file; /* Applic configuration file */ SYMTAB *config_table; /* Applic config values */ THREAD *applic_thread; /* Thread that manages applic */ TCB *applic_tcb; /* Applic thread context block */ tcb = thread-> tcb; /* Point to thread's context */ /* Create applications 1 to N as specified in wtpman config file */ for (applic_nbr = 1;; applic_nbr++) { sprintf (applic_key, "applications:%d", applic_nbr); if ((config_file = TCONFIG (applic_key, NULL)) == NULL) break; /* No more applications defined */ /* Load config file information */ config_table = ini_dyn_load (NULL, config_file); if (config_table) { uri = sym_get_value (config_table, "general:uri", NULL); if (uri) { applic_thread = new_thread (uri, "_APPLIC", applic_event); if (applic_thread) { applic_tcb = applic_thread-> tcb; applic_tcb-> config_table = config_table; applic_tcb-> config_file = mem_strdup (config_file); applic_tcb-> applic_uri = mem_strdup (uri); } else { sym_delete_table (config_table); sendfmt (&operq, "ERROR", "wtpman: could not create thread for %s: %s", config_file, smt_errlist [smt_errno]); } } else { sym_delete_table (config_table); sendfmt (&operq, "ERROR", "wtpman: no URI defined in %s", config_file); } } else sendfmt (&operq, "ERROR", "wtpman: could not read config file %s: %s", config_file, strerror (errno)); } } /*************************** DECODE WTP REQUEST **************************/ MODULE decode_wtp_request (THREAD *thread) { char *url_args, /* If &data supplied in arguments */ *post_data, /* Posted data string */ *post_name; FILE *post_file; DESCR *data_descr = NULL; qbyte file_nbr = 0; tcb = thread-> tcb; /* Point to thread's context */ /* Decode WTP request into structure */ ASSERT (tcb-> request == NULL); get_smt_wsx_request (thread-> event-> body, &tcb-> request); ASSERT (tcb-> request-> arguments); ASSERT (tcb-> request-> post_data); /* We always allocate a string to tcb-> http_data */ /* This code sucks, and should be changed... http_multipart_decode * makes too many assumptions, and can't handle data in a buffer... */ post_data = tcb-> request-> post_data; /* Content type : Multipart/form-data */ if (memfind (tcb-> request-> symbols_data, tcb-> request-> symbols_size, "CONTENT_TYPE=multipart/form-data", 32, FALSE) != NULL) { if (*post_data == '@') data_descr = http_multipart_decode ( post_data + 1, TCONFIG ("general:workdir", ""), "%s_tmp"); else { post_name = mem_strdup (get_tmp_file_name (".", &file_nbr, "pos")); post_file = fopen (post_name, FOPEN_WRITE_BINARY); fwrite (post_data, strlen (post_data), 1, post_file); fclose (post_file); data_descr = http_multipart_decode ( post_name, TCONFIG ("general:workdir", ""), "%s_tmp"); file_delete (post_name); mem_free (post_name); } } else if (*post_data == '@') /* Content type: application/x-www-form-urlencoded */ data_descr = file_slurp (post_data + 1); if (data_descr) post_data = (char *) data_descr-> data; /* If arguments contain &... data, then append to posted data */ url_args = strchr (tcb-> request-> arguments, '&'); tcb-> http_data = xstrcpy (NULL, post_data, url_args, NULL); if (data_descr) mem_free (data_descr); /* Set reply QID for all HTTP responses */ tcb-> http_qid = thread-> event-> sender; } /*********************** PARTIAL DECODE WTP REQUEST **********************/ MODULE partial_decode_wtp_request (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ ASSERT (tcb-> request == NULL); get_smt_wsx_request (thread-> event-> body, &tcb-> request); tcb-> http_data = NULL; /* Set reply QID for all HTTP responses */ tcb-> http_qid = thread-> event-> sender; } /********************** ROUTE WTP REQUEST TO APPLIC **********************/ MODULE route_wtp_request_to_applic (THREAD *thread) { char *method, /* Method to deliver */ *uri; /* URI from request URL */ THREAD *applic_thread; /* Applic thread for request */ tcb = thread-> tcb; /* Point to thread's context */ /* Look for a URL that we recognise, and parse it */ if ((uri = strdefix (tcb-> request-> request_url, "/wtp/control")) != NULL) method = "_CONTROL"; else if ((uri = strdefix (tcb-> request-> request_url, "/wtp")) != NULL) method = "_REQUEST"; /* If we parsed URL okay, look for thread, and pass request */ if (uri) { applic_thread = thread_lookup (AGENT_NAME, uri); if (applic_thread) { /* Route event body to applic thread, keeping original sender */ /* so that replies go to HTTP agent, not this thread. */ event_send ( &applic_thread-> queue-> qid, &thread-> event-> sender, method, thread-> event-> body, thread-> event-> body_size, NULL, NULL, NULL, 0); } else raise_exception (not_found_event); } else raise_exception (not_found_event); } /**************************** FREE WTP REQUEST ***************************/ MODULE free_wtp_request (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ ASSERT (tcb-> request != NULL); free_smt_wsx_request (&tcb-> request); mem_strfree (&tcb-> http_data); } /************************** HTTP REPLY NOT FOUND *************************/ MODULE http_reply_not_found (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ send_wsx_error (&tcb-> http_qid, HTTP_RESPONSE_NOTFOUND); } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A P P L I C T H R E A D - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /********************* INITIALISE APPLICATION THREAD *********************/ MODULE initialise_application_thread (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ tcb-> applic_name = mem_strdup (sym_get_value (tcb-> config_table, "general:name", tcb-> applic_uri)); tcb-> do_queue = queue_create (this_agent, 0); } /*********************** CHECK APPLICATION AUTORUN ***********************/ MODULE check_application_autorun (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ if (TCONFIG_SET ("general:autorun")) the_next_event = ok_event; else the_next_event = suspended_event; } /********************** CHECK APPLIC CONTROL COMMAND *********************/ MODULE check_applic_control_command (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ if (streq (tcb-> request-> arguments, "start")) the_next_event = control_start_event; else if (streq (tcb-> request-> arguments, "stop")) the_next_event = control_stop_event; else if (streq (tcb-> request-> arguments, "report")) the_next_event = control_report_event; else if (streq (tcb-> request-> arguments, "lock")) the_next_event = control_lock_event; else if (streq (tcb-> request-> arguments, "unlock")) the_next_event = control_unlock_event; else the_next_event = error_event; } /************************ OPEN APPLICATION SOCKET ************************/ MODULE open_application_socket (THREAD *thread) { static char service [10]; /* Formatted port number */ tcb = thread-> tcb; /* Point to thread's context */ /* Find an available socket starting at general:first_port */ tcb-> applic_port = atoi (TCONFIG ("general:first_port", "5500")) - ip_portbase; if (tcb-> applic_port < 0) tcb-> applic_port = 0; sprintf (service, "%d", (int) tcb-> applic_port); tcb-> handle = passive_TCP (service, 5); while (connect_error () == IP_BINDERROR) { tcb-> applic_port++; /* Port in use - try next one */ sprintf (service, "%d", (int) tcb-> applic_port); tcb-> handle = passive_TCP (service, 5); } if (tcb-> handle == INVALID_SOCKET) { tcb-> handle = 0; /* 0 means 'not open' */ sendfmt (&operq, "ERROR", "wtpman: could not open WTP port"); sendfmt (&operq, "ERROR", "wtpman: %s", connect_errlist [connect_error ()]); sendfmt (&operq, "ERROR", "wtpman: %s", sockmsg ()); raise_exception (fatal_error_event); sprintf (server_message, "Could not open WTP port - %s (%s)", connect_errlist [connect_error ()], sockmsg ()); } else sendfmt (&operq, "INFO", "wtpman: opened WTP port %d for %s", tcb-> applic_port + ip_portbase, tcb-> applic_name); } /************************ CLOSE APPLICATION SOCKET ***********************/ MODULE close_application_socket (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ close_socket (tcb-> handle); tcb-> handle = 0; } /*********************** START APPLICATION SERVERS ***********************/ MODULE start_application_servers (THREAD *thread) { int atp_nbr; /* ATP index, 1..n */ tcb = thread-> tcb; /* Point to thread's context */ /* Get the total of the server max= values */ tcb-> nbr_servers = 0; for (atp_nbr = 1; atp_max_value (atp_nbr); atp_nbr++) tcb-> nbr_servers += atp_max_value (atp_nbr); /* Fail if no servers are defined */ if (tcb-> nbr_servers == 0) { sendfmt (&operq, "ERROR", "wtpman: zero ATPs defined for %s", tcb-> applic_name); raise_exception (exception_event); } else { /* Allocate application tables */ alloc_programs_table (tcb); alloc_servers_table (tcb); alloc_info_table (tcb); start_required_atps (tcb); } if (!exception_raised) { tcb-> start_time = time_now (); tcb-> start_date = date_now (); tcb-> applic_state = APPLIC_RUNNING; } } /* ------------------------------------------------------------------------- * atp_max_value * * Get max number of server processes for an application server process. * Returns 0 if ATP is not defined, or is badly defined. Assumes tcb is * set to application TCB. */ static int atp_max_value (int atp_nbr) { static char atp_key [15]; /* AtpNNNNN:max */ sprintf (atp_key, "atp%d:max", atp_nbr); if (atp_name_value (atp_nbr)) /* If atp defined, default is 1 */ return (atoi (TCONFIG (atp_key, "1"))); else return (0); /* Else we return zero */ } /* ------------------------------------------------------------------------- * atp_name_value * * Get executable filename for an application server process. * Returns NULL if ATP is not defined, or is badly defined. Assumes * tcb is set to application TCB. */ static char * atp_name_value (int atp_nbr) { static char atp_key [15]; /* AtpNNNNN:name */ sprintf (atp_key, "atp%d:name", atp_nbr); return (TCONFIG (atp_key, NULL)); } /* ------------------------------------------------------------------------- * get_callback_key * * Generate a callback key by hashing the current time and the specified * server index. This is used at connect time to check that the server is * valid and permitted to connect. */ static long get_callback_key (int index) { return (time_now () + index); } /* ------------------------------------------------------------------------- * alloc_programs_table * * Create new program symbol table, set usage count to 1. */ static void alloc_programs_table (TCB *tcb) { ASSERT (tcb-> programs == NULL); tcb-> programs = mem_alloc (sizeof (*tcb-> programs)); if (tcb-> programs == NULL) raise_exception (out_of_memory_event); else { tcb-> programs-> table = sym_create_table (); tcb-> programs-> users = 1; if (tcb-> programs-> table == NULL) raise_exception (out_of_memory_event); } } /* ------------------------------------------------------------------------- * alloc_servers_table * * Create new application server table, set usage count to 1. */ static void alloc_servers_table (TCB *tcb) { size_t server_table_size; tcb-> servers = mem_alloc (sizeof (*tcb-> servers)); if (tcb-> servers == NULL) raise_exception (out_of_memory_event); else { server_table_size = tcb-> nbr_servers * sizeof (SERVER); tcb-> servers-> table = mem_alloc (server_table_size); tcb-> servers-> users = 1; if (tcb-> servers-> table == NULL) raise_exception (out_of_memory_event); else memset (tcb-> servers-> table, 0, server_table_size); } } /* ------------------------------------------------------------------------- * alloc_info_table * * Create new application information table, set usage count to 1. */ static void alloc_info_table (TCB *tcb) { ASSERT (tcb-> info == NULL); tcb-> info = mem_alloc (sizeof (*tcb-> info)); if (tcb-> info == NULL) raise_exception (out_of_memory_event); else { memset (tcb-> info, 0, sizeof (*tcb-> info)); tcb-> info-> users = 1; } } /* ------------------------------------------------------------------------- * start_required_atps * * Starts all application server processes that are not yet running, as * specified in the server table. */ static void start_required_atps (TCB *tcb) { char *atp_path, /* Executable program path */ *work_dir, /* Executable working directory */ *filename, /* Executable program filename */ **environment; /* Environment symbols */ int atp_nbr, /* ATP index, 1..n */ atp_max, /* Max. servers for this ATP */ atp_server, /* ATP server, 0..max-1 */ server_nbr; /* Server number, 0..max-1 */ SERVER *server; /* Entry into server table */ SYMTAB *symtab; /* Environment symbols */ SYMBOL *symbol, /* Config symbols */ *symnew; /* Newly-inserted env. symbol */ atp_path = TCONFIG ("general:binpath", "wtp-bin"); work_dir = TCONFIG ("general:workdir", ""); /* Build environment symbols table */ if (TCONFIG_SET ("general:environment")) symtab = env2symb (); /* Load current environment */ else symtab = sym_create_table (); for (symbol = tcb-> config_table-> symbols; symbol; symbol = symbol-> next) if (strprefixed (symbol-> name, "environment:")) { symnew = sym_assume_symbol ( symtab, symbol-> name + strlen ("environment:"), symbol-> value); if (symnew) strupc (symnew-> name); } environment = symb2strt (symtab); sym_delete_table (symtab); /* Launch any processes that have a zero process-id entry */ server_nbr = 0; for (atp_nbr = 1; atp_max_value (atp_nbr); atp_nbr++) { /* Check that executable file exists and is accessible */ filename = file_where ('r', atp_path, atp_name_value (atp_nbr), NULL); if (filename == NULL || !file_is_executable (filename)) { sendfmt (&operq, "ERROR", "wtpman: '%s' not executable on '%s'", atp_name_value (atp_nbr), atp_path); raise_exception (exception_event); } else { atp_max = atp_max_value (atp_nbr); for (atp_server = 0; atp_server < atp_max; atp_server++) { server = &tcb-> servers-> table [server_nbr]; server-> atp = atp_nbr; if (server-> pid == 0) start_atp (server, filename, work_dir, TCONFIG ("general:user", NULL), TCONFIG ("general:group", NULL), environment, tcb-> applic_port + ip_portbase, "tcp", get_callback_key (atp_server)); server_nbr++; } } } strtfree (environment); } static void start_atp ( SERVER *server, char *filename, char *workdir, char *user, char *group, char **environment, int port, char *protocol, qbyte callback) { static char arguments [30]; /* "WTP/1.0 tcp nnnn nnnnnnnn" */ char *command; /* Program + arguments to run */ PROCESS_DATA procinfo = PROCESS_DATA_INIT; PROCESS pid; /* ID of created process */ sprintf (arguments, "WTP/1.0 %s %d %ld", protocol, port, callback); command = xstrcpy (NULL, filename, " ", arguments, NULL); process_compatible = FALSE; procinfo.filename = command; procinfo.workdir = workdir; procinfo.envv = environment; procinfo.username = user; procinfo.groupname = group; procinfo.password = NULL; if (process_setinfo (&procinfo, NULL, "wtpout.log", FALSE, "wtperr.log", FALSE) == 0) { pid = process_create_full (&procinfo); process_close_io (&procinfo); if (pid == NULL_PROCESS) { sendfmt (&operq, "ERROR", "wtpman: can't run '%s' in '%s': %s", filename, workdir, strerror (procinfo.error)); raise_exception (exception_event); } } else { sendfmt (&operq, "ERROR", "wtpman: can't redirect output: %s", strerror (errno)); raise_exception (exception_event); pid = NULL_PROCESS; } mem_strfree (&command); server-> thread = NULL; server-> ready = FALSE; server-> pid = pid; server-> started = time (NULL); server-> callback = callback; server-> signature = 0; } /*********************** CHECK APPLICATION SERVERS ***********************/ MODULE check_application_servers (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ start_required_atps (tcb); /* Restart any halted servers */ } /************************ STOP APPLICATION SERVERS ***********************/ MODULE stop_application_servers (THREAD *thread) { int server_nbr; /* Index into application servers */ THREAD *server_thread; tcb = thread-> tcb; /* Point to thread's context */ /* Application is running if servers table is (still) allocated */ if (tcb-> servers) { /* Send _SHUTDOWN to all server threads for this application */ for (server_nbr = 0; server_nbr < tcb-> nbr_servers; server_nbr++) { server_thread = tcb-> servers-> table [server_nbr].thread; if (server_thread != NULL) SEND (&server_thread-> queue-> qid, "SHUTDOWN", ""); } unuse_programs_table (tcb); unuse_servers_table (tcb); unuse_info_table (tcb); } tcb-> applic_state = APPLIC_STOPPED; } /* ------------------------------------------------------------------------- * unuse_programs_table * * Decrement usage count; if zero, delete programs table. */ static void unuse_programs_table (TCB *tcb) { ASSERT (tcb-> programs); ASSERT (tcb-> programs-> users > 0); if (--tcb-> programs-> users == 0) { sym_delete_table (tcb-> programs-> table); mem_free (tcb-> programs); } tcb-> programs = NULL; /* We're finished with the table */ } /* ------------------------------------------------------------------------- * unuse_servers_table * * Decrement usage count; if zero, delete servers table. */ static void unuse_servers_table (TCB *tcb) { ASSERT (tcb-> servers-> users > 0); if (--tcb-> servers-> users == 0) { mem_free (tcb-> servers-> table); mem_free (tcb-> servers); } tcb-> servers = NULL; /* We're finished with the table */ } /* ------------------------------------------------------------------------- * unuse_info_table * * Decrement usage count; if zero, delete info table. */ static void unuse_info_table (TCB *tcb) { ASSERT (tcb-> info-> users > 0); if (--tcb-> info-> users == 0) mem_free (tcb-> info); tcb-> info = NULL; /* We're finished with the table */ } /*********************** LOCK APPLICATION SESSIONS ***********************/ MODULE lock_application_sessions (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ tcb-> applic_state = APPLIC_LOCKED; } /********************** UNLOCK APPLICATION SESSIONS **********************/ MODULE unlock_application_sessions (THREAD *thread) { tcb = thread-> tcb; /* Point to thread's context */ tcb-> applic_state = APPLIC_RUNNING; } /************************ ACCEPT SERVER CONNECTION ***********************/ MODULE accept_server_connection (THREAD *thread) { sock_t slave_socket; /* Connected socket */ THREAD *child_thread; /* Handle to child threads */ TCB *child_tcb; /* Handle to child TCB */ tcb = thread-> tcb; /* Point to thread's context */ slave_socket = accept_socket (tcb-> handle); if (slave_socket != INVALID_SOCKET) { child_thread = new_thread ("", "_SERVER", server_event); if (child_thread) { child_tcb = child_thread-> tcb; child_tcb-> handle = slave_socket; child_tcb-> applic_qid = thread-> queue-> qid; use_applic_values (tcb, child_tcb); } } else if (sockerrno != EAGAIN) { sprintf (server_message, "Network problem: %s", sockmsg ()); sendfmt (&operq, "ERROR", "wtpman: could not accept connection: %s", sockmsg ()); raise_exception (fatal_error_event); } } /* ------------------------------------------------------------------------- * use_applic_values * * Server and session threads share the parent application tables: we set * the applic_tcb pointer to the application TCB in these threads. */ static void use_applic_values (TCB *applic_tcb, TCB *child_tcb) { child_tcb-> config_table = applic_tcb-> config_table; child_tcb-> config_file = applic_tcb-> config_file; child_tcb-> applic_uri = applic_tcb-> applic_uri; child_tcb-> applic_name = applic_tcb-> applic_name; child_tcb-> do_queue = applic_tcb-> do_queue; child_tcb-> nbr_servers = applic_tcb-> nbr_servers; child_tcb-> programs = applic_tcb-> programs; child_tcb-> servers = applic_tcb-> servers; child_tcb-> info = applic_tcb-> info; child_tcb-> programs-> users++; child_tcb-> servers-> users++; child_tcb-> info-> users++; } /********************** ROUTE WTP REQUEST TO SESSION *********************/ MODULE route_wtp_request_to_session (THREAD *thread) { char *session_id, /* URL session id/thread name */ *arguments; /* URL arguments, if any */ dbyte session_step; /* URL session step */ THREAD *session_thread; /* Session thread found or created */ TCB *session_tcb; /* Session thread's context block */ tcb = thread-> tcb; /* Point to thread's context */ /* a) request-> arguments are empty or unrecognised: * create new session thread, pass _SESSION method, event body are * arguments. Pass original sender queue to thread. * * b) As (a), only application is locked - reject user. * * c) request-> arguments refer to expired thread: * create new session thread, pass _SESSION method, event body is * "-restart". Pass original sender queue to thread. * * d) request-> arguments refer to wrong thread step: * pass _REFRESH method, with "refresh" as event body. * Pass original sender queue to thread. * * e) request-> arguments refer to existing session thread: * pass _CONTINUE method, with request-> query_data as event body. * Pass original sender queue to thread. */ session_id = get_session_id (tcb-> request-> arguments); session_step = get_session_step (tcb-> request-> arguments); if (session_id == NULL) { if (tcb-> applic_state == APPLIC_RUNNING) { /* (a) Create new session thread, with a unique name */ /* Any URL arguments will be HTTP MIME encoded - decode them */ arguments = tcb-> request-> arguments; http_unescape (arguments, NULL); create_new_session (thread, arguments); } else /* (b) application is locked */ http_reply_html (thread, "New sessions not permitted at present", NULL); } else { session_thread = thread_lookup (AGENT_NAME, session_id); if (session_thread == NULL) /* (c) thread has expired */ create_new_session (thread, "-restart"); else { session_tcb = session_thread-> tcb; if (session_tcb-> session_step != session_step) /* (d) bad session step - ask for refresh, keeping */ /* original sender, for HTTP replies. */ event_send ( &session_thread-> queue-> qid, &thread-> event-> sender, "_REFRESH", (byte *) "refresh", strlen ("refresh") + 1, NULL, NULL, NULL, 0); else { /* (e) send query data to session thread, keeping */ /* original sender, for HTTP replies. */ event_send ( &session_thread-> queue-> qid, &thread-> event-> sender, "_CONTINUE", (byte *) tcb-> http_data, strlen (tcb-> http_data) + 1, NULL, NULL, NULL, 0); } } } } /* ------------------------------------------------------------------------- * create_new_session * * Creates a new session thread and passes it a _SESSION event. */ #define SESSID_SIZE 12 /* Exactly 12 characters long */ static void create_new_session (THREAD *thread, char *arguments) { static char session_name [SESSID_SIZE + 1]; /* Formatted session name */ static word session_count = 0; /* Global uniquifier */ THREAD *session_thread; /* Session thread found or created */ TCB *session_tcb; /* Session thread's context block */ session_count++; sprintf (session_name, "%04X%08lX", session_count, (long) (tcb-> session_count++ ^ tcb-> start_time)); session_thread = new_thread (session_name, NULL, session_event); if (session_thread) { session_tcb = session_thread-> tcb; use_applic_values (tcb, session_tcb); session_tcb-> applic_qid = thread-> queue-> qid; if (tcb-> info-> peak_sessions < ++tcb-> info-> cur_sessions) tcb-> info-> peak_sessions = tcb-> info-> cur_sessions; /* Copy HTTP environment */ session_tcb-> session_envsize = tcb-> request-> symbols_size; session_tcb-> session_envdata = mem_alloc (tcb-> request-> symbols_size); memcpy (session_tcb-> session_envdata, tcb-> request-> symbols_data, tcb-> request-> symbols_size); /* Any URL arguments will be HTTP MIME encoded - decode them */ arguments = tcb-> request-> arguments; http_unescape (arguments, NULL); /* Send initial event to session thread, keeping original */ /* sender, for HTTP replies. */ event_send ( &session_thread-> queue-> qid, &thread-> event-> sender, "_SESSION", /* Event name to send */ (byte *) arguments, strlen (arguments), /* Event body is argument data */ NULL, NULL, NULL, 0); /* No reply events */ } else raise_exception (out_of_memory_event); } /* ------------------------------------------------------------------------- * get_session_id * * Returns session id from URL arguments if valid. Else returns NULL. * URL argument is encoded as '+xxxxxxxx:yyyy" where xxxxxxxx is the * thread name (SESSID_SIZE chars) and yyyy is the session step (4 digits). */ static char * get_session_id (char *arguments) { static char session_id [SESSID_SIZE + 1]; /* Session id plus null */ ASSERT (arguments); if (strlen (arguments) >= SESSID_SIZE + 6 && arguments [0] == '+' && arguments [SESSID_SIZE + 1] == ':') { memcpy (session_id, arguments + 1, SESSID_SIZE); session_id [SESSID_SIZE] = '\0'; return (session_id); } else return (NULL); } /* ------------------------------------------------------------------------- * get_session_step * * Returns session step from URL arguments if valid. Else returns 0. * URL argument is encoded as '+xxxxxxxx:yyyy" where xxxxxxxx is the * thread name (SESSID_SIZE chars) and yyyy is the session step (4 digits). */ static dbyte get_session_step (char *arguments) { int session_step; ASSERT (arguments); if (strlen (arguments) >= SESSID_SIZE + 6 && arguments [0] == '+' && arguments [SESSID_SIZE + 1] == ':') { sscanf (arguments + SESSID_SIZE + 2, "%x", &session_step); return ((dbyte) session_step); } else return (0); } /* ------------------------------------------------------------------------- * http_reply_html * * Returns a formatted HTML message to the user. */ static void http_reply_html (THREAD *thread, char *header, char *message) { char *reply_text; tcb = thread-> tcb; /* Point to thread's context */ reply_text = xstrcpy (NULL, "
", "", message? message: "", "
" \
"Application is . . . . .: %s\n" \
"Started at date/time . .: %s %s\n" \
"Active server processes .: %d\n" \
"Total transaction steps .: %ld\n" \
"Average transaction cost : %ld msecs\n" \
"Current user sessions . .: %ld\n" \
"Peak user sessions . . .: %ld\n" \
"Total sessions handled .: %ld\n" \
"Total fatal errors . . .: %ld\n" \
""
static char
report [sizeof (REPORT_TEXT) + 100];
char
*state,
*fmtdate,
*fmttime;
qbyte
cur_sessions = 0,
peak_sessions = 0,
trans_count = 0,
trans_cost = 0,
fatal_errors = 0;
if (tcb-> applic_state == APPLIC_STOPPED)
{
state = "stopped";
fmtdate = "-";
fmttime = "";
}
else
{
fmtdate = conv_date_pict (tcb-> start_date, "d mmm, yy");
fmttime = conv_time_pict (tcb-> start_time, "hh:mm:ss");
cur_sessions = tcb-> info-> cur_sessions;
peak_sessions = tcb-> info-> peak_sessions;
trans_count = tcb-> info-> trans_count;
trans_cost = tcb-> info-> trans_cost;
if (trans_count > 0)
trans_cost = trans_cost / trans_count * 10;
fatal_errors = tcb-> info-> fatal_errors;
if (tcb-> applic_state == APPLIC_RUNNING)
state = "running";
else
if (tcb-> applic_state == APPLIC_LOCKED)
state = "running, locked";
else
state = "?";
}
sprintf (report, REPORT_TEXT,
state,
fmtdate, fmttime,
tcb-> nbr_servers,
trans_count, trans_cost,
cur_sessions, peak_sessions,
tcb-> session_count,
fatal_errors);
http_reply_html (thread, "Application report:", report);
}
/************************ HTTP REPLY APPLIC LOCKED ***********************/
MODULE http_reply_applic_locked (THREAD *thread)
{
http_reply_html (thread, "Application is now locked", NULL);
}
/*********************** HTTP REPLY APPLIC UNLOCKED **********************/
MODULE http_reply_applic_unlocked (THREAD *thread)
{
http_reply_html (thread, "Application is now unlocked", NULL);
}
/********************* HTTP REPLY APPLIC UNAVAILABLE *********************/
MODULE http_reply_applic_unavailable (THREAD *thread)
{
http_reply_html (thread, "Application not available",
"You can try to reconnect at a later time.");
}
/*********************** HTTP REPLY INVALID CONTROL **********************/
MODULE http_reply_invalid_control (THREAD *thread)
{
http_reply_html (thread, "Error", "Invalid control command.");
}
/************************* WAIT FOR SOCKET INPUT *************************/
MODULE wait_for_socket_input (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
send_input (&sockq, 0, tcb-> handle, 0);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
S E S S I O N T H R E A D
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
/************************ INITIALISE SESSION DATA ************************/
MODULE initialise_session_data (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
/* Allocate empty program stack */
tcb-> call_stack = mem_alloc (STACK_MAX * sizeof (char *));
tcb-> session_context = sym_create_table ();
if (tcb-> call_stack == NULL
|| tcb-> session_context == NULL)
raise_exception (out_of_memory_event);
}
/********************** SESSION USE INITIAL PROGRAM **********************/
MODULE session_use_initial_program (THREAD *thread)
{
char
*root_program;
tcb = thread-> tcb; /* Point to thread's context */
/* Set reply QID for all HTTP responses */
tcb-> http_qid = thread-> event-> sender;
/* Use application root program as defined in program table. Entry
* code is WTP_DOINIT. Build WTP_DO message with empty HTML stream.
* Thread event body contains initial program arguments (from URL).
*/
root_program = session_root_program (tcb);
if (root_program)
{
ASSERT (tcb-> session_program == NULL);
tcb-> session_program = mem_strdup (root_program);
ASSERT (tcb-> session_request == NULL);
tcb-> session_reqsize = put_wtp_msg_do (
&tcb-> session_request,
WTP_DO, /* Message type */
0, /* ATP signature */
tcb-> session_program, /* Program to execute */
WTP_DOINIT, /* Program entry code */
session_uri (thread), /* URL for hyperlinks */
NULL, /* Encoded HTTP data */
thread-> event-> body_size,
thread-> event-> body, /* Call arguments size & data */
0, /* Result of last call */
tcb-> session_envsize, /* HTTP environment block size */
tcb-> session_envdata, /* and contents */
0, NULL, /* Global context block */
0, NULL); /* Local context block */
}
else
{
raise_exception (exception_event);
http_reply_html (thread, "Application root program not found", NULL);
}
}
/* -------------------------------------------------------------------------
* session_root_program
*
* Locates the root program for the application. If no root program is
* defined, returns NULL, else returns the name of the root program.
*/
static char *
session_root_program (TCB *tcb)
{
SYMBOL
*symbol; /* Program symbol */
symbol = tcb-> programs-> table-> symbols;
while (symbol)
{
if (symbol-> value && streq (symbol-> value, "ROOT"))
return (symbol-> name);
symbol = symbol-> next;
}
return (NULL);
}
/* -------------------------------------------------------------------------
* session_uri
*
* Formats and returns URL for session. URL argument is encoded as
* '+xxxxxxxx:yyyy" where xxxxxxxx is the thread name (SESSID_SIZE chars)
* and yyyy is the session step (4 digits).
*/
static char *
session_uri (THREAD *thread)
{
static char
uri [LINE_MAX];
tcb = thread-> tcb; /* Point to thread's context */
tcb-> session_step++; /* Cycles from 1 to n and around */
sprintf (uri, "/wtp%s?+%s:%04X",
tcb-> applic_uri, thread-> name, tcb-> session_step);
return (uri);
}
/********************** SESSION USE CURRENT PROGRAM **********************/
MODULE session_use_current_program (THREAD *thread)
{
qbyte
global_size,
local_size;
byte
*global_data,
*local_data;
tcb = thread-> tcb; /* Point to thread's context */
/* Set reply QID for all HTTP responses */
tcb-> http_qid = thread-> event-> sender;
/* Current program is already okay - entry code is WTP_DOGET
* Build WTP_DO message using thread event body for HTML data
*/
get_context (&global_size, &global_data, &local_size, &local_data);
ASSERT (tcb-> session_request == NULL);
tcb-> session_reqsize = put_wtp_msg_do (
&tcb-> session_request,
WTP_DO, /* Message type */
0, /* ATP signature */
tcb-> session_program, /* Program to execute */
WTP_DOGET, /* Program entry code */
session_uri (thread), /* URL for hyperlinks */
(char *) thread-> event-> body, /* Encoded HTTP data */
0, NULL, /* Call arguments size & data */
0, /* Result of last call */
0, NULL, /* Environment data block */
global_size, global_data, /* Global context block */
local_size, local_data); /* Local context block */
}
static void
get_context (qbyte *global_size, byte **global_data,
qbyte *local_size, byte **local_data)
{
SYMBOL
*symbol;
symbol = sym_lookup_symbol (tcb-> session_context, "$global");
if (symbol)
{
*global_size = ((DESCR *) symbol-> data)-> size;
*global_data = ((DESCR *) symbol-> data)-> data;
}
else
{
*global_size = 0;
*global_data = NULL;
}
symbol = sym_lookup_symbol (tcb-> session_context, tcb-> session_program);
if (symbol)
{
*local_size = ((DESCR *) symbol-> data)-> size;
*local_data = ((DESCR *) symbol-> data)-> data;
}
else
{
*local_size = 0;
*local_data = NULL;
}
}
/*********************** SESSION USE CALLED PROGRAM **********************/
MODULE session_use_called_program (THREAD *thread)
{
struct_wtp_msg_donecall
*wtp_msg = NULL;
byte
entry_code, /* WTP_DO entry code */
call_result; /* If call failed */
qbyte
global_size,
local_size;
byte
*global_data,
*local_data;
tcb = thread-> tcb; /* Point to thread's context */
/* Unpack WTP_DONECALL message into usable structure */
get_wtp_msg_donecall (thread-> event-> body, &wtp_msg);
/* Save global and local context blocks, since we stay in program */
put_context (0, wtp_msg-> global_size, wtp_msg-> global_data);
put_context (1, wtp_msg-> local_size, wtp_msg-> local_data);
/* Push current program, use called program - entry code is WTP_DOINIT
* Build WTP_DO message, with empty HTML data, and pass arguments as
* supplied in wtp_donecall message.
*/
strupc (wtp_msg-> program); /* Normalise the program name */
/* Check various errors first of all */
if (tcb-> stack_ptr == STACK_MAX - 1)
call_result = WTP_ERROROVERFLOW;
else
if (!sym_lookup_symbol (tcb-> programs-> table, wtp_msg-> program))
call_result = WTP_ERRORNOTFOUND;
else
if (program_is_active (tcb, wtp_msg-> program))
call_result = WTP_ERRORWOULDLOOP;
else /* Else stack new program */
{
call_result = WTP_NOERROR;
tcb-> call_stack [tcb-> stack_ptr++] = tcb-> session_program;
tcb-> session_program = NULL;
session_set_program (tcb, wtp_msg-> program);
}
if (call_result == WTP_NOERROR)
entry_code = WTP_DOINIT;
else
entry_code = WTP_DOCONTINUE;
get_context (&global_size, &global_data, &local_size, &local_data);
ASSERT (tcb-> session_request == NULL);
tcb-> session_reqsize = put_wtp_msg_do (
&tcb-> session_request,
WTP_DO, /* Message type */
0, /* ATP signature */
tcb-> session_program, /* Program to execute */
entry_code, /* Program entry code */
session_uri (thread), /* URL for hyperlinks */
NULL, /* Encoded HTTP data */
wtp_msg-> args_size, /* Call arguments size */
wtp_msg-> args_data, /* Call arguments data */
call_result, /* Result of last call */
0, NULL, /* Environment data block */
global_size, global_data, /* Global context block */
local_size, local_data); /* Local context block */
/* Free areas used by message structure */
free_wtp_msg_donecall (&wtp_msg);
}
/* -------------------------------------------------------------------------
* program_is_active
*
* Returns TRUE if the program name is already active; the supplied name
* must have been normalised (put into uppercase) beforehand.
*/
static Bool
program_is_active (TCB *tcb, char *program)
{
int
prog_nbr; /* Program on active stack */
for (prog_nbr = 0; prog_nbr < tcb-> stack_ptr; prog_nbr++)
if (streq (tcb-> call_stack [prog_nbr], program))
return (TRUE);
return (FALSE);
}
static void
put_context (int local_block, qbyte size, byte *data)
{
char
*context_key;
SYMBOL
*symbol;
if (local_block)
context_key = tcb-> session_program;
else
context_key = "$global";
/* Delete context block if it already exists */
symbol = sym_lookup_symbol (tcb-> session_context, context_key);
if (symbol)
{
mem_free (symbol-> data);
sym_delete_symbol (tcb-> session_context, symbol);
}
/* Now store new block if wanted */
if (data)
{
symbol = sym_create_symbol (tcb-> session_context, context_key, NULL);
if (symbol)
symbol-> data = mem_descr (data, size);
else
raise_exception (out_of_memory_event);
}
}
/* -------------------------------------------------------------------------
* session_set_program
*
* Sets a new tcb-> sesssion_program. If the specified program does not
* exist, or is already active, leaves the session_program unchanged.
* Returns one of: WTP_NOERROR or WTP_ERRORNOTFOUND.
*/
static int
session_set_program (TCB *tcb, char *program)
{
char
*normalised;
normalised = mem_strdup (program);
strupc (normalised);
if (sym_lookup_symbol (tcb-> programs-> table, normalised))
{
mem_strfree (&tcb-> session_program);
tcb-> session_program = normalised;
return (WTP_NOERROR);
}
else
{
mem_strfree (&normalised);
return (WTP_ERRORNOTFOUND);
}
}
/*********************** SESSION USE PARENT PROGRAM **********************/
MODULE session_use_parent_program (THREAD *thread)
{
struct_wtp_msg_donereturn
*wtp_msg = NULL;
qbyte
global_size,
local_size;
byte
*global_data,
*local_data;
tcb = thread-> tcb; /* Point to thread's context */
/* Pop parent program - entry code is WTP_DOCONTINUE. If stack was
* empty, raise session_exit exception. Otherwise, build WTP_DO
* message with empty HTML data and return code from called program.
*/
if (tcb-> stack_ptr == 0)
{
raise_exception (session_exit_event);
return;
}
/* Unpack WTP_DONERETURN message into usable structure */
get_wtp_msg_donereturn (thread-> event-> body, &wtp_msg);
/* Save global context blocks, since we leave in program */
put_context (0, wtp_msg-> global_size, wtp_msg-> global_data);
put_context (1, 0, NULL); /* Wipe local context block */
mem_strfree (&tcb-> session_program);
tcb-> session_program = tcb-> call_stack [--tcb-> stack_ptr];
get_context (&global_size, &global_data, &local_size, &local_data);
ASSERT (tcb-> session_request == NULL);
tcb-> session_reqsize = put_wtp_msg_do (
&tcb-> session_request,
WTP_DO, /* Message type */
0, /* ATP signature */
tcb-> session_program, /* Program to execute */
WTP_DOCONTINUE, /* Program entry code */
session_uri (thread), /* URL for hyperlinks */
NULL, /* Encoded HTTP data */
wtp_msg-> args_size, /* Call arguments size */
wtp_msg-> args_data, /* Call arguments data */
WTP_NOERROR, /* Result of last call */
0, NULL, /* Environment data block */
global_size, global_data, /* Global context block */
local_size, local_data); /* Local context block */
/* Free areas used by message structure */
free_wtp_msg_donereturn (&wtp_msg);
}
/************************* SESSION SHOW HTML DATA ************************/
MODULE session_show_html_data (THREAD *thread)
{
struct_wtp_msg_doneshow
*wtp_msg = NULL;
char
*html_data;
tcb = thread-> tcb; /* Point to thread's context */
/* Unpack WTP_DONESHOW message into usable structure */
get_wtp_msg_doneshow (thread-> event-> body, &wtp_msg);
/* Save global and local context blocks, since we stay in program */
put_context (0, wtp_msg-> global_size, wtp_msg-> global_data);
put_context (1, wtp_msg-> local_size, wtp_msg-> local_data);
/* Save HTML data in session TCB, for refreshing if necessary */
/* We translate accented characters into SGML metacharacters */
if (tcb-> session_screen == NULL)
tcb-> session_screen = mem_alloc (FORM_MAX + 1);
html_data = wtp_msg-> html_data;
http_encode_meta (tcb-> session_screen, &html_data, FORM_MAX, TRUE);
/* Send HTML data back to HTTP agent and thence on to the browser */
send_wsx_ok (&tcb-> http_qid, tcb-> session_screen);
/* Free areas used by message structure */
free_wtp_msg_doneshow (&wtp_msg);
}
/************************* SESSION RESYNCHRONIZE *************************/
MODULE session_resynchronize (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
/* We have received a new request for a session that is already busy.
This typically happens when users click twice or are impatient for
a slow response to finish. The browser behavior is to close the
first active socket and open a new one with the second click. In
Xitami this is two threads. We handle this by discarding the old
http_qid and taking the new one, but we do not send anything back
to the browser - it has to wait until the server is correctly done
with its work and sends the reply back.
*/
tcb-> http_qid = thread-> event-> sender;
}
/********************** ROUTE WTP REQUEST TO SERVER **********************/
MODULE route_wtp_request_to_server (THREAD *thread)
{
SYMBOL
*symbol; /* Program symbol */
THREAD
*server_thread; /* Symbol refers to thread */
TCB
*server_tcb;
QID
target_qid; /* Where do we send the request? */
tcb = thread-> tcb; /* Point to thread's context */
/* Look for available server based on program symbol table and server
* ready flags... If none available, add do message to request queue.
*/
target_qid = tcb-> do_queue-> qid; /* By default */
symbol = sym_lookup_symbol (tcb-> programs-> table, tcb-> session_program);
while (symbol)
{
if (streq (symbol-> name, tcb-> session_program))
{
server_thread = symbol-> data;
server_tcb = server_thread-> tcb;
if (server_tcb-> server_ready)
{
server_tcb-> server_ready = FALSE;
target_qid = server_thread-> queue-> qid;
break;
}
}
symbol = symbol-> next;
}
event_send (
&target_qid,
&thread-> queue-> qid,
"_REQUEST",
tcb-> session_request,
tcb-> session_reqsize,
NULL, NULL, NULL, 0);
mem_free (tcb-> session_request);
tcb-> session_request = NULL;
}
/************************** SESSION SET TIMEOUT **************************/
MODULE session_set_timeout (THREAD *thread)
{
long
timeout; /* Timeout in seconds */
tcb = thread-> tcb; /* Point to thread's context */
timeout = atol (TCONFIG ("general:session_timeout", "60"));
timeout = timeout * 60 * 100; /* Minutes => 1/100th seconds */
send_alarm (&timeq, 0, timeout, 0, NULL);
}
/********************* SESSION SHOW APPLICATION ENDED ********************/
MODULE session_show_application_ended (THREAD *thread)
{
char
*goodbye; /* Sign-off message or URL */
goodbye = TCONFIG ("general:goodbye", "Goodbye");
if (*goodbye == '@')
send_wsx_redirect (&tcb-> http_qid, goodbye + 1);
else
http_reply_html (thread, "Finished", goodbye);
}
/********************* SESSION SHOW APPLICATION ERROR ********************/
MODULE session_show_application_error (THREAD *thread)
{
struct_wtp_msg_doneerror
*wtp_msg = NULL;
tcb = thread-> tcb; /* Point to thread's context */
get_wtp_msg_doneerror (thread-> event-> body, &wtp_msg);
http_reply_html (thread, "Error", wtp_msg-> error_text);
free_wtp_msg_doneerror (&wtp_msg);
}
/********************** HTTP REPLY SERVER TIMED OUT **********************/
MODULE http_reply_server_timed_out (THREAD *thread)
{
http_reply_html (thread, "Application program was looping", NULL);
}
/*********************** HTTP REPLY SERVER ABORTED ***********************/
MODULE http_reply_server_aborted (THREAD *thread)
{
http_reply_html (thread, "Application program aborted", NULL);
}
/******************* HTTP REPLY INSUFFICIENT RESOURCES *******************/
MODULE http_reply_insufficient_resources (THREAD *thread)
{
http_reply_html (thread, "Application under stress - try later", NULL);
}
/************************* SESSION END PROCESSING ************************/
MODULE session_end_processing (THREAD *thread)
{
SYMBOL
*symbol; /* Symbol in context table */
tcb = thread-> tcb; /* Point to thread's context */
tcb-> info-> cur_sessions--; /* Update info block */
/* Go through context table, and delete symbol context blocks. */
symbol = tcb-> session_context-> symbols;
while (symbol)
{
mem_free (symbol-> data);
symbol = symbol-> next;
}
sym_delete_table (tcb-> session_context);
/* Free program call stack and other areas */
while (tcb-> stack_ptr > 0)
{
tcb-> stack_ptr--;
mem_free (tcb-> call_stack [tcb-> stack_ptr]);
}
mem_free (tcb-> call_stack);
mem_free (tcb-> session_screen);
unuse_programs_table (tcb);
unuse_servers_table (tcb);
unuse_info_table (tcb);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
S E R V E R T H R E A D
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
/************************** READ SERVER MESSAGE **************************/
MODULE read_server_message (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
/* WTP messages are compatible with the GET_BLOCK method of SMTTRAN */
send_get_huge (&tranq, tcb-> handle, NULL);
event_wait (); /* Wait for transfer agent reply */
}
/************************* DECODE SERVER MESSAGE *************************/
MODULE decode_server_message (THREAD *thread)
{
struct_wtp_msg
*wtp_msg = NULL;
tcb = thread-> tcb; /* Point to thread's context */
if (the_external_event == ok_event)
{
/* Free any previous message, then get new one from event body */
mem_strfree (&tcb-> server_msg);
exdr_read (thread-> event-> body, SMT_TRAN_GETH_OK,
&tcb-> server_msgsize, &tcb-> server_msg);
/* Get just the message type */
get_wtp_msg ((byte *) tcb-> server_msg, &wtp_msg);
tcb-> message_type = wtp_msg-> type;
free_wtp_msg (&wtp_msg);
switch (tcb-> message_type)
{
case WTP_CONNECT:
the_next_event = wtp_connect_event;
break;
case WTP_REGISTER:
the_next_event = wtp_register_event;
break;
case WTP_READY:
the_next_event = wtp_ready_event;
break;
case WTP_DISCONNECT:
the_next_event = wtp_disconnect_event;
break;
case WTP_OK:
the_next_event = wtp_ok_event;
break;
case WTP_ERROR:
the_next_event = wtp_error_event;
break;
case WTP_DO:
the_next_event = wtp_do_event;
break;
case WTP_DONESHOW: /* We handle these in much the */
case WTP_DONECALL: /* way; reply to session thread */
case WTP_DONERETURN:
case WTP_DONEEXIT:
case WTP_DONEERROR:
the_next_event = wtp_done_event;
break;
default:
the_next_event = error_event;
}
}
else
raise_exception (the_external_event);
}
/************************ HANDLE SERVER CONNECTION ***********************/
MODULE handle_server_connection (THREAD *thread)
{
struct_wtp_msg_connect
*wtp_msg = NULL;
int
server_nbr; /* Index into application servers */
long
callback;
SERVER
*server; /* Entry into servers table */
tcb = thread-> tcb; /* Point to thread's context */
get_wtp_msg_connect ((byte *) tcb-> server_msg, &wtp_msg);
/* Look for server with matching callback key */
callback = atol (wtp_msg-> callback_key);
for (server_nbr = 0; server_nbr < tcb-> nbr_servers; server_nbr++)
{
server = &tcb-> servers-> table [server_nbr];
if (server-> callback == callback)
break;
}
/* Did we find it okay? */
if (server_nbr < tcb-> nbr_servers)
{
ASSERT (server-> thread == NULL);
tcb-> server_name = mem_strdup (atp_name_value (server-> atp));
tcb-> server_index = server_nbr;
server-> thread = thread;
server-> signature = wtp_msg-> signature;
}
else
{
sendfmt (&operq, "ERROR",
"wtpman: unauthorised server callback: %s",
wtp_msg-> callback_key);
raise_exception (error_event);
}
free_wtp_msg_connect (&wtp_msg);
}
/*********************** HANDLE SERVER REGISTRATION **********************/
MODULE handle_server_registration (THREAD *thread)
{
struct_wtp_msg_register
*wtp_msg = NULL;
SYMBOL
*symbol; /* Newly-created symbol */
tcb = thread-> tcb; /* Point to thread's context */
get_wtp_msg_register ((byte *) tcb-> server_msg, &wtp_msg);
strupc (wtp_msg-> program);
/* Create new entry in program lookup table; don't check duplicates
* If the program is a root program, its value is set to "ROOT", else
* to null.
*/
symbol = sym_create_symbol (tcb-> programs-> table, wtp_msg-> program, NULL);
if (symbol == NULL)
{
wtp_send_msg_error (thread, WTP_ERRORFATAL);
raise_exception (out_of_memory_event);
}
else
{
wtp_send_msg_ok (thread);
symbol-> data = thread;
if (wtp_msg-> is_root)
symbol-> value = mem_strdup ("ROOT");
}
free_wtp_msg_register (&wtp_msg);
}
static void
wtp_send_msg_error (THREAD *thread, dbyte code)
{
byte
*buffer; /* Buffer holds formatted WTP msg */
qbyte
size; /* Size of formatted message */
tcb = thread-> tcb; /* Point to thread's context */
size = put_wtp_msg_error (&buffer, WTP_ERROR, code, "Error");
send_put_huge (&tranq, tcb-> handle, size, buffer, NULL);
mem_free (buffer);
}
static void
wtp_send_msg_ok (THREAD *thread)
{
byte
*buffer; /* Buffer holds formatted WTP msg */
qbyte
size; /* Size of formatted message */
tcb = thread-> tcb; /* Point to thread's context */
size = put_wtp_msg_ok (&buffer, WTP_OK);
send_put_huge (&tranq, tcb-> handle, size, buffer, NULL);
mem_free (buffer);
}
/************************** HANDLE SERVER READY **************************/
MODULE handle_server_ready (THREAD *thread)
{
wtp_send_msg_ok (thread);
}
/************************** WTP SEND DO PROGRAM **************************/
MODULE wtp_send_do_program (THREAD *thread)
{
struct_wtp_msg_do
*wtp_msg = NULL;
byte
*buffer; /* Buffer holds formatted WTP msg */
qbyte
size; /* Size of formatted message */
tcb = thread-> tcb; /* Point to thread's context */
/* Set reply QID for all eventual responses */
tcb-> session_qid = thread-> event-> sender;
/* Event body holds a DO message complete except for the signature.
* We have to decode it, sign it, rebuild it, and send it to the
* server process.
*/
get_wtp_msg_do (thread-> event-> body, &wtp_msg);
size = put_wtp_msg_do (&buffer, WTP_DO,
tcb-> servers-> table [tcb-> server_index].signature,
wtp_msg-> program,
wtp_msg-> entry_code,
wtp_msg-> http_uri,
wtp_msg-> http_data,
wtp_msg-> args_size,
wtp_msg-> args_data,
wtp_msg-> call_result,
wtp_msg-> env_size,
wtp_msg-> env_data,
wtp_msg-> global_size,
wtp_msg-> global_data,
wtp_msg-> local_size,
wtp_msg-> local_data);
send_put_huge (&tranq, tcb-> handle, size, buffer, NULL);
mem_free (buffer);
free_wtp_msg_do (&wtp_msg);
tcb-> server_ready = FALSE;
tcb-> entry_time = time_now ();
}
/*************************** HANDLE SERVER DONE **************************/
MODULE handle_server_done (THREAD *thread)
{
char
*method = NULL; /* Method to send to session */
int
elapsed_time; /* Transaction elapsed time */
tcb = thread-> tcb; /* Point to thread's context */
switch (tcb-> message_type)
{
case WTP_DONESHOW:
method = "_SESSION_SHOW";
break;
case WTP_DONECALL:
method = "_SESSION_CALL";
break;
case WTP_DONERETURN:
method = "_SESSION_RETURN";
break;
case WTP_DONEEXIT:
method = "_SESSION_EXIT";
break;
case WTP_DONEERROR:
method = "_SESSION_ERROR";
break;
default:
sendfmt (&operq, "ERROR", "wtpman: handle_server_done() failed");
}
/* Unless there was a screw-up, send the ATP message through as-is to
* the session thread. The contents of the message depend on the type
* of the message.
*/
if (method)
event_send (
&tcb-> session_qid,
NULL,
method,
(byte *) tcb-> server_msg,
tcb-> server_msgsize,
NULL, NULL, NULL, 0);
/* Compute elapsed time and total so far */
elapsed_time = time_to_csecs (time_now ())
- time_to_csecs (tcb-> entry_time);
if (elapsed_time < 0) /* Passed over midnight */
elapsed_time += INTERVAL_DAY;
tcb-> info-> trans_count += 1;
tcb-> info-> trans_cost += elapsed_time;;
}
/*************************** SERVER SET TIMEOUT **************************/
MODULE server_set_timeout (THREAD *thread)
{
long
timeout; /* Timeout in seconds */
tcb = thread-> tcb; /* Point to thread's context */
timeout = atol (TCONFIG ("general:program_timeout", "60"));
timeout = timeout * 100; /* Seconds => 1/100th seconds */
send_alarm (&timeq, 0, timeout, 0, NULL);
}
/************************* WTP SEND NOT EXPECTED *************************/
MODULE wtp_send_not_expected (THREAD *thread)
{
wtp_send_msg_error (thread, WTP_ERRORUNEXPECTED);
}
/************************* WTP SEND NOT CONNECTED ************************/
MODULE wtp_send_not_connected (THREAD *thread)
{
wtp_send_msg_error (thread, WTP_ERRORUNCONNECTED);
}
/************************ WTP SEND NOT AUTHORISED ************************/
MODULE wtp_send_not_authorised (THREAD *thread)
{
wtp_send_msg_error (thread, WTP_ERRORUNAUTHORISED);
}
/**************************** WTP SEND INVALID ***************************/
MODULE wtp_send_invalid (THREAD *thread)
{
wtp_send_msg_error (thread, WTP_ERRORINVALID);
}
/************************* TAKE DO REQUEST IF ANY ************************/
MODULE take_do_request_if_any (THREAD *thread)
{
EVENT
*event; /* Event information block */
tcb = thread-> tcb; /* Point to thread's context */
/* Get next event off queue */
event = event_iterate (tcb-> do_queue, NULL);
if (event)
{
if (thread-> event) /* Release any thread event */
event_destroy (thread-> event);
/* Get event off queue; it now belongs to the thread */
thread-> event = event_accept (tcb-> do_queue, event);
the_next_event = ok_event;
}
else
the_next_event = not_found_event;
}
/************************ SERVER PROCESS IS READY ************************/
MODULE server_process_is_ready (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
tcb-> server_ready = TRUE;
}
/************************ SIGNAL SERVER TIMED OUT ************************/
MODULE signal_server_timed_out (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
/* Tell application thread to check & refresh its server table */
sendfmt (&tcb-> applic_qid, "_REFRESH", "");
sendfmt (&operq, "ERROR", "wtpman: server '%s' looping", tcb-> server_name);
/* Tell session thread to forget it, and inform the user */
sendfmt (&tcb-> session_qid, "_SERVER_TIMEOUT", "");
}
/************************* SIGNAL SERVER ABORTED *************************/
MODULE signal_server_aborted (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
/* Tell application thread to check & refresh its server table */
sendfmt (&tcb-> applic_qid, "_REFRESH", "");
sendfmt (&operq, "ERROR", "wtpman: server '%s' aborted", tcb-> server_name);
/* Tell session thread to forget it, and inform the user */
sendfmt (&tcb-> session_qid, "_SERVER_ABORT", "");
}
/************************* SERVER END PROCESSING *************************/
MODULE server_end_processing (THREAD *thread)
{
PROCESS
*pid;
SYMBOL
*symbol,
*next;
tcb = thread-> tcb; /* Point to thread's context */
/* Kill server process if it is still running */
pid = &tcb-> servers-> table [tcb-> server_index].pid;
if (*pid != NULL_PROCESS)
{
if (process_status (*pid) == PROCESS_RUNNING)
process_kill (*pid);
else
process_close (*pid);
*pid = NULL_PROCESS;
}
/* Go through program symbol table, and delete any symbols that refer
* to this thread.
*/
symbol = tcb-> programs-> table-> symbols;
while (symbol)
{
next = symbol-> next;
if (symbol-> data == thread)
sym_delete_symbol (tcb-> programs-> table, symbol);
symbol = next;
}
/* As far as the applic thread is concerned, we're gone */
tcb-> servers-> table [tcb-> server_index].thread = NULL;
unuse_programs_table (tcb);
unuse_servers_table (tcb);
unuse_info_table (tcb);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
A L L T H R E A D S
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
/************************ CLEAR ALARMS FOR THREAD ************************/
MODULE clear_alarms_for_thread (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
event_send (
&timeq,
&thread-> queue-> qid,
"FLUSH",
NULL, 0,
NULL, NULL, NULL, 0);
}
/************************** SIGNAL SOCKET ERROR **************************/
MODULE signal_socket_error (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
sendfmt (&operq, "ERROR",
"wtpman: error on socket: %s", thread-> event-> body);
}
/************************** SIGNAL OUT OF MEMORY *************************/
MODULE signal_out_of_memory (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
sendfmt (&operq, "ERROR", "wtpman: no memory available");
sprintf (server_message, "Insufficient memory");
}
/************************** SIGNAL INVALID EVENT *************************/
MODULE signal_invalid_event (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
sendfmt (&operq, "ERROR", "wtpman: invalid dialog event detected");
}
/*************************** CHECK THREAD TYPE ***************************/
MODULE check_thread_type (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
the_next_event = tcb-> thread_type;
}
/************************ SHUTDOWN THE APPLICATION ***********************/
MODULE shutdown_the_application (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
smt_shutdown (); /* Halt the application */
}
/************************* TERMINATE THE THREAD **************************/
MODULE terminate_the_thread (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
if (tcb-> handle)
close_socket (tcb-> handle);
/* Potentially used in each thread */
mem_free (tcb-> server_msg);
mem_free (tcb-> server_name);
mem_free (tcb-> session_program);
mem_free (tcb-> session_request);
mem_free (tcb-> session_envdata);
/* Allocated by applic thread but shared by server & session threads: */
if (tcb-> thread_type == master_event)
{
sym_delete_table (tcb-> config_table);
mem_free (tcb-> config_file);
}
else
if (tcb-> thread_type == applic_event)
{
if (tcb-> do_queue)
queue_destroy (tcb-> do_queue);
sym_delete_table (tcb-> config_table);
mem_free (tcb-> config_file);
mem_free (tcb-> applic_uri);
mem_free (tcb-> applic_name);
}
the_next_event = terminate_event;
}