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