Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtterm / DtTermServer.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 librararies 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 #ifndef lint
24 #ifdef  VERBOSE_REV_INFO
25 static char rcs_id[] = "$TOG: DtTermServer.c /main/5 1998/07/23 18:09:38 mgreess $";
26 #endif  /* VERBOSE_REV_INFO */
27 #endif  /* lint */
28
29 /*                                                                      *
30  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
31  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
32  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
33  * (c) Copyright 1993, 1994 Novell, Inc.                                *
34  */
35
36 #include <sys/types.h>
37 #ifdef  TERMINAL_SERVER
38 #include "TermHeader.h"
39 #include "TermPrimDebug.h"
40 #include "TermView.h"
41 #include "DtTermServer.h"
42 #include "TermPrimSetPty.h"
43 #ifdef  LOG_USAGE
44 #include "DtTermLogit.h"
45 #endif  /* LOG_USAGE */
46 #include <signal.h>
47 #include <errno.h>
48 #include <Dt/Service.h>
49
50 /* defines for types of service requests.  Upper case for the requestor
51  * types, lower case for the server types...
52  */
53 #define SVC_SUCCESS     'S'
54 #define SVC_FAIL        'F'
55 #define SVC_NOTIFY      'N'
56
57 #define SVC_REQUEST     'r'
58 #define SVC_LOSE        'l'
59
60 #define DTTERM_SVC_CLASS                "DTTERM"
61 #define DTTERM_SVC_START_MSG            "DTTERM-START"
62 #define DTTERM_SVC_TERMINATE_MSG        "DTTERM-TERMINATE"
63 #define DTTERM_SVC_TERMINATION_MSG      "DTTERM-TERMINATION"
64
65 static XtIntervalId pingId = (XtIntervalId) 0;
66 int PingInterval = 5;
67
68 #ifdef  TIMEOUT
69 #define STICKAROUND     15              /* 15 minutes... */
70 static XtIntervalId waitId = (XtIntervalId) 0;
71 #endif  /* TIMEOUT */
72
73 static Boolean ExitOnLastClose;
74
75 static char *serviceName = (char *) 0;
76 static DtSvcHandle serviceHandle;
77 static Boolean iAmTheServer = False;
78 static Boolean waitingForReply = False;
79 static Boolean waitedForReply = False;
80 static Widget refWidget;
81 char *ServerFailureMessage = (char *) 0;
82 int ServerFailureErrno = 0;
83 int InstanceCount = 0;
84
85 typedef struct _ServiceClientInfoRec {
86     pid_t pid;
87     Widget shellWidget;
88     struct _ServiceClientInfoRec *prev;
89     struct _ServiceClientInfoRec *next;
90 } ServiceClientInfoRec, *ServiceClientInfo;
91     
92 static ServiceClientInfoRec serviceClientInfoHeadRec;
93 static ServiceClientInfo serviceClientInfoHead = &serviceClientInfoHeadRec;
94 static Boolean initFlag = False;
95
96 static void Initialize(
97     Widget                topLevel,
98     int                   argc,
99     char                **argv,
100     char                 *serverId
101 );
102
103 static void clientMessageProc(
104     DtSvcHandle   service,
105     Pointer               clientData,
106     String               *messageFields,
107     int                   numFields
108 );
109
110 static void serverRequestProc(
111     DtSvcHandle   service,
112     DtSvcMsgContext       replyContext,
113     Pointer               clientData,
114     String               *messageFields,
115     int                   numFields
116 );
117
118 static void serverMessageProc(
119     DtSvcHandle   service,
120     Pointer               clientData,
121     String               *messageFields,
122     int                   numFields
123 );
124
125 static void Ping(
126     XtPointer             clientData,
127     XtIntervalId         *id
128 );
129
130 #ifdef  TIMEOUT
131 static void TimeOut(
132     XtPointer             clientData,
133     XtIntervalId         *id
134 );
135 #endif  /* TIMEOUT */
136
137 static void CleanUp(
138     int                   sig
139 );
140
141
142 static const int trapSignalList[] = {
143     SIGINT,
144     SIGQUIT,
145     SIGTERM,
146     SIGUSR1,
147     SIGUSR2,
148 };
149
150 /*ARGSUSED*/
151 static void
152 Initialize(
153     Widget                topLevel,
154     int                   argc,
155     char                **argv,
156     char                 *serverId
157 )
158 {
159     char                  hostname[BUFSIZ];
160     int                   i;
161     pid_t                 pid;
162     struct sigaction      sa;
163
164     if (initFlag) {
165         /* already initialized... */
166         return;
167     }
168
169     refWidget = topLevel;
170
171 #ifdef  NOTDEF
172     /* build a service name.  The service name needs to be application,
173      * host, and username specific.  Since we are talking through the
174      * display connection, it will already be display specific.
175      * The format of the service name will be "DTTERM-hostname-uid"
176      * (i.e., "DTTERM-hpcvxds.cv.hp.com-201")...
177      */
178     char *buffer = (char*) malloc(BUFSIZ);
179     if (gethostname(hostname, sizeof(hostname))) {
180         (void) strcpy(hostname, "unknown");
181     }
182     (void) sprintf(buffer, "%s-%s-%ld", DTTERM_SVC_CLASS, hostname, (long)getuid());
183     serviceName = XtMalloc(strlen(buffer) + 1);
184     (void) strcpy(serviceName, buffer);
185     free(buffer);
186 #endif  /* NOTDEF */
187
188     /* we will use serverId as the service name... */
189     serviceName = XtMalloc(strlen(serverId) + 1);
190     (void) strcpy(serviceName, serverId);
191
192     /* get a handle... */
193     serviceHandle = _DtSvcNewHandle(serviceName, refWidget);
194
195     /* register with the server... */
196     if (DT_SVC_SUCCESS == _DtSvcRegister(
197             serviceHandle,
198             False,
199             serverRequestProc,
200             (XtPointer) SVC_REQUEST,
201             serverMessageProc,
202             (XtPointer) SVC_LOSE)) {
203
204         /* We are the new server.  We need to do several things:
205          *
206          *  -fork ourself off.  The server needs to run as a child of
207          *   our application so that it can stay around when our session
208          *   is done.
209          *
210          *  -dissassociate ourself from our parent.
211          *
212          *  -have the child re-exec ourself.  This will allow the child
213          *   to request a session and talk to us as any other normal
214          *   requestor process.
215          */
216         /* if the 'n' flag is set, we will not daemonize ourself (i.e.,
217          * fork off and run as a child of the current process)...
218          */
219         if (!isDebugSet('n')) {
220             for (i = 0; (i < 10) && ((pid = fork()) < 0); i++) {
221                 /* if we are out of process slots, then let's sleep
222                  * a bit and try again...
223                  */
224                 if (errno != EAGAIN) {
225                     break;
226                 }
227
228                 /* give it a chance to clear up... */
229                 (void) sleep((unsigned long) 2);
230             }
231         } else {
232             pid = 0;
233         }
234
235         if (pid < 0) {
236             /* can't do much of anything and we haven't done much of
237              * anything.  Let's just error out...
238              */
239             (void) perror("fork()");
240             (void) exit(1);
241         } else if (pid > 0) {
242             /* parent.  Let's clean up, restart, and let the new process
243              * try again...
244              */
245             /* close the server connection... */
246             (void) close(ConnectionNumber(XtDisplay(refWidget)));
247             (void) execvp(argv[0], argv);
248             (void) perror(argv[0]);
249             (void) _exit(1);
250         }
251
252         /* child server process...
253          */
254         /* set the iAmTheServer flag to True.  This flag will remain True
255          * until we lose ownership of the service...
256          */
257         iAmTheServer = True;
258
259         /* set up signal handlers so that we can clean up nicely... */
260         (void) sigemptyset(&sa.sa_mask);
261         sa.sa_flags = 0;
262         sa.sa_handler = CleanUp;
263
264         for (i = 0; i < (sizeof(trapSignalList) / sizeof(trapSignalList[0]));
265                 i++) {
266             (void) sigaction(trapSignalList[i], &sa, (struct sigaction *) 0);
267         }
268
269         /* register for service.  This will allow us to deal with the
270          * case where we lose ownership of the service.  We will then
271          * be able to listen to termination requests from our client
272          * applications and shut down the sessions when requested to
273          * do so...
274          */
275         (void) _DtSvcNotifyGroupRegister(
276                 serviceHandle,
277                 serverMessageProc,
278                 (XtPointer) SVC_NOTIFY);
279
280         /* get our initial tty modes before we go and create a new
281          * session id...
282          */
283         (void) _DtTermPrimPtyGetDefaultModes();
284
285         /* new session group... */
286         (void) setsid();
287     } else {
288
289         /* we are not the server.  We need to register for messages.  Then
290          * we are done and the service world is now for us...
291          */
292         (void) _DtSvcNotifyGroupRegister(
293                 serviceHandle,
294                 clientMessageProc,
295                 (XtPointer) SVC_NOTIFY);
296     }
297
298     /* install a ping timeout... */
299     if (PingInterval > 0) {
300         pingId = XtAppAddTimeOut(XtWidgetToApplicationContext(topLevel),
301                 1000 * 60 * PingInterval, Ping, (XtPointer) topLevel);
302     }
303
304     /* make sure we are never called again... */
305     initFlag = True;
306 }
307
308
309 static void
310 NiceCleanUp(
311     XtPointer             clientData,
312     XtIntervalId         *id
313 )
314 {
315     ServiceClientInfo     serviceClientInfo;
316     char                  buffer[BUFSIZ];
317     String                args[20];
318     int                   argcnt;
319
320     /* find the serviceClientInfoRec for this widget... */
321     for (serviceClientInfo = serviceClientInfoHead->next; serviceClientInfo;
322             serviceClientInfo = serviceClientInfo->next) {
323
324         /* notify each client that the session (is being) terminated... */
325         argcnt = 0;
326         (void) sprintf(buffer, "%ld", (long)serviceClientInfo->pid);
327         args[argcnt] = buffer; argcnt++;
328         (void) _DtSvcNotifySend(
329                 serviceHandle,
330                 DTTERM_SVC_TERMINATION_MSG,
331                 args,
332                 argcnt);
333         (void) XSync(XtDisplay(refWidget), 0);
334     }
335
336     /* we can now exit... */
337     (void) exit(1);
338 }
339
340 static void
341 CleanUp(
342     int                   sig
343 )
344 {
345     static Boolean        firstTime = True;
346
347     if (firstTime) {
348         /* let's try to do this nicely and invoke our cleanup routine
349          * via the toolkit (i.e., outside of a signal handler)...
350          */
351         (void) XtAppAddTimeOut(XtWidgetToApplicationContext(refWidget),
352                     0, NiceCleanUp, (XtPointer) refWidget);
353         firstTime = False;
354     } else {
355         /* we have received multiple attempts to kill ourself.  Just
356          * exit...
357          */
358         (void) exit(1);
359     }
360 }
361
362
363 /*ARGSUSED*/
364 static void
365 serverMessageProc(
366     DtSvcHandle   service,
367     Pointer               clientData,
368     String               *messageFields,
369     int                   numFields
370 )
371 {
372     switch ((int) clientData) {
373     case SVC_LOSE:
374         /* we lost control of the service... */
375         iAmTheServer = False;
376
377         if (InstanceCount <= 0) {
378             /* no reason to stay around... */
379             (void) _DtSvcDestroyHandle(serviceHandle);
380             (void) exit(0);
381         }
382         break;
383     }
384 }
385
386
387 /*ARGSUSED*/
388 static void
389 serverRequestProc(
390     DtSvcHandle   service,
391     DtSvcMsgContext       replyContext,
392     Pointer               clientData,
393     String               *messageFields,
394     int                   numFields
395 )
396 {
397     Widget                shellWidget;
398     ServiceClientInfo     serviceClientInfo;
399     char                  buffer[BUFSIZ];
400     String                reply[20];
401     pid_t                 pid = -1;
402     Arg                   arglist[20];
403     int                   i1;
404     int                   i2;
405     int                   argcnt = 0;
406     char                **commandToExecute = (char **) 0;
407
408     switch ((int) clientData) {
409     case SVC_REQUEST:
410         if (!strcmp(messageFields[0], DTTERM_SVC_START_MSG)) {
411             /* create our shell widget... */
412             argcnt = 0;
413             (void) XtSetArg(arglist[argcnt], XmNallowShellResize, True);
414                     argcnt++;
415             shellWidget = XtAppCreateShell((char *) 0, "Dtterm",
416                     applicationShellWidgetClass, XtDisplay((Widget) refWidget),
417                     arglist, argcnt);
418
419             /* parse off messageFields and build the dttermview arglist... */
420             argcnt = 0;
421
422             for (i2 = 1; i2 < numFields; i2++) {
423                 if (!strcmp(messageFields[i2], "-pid")) {
424                     (void) i2++;
425                     if (i2 < numFields) {
426                         pid = (pid_t) strtol(messageFields[i2], (char **) 0, 0);
427                     }
428
429                 } else if (!strcmp(messageFields[i2], "-ls")) {
430                     (void) XtSetArg(arglist[argcnt], XmNloginShell,
431                             True); argcnt++;
432
433                 } else if (!strcmp(messageFields[i2], "+ls")) {
434                     (void) XtSetArg(arglist[argcnt], XmNloginShell,
435                             False); argcnt++;
436
437                 } else if (!strcmp(messageFields[i2], "-e")) {
438                     (void) i2++;
439                     if (i2 < numFields) {
440                         /* DKS: somewhere we will need to free this... */
441                         commandToExecute = (char **)
442                                 XtMalloc((numFields - i2 + 1) *
443                                 sizeof(char *));
444                         for (i1 = 0; i2 < numFields; i1++, i2++) {
445                             commandToExecute[i1] = messageFields[i2];
446                         }
447                         /* null term commandToExecute... */
448                         commandToExecute[i1] = (char *) 0;
449                         (void) XtSetArg(arglist[argcnt], XmNsubprocessArgv,
450                             commandToExecute); argcnt++;
451                     }
452                 }
453             }
454
455             /* create the dtterm... */
456             (void) CreateInstance(shellWidget, "Dtterm",
457                     arglist, argcnt);
458
459             /* create the ServiceClientInfoRec for this instance...
460              */
461             serviceClientInfo =
462                     (ServiceClientInfo) XtMalloc(sizeof(ServiceClientInfoRec));
463             serviceClientInfo->pid = pid;
464             serviceClientInfo->shellWidget = shellWidget;
465             /* insert it at the head of the list... */
466             serviceClientInfo->next = serviceClientInfoHead->next;
467             serviceClientInfo->prev = serviceClientInfoHead;
468             if (serviceClientInfoHead->next) {
469                 serviceClientInfoHead->next->prev = serviceClientInfo;
470             }
471             serviceClientInfoHead->next = serviceClientInfo;
472
473             (void) XtRealizeWidget(shellWidget);
474
475             InstanceCount++;
476             /* since we now have active instances, we can remove our
477              * wait timeout...
478              */
479 #ifdef  TIMEOUT
480             if (waitId) {
481                 (void) XtRemoveTimeOut(waitId);
482                 waitId = (XtIntervalId) 0;
483             }
484 #endif  /* TIMEOUT */
485             
486             /* ack the reply... */
487             i2 = 0;
488             (void) sprintf(buffer, "0x%lx", shellWidget);
489             reply[i2] = buffer; i2++;
490             (void) _DtSvcRequestReply(
491                     serviceHandle,
492                     replyContext,
493                     reply,
494                     i2,
495                     True);
496         }
497     }
498 }
499
500
501 /*ARGSUSED*/
502 static void
503 clientMessageProc(
504     DtSvcHandle   service,
505     Pointer               clientData,
506     String               *messageFields,
507     int                   numFields
508 )
509 {
510     int                   i1;
511     pid_t                 pid;
512     char                  buffer[BUFSIZ];
513
514     switch ((int) clientData) {
515         case SVC_NOTIFY:
516             /* process the notify message... */
517             if (!strcmp(messageFields[0], DTTERM_SVC_TERMINATION_MSG)) {
518                 if (numFields >= 2) {
519                     pid = (pid_t) strtol(messageFields[1], (char **) 0, 0);
520                 } else {
521                     pid = -1;
522                 }
523
524                 if (pid == getpid()) {
525                     /* our session terminated -- exit... */
526                     (void) _DtSvcDestroyHandle(serviceHandle);
527                     (void) exit(0);
528                 }
529             }
530             break;
531
532         case SVC_FAIL:
533             /* turn on the waitingForReply flag... */
534             waitingForReply = False;
535             /* set the waitedForReply flag to True (i.e., failure)... */
536             waitedForReply = True;
537
538             /* get errno (if returned)... */
539             if (numFields >= 2) {
540                 errno = (int) strtol(messageFields[1], (char **) 0, 0);
541             }
542             /* build any failure message... */
543             *buffer = '\0';
544             for (i1 = 2; i1 < numFields; i1++) {
545                 if (*buffer) {
546                     (void) strcat(buffer, " ");
547                 }
548                 (void) strcat(buffer, messageFields[i1]);
549             }
550             ServerFailureMessage =
551                     XtRealloc(ServerFailureMessage,
552                     strlen(buffer));
553             (void) strcpy(ServerFailureMessage, buffer);
554             break;
555
556         case SVC_SUCCESS:
557             /* turn on the waitingForReply flag... */
558             waitingForReply = False;
559             /* set the waitedForReply flag to False (i.e., success)... */
560             waitedForReply = False;
561             break;
562     }
563 }
564
565
566 Boolean
567 ServerStartSession(
568     Widget                topLevel,
569     int                   argc,
570     char                **argv,
571     Boolean               server,
572     char                 *serverId,
573     Boolean               exitOnLastClose,
574     Boolean               block,
575     Boolean               loginShell,
576     char                **commandToExec
577 )
578 {
579     char                  buffer[BUFSIZ];
580     String                args[20];
581     int                   argcnt;
582     int                   i1;
583     XEvent                event;
584     XtAppContext          appContext;
585
586     ExitOnLastClose = exitOnLastClose;
587
588     (void) Initialize(topLevel, argc, argv, serverId);
589     if (iAmTheServer) {
590         /* we are the server.  We need to wait for our clients to make
591          * a request of us.
592          */
593
594 #ifdef  LOG_USAGE
595         /* log our startup... */
596         (void) LogStart(0, argc, argv);
597 #endif  /* LOG_USAGE */
598
599         return(True);
600     }
601
602     /* if we go to this point and the -server option was specified, we
603      * should go away.  Otherwise we should request service...
604      */
605     if (server) {
606         /* all that was requested was creation of a server.  We can go
607          * away now...
608          */
609         (void) exit(0);
610     }
611
612     /* make a request of the server to start a session...
613      */
614     /* need 2 for "-pid" and "<pid>"... */
615     argcnt = 2;
616     if (commandToExec && *commandToExec) {
617         /* need one for "-e"... */
618         (void) argcnt++;
619
620         /* need one for each string in the command... */
621         /*EMPTY*/
622         for (i1 = 0; commandToExec[i1]; i1++) {
623             ;
624         }
625         argcnt += i1;
626     }
627
628     argcnt = 0;
629     args[argcnt] = "-pid"; argcnt++;
630     (void) sprintf(buffer, "%ld", (long)getpid());
631     args[argcnt] = buffer; argcnt++;
632
633     if (loginShell) {
634         args[argcnt] = "-ls"; argcnt++;
635     } else {
636         args[argcnt] = "+ls"; argcnt++;
637     }
638
639     if (commandToExec && *commandToExec) {
640         args[argcnt] = "-e"; argcnt++;
641         for (i1 = 0; commandToExec[i1]; i1++) {
642             args[argcnt] = commandToExec[i1]; argcnt++;
643         }
644     }
645
646     if (DT_SVC_FAIL == _DtSvcRequestSend(
647             serviceHandle,
648             DTTERM_SVC_START_MSG,
649             args,
650             argcnt,
651             clientMessageProc,
652             (XtPointer) SVC_SUCCESS,
653             clientMessageProc,
654             (XtPointer) SVC_FAIL)) {
655         (void) fprintf(stderr, "request to server failed\n");
656         return(True);
657     }
658
659     /* dispatch locally until we get back the results from our server... */
660     appContext = XtWidgetToApplicationContext(topLevel);
661     for (waitingForReply = True; waitingForReply; ) {
662         (void) XtAppNextEvent(appContext, &event);
663         (void) XtDispatchEvent(&event);
664     }
665
666     if (!block) {
667         /* we succeeded, we can exit now... */
668         (void) _DtSvcDestroyHandle(serviceHandle);
669         (void) exit(0);
670     }
671
672     return(waitedForReply);
673 }
674
675 void
676 ServerInstanceTerminated(
677     Widget                w
678 )
679 {
680     ServiceClientInfo     serviceClientInfo;
681     char                  buffer[BUFSIZ];
682     String                args[20];
683     int                   argcnt;
684
685     /* find the serviceClientInfoRec for this widget... */
686     for (serviceClientInfo = serviceClientInfoHead->next; serviceClientInfo;
687             serviceClientInfo = serviceClientInfo->next) {
688         if (serviceClientInfo->shellWidget == w) {
689             break;
690         }
691     }
692
693     if (serviceClientInfo && (serviceClientInfo->shellWidget == w)) {
694         /* notify the client that the session terminated... */
695         argcnt = 0;
696         (void) sprintf(buffer, "%ld", (long)serviceClientInfo->pid);
697         args[argcnt] = buffer; argcnt++;
698         (void) _DtSvcNotifySend(
699                 serviceHandle,
700                 DTTERM_SVC_TERMINATION_MSG,
701                 args,
702                 argcnt);
703
704         /* free up the serviceClientInfoRec... */
705         serviceClientInfo->prev->next = serviceClientInfo->next;
706         if (serviceClientInfo->next) {
707             serviceClientInfo->next->prev = serviceClientInfo->prev;
708         }
709         (void) XtFree((char *) serviceClientInfo);
710     }
711
712     if ((--InstanceCount <= 0) && (!iAmTheServer)) {
713         (void) _DtSvcDestroyHandle(serviceHandle);
714         (void) exit(0);
715     } else
716 #ifdef  TIMEOUT
717     if ((InstanceCount <= 0) && !waitId) {
718         /* set up a wait timeout and stick around for a while before
719          * we exit...
720          */
721         waitId = XtAppAddTimeOut(XtWidgetToApplicationContext(refWidget),
722                 1000 * 60 * STICKAROUND, TimeOut, (XtPointer) refWidget);
723     }
724 #endif  /* TIMEOUT */
725
726     if ((InstanceCount <= 0) && ExitOnLastClose) {
727         (void) exit(0);
728     }
729 }
730
731 #ifdef  TIMEOUT
732 static void
733 TimeOut(
734     XtPointer             clientData,
735     XtIntervalId         *id
736 )
737 {
738     /* if we have no instances active, go away... */
739     if (InstanceCount <= 0) {
740         (void) exit(0);
741     }
742
743     /* otherwise, clear the waitId... */
744     if (*id == waitId) {
745         waitId = (XtIntervalId) 0;
746     }
747 }
748 #endif  /* TIMEOUT */
749
750 static void
751 Ping(
752     XtPointer             clientData,
753     XtIntervalId         *id
754 )
755 {
756     Widget                w = (Widget) clientData;
757     Window                root;
758     Window                child;
759     int                   rootX;
760     int                   rootY;
761     int                   winX;
762     int                   winY;
763     unsigned int          mask;
764
765     if (*id != pingId) {
766         return;
767     }
768
769     /* cause a round trip to the server... */
770     (void) XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child,
771             &rootX, &rootY, &winX, &winY, &mask);
772
773     /* reset the timeout... */
774     if (PingInterval > 0) {
775         pingId = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
776                 1000 * 60 * PingInterval, Ping, clientData);
777     }
778 }
779 #else
780 /* dummy variable to get pass compilation phase */
781 static char *foo;
782 #endif  /* TERMINAL_SERVER */