/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $TOG: ActionTt.c /main/12 1999/09/16 14:56:00 mgreess $ */ /* * (c) Copyright 1997, The Open Group */ /*************************************<+>************************************* ***************************************************************************** ** ** File: ActionTt.c ** ** Project: CDE Execution Management ** ** Description: This file contains the Tooltalk portions of the ** action library source code. ** ** ** by Hewlett-Packard Company ** ** ** (c) Copyright 1993, 1994 Hewlett-Packard Company ** (c) Copyright 1993, 1994 International Business Machines Corp. ** (c) Copyright 1993, 1994 Sun Microsystems, Inc. ** (c) Copyright 1993, 1994 Novell, Inc. **************************************************************************** ************************************<+>*************************************/ /****************************************************************************** * * TT_Message Key Assignments For Attached Data (please note additions here!) * * 0 - client data 5/94 --tg * 1 - ms_timeout 7/94 --tg * 2 - invocation id - currently unused * 3 - child id - currently unused * *****************************************************************************/ /*LINTLIBRARY*/ #include #include #include #include #include #ifdef _SUN_OS /* Need this for the strtod () call */ #include #endif /* _SUN_OS */ #include #include #include #include #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include #include #include #include #include #include #include #include #include #include #include #include #include #include
#include
#include
#include "myassertP.h" #include "DtSvcLock.h" /******** Public Function Declarations ********/ void _DtProcessTtRequest( Widget w, ActionRequest *request, char * relPathHost, char * relPathDir ) ; Tt_status _DtInitializeToolTalk(Widget w); extern void _DtCreateErrorDialog( Widget w, char * actionName, XmString msg) ; extern Boolean _DtCompileMessagePiece( Widget w, ActionRequest *request, char * relPathHost, char * relPathDir, parsedMsg * piece, Boolean initialize, unsigned long processingMask, Boolean ** paramUsed, int * promptDataIndex ) ; extern ActionRequest * _DtCloneRequest ( ActionRequest * request) ; extern void _DtFreeRequest( ActionRequest *request) ; /******** End Public Function Declarations ********/ /******** Static Function Declarations ********/ static Boolean ResolveTtRequestMessagePieces( Widget w, ActionRequest *request, char * relPathHost, char * relPathDir ) ; static void InitiateTtRequest( Widget w, ActionRequest *request ) ; static Tt_callback_action TtRequestCallbackHandler( Tt_message message, Tt_pattern pattern ) ; static char * WrapMessageLines( char * errorMsg ) ; static void ReportToolTalkError( Widget w, String actionName, String errorMsg) ; static Tt_callback_action _DtActReceiveExecId( Tt_message msg, Tt_pattern pat ); static Tt_callback_action _DtActReceiveDoneMsg( Tt_message msg, Tt_pattern pat ); static Tt_message _DtTtContractIgnoreMsgCB( Tt_message msg, void *clientdata, Tt_message contract); /******** End Static Function Declarations ********/ /* Pointers to localizable strings */ /****************************************************************************** RWV: These Strings are static to the Action.c File. Defining these here creates a new strings static to this file which are never initialized. References to these uninitialized strings by the GETMESSAGE11 macro will cause a core dump. For the short term, to keep things running -- I've marked those strings actually used in this file as external both here and in Action.c. This violates naming conventions and the desire to keep global symbols to a minimum. We should come up with a permanent solution for this problem later. static String PromptDialogTitle; static String ErrorPostfix; static String PromptDialogLabel; static String ContinueMessage; static String HostErrorMsg; static String HostErrorMsg2; static String NoActionMsg; static String MapErrorMsg; static String ArgumentErrorMsg; static String NoActionMsg2; static String InvalidFileMsg; static String MultiHostErrorMsg; static String IcccmReqErrorMsg; static String NoToolTalkConnMsg; **************************************************************************/ extern String ToolTalkErrorMsg; extern String ToolTalkErrorMsg2; extern String TtFileArgMapErr; /***************************************************************************** * * Routines to field message patterns from dtexec * *****************************************************************************/ static Tt_callback_action _DtActReceiveExecId( Tt_message msg, Tt_pattern pat ) { int invId; int childId; char *procId; _DtActChildRecT *childp; switch(tt_message_arg_ival(msg,0,&invId)) { case TT_OK: /* got invocation Id (arg 0 value ) */ break; case TT_ERR_NOMP: /* tt_session_not_running */ case TT_ERR_NUM: /* integer value out of range */ case TT_ERR_POINTER: /* pointer does not point to correct object */ return TT_CALLBACK_CONTINUE; break; default: return TT_CALLBACK_CONTINUE; break; } switch(tt_message_arg_ival(msg,1,&childId) ) /* child Id (arg 1 value ) */ { case TT_OK: /* got child Id (arg 1 value ) */ break; case TT_ERR_NOMP: /* tt_session_not_running */ case TT_ERR_NUM: /* integer value out of range */ case TT_ERR_POINTER: /* pointer does not point to correct object */ return TT_CALLBACK_CONTINUE; break; default: return TT_CALLBACK_CONTINUE; break; } switch ( tt_ptr_error(procId = tt_message_arg_val(msg,2)) ) { case TT_OK: /* got dtexec's proc Id (arg 2 value ) */ break; case TT_ERR_NOMP: /* tt_session_not_running */ case TT_ERR_NUM: /* integer value out of range */ case TT_ERR_POINTER: /* pointer does not point to correct object */ return TT_CALLBACK_CONTINUE; break; default: return TT_CALLBACK_CONTINUE; break; } /* * Save the proc Id for dtexec returned in this message */ childp = _DtActFindChildRec( invId, childId ); myassert(childp); /* we should be able to find the child record */ if ( childp ) { /* * This should be a command action */ myassert( IS_CMD(childp->mask) ); childp->u.cmd.TTProcId = XtNewString( procId ); /* * Note that the child HAS identified itself. */ if (childp->childState == _DtActCHILD_ALIVE_UNKNOWN) childp->childState = _DtActCHILD_ALIVE; } tt_message_reply(msg); tttk_message_destroy(msg); tt_free(procId); return TT_CALLBACK_PROCESSED; } static Tt_callback_action _DtActReceiveDoneMsg( Tt_message msg, Tt_pattern pat ) { int invId; int childId; _DtActChildRecT *childp; DtActionArg *retArgv; /* returnable arguments */ int retArgc; /* returnable argument count */ int doneCode; /* returnable _DtActCHILD_ code */ switch(tt_message_arg_ival(msg,0,&invId)) { case TT_OK: /* got invocation Id (arg 0 value ) */ break; case TT_ERR_NOMP: /* tt_session_not_running */ case TT_ERR_NUM: /* integer value out of range */ case TT_ERR_POINTER: /* pointer does not point to correct object */ return TT_CALLBACK_CONTINUE; break; default: return TT_CALLBACK_CONTINUE; break; } switch(tt_message_arg_ival(msg,1,&childId) ) /* child Id (arg 1 value ) */ { case TT_OK: /* got child Id (arg 1 value ) */ break; case TT_ERR_NOMP: /* tt_session_not_running */ case TT_ERR_NUM: /* integer value out of range */ case TT_ERR_POINTER: /* pointer does not point to correct object */ return TT_CALLBACK_CONTINUE; break; default: return TT_CALLBACK_CONTINUE; break; } switch(tt_message_arg_ival(msg,2,&doneCode) ) /* done code (arg 2 value ) */ { case TT_OK: /* got done code (arg 2 value ) */ break; case TT_ERR_NOMP: /* tt_session_not_running */ case TT_ERR_NUM: /* integer value out of range */ case TT_ERR_POINTER: /* pointer does not point to correct object */ return TT_CALLBACK_CONTINUE; break; default: return TT_CALLBACK_CONTINUE; break; } /* * In some cases, dtexec may send a _DtActDtexecDone(Request) but * be under pressure to go down ASAP and not wait around for a Reply. */ tt_message_reply(msg); tttk_message_destroy(msg); childp = _DtActFindChildRec( invId, childId ); myassert(childp); /* we should be able to find the child record */ /* * Init return args incase there is not a childp. */ retArgv = NULL; retArgc = 0; if (childp) { childp->childState = doneCode; /* usually _DtActCHILD_DONE */ retArgc = _DtActGetCmdReturnArgs(invId,childp,&retArgv); } _DtActExecutionLeafNodeCleanup(invId, retArgv, retArgc, True); return TT_CALLBACK_PROCESSED; } /****************************************************************************** ****************************************************************************** * * Routine to query DtActionQuit() behavior if done now. * * DtActionStatus * DtActionQuitType( * DtActionInvocationID id, * int silent) * * This function has been obsoleted. See revision * number 1.6 for the implementation. * ****************************************************************************** *****************************************************************************/ /****************************************************************************** ****************************************************************************** * * Routines to quit actions. * * static Tt_callback_action * _DtActQuitCB( * Tt_message message, * Tt_pattern pattern) * * This function has been obsoleted. See revision * number 1.6 for the implementation. * ****************************************************************************** *****************************************************************************/ /****************************************************************************** * * Routine used to Quit a specific action. Note * that status is returned via the childState * flag. * * static void * _DtActionQuitChild( * DtActionInvocationID id, * _DtActChildRecT *childRecP, * int silent, * int force, * XtAppContext context, * int ms_timeout) * * This function has been obsoleted. See revision * number 1.6 for the implementation. * ******************************************************************************/ /****************************************************************************** * * Public API * * Routine used to Quit all actions associated with * a DtActionInvocationID. CMD and TT actions will * be quit-able using the same code. * * DtActionStatus * DtActionQuit( * DtActionInvocationID id, * unsigned long ms_timeout, * int silent) * * This function has been obsoleted. See revision * number 1.6 for the implementation. * ******************************************************************************/ /****************************************************************************** ****************************************************************************** * * Error Dialog Code * ****************************************************************************** *****************************************************************************/ static void ReportToolTalkError( Widget w, String actionName, String errorMsg ) { XmString msg; char * buf; if (errorMsg) { buf = XtMalloc(strlen(ToolTalkErrorMsg2) + strlen(errorMsg) + 10); sprintf(buf, ToolTalkErrorMsg2, errorMsg); } else buf = XtNewString(ToolTalkErrorMsg); msg = XmStringCreateLocalized(buf); _DtCreateErrorDialog(w, actionName, msg); XmStringFree(msg); XtFree(buf); } /****************************************************************************** ****************************************************************************** * * ToolTalk Specific Functions * ****************************************************************************** *****************************************************************************/ /****************************************************************************** * * This is the entry point into the ToolTalk message world. All of the code * before this has been written to handle any of the different transport * types. The code from this point on will know specifically how to * process a ToolTalk message. We will start by taking each * of the pieces of information making up the ToolTalk message, and * resolving any of the keywords, by replacing them with the appropriate * information. If this fails (which it only should do if we try to map * a file to a host which cannot be accessed), then we will terminate the * request, posting an error dialog for the user. Unlike a Command Invoker * request, where we may try several times to handle it (once for each * of the specified execution hosts), a ToolTalk message is handled only * once. */ void _DtProcessTtRequest( Widget w, ActionRequest *request, char * relPathHost, char * relPathDir) { ActionPtr action = request->clonedAction; if (ResolveTtRequestMessagePieces(w, request, relPathHost, relPathDir)) { /* * Issue the request; the success/failure notification comes * asynchronously; that's when everything gets cleaned up. */ InitiateTtRequest( w, request ); } else { /* Display error dialog */ /* * fdt: there really needs to be some policy defining where an error * message will be returned. Also, is this really the right message * to display here? Was the failure really caused by a file mapping * problem? In fact, is this a case we even can get to???? [Yes] */ /* fdt: this string must be localized */ ReportToolTalkError(w, request->clonedAction->label, TtFileArgMapErr); } } /****************************************************************************** * * This function takes all of the pieces making up a ToolTalk message, * and resolves any references to keywords, using both the passed-in * arguments, and any information collected from the prompt dialog. */ static Boolean ResolveTtRequestMessagePieces( Widget w, ActionRequest *request, char * relPathHost, char * relPathDir ) { ActionPtr action = request->clonedAction; tt_msgAttr * tt = &(action->u.tt_msg); int i; Boolean success; Boolean * paramUsed = NULL; int promptDataIndex = 0; if (_DtCompileMessagePiece(w, request, relPathHost, relPathDir, &(tt->tt_op), True, 0, ¶mUsed, &promptDataIndex) && _DtCompileMessagePiece(w, request, relPathHost, relPathDir, &(tt->tt_file), False, 0, ¶mUsed, &promptDataIndex)) { for (i = 0, success = True; (i < tt->value_count) && success; i++) { success = _DtCompileMessagePiece(w, request, relPathHost, relPathDir, &(tt->tt_argn_value[i]), False, _DTAct_TT_ARG, ¶mUsed, &promptDataIndex); } for (i = 0; (i < tt->vtype_count) && success; i++) { success = _DtCompileMessagePiece(w, request, relPathHost, relPathDir, &(tt->tt_argn_vtype[i]), False, _DTAct_TT_VTYPE, ¶mUsed, &promptDataIndex); } if (success) { /* We must have at least the op strings */ if ((tt->tt_op.compiledMessage) && (strlen(tt->tt_op.compiledMessage) > 0)) { XtFree(paramUsed); return(True); } } } /* ERROR: Free up any intermediate work we've done here */ XtFree(tt->tt_op.compiledMessage); XtFree(tt->tt_file.compiledMessage); tt->tt_op.compiledMessage = NULL; tt->tt_file.compiledMessage = NULL; for (i = 0; i < tt->value_count; i++) { XtFree(tt->tt_argn_value[i].compiledMessage); tt->tt_argn_value[i].compiledMessage = NULL; } for (i = 0; i < tt->vtype_count; i++) { XtFree(tt->tt_argn_vtype[i].compiledMessage); tt->tt_argn_vtype[i].compiledMessage = NULL; } XtFree(paramUsed); return(False); } /****************************************************************************** ****************************************************************************** * * Routines to handle Tooltalk responses. * ****************************************************************************** *****************************************************************************/ /****************************************************************************** * * Translate a ttmedia_load() callback into a regular * looking DtActionCallbackProc() for the user. */ Tt_message Ttmedia_to_Dt_StatusUpdateCB( Tt_message message, void *clientdata, Tttk_op op, unsigned char *contents, int len, char *file) { CallbackData *data; char * errorMsg; Tt_state state; Tt_status status; Boolean wrapMessageLines = False; _DtActChildRecT *childRec; _DtActInvRecT *invRec; DtActionInvocationID id; DtActionArg *newArgp = NULL; /* hanger for returned data if any */ int newArgc = 0; unsigned long evalStatus; DtActionStatus userStatus; int i, j, upIdx; char *upVType; status = (Tt_status) tt_message_status(message); state = (Tt_state) tt_message_state(message); if (state == TT_STARTED) { /* * Handler is just getting started. Eat it. */ tttk_message_destroy( message ); return( (Tt_message) NULL ); /* like TT_CALLBACK_PROCESSED */ } if ( (state != TT_HANDLED) && (state != TT_FAILED) && (state != TT_RETURNED) && (state != TT_SENT) ) { /* * This address space is probably the handler. Pass * on it, so that the handler gets a chance to handle it. */ return( (Tt_message) message ); /* like TT_CALLBACK_CONTINUE */ } /* * Process a TT_FAILED, TT_HANDLED, TT_RETURNED or TT_SEND related to our * original request. */ /* * Use ttmedia's client data capability since it can associate our * original client data with any reply *or* new request associated * with the original request. * * data = (CallbackData *) tt_message_user( message, 0 ); not good enough */ data = (CallbackData *) clientdata; id = data->actInvId; invRec = _DtActFindInvRec( id ); myassert(invRec); childRec = _DtActFindChildRec( id, data->childId ); if (state == TT_FAILED) { if (status != TT_DESKTOP_ECANCELED) { /* * Determine whether ToolTalk or the receiver failed the message */ if (status < TT_ERR_LAST) { errorMsg = tt_status_message(status); wrapMessageLines = True; } else { errorMsg = tt_message_status_string(message); } if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg) { if (wrapMessageLines) errorMsg = WrapMessageLines(errorMsg); ReportToolTalkError(data->associatedWidget, data->actionLabel, errorMsg); if (wrapMessageLines) tt_free(errorMsg); } else { ReportToolTalkError(data->associatedWidget, data->actionLabel, NULL); } } } else /* if (state == TT_HANDLED) or other things (?) */ { /* * We have a possible update - see if user wants arg back */ newArgc = invRec->ac; newArgp = _DtActMallocEmptyArgArray( newArgc ); /* * Our only return argument is TT_ARG0_VALUE or TT_FILE. * * If a docname went out, it went out as TT_IN, so we * won't need to push it back up. * * We will lean on the fact that we carefully selected * what action definitions were run through the ttmedia * machinery, and that we can assume from the ttmedia * reply where the arguments go back. */ i = 0; /* arg 0 */ if (file == NULL) { /* * DtACTION_BUFFER - possible update. */ /* * Calculate index into original argument list from the * user. */ upIdx = childRec->argMap[i].argIdx; if ( upIdx != -1 ) { if ( (IS_BUFFER_OBJ(invRec->info[upIdx].mask) && IS_WRITE_OBJ(invRec->info[upIdx].mask)) ) { /* * Need to push this object up. */ /* * Determine VTYPE */ upVType = tt_message_arg_type( message, i ); newArgp[upIdx].argClass = DtACTION_BUFFER; newArgp[upIdx].u.buffer.size = len; newArgp[upIdx].u.buffer.type = XtNewString(upVType); newArgp[upIdx].u.buffer.writable = 1; newArgp[upIdx].u.buffer.bp = (void *) XtMalloc(len); memcpy( newArgp[upIdx].u.buffer.bp, contents, len ); tt_free(upVType); } } /* * Done with the data. */ tt_free((char *) contents); } else { /* * DtACTION_FILE */ /* * Calculate index into original argument list from the * user. Notice that we're shipping data because of TT_FILE * and not TT_ARG0_FILE. To do this, argMap is arranged * as: * argMap[ ARG0, ... , ARGn, TT_FILE].argIdx * * The array-index for TT_FILE is *one beyond* all the normal * TT_ARGn_VALUE array-index values. The .argN for TT_FILE * will be -1 fyi. * * As the spec says, the action service "may choose to" * return objects, which is why it is o.k. to return * their "TT_FILE %Arg_n%" value for less than obvious * reasons. */ upIdx = childRec->argMap[i+1].argIdx; if ( upIdx != -1 ) { newArgp[upIdx].argClass = DtACTION_FILE; newArgp[upIdx].u.file.name = XtNewString( file ); } tt_free(file); } } /* * If a final response to our original message, we're done. */ if ((message == childRec->u.tt.reqMessage) && (state == TT_HANDLED)) childRec->childState = _DtActCHILD_DONE; else if ((message == childRec->u.tt.reqMessage) && (state == TT_FAILED)) { if (status == TT_DESKTOP_ECANCELED) childRec->childState = _DtActCHILD_CANCELED; else childRec->childState = _DtActCHILD_FAILED; } /* * If this was a deposit, reply that we've handled it. */ if (op == TTME_DEPOSIT) { tt_message_reply( message ); /* * Cleanup here since this message is not part of our long * term storage. */ tttk_message_destroy( message ); } /* * Cleanup message - handled by _DtActExecutionLeafNodeCleanup() * * tttk_message_destroy( message ); */ _DtActExecutionLeafNodeCleanup( id, newArgp, newArgc, 1 ); return( (Tt_message) NULL ); /* message consumed */ } /****************************************************************************** * * Guess at the REP_TYPE for a given argument. * String is just a variation of a buffer, so * DtACT_TT_REP_BUFFER is returned for buffers and * strings. */ static int _DtAct_tt_message_arg_reptype( Tt_message message, int arg ) { int testVal; Tt_status status; status = tt_message_arg_ival( message, arg, &testVal ); if (status == TT_ERR_NUM) return DtACT_TT_REP_BUFFER; return DtACT_TT_REP_INT; } /****************************************************************************** * * Translate a free-form request callback into a * regular looking DtActionCallbackProc() for the user. */ static Tt_callback_action TtRequestCallbackHandler( Tt_message message, Tt_pattern pattern ) { CallbackData *data; char * errorMsg; Tt_state state; Tt_status status; Boolean wrapMessageLines = False; _DtActChildRecT *childRec; _DtActInvRecT *invRec; DtActionInvocationID id; unsigned long evalStatus; DtActionStatus userStatus; DtActionArg *newArgp = NULL; /* hanger for returned data if any */ int newArgc = 0, totalArgs, i, j, upIdx; int upArgClass; int argRepType; char *upttbuf; int upttbuflen, ivalue; char *upVType = NULL, *upVType2 = NULL; status = (Tt_status) tt_message_status(message); state = (Tt_state) tt_message_state(message); if (state == TT_STARTED) { /* * Handler is just getting started. Eat it. */ return TT_CALLBACK_PROCESSED; } if ((state != TT_HANDLED) && (state != TT_FAILED) && (state != TT_RETURNED)) { /* * This address space is probably the handler. Pass * on it, so that the handler gets a chance to handle it. */ return TT_CALLBACK_CONTINUE; } /* * Process a TT_FAILED, TT_HANDLED or TT_RETURNED related to our * original request. */ if ( !(data = (CallbackData *) tt_message_user( message, 0 )) ) { myassert(data); /* data should always be non-null */ return( (Tt_callback_action) TT_CALLBACK_PROCESSED ); } id = data->actInvId; invRec = _DtActFindInvRec( id ); myassert(invRec); childRec = _DtActFindChildRec( id, data->childId ); myassert(childRec); if (state == TT_FAILED) { if (status != TT_DESKTOP_ECANCELED) { /* * Determine whether ToolTalk or the receiver failed the message */ if (status < TT_ERR_LAST) { errorMsg = tt_status_message(status); wrapMessageLines = True; } else { errorMsg = tt_message_status_string(message); } if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg) { if (wrapMessageLines) errorMsg = WrapMessageLines(errorMsg); ReportToolTalkError(data->associatedWidget, data->actionLabel, errorMsg); tt_free(errorMsg); } else { ReportToolTalkError(data->associatedWidget, data->actionLabel, NULL); } } } else if (state == TT_HANDLED) { /* * We have an update */ newArgc = invRec->ac; newArgp = _DtActMallocEmptyArgArray( newArgc ); totalArgs = tt_message_args_count(message); /* * Look through all the arguments returned in the request. */ for ( i = 0; i < totalArgs; i++ ) { /* * Look through the child's arg list to see which ones need to * be returned. */ upIdx = childRec->argMap[i].argIdx; if ( upIdx != -1 ) { if ( (IS_BUFFER_OBJ(invRec->info[upIdx].mask) && IS_WRITE_OBJ(invRec->info[upIdx].mask)) || IS_FILE_OBJ(invRec->info[upIdx].mask) ) { /* * Need to push this object up. */ /* * Determine argClass */ if ( IS_BUFFER_OBJ(invRec->info[upIdx].mask) ) upArgClass = DtACTION_BUFFER; else if ( IS_FILE_OBJ(invRec->info[upIdx].mask) ) upArgClass = DtACTION_FILE; else if ( IS_STRING_OBJ( invRec->info[upIdx].mask) ) upArgClass = DtACTION_STRING; else upArgClass = DtACTION_NULLARG; /* error situation */ /* * Determine VTYPE */ upVType = tt_message_arg_type( message, i ); /* * Determine REP_TYPE */ argRepType = _DtAct_tt_message_arg_reptype(message, i); /* * If we think TT_REP_BUFFER so far, but don't have * an argClass of DtACTION_BUFFER, then we really have * a DtACT_TT_REP_STRING. */ if ( (argRepType == DtACT_TT_REP_BUFFER) && !(upArgClass == DtACTION_BUFFER) ) argRepType = DtACT_TT_REP_STRING; /* * Do appropriate unpacking. */ switch (argRepType) { case DtACT_TT_REP_BUFFER: case DtACT_TT_REP_STRING: /* * By convention, if we're here, the buffer * is writable. Also, STRINGs can be * fetched using bval. */ status = tt_message_arg_bval( message, i, (unsigned char **) &upttbuf, &upttbuflen ); if ( status != TT_OK ) { /* * Give up - something bad happened. */ upArgClass = DtACTION_NULLARG; } break; case DtACT_TT_REP_INT: /* * Unpack an integer. */ status = tt_message_arg_ival( message, i, &ivalue ); if ( status != TT_OK ) { upArgClass = DtACTION_NULLARG; } else { /* * Fake up enough information that we * could return the argument in either * a DtACTION_FILE or DtACTION_BUFFER * argClass argument. */ upttbuflen = 64; upttbuf = XtMalloc( upttbuflen ); sprintf( upttbuf, "%u", ivalue ); } break; case DtACT_TT_REP_UNDEFINED: /* * Cannot figure out how to unpack arg. */ upArgClass = DtACTION_NULLARG; break; } switch (upArgClass) { case DtACTION_NULLARG: /* * We failed above, so give up. */ break; case DtACTION_BUFFER: /* * Convert from tt_free()-able to * XtFree()-able. */ newArgp[upIdx].argClass = upArgClass; newArgp[upIdx].u.buffer.size = upttbuflen; newArgp[upIdx].u.buffer.type = XtNewString( upVType); newArgp[upIdx].u.buffer.writable = 1; newArgp[upIdx].u.buffer.bp = (void *) XtMalloc( upttbuflen ); memcpy( newArgp[upIdx].u.buffer.bp, upttbuf, upttbuflen ); if (argRepType == DtACT_TT_REP_INT) { /* * upttbuf was XtMalloc'ed above */ XtFree( upttbuf ); upttbuf = 0; } break; case DtACTION_FILE: /* * Convert from tt_free()-able to * XtFree()-able. */ newArgp[upIdx].argClass = upArgClass; newArgp[upIdx].u.file.name = XtNewString( upttbuf ); if (argRepType == DtACT_TT_REP_INT) { /* * upttbuf was XtMalloc'ed above */ XtFree( upttbuf ); upttbuf = 0; } break; } } tt_free( upVType ); if (upttbuf != 0) { tt_free( upttbuf ); } } } } /* * If a final response to our original message, we're done. */ if ((message == childRec->u.tt.reqMessage) && (state == TT_HANDLED)) childRec->childState = _DtActCHILD_DONE; else if ((message == childRec->u.tt.reqMessage) && (state == TT_FAILED)) { if (status == TT_DESKTOP_ECANCELED) childRec->childState = _DtActCHILD_CANCELED; else childRec->childState = _DtActCHILD_FAILED; } /* * Message is destroyed by _DtActExecutionLeafNodeCleanup() */ _DtActExecutionLeafNodeCleanup( id, newArgp, newArgc, 1 ); return( (Tt_callback_action) TT_CALLBACK_PROCESSED ); /* message consumed */ } /****************************************************************************** ****************************************************************************** * * Process a ToolTalk request. * ****************************************************************************** *****************************************************************************/ /****************************************************************************** * * _DtTtContractIgnoreMsgCB * * Contract callback to ignore all messages received. This is consume * status messages sent back by some message handlers. If these messages * aren't consumed by the library, then a tooltalk client can become * confused and also spurious error messages are written to syslog or the * system console. ******************************************************************************/ static Tt_message _DtTtContractIgnoreMsgCB( Tt_message msg, void *clientdata, Tt_message contract) { /* * Artificially consume unwanted messages. */ tttk_message_abandon(msg); return NULL; } static void InitiateTtRequest( Widget w, ActionRequest *request ) { CallbackData *data = NULL; ActionPtr action = request->clonedAction; tt_msgAttr * tt = &(action->u.tt_msg); int i; char * p; Tt_message message; int destroy_message = 0; int mode; int repType; int intValue; char * value; char * vtype; char * errorMsg; Tt_status status; unsigned char * contents; int length; char * file; char * docname; Tt_pattern *subcon_patterns; _DtActInvRecT *actInvRecP; _DtActChildRecT *actChildRecP; unsigned long evalStatus; DtActionStatus userStatus; int j; char *bufFilename = NULL; int ttmedia_test; /* * Find associated long term records for this request */ actInvRecP = _DtActFindInvRec( request->invocId ); myassert(actInvRecP); actChildRecP = _DtActFindChildRec( request->invocId, request->childId ); myassert(actChildRecP); actChildRecP->childState = _DtActCHILD_UNKNOWN; /* really don't know */ /* * Future: * * Consider doing a check of argClass -vs- TT_ARGn_REP_TYPE, * and issue errors where appropriate. * * argClass=DtACTION_FILE * o REP_UNDEFINED - o.k. - will guess convert to REP_STRING * o REP_BUFFER - error - no sense, use DtACTION_BUFFER * o REP_STRING - o.k. - normal case * o REP_INTEGER - o.k. - normal case * * argClass=DtACTION_BUFFER * o REP_UNDEFINED - o.k. - will guess convert to REP_BUFFER * o REP_BUFFER - o.k. - normal case * o REP_STRING - ? - will try to handle * o REP_INTEGER - error - use DtACTION_FILE */ /* * Test request for ttmedia_load() suitability. * * To be ttmedia_load() suitable, action defs must look like: * * Action TtmediaBufferStyle * { * ARG_CLASS BUFFER * ARG_COUNT <3 * * TYPE TT_MSG * TT_OPERATION * TT_CLASS TT_REQUEST * TT_SCOPE TT_SESSION * TT_FILE --- must be null or unset --- * * TT_ARG0_MODE * TT_ARG0_VTYPE | %Arg_n% * TT_ARG0_REP_TYPE TT_REP_BUFFER or TT_REP_UNDEFINED * TT_ARG0_VALUE %Arg_n% * * optional TT_ARG1 for docname * * TT_ARG1_MODE TT_IN * TT_ARG1_VTYPE title * TT_ARG1_REP_TYPE TT_REP_STRING * TT_ARG1_VALUE | %Arg_n% * } * * * Action TtmediaFileStyle * { * ARG_CLASS FILE * ARG_COUNT <3 * * TYPE TT_MSG * TT_OPERATION * TT_CLASS TT_REQUEST * TT_SCOPE TT_SESSION * TT_FILE | %(File)Arg_n% * * TT_ARG0_MODE * TT_ARG0_VTYPE | %Arg_n% * TT_ARG0_REP_TYPE TT_REP_STRING * TT_ARG0_VALUE --- must be null or unset --- * * optional TT_ARG1 for docname * * TT_ARG1_MODE TT_IN * TT_ARG1_VTYPE title * TT_ARG1_REP_TYPE TT_REP_STRING * TT_ARG1_VALUE | %Arg_n% * } */ ttmedia_test = 0; /* default to free-form */ if ( (tt->tt_class == TT_REQUEST) && (tt->tt_scope == TT_SESSION) && ((tt->mode_count == 1) || (tt->mode_count == 2)) && (tt->tt_argn_rep_type[0] != DtACT_TT_REP_INT) ) { /* * 1- and 2-arg session-scoped requests not using ivals * can be sent via ttmedia_load()... */ ttmedia_test = 1; /* * ...as long as the second argument is the title. So: * Allow for an optional "docname" in TT_ARG1. If there is a * second argument, it must follow some specific rules or we'll * disqualify this request. */ if (tt->mode_count == 2) { if ((tt->tt_argn_mode[1] != TT_IN) || (tt->tt_argn_rep_type[1] != DtACT_TT_REP_STRING) || (strcmp( tt->tt_argn_vtype[1].compiledMessage, "title" )) ) { ttmedia_test = 0; } } } if (ttmedia_test) { /********************************************************************** * * TT_MEDIA_LOAD() * * Special case media-load requests *and* any other requests that * follow the "ttmedia style". * * If rep_type is INT, then ttmedia_load() cannot be used, and * a rep_type of UNDEFINED is too chancy to guess on. * * Using ttmedia_load() sets up additional machinery. * */ Tttk_op op = tttk_string_op(tt->tt_op.compiledMessage); /* * Some media ops are overloaded, and the actual media op that * should be used depends on the TT_ARGn_MODE of the argument. */ switch (tt->tt_argn_mode[0]) { case TT_IN: op = TTME_DISPLAY; break; case TT_OUT: op = TTME_COMPOSE; break; case TT_INOUT: op = TTME_EDIT; break; } contents = (unsigned char *) NULL; length = (int) 0; /* * Determine what the data looks like: file or a buffer? */ if ( tt->tt_argn_rep_type[0] == DtACT_TT_REP_BUFFER || tt->tt_argn_rep_type[0] == DtACT_TT_REP_UNDEFINED) { /* * Passing a buffer of data. */ /* * See if we should pluck the buffer out of a tmp file. */ length = -1; /* -1 means not a buffer */ if ( actChildRecP->argMap[0].argIdx != -1 ) { if ( IS_BUFFER_OBJ(actInvRecP-> info[actChildRecP->argMap[0].argIdx].mask) ) { length = actInvRecP-> info[actChildRecP->argMap[0].argIdx].size; bufFilename = actInvRecP-> info[actChildRecP->argMap[0].argIdx].name; } } } if (bufFilename) { contents = (unsigned char *) _DtActReadTmpFileToBuffer(bufFilename, &length); /* length plucked from above */ } else { contents = (unsigned char *) tt->tt_argn_value[0].compiledMessage; length = tt->tt_argn_value[0].msgLen; } file = (char *) tt->tt_file.compiledMessage; if (tt->mode_count == 2) docname = (char *) tt->tt_argn_value[1].compiledMessage; else docname = (char *) NULL; /* * Fill out a callback structure. */ data = (CallbackData *)XtMalloc(sizeof(CallbackData)); data->actionLabel = XtNewString(request->clonedAction->label); data->associatedWidget = w; data->actionPtr = action; data->requestPtr = _DtCloneRequest(request); data->actInvId = actInvRecP->id; data->childId = actChildRecP->childId; /* * Let tttk build the message and setup additional machinery. */ message = ttmedia_load( (Tt_message) 0, /* Am top level req */ Ttmedia_to_Dt_StatusUpdateCB, (void *) data, op, tt->tt_argn_vtype[0].compiledMessage, contents, length, file, docname, FALSE ); status = tt_ptr_error(message); if (! tt_is_err(status)) { /* * (Re)set the op. This allows arbitrary Media-like * TT_MSG actions to use the Media action machinery. */ tt_message_op_set(message, tt->tt_op.compiledMessage); /* * Attach client data to message using slot 0. */ tt_message_user_set(message, 0, (void *) data); actChildRecP->childState = _DtActCHILD_PENDING_START; /* starting */ /* * I'm assuming w is non-NULL, hence the lack of a callback * and callback data. */ subcon_patterns = ttdt_subcontract_manage(message, (Ttdt_contract_cb) _DtTtContractIgnoreMsgCB, w, (void *) NULL); status = tt_message_send(message); destroy_message = 1; } /* * See if we failed */ if (status != TT_OK) { /* * The child error'ed out. */ actChildRecP->childState = _DtActCHILD_FAILED; /* * Report the fact that the request failed */ errorMsg = tt_status_message(status); if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg) { errorMsg = WrapMessageLines(errorMsg); ReportToolTalkError(w, request->clonedAction->label, errorMsg); tt_free(errorMsg); } else ReportToolTalkError(w, request->clonedAction->label, NULL); /* * Free information since no callbacks will be triggered: * - Callback Client Data * - The message */ XtFree(data->actionLabel); _DtFreeRequest(data->requestPtr); XtFree((char *)data); if (destroy_message) tttk_message_destroy(message); } else { /* * The child is alive and well (so far as we can tell). */ actChildRecP->childState = _DtActCHILD_ALIVE; /* * Stash data away for long term usage. */ actChildRecP->u.tt.TTProcId = tt_message_sender( message ); actChildRecP->u.tt.reqMessage = message; actChildRecP->u.tt.isTtMedia = 1; actChildRecP->u.tt.TtMediaOp = op; actChildRecP->u.tt.subConPats = subcon_patterns; } /* * Throw away transition buffer if there was one. */ if (bufFilename) XtFree((char *) contents); } else { /********************************************************************** * * FREE FORM TT_MSG * * All other Requests and Notices go out "free form". Do best case. */ message = tttk_message_create( 0, tt->tt_class, tt->tt_scope, 0, tt->tt_op.compiledMessage, TtRequestCallbackHandler ); tt_message_file_set(message, tt->tt_file.compiledMessage); /* * Process each of the message arguments * * note: As of CDE 1.0, all the tt->xxx_count variables are * set the same. I doubt they would ever diverge, and * much of this code would break if they did. */ for (i = 0; i < tt->mode_count; i++) { /* * Pluck some data out of the argMap to help us make * some decisions here. */ length = -1; /* -1 means not a buffer */ bufFilename = (char *) NULL; /* possible buffer tmp file */ if ( actChildRecP->argMap[i].argIdx != -1 ) { if ( IS_BUFFER_OBJ(actInvRecP-> info[actChildRecP->argMap[i].argIdx].mask) ) { length = actInvRecP-> info[actChildRecP->argMap[i].argIdx].size; bufFilename = actInvRecP-> info[actChildRecP->argMap[i].argIdx].name; } } /* * Determine MODE */ mode = tt->tt_argn_mode[i]; /* * Determine REP TYPE */ if ((i < tt->rep_type_count) && (tt->tt_argn_rep_type[i] != DtACT_TT_REP_UNDEFINED)) { /* The representation type was supplied; use it */ repType = tt->tt_argn_rep_type[i]; } else { /* * No representation type given. If the argument was provided * and is a DtACTION_BUFFER, then use DtACT_TT_REP_BUFFER. * In all other cases, use DtACT_TT_REP_STRING. */ if ( length != -1 ) repType = DtACT_TT_REP_BUFFER; else repType = DtACT_TT_REP_STRING; } /* * Determine VALUE and VTYPE */ if (i < tt->value_count) { /* * Use the specified vtype */ vtype = tt->tt_argn_vtype[i].compiledMessage; /* * Determine where to get the value from */ if ( length != -1 ) { /* * Have a buffer - see if it's value can be fetched * from a tmp file. If so, will need to free later! */ if (bufFilename) { value = _DtActReadTmpFileToBuffer(bufFilename, &length); } else { value = tt->tt_argn_value[i].compiledMessage; } } else { value = tt->tt_argn_value[i].compiledMessage; } } else { /* No value specified; use 'NULL' */ value = NULL; /* * Use the specified vtype setting, if supplied; else * default it */ if (i < tt->vtype_count) vtype = tt->tt_argn_vtype[i].compiledMessage; else { /* * Time to guess what the vtype should be. This * is very funky. */ if (repType == DtACT_TT_REP_INT) vtype = "integer"; else if (repType == DtACT_TT_REP_STRING) vtype = "string"; else if (repType == DtACT_TT_REP_BUFFER) vtype = "buffer"; } } /* * Based on REP_TYPE, add the argument to the message. In * effect, rep_type is an instruction on how to package * the argument in the message. */ if (repType == DtACT_TT_REP_INT) { if (value) intValue = strtol(value, &p, 0); else intValue = 0; tt_message_iarg_add(message, mode, vtype, intValue); } else if (repType == DtACT_TT_REP_STRING) { tt_message_arg_add(message, mode, vtype, value); } else if (repType == DtACT_TT_REP_BUFFER) { tt_message_barg_add(message, mode, vtype, (unsigned char *) value, length); } if ( (length != -1) && (bufFilename) ) { /* * The value was from a buffer that we pulled from a tmp * file. Free the transition buffer since Tooltalk will have * made its own copy by now. */ XtFree(value); } } if (tt->tt_class == TT_REQUEST) { /* * For a request, we need to attach a callback, which will be * invoked when the reply is received. When the reply is * received, then we can free up the original message, and * determine whether we succeeded or failed. */ /* Fill out the callback structure */ data = (CallbackData *)XtMalloc(sizeof(CallbackData)); data->actionLabel = XtNewString(request->clonedAction->label); data->associatedWidget = w; data->actionPtr = action; data->requestPtr = _DtCloneRequest(request); data->actInvId = actInvRecP->id; data->childId = actChildRecP->childId; /* * Attach client data to message using slot 0. */ tt_message_user_set(message, 0, (void *) data); tt_message_callback_add(message, TtRequestCallbackHandler); } actChildRecP->childState = _DtActCHILD_PENDING_START; /* starting */ /* * I'm assuming w is non-NULL, hence the lack of a callback * and callback data. */ subcon_patterns = ttdt_subcontract_manage(message, (Ttdt_contract_cb) _DtTtContractIgnoreMsgCB, w, (void *) NULL); status = tt_message_send(message); /* See if we failed right off the bat */ if (status != TT_OK) { /* * The child error'ed out. * * By marking the child as _DtActCHILD_DONE*, see the comment * at the end of this routine that describes how DtActionInvoke() * will do a child evaluation and cleanup. */ actChildRecP->childState = _DtActCHILD_FAILED; /* * Report the fact that the request failed */ errorMsg = tt_status_message(status); if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg) { errorMsg = WrapMessageLines(errorMsg); ReportToolTalkError(w, request->clonedAction->label, errorMsg); tt_free(errorMsg); } else ReportToolTalkError(w, request->clonedAction->label, NULL); /* * Clean up now, cause there will not be a reply coming back */ if (tt->tt_class == TT_REQUEST) { _DtFreeRequest(data->requestPtr); XtFree(data->actionLabel); XtFree((char *)data); } tttk_message_destroy(message); } else { /* * The child is alive and well (so far as we can tell). */ actChildRecP->childState = _DtActCHILD_ALIVE; if (tt->tt_class == TT_NOTICE) { /* * The child is done. * * By marking the child as _DtActCHILD_DONE*, see the * comment at the end of this routine that describes how * DtActionInvoke() will do a child evaluation and cleanup. */ actChildRecP->childState = _DtActCHILD_DONE; /* * Need to clean up after a notify, since there is no * response. */ tttk_message_destroy(message); } else { /* * Stash data away for long term usage. */ actChildRecP->u.tt.TTProcId = tt_message_sender( message ); actChildRecP->u.tt.reqMessage = message; actChildRecP->u.tt.isTtMedia = 0; actChildRecP->u.tt.TtMediaOp = (Tttk_op) NULL; actChildRecP->u.tt.subConPats = subcon_patterns; } } } /* * _DtActExecutionLeafNodeCleanup( id, (DtActionArg *) NULL, 0, 1 ); * * This is an execution leaf node, but because SET_INV_COMPLETE() * has NOT been called yet, doing a _DtActExecutionLeafNodeCleanup() * is useless. As we bubble back up, let DtActionInvoke() flip * SET_INV_COMPLETE and then do a _DtActExecutionLeafNodeCleanup() * via a timer. */ } /****************************************************************************** * * This function checks to see if there is already a default ToolTalk * session active for this client. If there is one, then we will use * it for the ToolTalk message we are about to send. If there is not * a default ToolTalk session, then we will try to establish a connection * to a new session. If we succeed in connecting to ToolTalk, then we * will return 'True'; 'False' is returned if we fail. */ Tt_status _DtInitializeToolTalk(Widget w) { static int RegisteredPatterns = 0; char * procid; Tt_status status; int fd; Tt_pattern pat; /* pattern to register for exec* messages */ char *session_id; extern Widget _DtInitTtContextWidget; /* * See if the context widget is set. */ if ( w && !_DtInitTtContextWidget ) { _DtInitTtContextWidget = w; } /* The _Dt_context_widget should have been set in DtBigInitialize() */ myassert (_DtInitTtContextWidget != NULL); if ( (status = _DtSvcInitToolTalk(_DtInitTtContextWidget)) != TT_OK ) return status; _DtSvcProcessLock(); if ( !RegisteredPatterns ) { if ( !(session_id = tt_default_session())) { /* * Unable to get session id -- what now? */ myassert(0); _DtSvcProcessUnlock(); return(TT_ERR_NOMP); } /* * Register to receive interesting messages from dtexec */ pat = tt_pattern_create(); tt_pattern_category_set(pat,TT_HANDLE); tt_pattern_scope_add(pat, TT_SESSION); tt_pattern_op_add(pat,"_DtActDtexecID"); tt_pattern_callback_add( pat, (Tt_message_callback) _DtActReceiveExecId); tt_pattern_session_add(pat,session_id); tt_pattern_register(pat); /* * Create/register a new pattern for the done callback */ pat = tt_pattern_create(); tt_pattern_category_set(pat,TT_HANDLE); tt_pattern_scope_add(pat, TT_SESSION); tt_pattern_op_add(pat,"_DtActDtexecDone"); tt_pattern_callback_add(pat, (Tt_message_callback) _DtActReceiveDoneMsg); tt_pattern_session_add(pat,session_id); tt_pattern_register(pat); tt_free(session_id); RegisteredPatterns = 1; } _DtSvcProcessUnlock(); return (TT_OK); } /****************************************************************************** * * fdt: This function needs to be written! * * All of the error messages generated by the tooltalk layer itself (as * opposed to those messages generated by the handler of a message) are * returned as one long message string, with no line breaks. When one of * these messages is displayed in an error dialog, the dialog stretches * across the full width of the display, and even then, some of the message * string is not visible. To remedy this, we need to 'wrap' these lines, * so that they are of a reasonable length, and we also need to expand * 'tab' characters, since these messages also use them. * * tt_free()s the string passed in. * The returned character string must be tt_free()d by the caller. */ static char * WrapMessageLines( char * errorMsg ) { return errorMsg; }