cc15e2b6980456713fc4dc78fcac95a19cbc5134
[oweals/cde.git] / cde / lib / DtSvc / DtUtil1 / CmdMain.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
23 /* $TOG: CmdMain.c /main/15 1998/04/20 12:46:37 mgreess $ */
24 /* 
25  * (c) Copyright 1997, The Open Group 
26  */
27 /***************************************************************************
28 *
29 * File:         CmdMain.c
30 * Description:  Command execution system
31 * Language:     C
32 *
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 ***************************************************************************/
38
39 #include "CmdInvP.h"
40 #include <Dt/CmdInv.h>
41
42 #include <fcntl.h>
43 #ifdef __apollo
44 #include "/sys5/usr/include/sys/termio.h"
45 #else
46 #include <termios.h>
47 #endif
48 #include <errno.h>
49 #include <unistd.h>
50 #include <sys/stat.h>
51 #ifdef _SUN_OS   /* to get the define for NOFILE */
52 #include <sys/param.h>
53 #endif /* _SUN_OS */
54 #define X_INCLUDE_PWD_H
55 #define XOS_USE_XT_LOCKING
56 #include <X11/Xos_r.h>
57
58 #include <Dt/CommandM.h>
59 #include <Dt/EnvControlP.h>
60 #include <Dt/DtNlUtils.h>
61 #include <Dt/Utility.h>
62
63 #include <Dt/ActionDb.h>
64 #include <Dt/ActionUtilP.h>
65 #include "myassertP.h"
66 #include "DtSvcLock.h"
67
68 #include <SPC/spcE.h>
69 #include <SPC/spcP.h>
70 #include <SPC/spc-proto.h>
71 #include <Tt/tt_c.h>
72
73 #define MAX_EXEC_ARGS           1000    /* Maximum number of arguments for */
74                                         /* execvp call. */
75 /*
76  * Dtexec return status:
77  */
78 #define COMMAND_CHECK_FAILURE           1
79
80 #ifdef __hpux
81 #ifdef hpV4
82 #define INETD_SECURITY_FILE     "/var/adm/inetd.sec"
83 #else /* hpV4 */
84 #define INETD_SECURITY_FILE     "/usr/adm/inetd.sec"
85 #endif /* hpV4 */
86 #endif /* __hpux */
87
88 #define Cmd_FreeAllocatedStringVector(sv) \
89                 _DtCmdFreeStringVector(sv);\
90                 XtFree((char *)sv);
91
92 /*
93  * Global variables for the Command Invoker.
94  */
95 static char _cmdClientHost[MAXHOSTNAMELEN];
96
97 /*
98  * Static variables for the Command Invoker.
99  */
100 static Cmd_RequestQueue *requestQueue = NULL;
101
102 /*
103  * Static function declarations:.
104  */
105
106 static void QueueRequest (
107                         SPC_Channel_Ptr channel,
108                         char *context,
109                         char *execHost,
110                         char *execString,
111                         char **argv,
112                         int  windowType,
113                         unsigned long requestNum,
114                         DtSvcMsgContext replyContext,
115                         DtCmdInvExecuteProc success_proc,
116                         void *success_data,
117                         DtCmdInvExecuteProc failure_proc,
118                         void *failure_data);
119 static void ExecuteQueuedRequest ( 
120                         unsigned long requestNum);
121
122 static void FreeRequest (Cmd_RequestQueue *pNode);
123                         
124 static void DtexecTerminator (
125                         SPC_Channel_Ptr cmdChannel,
126                         int pid,
127                         int type,
128                         int cause,
129                         unsigned long ind) ;
130 static void CheckCommandTerminator (
131                         SPC_Channel_Ptr cmdChannel,
132                         int pid,
133                         int type,
134                         int cause,
135                         unsigned long ind) ;
136 static int DtCmdGetWindowType(
137         unsigned long windowTypeMask);
138 static void _DtCmdInitializeErrorMessages(void);
139
140 /*
141  * Command invocatin error messages.
142  */
143 static char     *errorExec,
144                 *errorSpawn,
145                 *errorFork,
146                 *errorSpcTerminator,
147                 *errorLength,
148                 *errorRequest,
149                 *errorChdir,
150                 *errorRemoteSubprocess,
151                 *errorUnknownHost,
152                 *errorBadConnect,
153                 *errorBadService,
154                 *errorRegisterHandshake,
155                 *errorRegisterUsername,
156                 *errorRegisterNetrc,
157                 *errorRegisterOpen,
158                 *errorEnvTooBig,
159                 *errorInetSecurity,
160                 *successHost;
161
162 /*******************************************************************************
163  *
164  * _DtSPCSpawn()
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.
169  *
170  ******************************************************************************/
171
172 int
173 _DtSPCSpawn( 
174         char            *path, 
175         char            *cwd,
176         char            **args,
177         char            **env,
178         SPC_Channel_Ptr chan,
179         char            *execHost,
180         char            *contextHost,
181         char            *contextDir,
182         char            *errorMessage)
183 {
184         int     retVal;
185
186         /*
187          * Restore the original environment
188          */
189         (void) _DtEnvControl (DT_ENV_RESTORE_PRE_DT);
190
191         /*
192          * Map some env var paths to execHost.
193          */
194         (void) _DtEnvMapForRemote(execHost);
195
196         if ((retVal = XeSPCSpawn(path,
197                                  cwd,
198                                  args,
199                                  env,
200                                  chan)) == SPC_ERROR)
201         {
202                 switch (DtSPCErrorNumber) 
203                 {
204                         case SPC_cannot_Chdir:
205                                 (void) sprintf (errorMessage, 
206                                                 errorChdir, 
207                                                 contextDir, 
208                                                 execHost);
209                                 break;
210                         case SPC_Cannot_Fork:
211                                 (void) sprintf (errorMessage, 
212                                                 errorFork, 
213                                                 execHost);
214                                 break;
215                         case SPC_Env_Too_Big:
216                         case SPC_Arg_Too_Long:
217                                 (void) sprintf (errorMessage, 
218                                                 errorLength, 
219                                                 SPC_BUFSIZ);
220                                 break;
221                         default:
222                                 /*
223                                  * SPC_Cannot_Exec
224                                  */
225                                 (void) sprintf (errorMessage, 
226                                                 errorSpawn,
227                                                 execHost,
228                                                 path);
229                 }
230         }
231
232         /*
233          * Restore some env var paths.
234          */
235         (void) _DtEnvRestoreLocal();
236
237         /*
238          * Restore the DT environment
239          */
240         (void) _DtEnvControl (DT_ENV_RESTORE_POST_DT);
241
242         /*
243          * Return the result of DtSPCSpawn
244          */
245         return retVal;
246 }
247
248 /*******************************************************************************
249  *
250  * _DtSPCOpen()
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.
255  *
256  ******************************************************************************/
257 SPC_Channel_Ptr
258 _DtSPCOpen(
259         char *hostname, 
260         int iomode, 
261         char *errorMessage)
262 {
263         SPC_Channel_Ptr chan;
264         _Xgetpwparams   pwd_buf;
265         struct passwd * pwd_ret;
266
267         /*
268          * Restore the original environment
269          */
270         (void) _DtEnvControl (DT_ENV_RESTORE_PRE_DT);
271
272         /*
273          * Map some env var paths to execHost.
274          */
275         (void) _DtEnvMapForRemote(hostname);
276
277         if ((chan = XeSPCOpen(hostname, iomode)) == SPC_ERROR)
278         {
279                 uid_t           this_uid;
280                 char            *username;
281
282                 switch (DtSPCErrorNumber) 
283                 {
284                         case SPC_Unknown_Host:
285                                 (void) sprintf (errorMessage, 
286                                                 errorUnknownHost,
287                                                 hostname);
288                                 break;
289                         case SPC_Bad_Connect:
290                                 (void) sprintf (errorMessage, 
291                                                 errorBadConnect,
292                                                 hostname,
293                                                 SPC_SERVICE,
294                                                 _cmdClientHost);
295                                 break;
296                         case SPC_Bad_Service:
297                                 (void) sprintf (errorMessage, 
298                                                 errorBadService,
299                                                 SPC_SERVICE,
300                                                 _cmdClientHost);
301                                 break;
302                         case SPC_Register_Handshake:
303                                 this_uid = getuid();
304                                 if((pwd_ret = _XGetpwuid(this_uid, pwd_buf)) == 
305                                    NULL)
306                                         username = NULL;
307                                 else
308                                         username = pwd_ret->pw_name;
309                                 (void) sprintf (errorMessage, 
310                                                 errorRegisterHandshake,
311                                                 hostname,
312                                                 username,
313                                                 this_uid,
314                                                 _cmdClientHost,
315                                                 hostname);
316                                 break;
317                         case SPC_Register_Username:
318                                 this_uid = getuid();
319                                 if((pwd_ret = _XGetpwuid(this_uid, pwd_buf)) == 
320                                    NULL)
321                                         username = NULL;
322                                 else
323                                         username = pwd_ret->pw_name;
324                                 (void) sprintf (errorMessage, 
325                                                 errorRegisterUsername,
326                                                 hostname,
327                                                 username);
328                                 break;
329                         case SPC_Register_Netrc:
330                                 (void) sprintf (errorMessage, 
331                                                 errorRegisterNetrc,
332                                                 hostname);
333                                 break;
334                         case SPC_Env_Too_Big:
335                                 (void) sprintf (errorMessage, 
336                                                 errorEnvTooBig,
337                                                 hostname,
338                                                 SPC_BUFSIZ);
339                                 break;
340                         case SPC_Connection_EOF:
341 #ifdef __hpux
342                                 (void) sprintf (errorMessage, 
343                                                 errorInetSecurity,
344                                                 hostname,
345                                                 _cmdClientHost,
346                                                 SPC_SERVICE,
347                                                 _cmdClientHost,
348                                                 SPC_SERVICE,
349                                                 INETD_SECURITY_FILE,
350                                                 hostname);
351
352 #else /* __hpux */
353                                 (void) sprintf (errorMessage, 
354                                                 errorBadConnect,
355                                                 hostname,
356                                                 SPC_SERVICE,
357                                                 _cmdClientHost);
358 #endif /* __hpux */
359                                 break;
360                         default:
361                                 /*
362                                  * SPC_Register_Open:
363                                  */
364                                 (void) sprintf (errorMessage, 
365                                                 errorRegisterOpen,
366                                                 hostname);
367                 }
368         }
369         
370         /*
371          * Restore some env var paths.
372          */
373         (void) _DtEnvRestoreLocal();
374
375         /*
376          * Restore the DT environment
377          */
378         (void) _DtEnvControl (DT_ENV_RESTORE_POST_DT);
379         
380         return chan;
381 }
382
383
384 /******************************************************************************
385  *
386  * QueueRequest - takes the "state" from a request and puts it on the
387  *   "requestQueue".
388  *
389  * PARAMETERS:
390  *
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.
404  *
405  * MODIFIED:
406  *
407  *   Cmd_RequestQueue *requestQueue;            - This request to added.
408  *
409  *****************************************************************************/
410
411 static void
412 QueueRequest (
413         SPC_Channel_Ptr channel,
414         char *context,
415         char *execHost,
416         char *execString,
417         char **argv,
418         int  winType,
419         unsigned long requestNum,
420         DtSvcMsgContext replyContext,
421         DtCmdInvExecuteProc success_proc,
422         void *success_data,
423         DtCmdInvExecuteProc failure_proc,
424         void *failure_data)
425
426 {
427    Cmd_RequestQueue *pNode;
428    Cmd_RequestQueue *pNewNode;
429
430    pNewNode = (Cmd_RequestQueue *) XtMalloc (sizeof (Cmd_RequestQueue));
431
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;
442    else
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;
448
449    if (requestQueue == NULL) 
450    {
451       requestQueue = pNewNode;
452       return;
453    }
454
455    /*
456     * Find the End Of the Queue and link in the NewNode.
457     */
458    for (pNode = requestQueue; pNode->next != NULL; pNode = pNode->next);
459
460    pNode->next = pNewNode;
461
462 }
463
464 /******************************************************************************
465  *
466  * ExecuteQueuedRequest - given a key into the requestQueue (requestNum)
467  *   find the appropriate request and execute it.
468  *
469  * PARAMETERS:
470  *
471  *   unsigned long      requestNum;             - Key into the requestQueue.
472  *
473  * MODIFIED:
474  *
475  *   Cmd_RequestQueue   *requestQueue;          - The executed request gets
476  *                                                freed.
477  *
478  *****************************************************************************/
479
480 static void
481 ExecuteQueuedRequest (
482         unsigned long requestNum)
483 {
484    char                 *errorMessage;
485    Boolean              success = True;
486    Cmd_RequestQueue     *prev  = NULL;
487    Cmd_RequestQueue     *pNode = requestQueue;
488    unsigned long        iomode;
489
490    for (; pNode != NULL; pNode = pNode->next)
491    {
492        if ( pNode->request_num == requestNum )
493        {
494            /*
495             * Pluck pNode out of the queue
496             */
497            if (prev)
498                prev->next = pNode->next;
499            else
500                requestQueue = pNode->next;
501
502            pNode->next = NULL;
503            break;
504        }
505        /*
506         * Move alone to the next node
507         */
508        prev = pNode;
509    }
510
511    if (pNode == NULL)
512       return;
513
514    errorMessage = XtMalloc (MAX_BUF_SIZE * sizeof (char));
515
516    /*
517     *  Reopen SPC Channel
518     */
519
520
521    iomode = ( SPCIO_NOIO 
522             | SPCIO_SYNC_TERMINATOR 
523             | SPCIO_FORCE_CONTEXT );
524
525    
526    if ((pNode->channel = (_DtSPCOpen(pNode->exec_host, 
527           iomode,
528           errorMessage))) == SPC_ERROR) 
529       {
530          success = False;
531       }
532
533    if ( success )
534        if ((_DtSPCSpawn(pNode->argv[0], pNode->context, pNode->argv, NULL, 
535                     pNode->channel, pNode->exec_host, NULL, NULL,
536                     errorMessage)) == SPC_ERROR) 
537        {
538           success = False;
539           if (DtSPCErrorNumber != SPC_Arg_Too_Long)
540              DtSPCClose(pNode->channel);
541        }
542
543    if (success && pNode->success_proc != NULL)
544    {
545       if (cmd_Resources.executionHostLogging && pNode->success_data != NULL)
546       {
547          CallbackData *data;
548          data = (CallbackData *) pNode->success_data;
549          (void) sprintf (errorMessage, successHost, 
550                          data->actionLabel, pNode->exec_host);
551          _DtCmdLogErrorMessage (errorMessage);
552       }
553
554       (*pNode->success_proc) (NULL, pNode->success_data);
555    }
556    else if (!success)
557    {
558       if (cmd_Resources.executionHostLogging)
559       {
560          if (DtSPCErrorNumber == SPC_Arg_Too_Long)
561          {
562             int cmdlen,i;
563             char *cmdp; /* pointer to complete command string */
564             char *tmp_message;
565
566             /*
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.
572              */
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 */
576             }
577          
578             tmp_message = (char *) XtMalloc (strlen (errorSpawn) + 
579                                              strlen (pNode->exec_host) +
580                                              cmdlen + 4);
581             cmdp = (char *) XtMalloc(cmdlen + 1);
582             *cmdp = '\0';
583             for (i = 0; pNode->argv[i]; i++) {
584                strcat(cmdp,pNode->argv[i]);
585                strcat(cmdp, " ");
586             }
587             (void) sprintf (tmp_message, errorSpawn, pNode->exec_host, cmdp);
588             _DtCmdLogErrorMessage (tmp_message);
589             XtFree(cmdp);
590             XtFree(tmp_message);
591          }
592       }
593       if (pNode->failure_proc != NULL)
594          (*pNode->failure_proc) (errorMessage, pNode->failure_data);
595    }
596
597    XtFree ((char *)errorMessage);
598    FreeRequest (pNode);
599 }
600
601 Cmd_RequestQueue *
602 _DtCmdGetQueueHead(void)
603 {
604         return requestQueue;
605 }
606
607 /******************************************************************************
608  *
609  * FreeRequest - Frees the malloced data associated with the node.
610  *               and frees the node.
611  *
612  * PARAMETERS:
613  *
614  *  Cmd_RequestQueue *pNode
615  *
616  *
617  *****************************************************************************/
618
619 static void
620 FreeRequest (Cmd_RequestQueue *pNode)
621 {
622
623    if (pNode == NULL)
624       return;
625
626    XtFree (pNode->context);
627    XtFree (pNode->exec_host);
628    XtFree (pNode->exec_string);
629    Cmd_FreeAllocatedStringVector (pNode->argv);
630
631    XtFree ((char *) pNode);
632 }
633
634 /******************************************************************************
635  *
636  * _DtCmdCommandInvokerExecute - executes a request on the specified host.
637  *
638  * RETURNS:  int
639  *
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
644  *
645  *
646  *****************************************************************************/
647
648 int
649 _DtCmdCommandInvokerExecute (
650         char *errorMessage,             /* MODIFIED */
651         DtSvcMsgContext replyContext,   /* OBSOLETE -- always NULL */
652         int  winMask,
653         char *contextHost,
654         char *contextDir,
655         char *contextFile,              /* OBSOLETE -- always NULL */
656         char *execParms,
657         char *execHost,
658         char *execString,
659         char *procId,
660         char *tmpFiles,
661         DtCmdInvExecuteProc success_proc,
662         void *success_data,
663         DtCmdInvExecuteProc failure_proc,
664         void *failure_data)
665
666 {
667    int ioMode, i, index1;
668    int windowType;
669    pid_t commandPid;
670    char context[MAXPATHLEN];
671    char tmpDir [MAXPATHLEN];
672    char **commandArray;
673    SPC_Channel_Ptr cmdChannel;
674    char *theCommand = NULL;
675    Boolean terminalRequest = False;
676    char *commandArray2[MAX_EXEC_ARGS];
677    Boolean localExecution = True;
678    Boolean xhostError;
679    static unsigned long requestNum = 0;
680    char *toolRequest = NULL;    /* backward compatibility kludge */
681
682
683    myassert( !(contextFile && replyContext) );
684
685    /*
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.
689     */
690    if ((windowType=
691         DtCmdGetWindowType(winMask))== -1) 
692    {
693       (void) sprintf (errorMessage, errorRequest, toolRequest, 
694                       DtTERMINAL, DtPERM_TERMINAL, DtOUTPUT_ONLY, 
695                       DtSHARED_OUTPUT, "" /* Obsolete shell window */,
696                       DtNO_STDIO);
697       return (_CMD_EXECUTE_FATAL);
698    }
699
700    /* 
701     * Create the command to be exec'ed.
702     */
703    if (windowType == PERM_TERMINAL || windowType == TERMINAL) 
704    {
705       _DtCmdCreateTerminalCommand (&theCommand, windowType, execString, 
706          execParms, execHost, procId, tmpFiles);
707       terminalRequest = True;
708    }
709    else 
710    {
711       /* 
712        * NO-STDIO || START-SESSION request.
713        */
714       
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 */);
722
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), 
728           execString);
729
730    }
731
732    /*
733     * See if the request requires Remote Execution.
734     */
735    localExecution = _DtIsSameHost(execHost,NULL);
736
737    /*
738     * If this is a terminalRequest and the Command Invoker subprocess
739     * is not executable, return now.
740     */
741    if (localExecution && terminalRequest && !cmd_Globals.subprocess_ok) 
742    {
743       if (!(_DtCmdCheckForExecutable (cmd_Resources.dtexecPath))) 
744       {
745          (void) sprintf (errorMessage, cmd_Globals.error_subprocess,
746                          cmd_Resources.dtexecPath);
747          XtFree ((char *) theCommand);
748          return (_CMD_EXECUTE_FAILURE);
749       }
750       else
751          cmd_Globals.subprocess_ok = True;
752    }
753
754    /*
755     * If this is a terminalRequest and the terminal emulator
756     * is not executable, return now.
757     */
758    if (localExecution && terminalRequest && !cmd_Globals.terminal_ok) 
759    {
760       if (!(_DtCmdCheckForExecutable (cmd_Resources.localTerminal))) 
761       {
762          (void) sprintf (errorMessage, cmd_Globals.error_terminal, 
763                          cmd_Resources.localTerminal);
764          XtFree ((char *) theCommand);
765          return (_CMD_EXECUTE_FAILURE);
766       }
767       else
768          cmd_Globals.terminal_ok = True;
769    }
770
771    /* 
772     * Break the command into something execvp or SPCSpawn can handle
773     * and then free "theCommand" if this is a termianl-based request.
774     */
775    commandArray = (char **) XtMalloc (MAX_EXEC_ARGS * sizeof (char *));
776    _DtCmdStringToArrayOfStrings (theCommand, commandArray);
777
778    XtFree (theCommand);
779
780    if (!localExecution) 
781    {
782       char *netfile;
783       char *argv[4];
784       char *tmp;
785
786       /* REMOTE Execution */
787
788       ioMode = SPCIO_NOIO | SPCIO_SYNC_TERMINATOR | SPCIO_FORCE_CONTEXT;
789
790       if ((cmdChannel = (_DtSPCOpen(execHost, 
791                          ioMode, 
792                          errorMessage))) == SPC_ERROR) 
793       {
794          Cmd_FreeAllocatedStringVector (commandArray);
795          return (_CMD_EXECUTE_FAILURE);
796       }
797
798       /* Old syntax should no longer appear in contextHost/Dir */
799       myassert( (contextHost?*contextHost != '*':1) && 
800                 (contextDir?*contextDir != '*':1) );
801       /* 
802        * Create a "netfile" for the cwd to be used.
803        */
804       netfile = (char *) tt_host_file_netfile (
805          ((contextHost == NULL) ? execHost : contextHost),
806          ((contextDir  == NULL) ? (char *) getenv ("HOME") : contextDir));
807
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);
815       }
816       (void) strcpy (context, netfile);
817       tt_free (netfile);
818
819      /*
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.
823       *
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.
827       */
828
829      argv[0] = cmd_Resources.dtexecPath;
830      argv[1] = (char *) NULL;
831
832      if ((_DtSPCSpawn(argv[0], context, argv, NULL, cmdChannel,
833                       execHost, contextHost, contextDir, errorMessage)) 
834           == SPC_ERROR) 
835      {
836         if (DtSPCErrorNumber != SPC_cannot_Chdir &&
837             DtSPCErrorNumber != SPC_Cannot_Fork  &&
838             DtSPCErrorNumber != SPC_Env_Too_Big  &&
839             DtSPCErrorNumber != SPC_Arg_Too_Long)
840            /*
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.
845             */
846            (void) sprintf (errorMessage, errorRemoteSubprocess, execHost, 
847                            cmd_Resources.dtexecPath);
848         DtSPCClose(cmdChannel);
849         Cmd_FreeAllocatedStringVector (commandArray);
850         return (_CMD_EXECUTE_FAILURE);
851      }
852
853      /* The dtexec process is now known to exist on the remote host */
854
855      /*
856       *  Now run a test to see if the command is executable
857       *  on this exec host.
858       */
859      _DtCmdStringToArrayOfStrings (execString, commandArray2);
860
861      tmp = (char *) XtMalloc (strlen (commandArray2[0]) +
862                              strlen ("whence ") + 2);
863      (void) sprintf (tmp, "whence %s", commandArray2[0]);
864
865      _DtCmdFreeStringVector (commandArray2);
866
867      argv[0] = "ksh";
868      argv[1] = "-c";
869      argv[2] = tmp;
870      argv[3] = (char *) NULL;
871
872      /*
873       * Reopen the channel
874       */
875      if ((cmdChannel = (_DtSPCOpen(execHost, 
876                          ioMode, 
877                          errorMessage))) == SPC_ERROR) 
878       {
879          Cmd_FreeAllocatedStringVector (commandArray);
880          return (_CMD_EXECUTE_FAILURE);
881       }
882
883       /* 
884        * Set up a callback to be invoked when the test command 
885        * terminates.
886        */
887       _DtSvcProcessLock();
888       if ((DtSPCRegisterTerminator(cmdChannel,
889                     (SPC_TerminateHandlerType) CheckCommandTerminator,
890                     (void *) ++requestNum)) == SPC_ERROR) 
891       {
892         DtSPCClose(cmdChannel);
893         Cmd_FreeAllocatedStringVector (commandArray);
894         (void) strcpy (errorMessage, errorSpcTerminator);
895         XtFree ((char *) tmp);
896         _DtSvcProcessUnlock();
897         return (_CMD_EXECUTE_FAILURE);
898       }
899       
900       if ((_DtSPCSpawn(argv[0], context, argv, NULL, cmdChannel,
901                       execHost, contextHost, contextDir, errorMessage)) 
902           == SPC_ERROR) 
903       {
904         DtSPCClose(cmdChannel);
905         (void) sprintf (errorMessage, errorRemoteSubprocess, execHost,
906                 argv[0]);
907         Cmd_FreeAllocatedStringVector (commandArray);
908         XtFree ((char *) tmp);
909         _DtSvcProcessUnlock();
910         return (_CMD_EXECUTE_FAILURE);
911       }
912       /* 
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
917        * terminates.
918        */
919       QueueRequest (cmdChannel, context, execHost, execString, 
920               commandArray, windowType, requestNum, replyContext,
921               success_proc, success_data, failure_proc, failure_data);
922       _DtSvcProcessUnlock();
923       XtFree(tmp);
924       return (_CMD_EXECUTE_QUEUED);
925    }
926    else 
927    {     
928       /* LOCAL Execution */
929
930       /* 
931        * Must first check to see if the execvp will potentially fail.
932        *
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.
937        */
938      /* Break the command into something execvp can handle */
939        _DtCmdStringToArrayOfStrings (execString, commandArray2);
940
941       if (!_DtCmdCheckForExecutable (commandArray2[0])) 
942       {
943          (void) sprintf (errorMessage, errorExec, commandArray2[0]);
944          Cmd_FreeAllocatedStringVector (commandArray);
945          _DtCmdFreeStringVector (commandArray2);
946          return (_CMD_EXECUTE_FAILURE);
947       }
948       _DtCmdFreeStringVector (commandArray2);
949
950       /*
951        * Save the current directory and then "chdir" to the directory
952        * to do the execution.  If the chdir fails, return.
953        */
954       if(NULL == getcwd (tmpDir, MAXPATHLEN))
955       {
956          perror(strerror(errno));
957          return (_CMD_EXECUTE_FAILURE);  
958       }
959
960       if (!_DtCmdValidDir (_cmdClientHost, contextDir, contextHost)) 
961       {
962          Cmd_FreeAllocatedStringVector (commandArray);
963          (void) sprintf (errorMessage, errorChdir, contextDir, execHost);
964          if(-1 == chdir (tmpDir)) {
965             perror(strerror(errno));
966          }
967          return (_CMD_EXECUTE_FAILURE);
968       }
969
970       /*
971        * Restore the original environment and remove any DT
972        * specific environment variables that were added.
973        */
974       (void) _DtEnvControl (DT_ENV_RESTORE_PRE_DT);
975
976       /* Fork and then execvp the execution string */
977       for (index1 = 0; (index1 < 10) && 
978                ((commandPid = fork ()) < 0); index1++) 
979       {
980          /* Out of resources ? */
981          if (errno != EAGAIN) 
982             break;
983          /* If not out of resources, sleep and try again */
984          (void) sleep ((unsigned long) 2);
985       }
986
987       if (commandPid < 0) 
988       {
989          Cmd_FreeAllocatedStringVector (commandArray);
990          if(-1 == chdir (tmpDir)) {
991              perror(strerror(errno)); 
992          }
993          (void) sprintf(errorMessage, errorFork, execHost);
994          (void) _DtEnvControl (DT_ENV_RESTORE_POST_DT);
995          return (_CMD_EXECUTE_FAILURE);
996       }
997
998       if (commandPid == 0) 
999       {
1000
1001 #if defined(__hp_osf) || defined(__osf__) || defined(CSRG_BASED)
1002          setsid() ;
1003 #else
1004          (void) setpgrp ();
1005 #endif
1006         
1007          if (!terminalRequest ) 
1008          {
1009             int fd;
1010
1011             /*
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.
1016              */
1017             if ((fd = open ("/dev/null", O_RDWR)) > 0)
1018                (void) dup2 (fd, fileno (stdout));
1019          }
1020
1021          /* 
1022           * Mark file descriptiors >=3 as "Close on Exec".
1023           */
1024          {
1025            long open_max;
1026
1027            open_max = sysconf(_SC_OPEN_MAX);
1028            if (open_max == -1)
1029            {
1030 #ifdef _SUN_OS
1031              open_max = NOFILE;
1032 #else
1033 #if defined(USL) || defined(_AIX)
1034              open_max = FOPEN_MAX;
1035 #else
1036              open_max = FD_SETSIZE;
1037 #endif
1038 #endif /* _SUN_OS */
1039            }
1040
1041            for (i=3; i < open_max; i++)
1042                (void) fcntl (i, F_SETFD, 1);
1043          }
1044
1045
1046          (void) execvp (commandArray[0], commandArray);
1047          
1048          /* Should never get here, but if you do, must exit */
1049
1050          /*
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
1054           * terminal.
1055           */
1056          (void) sprintf (errorMessage, errorExec, commandArray[0]);
1057          (void) printf ("%s\n", errorMessage);
1058          (void) _exit (1);
1059       }
1060
1061       /*
1062        * Restore the pre-fork environment.
1063        */
1064       (void) chdir (tmpDir);
1065       (void) _DtEnvControl (DT_ENV_RESTORE_POST_DT);
1066    }
1067
1068    Cmd_FreeAllocatedStringVector (commandArray);
1069
1070    return (_CMD_EXECUTE_SUCCESS);
1071 }
1072
1073 /******************************************************************************
1074  *
1075  * CheckCommandTerminator - this is the SPC termination callback 
1076  *   that is invoked when the command line checking process terminates.  
1077  *
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,
1081  *   return.
1082  *
1083  * PARAMETERS:  This parameters for this callback are those defined by
1084  *   the type "SPC_TerminateHandlerType".  Most are not used.
1085  *
1086  *****************************************************************************/
1087
1088 static void 
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. */
1095 {
1096    Boolean xhostError;
1097    char errorMessage[MAX_BUF_SIZE];
1098    Cmd_RequestQueue *prev  = NULL;
1099    Cmd_RequestQueue *pNode = requestQueue;
1100
1101    DtSPCClose(cmdChannel);
1102
1103    /*
1104     * Must find the node in the queue.
1105     */
1106    for (; pNode != NULL; pNode = pNode->next)
1107    {
1108        if ( pNode->request_num == requestNum )
1109        {
1110            /*
1111             * Pluck pNode out of the queue
1112             */
1113            if (prev)
1114                prev->next = pNode->next;
1115            else
1116                requestQueue = pNode->next;
1117
1118            pNode->next = NULL;
1119            break;
1120        }
1121        /*
1122         * Move alone to the next node
1123         */
1124        prev = pNode;
1125    }
1126
1127    if (pNode == NULL)
1128       return;
1129
1130    /*
1131     * Check the exit status of the remote process.
1132     */
1133    if (cause == COMMAND_CHECK_FAILURE)
1134    {
1135       if (pNode->failure_proc != NULL)
1136       {
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);
1142       }
1143
1144       FreeRequest (pNode);
1145
1146       return;
1147    }
1148
1149     /*
1150      * Re-queue this node -- we will execute the command
1151      */
1152     pNode->next = requestQueue;
1153     requestQueue = pNode;
1154
1155     ExecuteQueuedRequest (requestNum);
1156 }
1157
1158 /******************************************************************************
1159  * 
1160  * DtCmdGetWindowType - given a window-type mask, determine its'
1161  *   internal window type number.
1162  *
1163  * It should not be possible for this to be called with a bogus mask, 
1164  *
1165  * PARAMETERS:
1166  *
1167  *   unsigned long mask;        - The request window type mask
1168  *
1169  * RETURNS: The window type if one if found, otherwise "-1".
1170  *
1171  *****************************************************************************/
1172
1173 static int 
1174 DtCmdGetWindowType(
1175         unsigned long windowTypeMask) 
1176 {
1177         int winTypeNum = -1;
1178
1179         /*
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.
1184          */
1185
1186         switch ( windowTypeMask )
1187         {
1188         case _DtAct_NO_STDIO_BIT:
1189                 winTypeNum = 0;
1190                 break;
1191         case _DtAct_TERMINAL_BIT:
1192                 winTypeNum = TERMINAL;
1193                 break;
1194         case _DtAct_PERM_TERM_BIT:
1195                 winTypeNum = PERM_TERMINAL;
1196                 break;
1197         default:
1198                 myassert(0);    /* should never get here */
1199                 winTypeNum = PERM_TERMINAL;
1200                 break;
1201         } 
1202
1203         
1204         return winTypeNum;
1205 }
1206
1207 /******************************************************************************
1208  *
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 
1214  *      commands.
1215  *
1216  *      If logging is turned on, the success or failure message is logged.
1217  ******************************************************************************/
1218 int
1219 _DtActionCommandInvoke(
1220         long wintype,
1221         char * cwdHost,
1222         char * cwdDir,
1223         char * execString,
1224         char * termOpts,
1225         char * execHost,
1226         char * procId,
1227         char * tmpFiles,
1228         void (*success_proc)(),
1229         void *success_data,
1230         void (*failure_proc)(),
1231         void *failure_data)
1232 {
1233         int status;
1234         char errorMessage[MAX_BUF_SIZE * 2];
1235
1236         status = _DtCmdCommandInvokerExecute (errorMessage, NULL, 
1237                                wintype, 
1238                                cwdHost, cwdDir, NULL,
1239                                termOpts, execHost, execString,
1240                                procId, tmpFiles,
1241                                success_proc, success_data,
1242                                failure_proc, failure_data);
1243         switch (status)
1244         {
1245         case _CMD_EXECUTE_SUCCESS :
1246                 if (cmd_Resources.executionHostLogging && success_data != NULL)
1247                 {
1248                         CallbackData *data;
1249                         data = (CallbackData *) success_data;
1250                         (void) sprintf (errorMessage, successHost, 
1251                                 data->actionLabel, execHost);
1252                         _DtCmdLogErrorMessage (errorMessage);
1253                 }
1254                 if (success_proc != NULL)
1255                         (*success_proc) (NULL, success_data);
1256                 break;
1257         case _CMD_EXECUTE_QUEUED :
1258                  /*
1259                   * Return for now and when the termination handler
1260                   * gets hit, the queued request will be executed.
1261                   */
1262                  break;
1263         default :
1264                  /*
1265                   * _CMD_EXECUTE_FAILURE or _CMD_EXECUTE_FATAL
1266                   */
1267                 if (cmd_Resources.executionHostLogging)
1268                         _DtCmdLogErrorMessage (errorMessage);
1269                 if (failure_proc != NULL)
1270                         (*failure_proc) (errorMessage, failure_data);
1271                 break;
1272         }
1273
1274         return (status == _CMD_EXECUTE_QUEUED) ? 1 : 0;
1275 }
1276
1277 /******************************************************************************
1278  *
1279  * _DtCommandInvokerExecute -
1280  *
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.
1284  *
1285  *   For local execution, the "fork" and "execvp" system calls are
1286  *   used.  For remote execution, the "SPCD" is used.
1287  *
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.
1291  *
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).  
1295  *
1296  *****************************************************************************/
1297
1298 void
1299 _DtCommandInvokerExecute(
1300         char *request_name,
1301         char *context_host,
1302         char *context_dir,
1303         char *context_file,
1304         char *exec_parameters,
1305         char *exec_host,
1306         char *exec_string,
1307         DtCmdInvExecuteProc success_proc,
1308         void *success_data,
1309         DtCmdInvExecuteProc failure_proc,
1310         void *failure_data)
1311
1312 {
1313    _DtSvcAppLock(cmd_Globals.app_context);
1314
1315    _DtActionCommandInvoke(_DtAct_NO_STDIO_BIT,context_host,
1316                 context_dir, exec_string, exec_parameters, exec_host,
1317                 NULL, NULL,
1318                 success_proc,success_data, failure_proc,failure_data);
1319    _DtSvcAppUnlock(cmd_Globals.app_context);
1320                 
1321 }
1322
1323 /******************************************************************************
1324  *
1325  * _DtInitializeCommandInvoker - initialize some global variables and
1326  *   and call the appropriate initialization routines.
1327  *
1328  * PARAMETERS:
1329  *
1330  *   Display *display;          - The X server connection.
1331  *
1332  *   char *toolClass;           - The client's tool class.
1333  *
1334  *   char *appClass;            - The client's application class.
1335  *
1336  *   DtSvcMessageProc reloadDBHandler; - The callback function for handling
1337  *                                       a "RELOAD-TYPES-DB" request. (OBSOLETE)
1338  *
1339  *   XtAppContext appContext;   - The client's application context.
1340  *
1341  * MODIFIED:
1342  *
1343  *   SbInputId (*SbAddInput_hookfn);            - Set to _DtCmdSPCAddInputHandler
1344  *
1345  *   SbInputId (*SbRemoveInput_hookfn);         - Set to XtRemoveInput
1346
1347  *   SbInputId (*SbAddException_hookfn);        - Set to _DtCmdSPCAddExceptionHandler
1348  *   SbInputId (*SbRemoveException_hookfn);     - Set to XtRemoveInput
1349  *
1350  *****************************************************************************/
1351
1352 void 
1353 _DtInitializeCommandInvoker(
1354         Display *display,
1355         char *toolClass,        /* ignored */
1356         char *appClass,         /* ignored */
1357         DtSvcMessageProc reloadDBHandler,       /* OBSOLETE -- ignored */
1358         XtAppContext appContext)
1359 {
1360    static       int beenCalled = 0;
1361
1362    _DtSvcAppLock(appContext);
1363
1364    /*
1365     * Prevent repeat calls
1366     */
1367    _DtSvcProcessLock();
1368    if ( beenCalled ) {
1369         _DtSvcProcessUnlock();
1370         return;
1371       }
1372
1373    beenCalled++;
1374
1375    cmd_Globals.app_context = appContext;
1376
1377    SbAddInput_hookfn = _DtCmdSPCAddInputHandler;
1378
1379    SbRemoveInput_hookfn = XtRemoveInput;
1380
1381    SbAddException_hookfn = _DtCmdSPCAddExceptionHandler;
1382
1383    SbRemoveException_hookfn = XtRemoveInput;
1384
1385    _DtCmdBuildPathList ();
1386
1387    _DtCmdInitializeErrorMessages ();
1388
1389    /*
1390     * Must get the name of the invoking host, so that requests
1391     * can be checked for remote execution.
1392     */
1393    if ((DtGetShortHostname(_cmdClientHost, MAXHOSTNAMELEN)) == -1) {
1394       _DtCmdLogErrorMessage ("Cannot determine the local host name.\n");
1395    }
1396
1397    _DtCmdGetResources (display);
1398
1399    _DtSvcProcessUnlock();
1400    _DtSvcAppUnlock(appContext);
1401 }
1402
1403 /*****************************************************************************
1404  *
1405  * _DtCmdInitializeErrorMessages - initializes all of the command invoker's
1406  *   error messages.
1407  *
1408  * PARAMETERS: None.
1409  *
1410  * MODIFIED:  all of the Command Invoker's error messages are initialized.
1411  *
1412  *****************************************************************************/
1413
1414 static void 
1415 _DtCmdInitializeErrorMessages( void )
1416 {
1417
1418    /* 
1419     * Non-Fatal -> Abort the request
1420     */
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.")));
1422
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.")));
1424
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.")));
1426
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\".")));
1428
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.")));
1430
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.")));
1432
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")));
1434
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.")));
1436
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")));
1438
1439         /*
1440          * The following errors may occur when a SPC
1441          * channel is opened.
1442          */
1443    errorUnknownHost = XtNewString (((char *)Dt11GETMESSAGE(3, 24, "This action cannot be executed because\nhost \"%s\" cannot be reached.")));
1444
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.")));
1446
1447    errorBadService = XtNewString (((char *)Dt11GETMESSAGE(3, 26, "This action cannot be executed because the \"%s\"\nservice is not configured on host \"%s\".")));
1448
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\".")));
1450
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.")));
1452
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.")));
1454
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.")));
1456
1457    errorEnvTooBig = XtNewString (((char *)Dt11GETMESSAGE(3, 31, "This action cannot be executed on host \"%s\" because\nthe environment exceeds \"%d\" bytes.")));
1458
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\".")));
1460
1461    /* 
1462     * Do not post a dialog, write to the error log file only.
1463     */
1464
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.")));
1466
1467    successHost = strdup (((char *)Dt11GETMESSAGE(3, 21, "The action \"%s\" was successfully executed on host \"%s\".")));
1468
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)")));
1470
1471 }