2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $TOG: CmdMain.c /main/15 1998/04/20 12:46:37 mgreess $ */
25 * (c) Copyright 1997, The Open Group
27 /***************************************************************************
30 * Description: Command execution system
33 ** (c) Copyright 1993, 1994 Hewlett-Packard Company
34 ** (c) Copyright 1993, 1994 International Business Machines Corp.
35 ** (c) Copyright 1993, 1994 Sun Microsystems, Inc.
36 ** (c) Copyright 1993, 1994 Novell, Inc.
37 ***************************************************************************/
40 #include <Dt/CmdInv.h>
44 #include "/sys5/usr/include/sys/termio.h"
51 #ifdef _SUN_OS /* to get the define for NOFILE */
52 #include <sys/param.h>
54 #define X_INCLUDE_PWD_H
55 #define XOS_USE_XT_LOCKING
56 #include <X11/Xos_r.h>
58 #include <Dt/CommandM.h>
59 #include <Dt/EnvControlP.h>
60 #include <Dt/DtNlUtils.h>
61 #include <Dt/Utility.h>
63 #include <Dt/ActionDb.h>
64 #include <Dt/ActionUtilP.h>
65 #include "myassertP.h"
66 #include "DtSvcLock.h"
70 #include <SPC/spc-proto.h>
73 #define MAX_EXEC_ARGS 1000 /* Maximum number of arguments for */
76 * Dtexec return status:
78 #define COMMAND_CHECK_FAILURE 1
82 #define INETD_SECURITY_FILE "/var/adm/inetd.sec"
84 #define INETD_SECURITY_FILE "/usr/adm/inetd.sec"
88 #define Cmd_FreeAllocatedStringVector(sv) \
89 _DtCmdFreeStringVector(sv);\
93 * Global variables for the Command Invoker.
95 static char _cmdClientHost[MAXHOSTNAMELEN];
98 * Static variables for the Command Invoker.
100 static Cmd_RequestQueue *requestQueue = NULL;
103 * Static function declarations:.
106 static void QueueRequest (
107 SPC_Channel_Ptr channel,
113 unsigned long requestNum,
114 DtSvcMsgContext replyContext,
115 DtCmdInvExecuteProc success_proc,
117 DtCmdInvExecuteProc failure_proc,
119 static void ExecuteQueuedRequest (
120 unsigned long requestNum);
122 static void FreeRequest (Cmd_RequestQueue *pNode);
124 static void DtexecTerminator (
125 SPC_Channel_Ptr cmdChannel,
130 static void CheckCommandTerminator (
131 SPC_Channel_Ptr cmdChannel,
136 static int DtCmdGetWindowType(
137 unsigned long windowTypeMask);
138 static void _DtCmdInitializeErrorMessages(void);
141 * Command invocatin error messages.
143 static char *errorExec,
150 *errorRemoteSubprocess,
154 *errorRegisterHandshake,
155 *errorRegisterUsername,
162 /*******************************************************************************
165 * This is a wrapper around DtSPCSpawn (i.e. XeSPCSPawn) that makes sure
166 * the original environment is restored before the spawn and the DT
167 * environment is reinstated after the spawn. It returns the value
168 * originally returned by DtSPCSpawn.
170 ******************************************************************************/
178 SPC_Channel_Ptr chan,
187 * Restore the original environment
189 (void) _DtEnvControl (DT_ENV_RESTORE_PRE_DT);
192 * Map some env var paths to execHost.
194 (void) _DtEnvMapForRemote(execHost);
196 if ((retVal = XeSPCSpawn(path,
202 switch (DtSPCErrorNumber)
204 case SPC_cannot_Chdir:
205 (void) sprintf (errorMessage,
210 case SPC_Cannot_Fork:
211 (void) sprintf (errorMessage,
215 case SPC_Env_Too_Big:
216 case SPC_Arg_Too_Long:
217 (void) sprintf (errorMessage,
225 (void) sprintf (errorMessage,
233 * Restore some env var paths.
235 (void) _DtEnvRestoreLocal();
238 * Restore the DT environment
240 (void) _DtEnvControl (DT_ENV_RESTORE_POST_DT);
243 * Return the result of DtSPCSpawn
248 /*******************************************************************************
251 * This is a wrapper around DtSPCOpen (i.e. XeSPCOpen) that makes sure
252 * the original environment is restored before the spawn and the DT
253 * environment is reinstated after the spawn. It returns the value
254 * originally returned by DtSPCOpen.
256 ******************************************************************************/
263 SPC_Channel_Ptr chan;
264 _Xgetpwparams pwd_buf;
265 struct passwd * pwd_ret;
268 * Restore the original environment
270 (void) _DtEnvControl (DT_ENV_RESTORE_PRE_DT);
273 * Map some env var paths to execHost.
275 (void) _DtEnvMapForRemote(hostname);
277 if ((chan = XeSPCOpen(hostname, iomode)) == SPC_ERROR)
282 switch (DtSPCErrorNumber)
284 case SPC_Unknown_Host:
285 (void) sprintf (errorMessage,
289 case SPC_Bad_Connect:
290 (void) sprintf (errorMessage,
296 case SPC_Bad_Service:
297 (void) sprintf (errorMessage,
302 case SPC_Register_Handshake:
304 if((pwd_ret = _XGetpwuid(this_uid, pwd_buf)) ==
308 username = pwd_ret->pw_name;
309 (void) sprintf (errorMessage,
310 errorRegisterHandshake,
317 case SPC_Register_Username:
319 if((pwd_ret = _XGetpwuid(this_uid, pwd_buf)) ==
323 username = pwd_ret->pw_name;
324 (void) sprintf (errorMessage,
325 errorRegisterUsername,
329 case SPC_Register_Netrc:
330 (void) sprintf (errorMessage,
334 case SPC_Env_Too_Big:
335 (void) sprintf (errorMessage,
340 case SPC_Connection_EOF:
342 (void) sprintf (errorMessage,
353 (void) sprintf (errorMessage,
364 (void) sprintf (errorMessage,
371 * Restore some env var paths.
373 (void) _DtEnvRestoreLocal();
376 * Restore the DT environment
378 (void) _DtEnvControl (DT_ENV_RESTORE_POST_DT);
384 /******************************************************************************
386 * QueueRequest - takes the "state" from a request and puts it on the
391 * SPC_Channel_Ptr channel; - Spcd channel id.
392 * char *context; - Context for SPCSpawn.
393 * char *execHost; - The execution host.
394 * char *execString; - The execution string.
395 * char **argv; - Arg vector for SPCSpawn.
396 * (Arg vector is XtFree'd)
397 * int windowType; - window type of queued command.
398 * unsigned long requestNum; - Id number into the queue.
399 * DtSvcMsgContext replyContext; - Reply info.
400 * DtCmdInvExecuteProc success_proc; - Success callback.
401 * void *success_data; - Success client_data.
402 * DtCmdInvExecuteProc failure_proc; - Failure callback.
403 * void *failure_data; - Failure client_data.
407 * Cmd_RequestQueue *requestQueue; - This request to added.
409 *****************************************************************************/
413 SPC_Channel_Ptr channel,
419 unsigned long requestNum,
420 DtSvcMsgContext replyContext,
421 DtCmdInvExecuteProc success_proc,
423 DtCmdInvExecuteProc failure_proc,
427 Cmd_RequestQueue *pNode;
428 Cmd_RequestQueue *pNewNode;
430 pNewNode = (Cmd_RequestQueue *) XtMalloc (sizeof (Cmd_RequestQueue));
432 pNewNode->next = (Cmd_RequestQueue *) NULL;
433 pNewNode->channel = channel;
434 pNewNode->context = XtNewString (context);
435 pNewNode->exec_host = XtNewString (execHost);
436 pNewNode->exec_string = XtNewString (execString);
437 pNewNode->argv = argv;
438 pNewNode->winType = winType;
439 pNewNode->request_num = requestNum;
440 if (replyContext == NULL)
441 pNewNode->replyContext = NULL;
443 pNewNode->replyContext = replyContext;
444 pNewNode->success_proc = success_proc;
445 pNewNode->success_data = success_data;
446 pNewNode->failure_proc = failure_proc;
447 pNewNode->failure_data = failure_data;
449 if (requestQueue == NULL)
451 requestQueue = pNewNode;
456 * Find the End Of the Queue and link in the NewNode.
458 for (pNode = requestQueue; pNode->next != NULL; pNode = pNode->next);
460 pNode->next = pNewNode;
464 /******************************************************************************
466 * ExecuteQueuedRequest - given a key into the requestQueue (requestNum)
467 * find the appropriate request and execute it.
471 * unsigned long requestNum; - Key into the requestQueue.
475 * Cmd_RequestQueue *requestQueue; - The executed request gets
478 *****************************************************************************/
481 ExecuteQueuedRequest (
482 unsigned long requestNum)
485 Boolean success = True;
486 Cmd_RequestQueue *prev = NULL;
487 Cmd_RequestQueue *pNode = requestQueue;
488 unsigned long iomode;
490 for (; pNode != NULL; pNode = pNode->next)
492 if ( pNode->request_num == requestNum )
495 * Pluck pNode out of the queue
498 prev->next = pNode->next;
500 requestQueue = pNode->next;
506 * Move alone to the next node
514 errorMessage = XtMalloc (MAX_BUF_SIZE * sizeof (char));
521 iomode = ( SPCIO_NOIO
522 | SPCIO_SYNC_TERMINATOR
523 | SPCIO_FORCE_CONTEXT );
526 if ((pNode->channel = (_DtSPCOpen(pNode->exec_host,
528 errorMessage))) == SPC_ERROR)
534 if ((_DtSPCSpawn(pNode->argv[0], pNode->context, pNode->argv, NULL,
535 pNode->channel, pNode->exec_host, NULL, NULL,
536 errorMessage)) == SPC_ERROR)
539 if (DtSPCErrorNumber != SPC_Arg_Too_Long)
540 DtSPCClose(pNode->channel);
543 if (success && pNode->success_proc != NULL)
545 if (cmd_Resources.executionHostLogging && pNode->success_data != NULL)
548 data = (CallbackData *) pNode->success_data;
549 (void) sprintf (errorMessage, successHost,
550 data->actionLabel, pNode->exec_host);
551 _DtCmdLogErrorMessage (errorMessage);
554 (*pNode->success_proc) (NULL, pNode->success_data);
558 if (cmd_Resources.executionHostLogging)
560 if (DtSPCErrorNumber == SPC_Arg_Too_Long)
563 char *cmdp; /* pointer to complete command string */
567 * The message should include all of the command because
568 * the problem may be with some on the internally generated
569 * parts of the command (e.g. the terminal emulator and args).
570 * This means going through all of argv to determine the
571 * length of the string.
573 for (cmdlen = 0, i = 0; pNode->argv[i]; i++) {
574 cmdlen+=strlen(pNode->argv[i]);
575 cmdlen+=2; /* make room for a space + string terminator */
578 tmp_message = (char *) XtMalloc (strlen (errorSpawn) +
579 strlen (pNode->exec_host) +
581 cmdp = (char *) XtMalloc(cmdlen + 1);
583 for (i = 0; pNode->argv[i]; i++) {
584 strcat(cmdp,pNode->argv[i]);
587 (void) sprintf (tmp_message, errorSpawn, pNode->exec_host, cmdp);
588 _DtCmdLogErrorMessage (tmp_message);
593 if (pNode->failure_proc != NULL)
594 (*pNode->failure_proc) (errorMessage, pNode->failure_data);
597 XtFree ((char *)errorMessage);
602 _DtCmdGetQueueHead(void)
607 /******************************************************************************
609 * FreeRequest - Frees the malloced data associated with the node.
610 * and frees the node.
614 * Cmd_RequestQueue *pNode
617 *****************************************************************************/
620 FreeRequest (Cmd_RequestQueue *pNode)
626 XtFree (pNode->context);
627 XtFree (pNode->exec_host);
628 XtFree (pNode->exec_string);
629 Cmd_FreeAllocatedStringVector (pNode->argv);
631 XtFree ((char *) pNode);
634 /******************************************************************************
636 * _DtCmdCommandInvokerExecute - executes a request on the specified host.
640 * _CMD_EXECUTE_SUCCESS - successful execution
641 * _CMD_EXECUTE_FAILURE - execution failed
642 * _CMD_EXECUTE_QUEUED - the request was queued
643 * _CMD_EXECUTE_FATAL - the request contains invalid information
646 *****************************************************************************/
649 _DtCmdCommandInvokerExecute (
650 char *errorMessage, /* MODIFIED */
651 DtSvcMsgContext replyContext, /* OBSOLETE -- always NULL */
655 char *contextFile, /* OBSOLETE -- always NULL */
661 DtCmdInvExecuteProc success_proc,
663 DtCmdInvExecuteProc failure_proc,
667 int ioMode, i, index1;
670 char context[MAXPATHLEN];
671 char tmpDir [MAXPATHLEN];
673 SPC_Channel_Ptr cmdChannel;
674 char *theCommand = NULL;
675 Boolean terminalRequest = False;
676 char *commandArray2[MAX_EXEC_ARGS];
677 Boolean localExecution = True;
679 static unsigned long requestNum = 0;
680 char *toolRequest = NULL; /* backward compatibility kludge */
683 myassert( !(contextFile && replyContext) );
686 * Check for a valid window-type.
687 * This check is probably redundant but it converts the mask bits into
688 * small integer values used by the rest of the command invoker code.
691 DtCmdGetWindowType(winMask))== -1)
693 (void) sprintf (errorMessage, errorRequest, toolRequest,
694 DtTERMINAL, DtPERM_TERMINAL, DtOUTPUT_ONLY,
695 DtSHARED_OUTPUT, "" /* Obsolete shell window */,
697 return (_CMD_EXECUTE_FATAL);
701 * Create the command to be exec'ed.
703 if (windowType == PERM_TERMINAL || windowType == TERMINAL)
705 _DtCmdCreateTerminalCommand (&theCommand, windowType, execString,
706 execParms, execHost, procId, tmpFiles);
707 terminalRequest = True;
712 * NO-STDIO || START-SESSION request.
715 theCommand = XtMalloc(
716 + strlen (cmd_Resources.dtexecPath)
717 + strlen(" -open ") + 4 /* waitTime len */
718 + strlen(" -ttprocid ") + strlen(_DtActNULL_GUARD(procId))
719 + strlen(_DtActNULL_GUARD(tmpFiles))
720 + strlen (execString)
721 + 5 /* for 2 quotes,2 blanks,null */);
723 sprintf(theCommand,"%s -open %d -ttprocid '%s' %s %s",
724 cmd_Resources.dtexecPath,
725 0 /* wait time zero for NO_STDIO */,
726 _DtActNULL_GUARD(procId),
727 _DtActNULL_GUARD(tmpFiles),
733 * See if the request requires Remote Execution.
735 localExecution = _DtIsSameHost(execHost,NULL);
738 * If this is a terminalRequest and the Command Invoker subprocess
739 * is not executable, return now.
741 if (localExecution && terminalRequest && !cmd_Globals.subprocess_ok)
743 if (!(_DtCmdCheckForExecutable (cmd_Resources.dtexecPath)))
745 (void) sprintf (errorMessage, cmd_Globals.error_subprocess,
746 cmd_Resources.dtexecPath);
747 XtFree ((char *) theCommand);
748 return (_CMD_EXECUTE_FAILURE);
751 cmd_Globals.subprocess_ok = True;
755 * If this is a terminalRequest and the terminal emulator
756 * is not executable, return now.
758 if (localExecution && terminalRequest && !cmd_Globals.terminal_ok)
760 if (!(_DtCmdCheckForExecutable (cmd_Resources.localTerminal)))
762 (void) sprintf (errorMessage, cmd_Globals.error_terminal,
763 cmd_Resources.localTerminal);
764 XtFree ((char *) theCommand);
765 return (_CMD_EXECUTE_FAILURE);
768 cmd_Globals.terminal_ok = True;
772 * Break the command into something execvp or SPCSpawn can handle
773 * and then free "theCommand" if this is a termianl-based request.
775 commandArray = (char **) XtMalloc (MAX_EXEC_ARGS * sizeof (char *));
776 _DtCmdStringToArrayOfStrings (theCommand, commandArray);
786 /* REMOTE Execution */
788 ioMode = SPCIO_NOIO | SPCIO_SYNC_TERMINATOR | SPCIO_FORCE_CONTEXT;
790 if ((cmdChannel = (_DtSPCOpen(execHost,
792 errorMessage))) == SPC_ERROR)
794 Cmd_FreeAllocatedStringVector (commandArray);
795 return (_CMD_EXECUTE_FAILURE);
798 /* Old syntax should no longer appear in contextHost/Dir */
799 myassert( (contextHost?*contextHost != '*':1) &&
800 (contextDir?*contextDir != '*':1) );
802 * Create a "netfile" for the cwd to be used.
804 netfile = (char *) tt_host_file_netfile (
805 ((contextHost == NULL) ? execHost : contextHost),
806 ((contextDir == NULL) ? (char *) getenv ("HOME") : contextDir));
808 if (tt_pointer_error (netfile) != TT_OK) {
809 (void) sprintf (errorMessage, cmd_Globals.error_directory_name_map,
810 ((contextDir == NULL) ? (char *) getenv ("HOME") : contextDir),
811 ((contextHost == NULL) ? execHost : contextHost),
812 tt_status_message (tt_pointer_error(netfile)));
813 Cmd_FreeAllocatedStringVector (commandArray);
814 return (_CMD_EXECUTE_FAILURE);
816 (void) strcpy (context, netfile);
820 * First check to see if the "dtexecPath" is executable on
821 * the remote execution host by executing it with no
822 * options which will cause it to immediately die.
824 * There is no need to set up termination handler for this
825 * because we don't care when it dies, we only care about
826 * whether or not it can be executed.
829 argv[0] = cmd_Resources.dtexecPath;
830 argv[1] = (char *) NULL;
832 if ((_DtSPCSpawn(argv[0], context, argv, NULL, cmdChannel,
833 execHost, contextHost, contextDir, errorMessage))
836 if (DtSPCErrorNumber != SPC_cannot_Chdir &&
837 DtSPCErrorNumber != SPC_Cannot_Fork &&
838 DtSPCErrorNumber != SPC_Env_Too_Big &&
839 DtSPCErrorNumber != SPC_Arg_Too_Long)
841 * The Error message must mention that the dtexec
842 * process is not executable so must overwrite the
843 * error message returned by the Spawn function with
844 * an appropriate message.
846 (void) sprintf (errorMessage, errorRemoteSubprocess, execHost,
847 cmd_Resources.dtexecPath);
848 DtSPCClose(cmdChannel);
849 Cmd_FreeAllocatedStringVector (commandArray);
850 return (_CMD_EXECUTE_FAILURE);
853 /* The dtexec process is now known to exist on the remote host */
856 * Now run a test to see if the command is executable
859 _DtCmdStringToArrayOfStrings (execString, commandArray2);
861 tmp = (char *) XtMalloc (strlen (commandArray2[0]) +
862 strlen ("whence ") + 2);
863 (void) sprintf (tmp, "whence %s", commandArray2[0]);
865 _DtCmdFreeStringVector (commandArray2);
870 argv[3] = (char *) NULL;
875 if ((cmdChannel = (_DtSPCOpen(execHost,
877 errorMessage))) == SPC_ERROR)
879 Cmd_FreeAllocatedStringVector (commandArray);
880 return (_CMD_EXECUTE_FAILURE);
884 * Set up a callback to be invoked when the test command
888 if ((DtSPCRegisterTerminator(cmdChannel,
889 (SPC_TerminateHandlerType) CheckCommandTerminator,
890 (void *) ++requestNum)) == SPC_ERROR)
892 DtSPCClose(cmdChannel);
893 Cmd_FreeAllocatedStringVector (commandArray);
894 (void) strcpy (errorMessage, errorSpcTerminator);
895 XtFree ((char *) tmp);
896 _DtSvcProcessUnlock();
897 return (_CMD_EXECUTE_FAILURE);
900 if ((_DtSPCSpawn(argv[0], context, argv, NULL, cmdChannel,
901 execHost, contextHost, contextDir, errorMessage))
904 DtSPCClose(cmdChannel);
905 (void) sprintf (errorMessage, errorRemoteSubprocess, execHost,
907 Cmd_FreeAllocatedStringVector (commandArray);
908 XtFree ((char *) tmp);
909 _DtSvcProcessUnlock();
910 return (_CMD_EXECUTE_FAILURE);
913 * The command line checking process has been spawned.
914 * There is nothing left to do but to queue the request
915 * and return to the client's main loop. The command
916 * line will be executed after the above spawned process
919 QueueRequest (cmdChannel, context, execHost, execString,
920 commandArray, windowType, requestNum, replyContext,
921 success_proc, success_data, failure_proc, failure_data);
922 _DtSvcProcessUnlock();
924 return (_CMD_EXECUTE_QUEUED);
928 /* LOCAL Execution */
931 * Must first check to see if the execvp will potentially fail.
933 * Since the terminal emulator is pre-appended onto the execution
934 * string, don't want to check it (should have been done during
935 * startup (in _DtInitializeCommandInvoker)) but must check the
936 * execution string that was passed in as part of the message.
938 /* Break the command into something execvp can handle */
939 _DtCmdStringToArrayOfStrings (execString, commandArray2);
941 if (!_DtCmdCheckForExecutable (commandArray2[0]))
943 (void) sprintf (errorMessage, errorExec, commandArray2[0]);
944 Cmd_FreeAllocatedStringVector (commandArray);
945 _DtCmdFreeStringVector (commandArray2);
946 return (_CMD_EXECUTE_FAILURE);
948 _DtCmdFreeStringVector (commandArray2);
951 * Save the current directory and then "chdir" to the directory
952 * to do the execution. If the chdir fails, return.
954 if(NULL == getcwd (tmpDir, MAXPATHLEN))
956 perror(strerror(errno));
957 return (_CMD_EXECUTE_FAILURE);
960 if (!_DtCmdValidDir (_cmdClientHost, contextDir, contextHost))
962 Cmd_FreeAllocatedStringVector (commandArray);
963 (void) sprintf (errorMessage, errorChdir, contextDir, execHost);
964 if(-1 == chdir (tmpDir)) {
965 perror(strerror(errno));
967 return (_CMD_EXECUTE_FAILURE);
971 * Restore the original environment and remove any DT
972 * specific environment variables that were added.
974 (void) _DtEnvControl (DT_ENV_RESTORE_PRE_DT);
976 /* Fork and then execvp the execution string */
977 for (index1 = 0; (index1 < 10) &&
978 ((commandPid = fork ()) < 0); index1++)
980 /* Out of resources ? */
983 /* If not out of resources, sleep and try again */
984 (void) sleep ((unsigned long) 2);
989 Cmd_FreeAllocatedStringVector (commandArray);
990 if(-1 == chdir (tmpDir)) {
991 perror(strerror(errno));
993 (void) sprintf(errorMessage, errorFork, execHost);
994 (void) _DtEnvControl (DT_ENV_RESTORE_POST_DT);
995 return (_CMD_EXECUTE_FAILURE);
1001 #if defined(__hp_osf) || defined(__osf__) || defined(CSRG_BASED)
1007 if (!terminalRequest )
1012 * Close stdout and redirect it to /dev/null. If this
1013 * is not done and the request writes to stdout, the
1014 * output will be queued in an "unlinked" file in
1015 * /tmp until the client using this code terminates.
1017 if ((fd = open ("/dev/null", O_RDWR)) > 0)
1018 (void) dup2 (fd, fileno (stdout));
1022 * Mark file descriptiors >=3 as "Close on Exec".
1027 open_max = sysconf(_SC_OPEN_MAX);
1033 #if defined(USL) || defined(_AIX)
1034 open_max = FOPEN_MAX;
1036 open_max = FD_SETSIZE;
1038 #endif /* _SUN_OS */
1041 for (i=3; i < open_max; i++)
1042 (void) fcntl (i, F_SETFD, 1);
1046 (void) execvp (commandArray[0], commandArray);
1048 /* Should never get here, but if you do, must exit */
1051 * The following message will be written to the errorlog
1052 * file if the request is not a terminal requests or
1053 * to the terminal window if the request requires a
1056 (void) sprintf (errorMessage, errorExec, commandArray[0]);
1057 (void) printf ("%s\n", errorMessage);
1062 * Restore the pre-fork environment.
1064 (void) chdir (tmpDir);
1065 (void) _DtEnvControl (DT_ENV_RESTORE_POST_DT);
1068 Cmd_FreeAllocatedStringVector (commandArray);
1070 return (_CMD_EXECUTE_SUCCESS);
1073 /******************************************************************************
1075 * CheckCommandTerminator - this is the SPC termination callback
1076 * that is invoked when the command line checking process terminates.
1078 * When this callback is invoked, the next step is to check the
1079 * exit status of the remote checking process and if the "command_
1080 * line" is executable, execute the QueuedRequest. Otherwise,
1083 * PARAMETERS: This parameters for this callback are those defined by
1084 * the type "SPC_TerminateHandlerType". Most are not used.
1086 *****************************************************************************/
1089 CheckCommandTerminator(
1090 SPC_Channel_Ptr cmdChannel,
1091 int pid, /* NOT USED */
1092 int type, /* NOT USED */
1093 int cause, /* Exit value of the remote process. */
1094 unsigned long requestNum) /* Specifies the request number. */
1097 char errorMessage[MAX_BUF_SIZE];
1098 Cmd_RequestQueue *prev = NULL;
1099 Cmd_RequestQueue *pNode = requestQueue;
1101 DtSPCClose(cmdChannel);
1104 * Must find the node in the queue.
1106 for (; pNode != NULL; pNode = pNode->next)
1108 if ( pNode->request_num == requestNum )
1111 * Pluck pNode out of the queue
1114 prev->next = pNode->next;
1116 requestQueue = pNode->next;
1122 * Move alone to the next node
1131 * Check the exit status of the remote process.
1133 if (cause == COMMAND_CHECK_FAILURE)
1135 if (pNode->failure_proc != NULL)
1137 (void) sprintf (errorMessage, errorSpawn, pNode->exec_host,
1138 pNode->exec_string);
1139 if (cmd_Resources.executionHostLogging)
1140 _DtCmdLogErrorMessage (errorMessage);
1141 (*pNode->failure_proc) (errorMessage, pNode->failure_data);
1144 FreeRequest (pNode);
1150 * Re-queue this node -- we will execute the command
1152 pNode->next = requestQueue;
1153 requestQueue = pNode;
1155 ExecuteQueuedRequest (requestNum);
1158 /******************************************************************************
1160 * DtCmdGetWindowType - given a window-type mask, determine its'
1161 * internal window type number.
1163 * It should not be possible for this to be called with a bogus mask,
1167 * unsigned long mask; - The request window type mask
1169 * RETURNS: The window type if one if found, otherwise "-1".
1171 *****************************************************************************/
1175 unsigned long windowTypeMask)
1177 int winTypeNum = -1;
1180 * Determine the winType number recognized by dtexec
1181 * The expected wintype input here is the value of the
1182 * wintype bits in the action mask. Convert this to the
1183 * simple integers expected by dtexec.
1186 switch ( windowTypeMask )
1188 case _DtAct_NO_STDIO_BIT:
1191 case _DtAct_TERMINAL_BIT:
1192 winTypeNum = TERMINAL;
1194 case _DtAct_PERM_TERM_BIT:
1195 winTypeNum = PERM_TERMINAL;
1198 myassert(0); /* should never get here */
1199 winTypeNum = PERM_TERMINAL;
1207 /******************************************************************************
1209 * _DtActionCommandInvoke
1210 * ----------------------
1211 * This is the primary entry point into the command invoker portion
1212 * of the Dt Services library. Command Actions are routed to this
1213 * entry point. This function is used to invoke both local and remote
1216 * If logging is turned on, the success or failure message is logged.
1217 ******************************************************************************/
1219 _DtActionCommandInvoke(
1228 void (*success_proc)(),
1230 void (*failure_proc)(),
1234 char errorMessage[MAX_BUF_SIZE * 2];
1236 status = _DtCmdCommandInvokerExecute (errorMessage, NULL,
1238 cwdHost, cwdDir, NULL,
1239 termOpts, execHost, execString,
1241 success_proc, success_data,
1242 failure_proc, failure_data);
1245 case _CMD_EXECUTE_SUCCESS :
1246 if (cmd_Resources.executionHostLogging && success_data != NULL)
1249 data = (CallbackData *) success_data;
1250 (void) sprintf (errorMessage, successHost,
1251 data->actionLabel, execHost);
1252 _DtCmdLogErrorMessage (errorMessage);
1254 if (success_proc != NULL)
1255 (*success_proc) (NULL, success_data);
1257 case _CMD_EXECUTE_QUEUED :
1259 * Return for now and when the termination handler
1260 * gets hit, the queued request will be executed.
1265 * _CMD_EXECUTE_FAILURE or _CMD_EXECUTE_FATAL
1267 if (cmd_Resources.executionHostLogging)
1268 _DtCmdLogErrorMessage (errorMessage);
1269 if (failure_proc != NULL)
1270 (*failure_proc) (errorMessage, failure_data);
1274 return (status == _CMD_EXECUTE_QUEUED) ? 1 : 0;
1277 /******************************************************************************
1279 * _DtCommandInvokerExecute -
1281 * This function allows a client to use the DT "Command Invoker"
1282 * Library for its' process execution. This function is intended
1283 * for processes which do not use the Action Library.
1285 * For local execution, the "fork" and "execvp" system calls are
1286 * used. For remote execution, the "SPCD" is used.
1288 * RETURNS: Nothing directly. However, if the command is successfully
1289 * executed, the "success_proc" function is executed; otherwise,
1290 * the "failure_proc" function is executed.
1292 * NOTE: This API is NOT public it is only here for use by the Session
1293 * manager which uses to start up remote clients at session restore
1294 * time (see SmRestore.c).
1296 *****************************************************************************/
1299 _DtCommandInvokerExecute(
1304 char *exec_parameters,
1307 DtCmdInvExecuteProc success_proc,
1309 DtCmdInvExecuteProc failure_proc,
1313 _DtSvcAppLock(cmd_Globals.app_context);
1315 _DtActionCommandInvoke(_DtAct_NO_STDIO_BIT,context_host,
1316 context_dir, exec_string, exec_parameters, exec_host,
1318 success_proc,success_data, failure_proc,failure_data);
1319 _DtSvcAppUnlock(cmd_Globals.app_context);
1323 /******************************************************************************
1325 * _DtInitializeCommandInvoker - initialize some global variables and
1326 * and call the appropriate initialization routines.
1330 * Display *display; - The X server connection.
1332 * char *toolClass; - The client's tool class.
1334 * char *appClass; - The client's application class.
1336 * DtSvcMessageProc reloadDBHandler; - The callback function for handling
1337 * a "RELOAD-TYPES-DB" request. (OBSOLETE)
1339 * XtAppContext appContext; - The client's application context.
1343 * SbInputId (*SbAddInput_hookfn); - Set to _DtCmdSPCAddInputHandler
1345 * SbInputId (*SbRemoveInput_hookfn); - Set to XtRemoveInput
1347 * SbInputId (*SbAddException_hookfn); - Set to _DtCmdSPCAddExceptionHandler
1348 * SbInputId (*SbRemoveException_hookfn); - Set to XtRemoveInput
1350 *****************************************************************************/
1353 _DtInitializeCommandInvoker(
1355 char *toolClass, /* ignored */
1356 char *appClass, /* ignored */
1357 DtSvcMessageProc reloadDBHandler, /* OBSOLETE -- ignored */
1358 XtAppContext appContext)
1360 static int beenCalled = 0;
1362 _DtSvcAppLock(appContext);
1365 * Prevent repeat calls
1367 _DtSvcProcessLock();
1369 _DtSvcProcessUnlock();
1375 cmd_Globals.app_context = appContext;
1377 SbAddInput_hookfn = _DtCmdSPCAddInputHandler;
1379 SbRemoveInput_hookfn = XtRemoveInput;
1381 SbAddException_hookfn = _DtCmdSPCAddExceptionHandler;
1383 SbRemoveException_hookfn = XtRemoveInput;
1385 _DtCmdBuildPathList ();
1387 _DtCmdInitializeErrorMessages ();
1390 * Must get the name of the invoking host, so that requests
1391 * can be checked for remote execution.
1393 if ((DtGetShortHostname(_cmdClientHost, MAXHOSTNAMELEN)) == -1) {
1394 _DtCmdLogErrorMessage ("Cannot determine the local host name.\n");
1397 _DtCmdGetResources (display);
1399 _DtSvcProcessUnlock();
1400 _DtSvcAppUnlock(appContext);
1403 /*****************************************************************************
1405 * _DtCmdInitializeErrorMessages - initializes all of the command invoker's
1410 * MODIFIED: all of the Command Invoker's error messages are initialized.
1412 *****************************************************************************/
1415 _DtCmdInitializeErrorMessages( void )
1419 * Non-Fatal -> Abort the request
1421 errorChdir = strdup (((char *)Dt11GETMESSAGE(3, 2, "An attempt to change to the following directory:\n\n %s\n\nfrom host \"%s\" failed.\n\nCheck the spelling and permissions and make sure the directory exists.")));
1423 errorSpawn = strdup (((char *)Dt11GETMESSAGE(3, 5, "An attempt to execute the following command on host\n\"%s\" failed:\n\n %s\n\nCheck that the program exists, has the correct\npermissions and is executable.")));
1425 errorExec = strdup (((char *)Dt11GETMESSAGE(3, 6, "An attempt to execute the following command failed:\n\n %s\n\nCheck that the program exists, has the correct\npermissions and is executable.")));
1427 cmd_Globals.error_terminal = strdup (((char *)Dt11GETMESSAGE(3, 7, "This action cannot be started because the following\nterminal emulator cannot be executed:\n\n %s\n\nCheck that the program exists, has the correct permissions\nand is executable. This problem may have occurred because the\nprogram is not in your \"PATH\".")));
1429 errorLength = strdup (((char *)Dt11GETMESSAGE(3, 9, "The total number of characters in this action exceeds the limit of \"%d\".\n\nYou may need to break the action into more than one action.")));
1431 errorFork = strdup (((char *)Dt11GETMESSAGE(3, 11, "An attempt to start a new process on host \"%s\" failed.\n\nTo continue, you may need to stop an unneeded process on this host.")));
1433 errorRequest = strdup (((char *)Dt11GETMESSAGE(3, 17, "This action's WINDOW_TYPE \"%s\" is un-recognized.\n\nThe WINDOW_TYPE should be one of the following:\n\n %s, %s, %s,\n %s, %s, or %s\n")));
1435 cmd_Globals.error_subprocess = strdup (((char *)Dt11GETMESSAGE(3, 18, "This action cannot be started because the DT subprocess program:\n\n %s\n\ncannot be executed. Check that the program has the correct\npermissions and is executable.")));
1437 errorRemoteSubprocess = strdup (((char *)Dt11GETMESSAGE(3, 20, "This action cannot be executed on host \"%s\"\nbecause the following required program either\ndoes not exist or it is not executable:\n\n %s\n")));
1440 * The following errors may occur when a SPC
1441 * channel is opened.
1443 errorUnknownHost = XtNewString (((char *)Dt11GETMESSAGE(3, 24, "This action cannot be executed because\nhost \"%s\" cannot be reached.")));
1445 errorBadConnect = XtNewString (((char *)Dt11GETMESSAGE(3, 25, "This action cannot be executed on host \"%s\" because the\n\"%s\" service is not properly configured on this host.")));
1447 errorBadService = XtNewString (((char *)Dt11GETMESSAGE(3, 26, "This action cannot be executed because the \"%s\"\nservice is not configured on host \"%s\".")));
1449 errorRegisterHandshake = XtNewString (((char *)Dt11GETMESSAGE(3, 27, "This action cannot be executed on host \"%s\" because user\n\"%s\" has a user id of \"%d\" on host \"%s\" and this does\nnot match the username and user id on the action\ninvocation host \"%s\".")));
1451 errorRegisterUsername = XtNewString (((char *)Dt11GETMESSAGE(3, 28, "This action cannot be executed on host \"%s\" because\nuser \"%s\" does not have an account on this host.")));
1453 errorRegisterNetrc = XtNewString (((char *)Dt11GETMESSAGE(3, 29, "This action cannot be executed on host \"%s\" because\na pathname to the authentication file cannot be created.")));
1455 errorRegisterOpen = XtNewString (((char *)Dt11GETMESSAGE(3, 30, "This action cannot be executed on host \"%s\" because\nthe authentication file on this host cannot be opened.\n\nThis may be caused by your network home not being\nproperly configured.")));
1457 errorEnvTooBig = XtNewString (((char *)Dt11GETMESSAGE(3, 31, "This action cannot be executed on host \"%s\" because\nthe environment exceeds \"%d\" bytes.")));
1459 errorInetSecurity = XtNewString (((char *)Dt11GETMESSAGE(3, 32, "This action cannot be executed on host \"%s\" because\nhost \"%s\" is not authorized to use the \"%s\" service.\n\nTo fix this, add host \"%s\" to the \"%s\" service\nentry in file \"%s\" on host \"%s\".")));
1462 * Do not post a dialog, write to the error log file only.
1465 errorSpcTerminator = strdup (((char *)Dt11GETMESSAGE(3, 15, "An attempt to register the output log from a remote host failed.\n\nTo continue, you may need to stop an existing process.")));
1467 successHost = strdup (((char *)Dt11GETMESSAGE(3, 21, "The action \"%s\" was successfully executed on host \"%s\".")));
1469 cmd_Globals.error_directory_name_map = strdup (((char *)Dt11GETMESSAGE(3, 22, "The directory \"%s\" on host \"%s\"\ncould not be converted to a network path.\n(%s)")));