2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
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 */
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. *
36 #include <sys/types.h>
37 #ifdef TERMINAL_SERVER
38 #include "TermHeader.h"
39 #include "TermPrimDebug.h"
41 #include "DtTermServer.h"
42 #include "TermPrimSetPty.h"
44 #include "DtTermLogit.h"
45 #endif /* LOG_USAGE */
48 #include <Dt/Service.h>
50 /* defines for types of service requests. Upper case for the requestor
51 * types, lower case for the server types...
53 #define SVC_SUCCESS 'S'
55 #define SVC_NOTIFY 'N'
57 #define SVC_REQUEST 'r'
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"
65 static XtIntervalId pingId = (XtIntervalId) 0;
69 #define STICKAROUND 15 /* 15 minutes... */
70 static XtIntervalId waitId = (XtIntervalId) 0;
73 static Boolean ExitOnLastClose;
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;
85 typedef struct _ServiceClientInfoRec {
88 struct _ServiceClientInfoRec *prev;
89 struct _ServiceClientInfoRec *next;
90 } ServiceClientInfoRec, *ServiceClientInfo;
92 static ServiceClientInfoRec serviceClientInfoHeadRec;
93 static ServiceClientInfo serviceClientInfoHead = &serviceClientInfoHeadRec;
94 static Boolean initFlag = False;
96 static void Initialize(
103 static void clientMessageProc(
106 String *messageFields,
110 static void serverRequestProc(
112 DtSvcMsgContext replyContext,
114 String *messageFields,
118 static void serverMessageProc(
121 String *messageFields,
126 XtPointer clientData,
132 XtPointer clientData,
142 static const int trapSignalList[] = {
159 char hostname[BUFSIZ];
165 /* already initialized... */
169 refWidget = topLevel;
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")...
178 char *buffer = (char*) malloc(BUFSIZ);
179 if (gethostname(hostname, sizeof(hostname))) {
180 (void) strcpy(hostname, "unknown");
182 (void) sprintf(buffer, "%s-%s-%ld", DTTERM_SVC_CLASS, hostname, (long)getuid());
183 serviceName = XtMalloc(strlen(buffer) + 1);
184 (void) strcpy(serviceName, buffer);
188 /* we will use serverId as the service name... */
189 serviceName = XtMalloc(strlen(serverId) + 1);
190 (void) strcpy(serviceName, serverId);
192 /* get a handle... */
193 serviceHandle = _DtSvcNewHandle(serviceName, refWidget);
195 /* register with the server... */
196 if (DT_SVC_SUCCESS == _DtSvcRegister(
200 (XtPointer) SVC_REQUEST,
202 (XtPointer) SVC_LOSE)) {
204 /* We are the new server. We need to do several things:
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
210 * -dissassociate ourself from our parent.
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
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)...
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...
224 if (errno != EAGAIN) {
228 /* give it a chance to clear up... */
229 (void) sleep((unsigned long) 2);
236 /* can't do much of anything and we haven't done much of
237 * anything. Let's just error out...
239 (void) perror("fork()");
241 } else if (pid > 0) {
242 /* parent. Let's clean up, restart, and let the new process
245 /* close the server connection... */
246 (void) close(ConnectionNumber(XtDisplay(refWidget)));
247 (void) execvp(argv[0], argv);
248 (void) perror(argv[0]);
252 /* child server process...
254 /* set the iAmTheServer flag to True. This flag will remain True
255 * until we lose ownership of the service...
259 /* set up signal handlers so that we can clean up nicely... */
260 (void) sigemptyset(&sa.sa_mask);
262 sa.sa_handler = CleanUp;
264 for (i = 0; i < (sizeof(trapSignalList) / sizeof(trapSignalList[0]));
266 (void) sigaction(trapSignalList[i], &sa, (struct sigaction *) 0);
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
275 (void) _DtSvcNotifyGroupRegister(
278 (XtPointer) SVC_NOTIFY);
280 /* get our initial tty modes before we go and create a new
283 (void) _DtTermPrimPtyGetDefaultModes();
285 /* new session group... */
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...
292 (void) _DtSvcNotifyGroupRegister(
295 (XtPointer) SVC_NOTIFY);
298 /* install a ping timeout... */
299 if (PingInterval > 0) {
300 pingId = XtAppAddTimeOut(XtWidgetToApplicationContext(topLevel),
301 1000 * 60 * PingInterval, Ping, (XtPointer) topLevel);
304 /* make sure we are never called again... */
311 XtPointer clientData,
315 ServiceClientInfo serviceClientInfo;
320 /* find the serviceClientInfoRec for this widget... */
321 for (serviceClientInfo = serviceClientInfoHead->next; serviceClientInfo;
322 serviceClientInfo = serviceClientInfo->next) {
324 /* notify each client that the session (is being) terminated... */
326 (void) sprintf(buffer, "%ld", (long)serviceClientInfo->pid);
327 args[argcnt] = buffer; argcnt++;
328 (void) _DtSvcNotifySend(
330 DTTERM_SVC_TERMINATION_MSG,
333 (void) XSync(XtDisplay(refWidget), 0);
336 /* we can now exit... */
345 static Boolean firstTime = True;
348 /* let's try to do this nicely and invoke our cleanup routine
349 * via the toolkit (i.e., outside of a signal handler)...
351 (void) XtAppAddTimeOut(XtWidgetToApplicationContext(refWidget),
352 0, NiceCleanUp, (XtPointer) refWidget);
355 /* we have received multiple attempts to kill ourself. Just
368 String *messageFields,
372 switch ((int) clientData) {
374 /* we lost control of the service... */
375 iAmTheServer = False;
377 if (InstanceCount <= 0) {
378 /* no reason to stay around... */
379 (void) _DtSvcDestroyHandle(serviceHandle);
391 DtSvcMsgContext replyContext,
393 String *messageFields,
398 ServiceClientInfo serviceClientInfo;
406 char **commandToExecute = (char **) 0;
408 switch ((int) clientData) {
410 if (!strcmp(messageFields[0], DTTERM_SVC_START_MSG)) {
411 /* create our shell widget... */
413 (void) XtSetArg(arglist[argcnt], XmNallowShellResize, True);
415 shellWidget = XtAppCreateShell((char *) 0, "Dtterm",
416 applicationShellWidgetClass, XtDisplay((Widget) refWidget),
419 /* parse off messageFields and build the dttermview arglist... */
422 for (i2 = 1; i2 < numFields; i2++) {
423 if (!strcmp(messageFields[i2], "-pid")) {
425 if (i2 < numFields) {
426 pid = (pid_t) strtol(messageFields[i2], (char **) 0, 0);
429 } else if (!strcmp(messageFields[i2], "-ls")) {
430 (void) XtSetArg(arglist[argcnt], XmNloginShell,
433 } else if (!strcmp(messageFields[i2], "+ls")) {
434 (void) XtSetArg(arglist[argcnt], XmNloginShell,
437 } else if (!strcmp(messageFields[i2], "-e")) {
439 if (i2 < numFields) {
440 /* DKS: somewhere we will need to free this... */
441 commandToExecute = (char **)
442 XtMalloc((numFields - i2 + 1) *
444 for (i1 = 0; i2 < numFields; i1++, i2++) {
445 commandToExecute[i1] = messageFields[i2];
447 /* null term commandToExecute... */
448 commandToExecute[i1] = (char *) 0;
449 (void) XtSetArg(arglist[argcnt], XmNsubprocessArgv,
450 commandToExecute); argcnt++;
455 /* create the dtterm... */
456 (void) CreateInstance(shellWidget, "Dtterm",
459 /* create the ServiceClientInfoRec for this instance...
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;
471 serviceClientInfoHead->next = serviceClientInfo;
473 (void) XtRealizeWidget(shellWidget);
476 /* since we now have active instances, we can remove our
481 (void) XtRemoveTimeOut(waitId);
482 waitId = (XtIntervalId) 0;
486 /* ack the reply... */
488 (void) sprintf(buffer, "0x%lx", shellWidget);
489 reply[i2] = buffer; i2++;
490 (void) _DtSvcRequestReply(
506 String *messageFields,
514 switch ((int) clientData) {
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);
524 if (pid == getpid()) {
525 /* our session terminated -- exit... */
526 (void) _DtSvcDestroyHandle(serviceHandle);
533 /* turn on the waitingForReply flag... */
534 waitingForReply = False;
535 /* set the waitedForReply flag to True (i.e., failure)... */
536 waitedForReply = True;
538 /* get errno (if returned)... */
539 if (numFields >= 2) {
540 errno = (int) strtol(messageFields[1], (char **) 0, 0);
542 /* build any failure message... */
544 for (i1 = 2; i1 < numFields; i1++) {
546 (void) strcat(buffer, " ");
548 (void) strcat(buffer, messageFields[i1]);
550 ServerFailureMessage =
551 XtRealloc(ServerFailureMessage,
553 (void) strcpy(ServerFailureMessage, buffer);
557 /* turn on the waitingForReply flag... */
558 waitingForReply = False;
559 /* set the waitedForReply flag to False (i.e., success)... */
560 waitedForReply = False;
573 Boolean exitOnLastClose,
584 XtAppContext appContext;
586 ExitOnLastClose = exitOnLastClose;
588 (void) Initialize(topLevel, argc, argv, serverId);
590 /* we are the server. We need to wait for our clients to make
595 /* log our startup... */
596 (void) LogStart(0, argc, argv);
597 #endif /* LOG_USAGE */
602 /* if we go to this point and the -server option was specified, we
603 * should go away. Otherwise we should request service...
606 /* all that was requested was creation of a server. We can go
612 /* make a request of the server to start a session...
614 /* need 2 for "-pid" and "<pid>"... */
616 if (commandToExec && *commandToExec) {
617 /* need one for "-e"... */
620 /* need one for each string in the command... */
622 for (i1 = 0; commandToExec[i1]; i1++) {
629 args[argcnt] = "-pid"; argcnt++;
630 (void) sprintf(buffer, "%ld", (long)getpid());
631 args[argcnt] = buffer; argcnt++;
634 args[argcnt] = "-ls"; argcnt++;
636 args[argcnt] = "+ls"; argcnt++;
639 if (commandToExec && *commandToExec) {
640 args[argcnt] = "-e"; argcnt++;
641 for (i1 = 0; commandToExec[i1]; i1++) {
642 args[argcnt] = commandToExec[i1]; argcnt++;
646 if (DT_SVC_FAIL == _DtSvcRequestSend(
648 DTTERM_SVC_START_MSG,
652 (XtPointer) SVC_SUCCESS,
654 (XtPointer) SVC_FAIL)) {
655 (void) fprintf(stderr, "request to server failed\n");
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);
667 /* we succeeded, we can exit now... */
668 (void) _DtSvcDestroyHandle(serviceHandle);
672 return(waitedForReply);
676 ServerInstanceTerminated(
680 ServiceClientInfo serviceClientInfo;
685 /* find the serviceClientInfoRec for this widget... */
686 for (serviceClientInfo = serviceClientInfoHead->next; serviceClientInfo;
687 serviceClientInfo = serviceClientInfo->next) {
688 if (serviceClientInfo->shellWidget == w) {
693 if (serviceClientInfo && (serviceClientInfo->shellWidget == w)) {
694 /* notify the client that the session terminated... */
696 (void) sprintf(buffer, "%ld", (long)serviceClientInfo->pid);
697 args[argcnt] = buffer; argcnt++;
698 (void) _DtSvcNotifySend(
700 DTTERM_SVC_TERMINATION_MSG,
704 /* free up the serviceClientInfoRec... */
705 serviceClientInfo->prev->next = serviceClientInfo->next;
706 if (serviceClientInfo->next) {
707 serviceClientInfo->next->prev = serviceClientInfo->prev;
709 (void) XtFree((char *) serviceClientInfo);
712 if ((--InstanceCount <= 0) && (!iAmTheServer)) {
713 (void) _DtSvcDestroyHandle(serviceHandle);
717 if ((InstanceCount <= 0) && !waitId) {
718 /* set up a wait timeout and stick around for a while before
721 waitId = XtAppAddTimeOut(XtWidgetToApplicationContext(refWidget),
722 1000 * 60 * STICKAROUND, TimeOut, (XtPointer) refWidget);
726 if ((InstanceCount <= 0) && ExitOnLastClose) {
734 XtPointer clientData,
738 /* if we have no instances active, go away... */
739 if (InstanceCount <= 0) {
743 /* otherwise, clear the waitId... */
745 waitId = (XtIntervalId) 0;
752 XtPointer clientData,
756 Widget w = (Widget) clientData;
769 /* cause a round trip to the server... */
770 (void) XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child,
771 &rootX, &rootY, &winX, &winY, &mask);
773 /* reset the timeout... */
774 if (PingInterval > 0) {
775 pingId = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
776 1000 * 60 * PingInterval, Ping, clientData);
780 /* dummy variable to get pass compilation phase */
782 #endif /* TERMINAL_SERVER */