Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtlogin / server.c
1 /*                                                                      *
2  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
3  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
4  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
5  * (c) Copyright 1993, 1994 Novell, Inc.                                *
6  */
7 /*
8  * xdm - display manager daemon
9  *
10  * $XConsortium: server.c /main/4 1995/10/27 16:14:56 rswiston $
11  *
12  * Copyright 1988 Massachusetts Institute of Technology
13  *
14  * Permission to use, copy, modify, and distribute this software and its
15  * documentation for any purpose and without fee is hereby granted, provided
16  * that the above copyright notice appear in all copies and that both that
17  * copyright notice and this permission notice appear in supporting
18  * documentation, and that the name of M.I.T. not be used in advertising or
19  * publicity pertaining to distribution of the software without specific,
20  * written prior permission.  M.I.T. makes no representations about the
21  * suitability of this software for any purpose.  It is provided "as is"
22  * without express or implied warranty.
23  *
24  * Author:  Keith Packard, MIT X Consortium
25  */
26
27 # include       <sys/signal.h>
28 # include       <setjmp.h>
29 # include       <pwd.h>
30 # include       "dm.h"
31 # include       "vgmsg.h"
32
33 static receivedUsr1;
34
35
36
37 /***************************************************************************
38  *
39  *  Local procedure declarations
40  *
41  ***************************************************************************/
42
43 static char * _SysErrorMsg( int n) ;
44 static SIGVAL CatchUsr1( int arg ) ;
45 static void   GetRemoteAddress( struct display *d, int fd) ;
46 static SIGVAL PingBlocked( int arg ) ;
47 static SIGVAL PingLost( int arg ) ;
48 static SIGVAL abortOpen( int arg ) ;
49 static int    serverPause( unsigned t, int serverPid) ;
50 static SIGVAL serverPauseAbort( int arg ) ;
51 static SIGVAL serverPauseUsr1( int arg ) ;
52
53
54
55
56
57 /***************************************************************************
58  *
59  *  Global variables
60  *
61  ***************************************************************************/
62
63 static Display  *dpy;
64
65
66
67 static SIGVAL
68 CatchUsr1( int arg )
69 {
70 #if defined(SYSV) || defined(SVR4)
71     (void) signal (SIGUSR1, CatchUsr1);
72 #endif
73     Debug ("Display Manager caught SIGUSR1\n");
74     ++receivedUsr1;
75 }
76
77 static char * 
78 _SysErrorMsg( int n )
79 {
80
81     char *s = ((n >= 0 && n < sys_nerr) ? sys_errlist[n] : "unknown error");
82
83     return (s ? s : "no such error");
84 }
85
86 int 
87 StartServerOnce( struct display *d )
88 {
89     char        **f;
90     char        **argv;
91     char        arg[1024];
92     int         pid;
93     char        **env;
94     
95     extern struct passwd   puser;       /* pseudo_user password entry   */
96
97     Debug ("Starting server for %s\n", d->name);
98     receivedUsr1 = 0;
99     signal (SIGUSR1, CatchUsr1);
100     argv = d->argv;
101     switch (pid = fork ()) {
102     case 0:
103         CleanUpChild ();
104         if (d->authFile) {
105             sprintf (arg, "-auth %s", d->authFile);
106             argv = parseArgs (argv, arg);
107         }
108         if (!argv) {
109             LogError(ReadCatalog(MC_LOG_SET,MC_LOG_NO_ARGS,MC_DEF_LOG_NO_ARGS));
110             exit(1);
111         }
112         Debug("Server invoked as ");
113         for (f = argv; *f; f++)
114             Debug ("'%s' ", *f);
115         Debug ("\n");
116
117         /*
118          *  set the permissions on  console devices to  pseudo-user.
119          *  run the server as a pseudo-user, not root...
120          */
121 #ifdef sun
122         if (solaris_setdevperm(d->gettyLine, puser.pw_uid, puser.pw_gid) == 0)
123              Debug ("Unable to set permissions on console devices ..\n");
124         else {
125 #endif
126              setgid (puser.pw_gid);
127              setuid (puser.pw_uid);
128 #ifdef sun
129         }
130 #endif
131
132         /*
133          *  build the server environment (if any)...
134          */
135         env = 0;
136         if (d->environStr && strlen(d->environStr) > 0)
137             env = parseEnv(env, d->environStr);
138 #ifdef sun
139         if (getEnv (env, "OPENWINHOME") == NULL) 
140             env = setEnv(env, "OPENWINHOME", "/usr/openwin");
141 #endif
142 #ifdef _AIX
143         if (getEnv (env, "ODMDIR") == NULL)
144             env = setEnv(env, "ODMDIR", "/etc/objrepos");
145 #ifdef _POWER
146         env = setEnv(env, "XTOEXEC", "true");           /* flag for xserverrc */
147 #endif
148 #endif
149
150         /*
151          * give the server SIGUSR1 ignored,
152          * it will notice that and send SIGUSR1
153          * when ready
154          */
155         signal (SIGUSR1, SIG_IGN);
156         (void) execve (argv[0], argv, env);
157         LogError(ReadCatalog(
158                 MC_LOG_SET,MC_LOG_NO_EXESRV,MC_DEF_LOG_NO_EXESRV),argv[0]);
159         exit(1);
160     case -1:
161         LogError(ReadCatalog(MC_LOG_SET,MC_LOG_FAIL_FORK,MC_DEF_LOG_FAIL_FORK));
162         return 0;
163     default:
164         break;
165     }
166     Debug ("Server started. Process ID = %d\n", pid);
167     d->serverPid = pid;
168     if (serverPause ((unsigned) d->openDelay, pid))
169         return FALSE;
170     return TRUE;
171 }
172
173
174 int 
175 StartServer( struct display *d )
176 {
177     int i;
178     int ret = FALSE;
179
180     i = 0;
181     while (d->serverAttempts == 0 || i < d->serverAttempts)
182     {
183         if ((ret = StartServerOnce (d)) == TRUE)
184             break;
185         sleep (d->openDelay);
186         i++;
187     }
188     return ret;
189 }
190
191
192 /*
193  * sleep for t seconds, return 1 if the server is dead when
194  * the sleep finishes, 0 else
195  */
196
197 static jmp_buf  pauseAbort;
198 static int      serverPauseRet;
199
200 static SIGVAL
201 serverPauseAbort( int arg )
202 {
203     Debug ("Display Manager pause timed out\n");
204     longjmp (pauseAbort, 1);
205 }
206
207 static SIGVAL
208 serverPauseUsr1( int arg )
209 {
210     Debug ("Display Manager pause received SIGUSR1\n");
211     ++receivedUsr1;
212     longjmp (pauseAbort, 1);
213 }
214
215 static int 
216 serverPause( unsigned t, int serverPid )
217 {
218     int         pid;
219
220     serverPauseRet = 0;
221     Debug ("Display Manager pausing until SIGUSR1 from server or timeout\n");
222     if (!setjmp (pauseAbort)) {
223         signal (SIGALRM, serverPauseAbort);
224         signal (SIGUSR1, serverPauseUsr1);
225 #ifdef SYSV
226         if (receivedUsr1)
227             alarm ((unsigned) 1);
228         else
229             alarm (t);
230 #else
231         if (!receivedUsr1)
232             alarm (t);
233         else
234             Debug ("ServerPause(): already received USR1\n");
235 #endif
236         for (;;) {
237 #ifdef SYSV
238             pid = wait ((waitType *) 0);
239 #else
240             if (!receivedUsr1)
241                 pid = wait ((waitType *) 0);
242             else
243 #  ifdef        SVR4
244                 {
245                     int dummy;
246                 pid = waitpid ((pid_t) 0,&dummy,WNOHANG);
247                 }
248 #  else
249                 pid = wait3 ((waitType *) 0, WNOHANG,
250                              (struct rusage *) 0);
251 #  endif
252 #endif
253             if (pid == serverPid ||
254                 pid == -1 && errno == ECHILD)
255             {
256                 Debug ("Server dead\n");
257                 serverPauseRet = 1;
258                 break;
259             }
260 #ifndef SYSV
261             if (pid == 0) {
262                 Debug ("Server alive and kicking\n");
263                 break;
264             }
265 #endif
266         }
267     }
268     alarm ((unsigned) 0);
269     signal (SIGALRM, SIG_DFL);
270     signal (SIGUSR1, CatchUsr1);
271     if (serverPauseRet) {
272         Debug ("Server died\n");
273         LogError(ReadCatalog(MC_LOG_SET,MC_LOG_SRV_DIED,MC_DEF_LOG_SRV_DIED));
274     }
275     return serverPauseRet;
276 }
277
278
279 /*
280  * this code is complicated by some TCP failings.  On
281  * many systems, the connect will occasionally hang forever,
282  * this trouble is avoided by setting up a timeout to longjmp
283  * out of the connect (possibly leaving piles of garbage around
284  * inside Xlib) and give up, terminating the server.
285  */
286
287 static jmp_buf  openAbort;
288
289 static SIGVAL
290 abortOpen( int arg )
291 {
292         longjmp (openAbort, 1);
293 }
294
295 static void
296 GetRemoteAddress( struct display *d, int fd )
297 {
298     char    buf[512];
299     int     len = sizeof (buf);
300
301     if (d->peer)
302         free ((char *) d->peer);
303     getpeername (fd, (struct sockaddr *) buf, &len);
304     d->peerlen = 0;
305     if (len)
306     {
307         d->peer = (struct sockaddr *) malloc (len);
308         if (d->peer)
309         {
310             bcopy (buf, (char *) d->peer, len);
311             d->peerlen = len;
312         }
313     }
314     Debug ("Got remote address %s %d\n", d->name, d->peerlen);
315 }
316
317
318
319 /****************************************************************************
320  *
321  *  LogOpenError()
322  *
323  *  If d->startAttempts is a large number and a connection cannot be made to
324  *  the server, the error log can fill up rapidly with error messages. This
325  *  could be common in X-terminals that do not support XDMCP and are turned
326  *  off over a weekend. This routine attempts to reduce the number of error
327  *  messages logged in this scenario.
328  *  
329  ****************************************************************************/
330
331 int 
332 LogOpenError( int count )
333 {
334
335     if ( count <= 10                        ) return 1;
336     if ( count <= 100 && (count %  10 == 0) ) return 1;
337     if ( count <= 500 && (count %  50 == 0) ) return 1;
338     if (                 (count % 100 == 0) ) return 1;
339     
340     return 0;
341 }
342
343
344 int 
345 WaitForServer( struct display *d )
346 {
347     int     i;
348
349     for (i = 0; i < (d->openRepeat > 0 ? d->openRepeat : 1); i++) {
350         (void) signal (SIGALRM, abortOpen);
351         (void) alarm ((unsigned) d->openTimeout);
352         if (!setjmp (openAbort)) {
353             Debug ("Before XOpenDisplay(%s)\n", d->name);
354             errno = 0;
355             dpy = XOpenDisplay (d->name);
356             (void) alarm ((unsigned) 0);
357             (void) signal (SIGALRM, SIG_DFL);
358             Debug ("After XOpenDisplay()\n");
359             if (dpy) {
360                 if (d->displayType.location == Foreign)
361                     GetRemoteAddress (d, ConnectionNumber (dpy));
362                 RegisterCloseOnFork (ConnectionNumber (dpy));
363                 (void) fcntl (ConnectionNumber (dpy), F_SETFD, 0);
364                 return 1;
365             } else {
366                 Debug ("OpenDisplay failed %d (%s)\n",
367                        errno, _SysErrorMsg (errno));
368             }
369             Debug ("Waiting for server to start %d\n", i);
370             sleep ((unsigned) d->openDelay);
371         } else {
372             Debug ("Hung in open, aborting\n");
373             if (LogOpenError(d->startTries))
374                 LogError(ReadCatalog(
375                         MC_LOG_SET,MC_LOG_HUNG_DPY,MC_DEF_LOG_HUNG_DPY),
376                            d->name, d->startTries);
377             (void) signal (SIGALRM, SIG_DFL);
378             break;
379         }
380     }
381     Debug ("Giving up on server\n");
382     if (LogOpenError(d->startTries))
383         LogError(ReadCatalog(
384                 MC_LOG_SET,MC_LOG_FAIL_SRVOPEN,MC_DEF_LOG_FAIL_SRVOPEN),
385                 d->startTries, d->name);
386     return 0;
387 }
388
389 void
390 ResetServer( struct display *d )
391 {
392     if (dpy && d->displayType.origin != FromXDMCP)
393         pseudoReset (dpy);
394 }
395
396
397 /****************************************************************************
398  *
399  *  Server pinging routines...
400  *
401  *  These routines attempt to determine if the server is still alive.
402  *  Periodically (d->pingInterval) an XSync is sent to the server. If an I/O
403  *  error occurs, then the connection has been lost and the server needs to
404  *  be reset. If the server is blocked for some reason (i.e. server grab) the
405  *  XSync will block until a local timer expires. In this case, we just note
406  *  the block and continue...
407  *
408  *  7/26/90 - prr
409  *  The XSync was replaced by a socket-level ping. For some reason, an XSync
410  *  to a grabbed server causes a subsequent pseudoReset (KillClients) to not 
411  *  kill all clients. The socket ping is not affected by a grabbed server,
412  *  but it cannot detect a server shutdown and restart within one ping
413  *  interval. 
414  *
415  ****************************************************************************/
416
417 static jmp_buf  pingTime;
418 static int      serverDead = FALSE;
419
420 static SIGVAL
421 PingLost( int arg )
422 {
423     serverDead = TRUE;
424     longjmp (pingTime, 1);
425 }
426
427
428 static SIGVAL
429 PingBlocked( int arg )
430 {
431     serverDead = FALSE;
432     longjmp (pingTime, 1);
433 }
434
435
436 int 
437 PingServer( struct display *d, Display *alternateDpy )
438 {
439     int     (*oldError)();
440     SIGVAL  (*oldSig)();
441     int     oldAlarm;
442
443     if (!alternateDpy)
444         alternateDpy = dpy;
445     oldError = XSetIOErrorHandler ((XIOErrorHandler)PingLost);
446     oldAlarm = alarm (0);
447     oldSig = signal (SIGALRM, PingBlocked);
448     alarm (d->pingTimeout * 60);
449     if (!setjmp (pingTime))
450     {
451         Debug ("Ping server\n");
452         XNoOp (alternateDpy);
453         XSync (alternateDpy, 0);
454         Debug ("Server alive\n");
455
456         while (XPending(alternateDpy)) {
457             XEvent event;
458             XNextEvent(alternateDpy, &event);
459         }
460     }
461     else
462     {
463         if ( serverDead ) {
464             Debug ("Server dead\n");
465             alarm (0);
466             signal (SIGALRM, SIG_DFL);
467             XSetIOErrorHandler (oldError);
468             return 0;
469         }
470         else
471             Debug ("Server blocked, continuing...\n");
472     }
473     alarm (0);
474     signal (SIGALRM, oldSig);
475     alarm (oldAlarm);
476     XSetIOErrorHandler (oldError);
477     return 1;
478 }