utils.c revision 65b04b38
1/*
2
3Copyright 1987, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27
28Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
29Copyright 1994 Quarterdeck Office Systems.
30
31                        All Rights Reserved
32
33Permission to use, copy, modify, and distribute this software and its
34documentation for any purpose and without fee is hereby granted,
35provided that the above copyright notice appear in all copies and that
36both that copyright notice and this permission notice appear in
37supporting documentation, and that the names of Digital and
38Quarterdeck not be used in advertising or publicity pertaining to
39distribution of the software without specific, written prior
40permission.
41
42DIGITAL AND QUARTERDECK DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
43SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
44FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT
45OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
46OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
47OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
48OR PERFORMANCE OF THIS SOFTWARE.
49
50*/
51
52#ifdef HAVE_DIX_CONFIG_H
53#include <dix-config.h>
54#endif
55
56#ifdef __CYGWIN__
57#include <stdlib.h>
58#include <signal.h>
59/*
60   Sigh... We really need a prototype for this to know it is stdcall,
61   but #include-ing <windows.h> here is not a good idea...
62*/
63__stdcall unsigned long GetTickCount(void);
64#endif
65
66#if defined(WIN32) && !defined(__CYGWIN__)
67#include <X11/Xwinsock.h>
68#endif
69#include <X11/Xos.h>
70#include <stdio.h>
71#include <time.h>
72#if !defined(WIN32) || !defined(__MINGW32__)
73#include <sys/time.h>
74#include <sys/resource.h>
75#endif
76#include "misc.h"
77#include <X11/X.h>
78#define XSERV_t
79#define TRANS_SERVER
80#define TRANS_REOPEN
81#include <X11/Xtrans/Xtrans.h>
82#include "input.h"
83#include "dixfont.h"
84#include "osdep.h"
85#include "extension.h"
86#ifdef X_POSIX_C_SOURCE
87#define _POSIX_C_SOURCE X_POSIX_C_SOURCE
88#include <signal.h>
89#undef _POSIX_C_SOURCE
90#else
91#if defined(_POSIX_SOURCE)
92#include <signal.h>
93#else
94#define _POSIX_SOURCE
95#include <signal.h>
96#undef _POSIX_SOURCE
97#endif
98#endif
99#ifndef WIN32
100#include <sys/wait.h>
101#endif
102#if !defined(SYSV) && !defined(WIN32)
103#include <sys/resource.h>
104#endif
105#include <sys/stat.h>
106#include <ctype.h>    /* for isspace */
107#include <stdarg.h>
108
109#include <stdlib.h>	/* for malloc() */
110
111#if defined(TCPCONN) || defined(STREAMSCONN)
112# ifndef WIN32
113#  include <netdb.h>
114# endif
115#endif
116
117#include "opaque.h"
118
119#include "dixstruct.h"
120
121#include "xkbsrv.h"
122
123#include "picture.h"
124
125Bool noTestExtensions;
126#ifdef COMPOSITE
127Bool noCompositeExtension = FALSE;
128#endif
129
130#ifdef DAMAGE
131Bool noDamageExtension = FALSE;
132#endif
133#ifdef DBE
134Bool noDbeExtension = FALSE;
135#endif
136#ifdef DPMSExtension
137Bool noDPMSExtension = FALSE;
138#endif
139#ifdef GLXEXT
140Bool noGlxExtension = FALSE;
141Bool noGlxVisualInit = FALSE;
142#endif
143#ifdef SCREENSAVER
144Bool noScreenSaverExtension = FALSE;
145#endif
146#ifdef MITSHM
147Bool noMITShmExtension = FALSE;
148#endif
149#ifdef RANDR
150Bool noRRExtension = FALSE;
151#endif
152Bool noRenderExtension = FALSE;
153#ifdef XCSECURITY
154Bool noSecurityExtension = FALSE;
155#endif
156#ifdef RES
157Bool noResExtension = FALSE;
158#endif
159#ifdef XF86BIGFONT
160Bool noXFree86BigfontExtension = FALSE;
161#endif
162#ifdef XFreeXDGA
163Bool noXFree86DGAExtension = FALSE;
164#endif
165#ifdef XF86DRI
166Bool noXFree86DRIExtension = FALSE;
167#endif
168#ifdef XF86VIDMODE
169Bool noXFree86VidModeExtension = FALSE;
170#endif
171#ifdef XFIXES
172Bool noXFixesExtension = FALSE;
173#endif
174#ifdef PANORAMIX
175/* Xinerama is disabled by default unless enabled via +xinerama */
176Bool noPanoramiXExtension = TRUE;
177#endif
178#ifdef XSELINUX
179Bool noSELinuxExtension = FALSE;
180int selinuxEnforcingState = SELINUX_MODE_DEFAULT;
181#endif
182#ifdef XV
183Bool noXvExtension = FALSE;
184#endif
185#ifdef DRI2
186Bool noDRI2Extension = FALSE;
187#endif
188
189Bool noGEExtension = FALSE;
190
191#define X_INCLUDE_NETDB_H
192#include <X11/Xos_r.h>
193
194#include <errno.h>
195
196Bool CoreDump;
197
198#ifdef PANORAMIX
199Bool PanoramiXExtensionDisabledHack = FALSE;
200#endif
201
202int auditTrailLevel = 1;
203
204#if defined(SVR4) || defined(__linux__) || defined(CSRG_BASED)
205#define HAS_SAVED_IDS_AND_SETEUID
206#endif
207
208OsSigHandlerPtr
209OsSignal(int sig, OsSigHandlerPtr handler)
210{
211    struct sigaction act, oact;
212
213    sigemptyset(&act.sa_mask);
214    if (handler != SIG_IGN)
215	sigaddset(&act.sa_mask, sig);
216    act.sa_flags = 0;
217    act.sa_handler = handler;
218    if (sigaction(sig, &act, &oact))
219      perror("sigaction");
220    return oact.sa_handler;
221}
222
223/*
224 * Explicit support for a server lock file like the ones used for UUCP.
225 * For architectures with virtual terminals that can run more than one
226 * server at a time.  This keeps the servers from stomping on each other
227 * if the user forgets to give them different display numbers.
228 */
229#define LOCK_DIR "/tmp"
230#define LOCK_TMP_PREFIX "/.tX"
231#define LOCK_PREFIX "/.X"
232#define LOCK_SUFFIX "-lock"
233
234#ifndef PATH_MAX
235#include <sys/param.h>
236#ifndef PATH_MAX
237#ifdef MAXPATHLEN
238#define PATH_MAX MAXPATHLEN
239#else
240#define PATH_MAX 1024
241#endif
242#endif
243#endif
244
245static Bool StillLocking = FALSE;
246static char LockFile[PATH_MAX];
247static Bool nolock = FALSE;
248
249/*
250 * LockServer --
251 *      Check if the server lock file exists.  If so, check if the PID
252 *      contained inside is valid.  If so, then die.  Otherwise, create
253 *      the lock file containing the PID.
254 */
255void
256LockServer(void)
257{
258  char tmp[PATH_MAX], pid_str[12];
259  int lfd, i, haslock, l_pid, t;
260  char *tmppath = NULL;
261  int len;
262  char port[20];
263
264  if (nolock) return;
265  /*
266   * Path names
267   */
268  tmppath = LOCK_DIR;
269
270  sprintf(port, "%d", atoi(display));
271  len = strlen(LOCK_PREFIX) > strlen(LOCK_TMP_PREFIX) ? strlen(LOCK_PREFIX) :
272						strlen(LOCK_TMP_PREFIX);
273  len += strlen(tmppath) + strlen(port) + strlen(LOCK_SUFFIX) + 1;
274  if (len > sizeof(LockFile))
275    FatalError("Display name `%s' is too long\n", port);
276  (void)sprintf(tmp, "%s" LOCK_TMP_PREFIX "%s" LOCK_SUFFIX, tmppath, port);
277  (void)sprintf(LockFile, "%s" LOCK_PREFIX "%s" LOCK_SUFFIX, tmppath, port);
278
279  /*
280   * Create a temporary file containing our PID.  Attempt three times
281   * to create the file.
282   */
283  StillLocking = TRUE;
284  i = 0;
285  do {
286    i++;
287    lfd = open(tmp, O_CREAT | O_EXCL | O_WRONLY, 0644);
288    if (lfd < 0)
289       sleep(2);
290    else
291       break;
292  } while (i < 3);
293  if (lfd < 0) {
294    unlink(tmp);
295    i = 0;
296    do {
297      i++;
298      lfd = open(tmp, O_CREAT | O_EXCL | O_WRONLY, 0644);
299      if (lfd < 0)
300         sleep(2);
301      else
302         break;
303    } while (i < 3);
304  }
305  if (lfd < 0)
306    FatalError("Could not create lock file in %s\n", tmp);
307  (void) sprintf(pid_str, "%10ld\n", (long)getpid());
308  (void) write(lfd, pid_str, 11);
309  (void) chmod(tmp, 0444);
310  (void) close(lfd);
311
312  /*
313   * OK.  Now the tmp file exists.  Try three times to move it in place
314   * for the lock.
315   */
316  i = 0;
317  haslock = 0;
318  while ((!haslock) && (i++ < 3)) {
319    haslock = (link(tmp,LockFile) == 0);
320    if (haslock) {
321      /*
322       * We're done.
323       */
324      break;
325    }
326    else {
327      /*
328       * Read the pid from the existing file
329       */
330      lfd = open(LockFile, O_RDONLY);
331      if (lfd < 0) {
332        unlink(tmp);
333        FatalError("Can't read lock file %s\n", LockFile);
334      }
335      pid_str[0] = '\0';
336      if (read(lfd, pid_str, 11) != 11) {
337        /*
338         * Bogus lock file.
339         */
340        unlink(LockFile);
341        close(lfd);
342        continue;
343      }
344      pid_str[11] = '\0';
345      sscanf(pid_str, "%d", &l_pid);
346      close(lfd);
347
348      /*
349       * Now try to kill the PID to see if it exists.
350       */
351      errno = 0;
352      t = kill(l_pid, 0);
353      if ((t< 0) && (errno == ESRCH)) {
354        /*
355         * Stale lock file.
356         */
357        unlink(LockFile);
358        continue;
359      }
360      else if (((t < 0) && (errno == EPERM)) || (t == 0)) {
361        /*
362         * Process is still active.
363         */
364        unlink(tmp);
365	FatalError("Server is already active for display %s\n%s %s\n%s\n",
366		   port, "\tIf this server is no longer running, remove",
367		   LockFile, "\tand start again.");
368      }
369    }
370  }
371  unlink(tmp);
372  if (!haslock)
373    FatalError("Could not create server lock file: %s\n", LockFile);
374  StillLocking = FALSE;
375}
376
377/*
378 * UnlockServer --
379 *      Remove the server lock file.
380 */
381void
382UnlockServer(void)
383{
384  if (nolock) return;
385
386  if (!StillLocking){
387
388  (void) unlink(LockFile);
389  }
390}
391
392/* Force connections to close on SIGHUP from init */
393
394void
395AutoResetServer (int sig)
396{
397    int olderrno = errno;
398
399    dispatchException |= DE_RESET;
400    isItTimeToYield = TRUE;
401    errno = olderrno;
402}
403
404/* Force connections to close and then exit on SIGTERM, SIGINT */
405
406void
407GiveUp(int sig)
408{
409    int olderrno = errno;
410
411    dispatchException |= DE_TERMINATE;
412    isItTimeToYield = TRUE;
413    errno = olderrno;
414}
415
416#if (defined WIN32 && defined __MINGW32__) || defined(__CYGWIN__)
417CARD32
418GetTimeInMillis (void)
419{
420  return GetTickCount ();
421}
422#else
423CARD32
424GetTimeInMillis(void)
425{
426    struct timeval tv;
427
428#ifdef MONOTONIC_CLOCK
429    struct timespec tp;
430    static clockid_t clockid;
431    if (!clockid) {
432#ifdef CLOCK_MONOTONIC_COARSE
433        if (clock_getres(CLOCK_MONOTONIC_COARSE, &tp) == 0 &&
434            (tp.tv_nsec / 1000) <= 1000 &&
435            clock_gettime(CLOCK_MONOTONIC_COARSE, &tp) == 0)
436            clockid = CLOCK_MONOTONIC_COARSE;
437        else
438#endif
439        if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
440            clockid = CLOCK_MONOTONIC;
441        else
442            clockid = ~0L;
443    }
444    if (clockid != ~0L && clock_gettime(clockid, &tp) == 0)
445        return (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000L);
446#endif
447
448    X_GETTIMEOFDAY(&tv);
449    return(tv.tv_sec * 1000) + (tv.tv_usec / 1000);
450}
451#endif
452
453void
454AdjustWaitForDelay (pointer waitTime, unsigned long newdelay)
455{
456    static struct timeval   delay_val;
457    struct timeval	    **wt = (struct timeval **) waitTime;
458    unsigned long	    olddelay;
459
460    if (*wt == NULL)
461    {
462	delay_val.tv_sec = newdelay / 1000;
463	delay_val.tv_usec = 1000 * (newdelay % 1000);
464	*wt = &delay_val;
465    }
466    else
467    {
468	olddelay = (*wt)->tv_sec * 1000 + (*wt)->tv_usec / 1000;
469	if (newdelay < olddelay)
470	{
471	    (*wt)->tv_sec = newdelay / 1000;
472	    (*wt)->tv_usec = 1000 * (newdelay % 1000);
473	}
474    }
475}
476
477void UseMsg(void)
478{
479    ErrorF("use: X [:<display>] [option]\n");
480    ErrorF("-a #                   default pointer acceleration (factor)\n");
481    ErrorF("-ac                    disable access control restrictions\n");
482    ErrorF("-audit int             set audit trail level\n");
483    ErrorF("-auth file             select authorization file\n");
484    ErrorF("-br                    create root window with black background\n");
485    ErrorF("+bs                    enable any backing store support\n");
486    ErrorF("-bs                    disable any backing store support\n");
487    ErrorF("-c                     turns off key-click\n");
488    ErrorF("c #                    key-click volume (0-100)\n");
489    ErrorF("-cc int                default color visual class\n");
490    ErrorF("-nocursor              disable the cursor\n");
491    ErrorF("-core                  generate core dump on fatal error\n");
492    ErrorF("-dpi int               screen resolution in dots per inch\n");
493#ifdef DPMSExtension
494    ErrorF("-dpms                  disables VESA DPMS monitor control\n");
495#endif
496    ErrorF("-deferglyphs [none|all|16] defer loading of [no|all|16-bit] glyphs\n");
497    ErrorF("-f #                   bell base (0-100)\n");
498    ErrorF("-fc string             cursor font\n");
499    ErrorF("-fn string             default font name\n");
500    ErrorF("-fp string             default font path\n");
501    ErrorF("-help                  prints message with these options\n");
502    ErrorF("-I                     ignore all remaining arguments\n");
503#ifdef RLIMIT_DATA
504    ErrorF("-ld int                limit data space to N Kb\n");
505#endif
506#ifdef RLIMIT_NOFILE
507    ErrorF("-lf int                limit number of open files to N\n");
508#endif
509#ifdef RLIMIT_STACK
510    ErrorF("-ls int                limit stack space to N Kb\n");
511#endif
512    ErrorF("-nolock                disable the locking mechanism\n");
513    ErrorF("-nolisten string       don't listen on protocol\n");
514    ErrorF("-noreset               don't reset after last client exists\n");
515    ErrorF("-background [none]     create root window with no background\n");
516    ErrorF("-reset                 reset after last client exists\n");
517    ErrorF("-p #                   screen-saver pattern duration (minutes)\n");
518    ErrorF("-pn                    accept failure to listen on all ports\n");
519    ErrorF("-nopn                  reject failure to listen on all ports\n");
520    ErrorF("-r                     turns off auto-repeat\n");
521    ErrorF("r                      turns on auto-repeat \n");
522    ErrorF("-render [default|mono|gray|color] set render color alloc policy\n");
523    ErrorF("-retro                 start with classic stipple and cursor\n");
524    ErrorF("-noretro               start with black background and no cursor\n");
525    ErrorF("-s #                   screen-saver timeout (minutes)\n");
526    ErrorF("-t #                   default pointer threshold (pixels/t)\n");
527    ErrorF("-terminate             terminate at server reset\n");
528    ErrorF("-to #                  connection time out\n");
529    ErrorF("-tst                   disable testing extensions\n");
530    ErrorF("ttyxx                  server started from init on /dev/ttyxx\n");
531    ErrorF("v                      video blanking for screen-saver\n");
532    ErrorF("-v                     screen-saver without video blanking\n");
533    ErrorF("-wm                    WhenMapped default backing-store\n");
534    ErrorF("-wr                    create root window with white background\n");
535    ErrorF("-maxbigreqsize         set maximal bigrequest size \n");
536#ifdef PANORAMIX
537    ErrorF("+xinerama              Enable XINERAMA extension\n");
538    ErrorF("-xinerama              Disable XINERAMA extension\n");
539#endif
540    ErrorF("-dumbSched             Disable smart scheduling, enable old behavior\n");
541    ErrorF("-schedInterval int     Set scheduler interval in msec\n");
542    ErrorF("-sigstop               Enable SIGSTOP based startup\n");
543    ErrorF("+extension name        Enable extension\n");
544    ErrorF("-extension name        Disable extension\n");
545#ifdef XDMCP
546    XdmcpUseMsg();
547#endif
548    XkbUseMsg();
549    ddxUseMsg();
550}
551
552/*  This function performs a rudimentary sanity check
553 *  on the display name passed in on the command-line,
554 *  since this string is used to generate filenames.
555 *  It is especially important that the display name
556 *  not contain a "/" and not start with a "-".
557 *                                            --kvajk
558 */
559static int
560VerifyDisplayName(const char *d)
561{
562    if ( d == (char *)0 ) return 0;  /*  null  */
563    if ( *d == '\0' ) return 0;  /*  empty  */
564    if ( *d == '-' ) return 0;  /*  could be confused for an option  */
565    if ( *d == '.' ) return 0;  /*  must not equal "." or ".."  */
566    if ( strchr(d, '/') != (char *)0 ) return 0;  /*  very important!!!  */
567    return 1;
568}
569
570/*
571 * This function parses the command line. Handles device-independent fields
572 * and allows ddx to handle additional fields.  It is not allowed to modify
573 * argc or any of the strings pointed to by argv.
574 */
575void
576ProcessCommandLine(int argc, char *argv[])
577{
578    int i, skip;
579
580    defaultKeyboardControl.autoRepeat = TRUE;
581
582#ifdef NO_PART_NET
583    PartialNetwork = FALSE;
584#else
585    PartialNetwork = TRUE;
586#endif
587
588    for ( i = 1; i < argc; i++ )
589    {
590	/* call ddx first, so it can peek/override if it wants */
591        if((skip = ddxProcessArgument(argc, argv, i)))
592	{
593	    i += (skip - 1);
594	}
595	else if(argv[i][0] ==  ':')
596	{
597	    /* initialize display */
598	    display = argv[i];
599	    display++;
600            if( ! VerifyDisplayName( display ) ) {
601                ErrorF("Bad display name: %s\n", display);
602                UseMsg();
603		FatalError("Bad display name, exiting: %s\n", display);
604            }
605	}
606	else if ( strcmp( argv[i], "-a") == 0)
607	{
608	    if(++i < argc)
609	        defaultPointerControl.num = atoi(argv[i]);
610	    else
611		UseMsg();
612	}
613	else if ( strcmp( argv[i], "-ac") == 0)
614	{
615	    defeatAccessControl = TRUE;
616	}
617	else if ( strcmp( argv[i], "-audit") == 0)
618	{
619	    if(++i < argc)
620	        auditTrailLevel = atoi(argv[i]);
621	    else
622		UseMsg();
623	}
624	else if ( strcmp( argv[i], "-auth") == 0)
625	{
626	    if(++i < argc)
627	        InitAuthorization (argv[i]);
628	    else
629		UseMsg();
630	}
631	else if ( strcmp( argv[i], "-br") == 0) ; /* default */
632	else if ( strcmp( argv[i], "+bs") == 0)
633	    enableBackingStore = TRUE;
634	else if ( strcmp( argv[i], "-bs") == 0)
635	    disableBackingStore = TRUE;
636	else if ( strcmp( argv[i], "c") == 0)
637	{
638	    if(++i < argc)
639	        defaultKeyboardControl.click = atoi(argv[i]);
640	    else
641		UseMsg();
642	}
643	else if ( strcmp( argv[i], "-c") == 0)
644	{
645	    defaultKeyboardControl.click = 0;
646	}
647	else if ( strcmp( argv[i], "-cc") == 0)
648	{
649	    if(++i < argc)
650	        defaultColorVisualClass = atoi(argv[i]);
651	    else
652		UseMsg();
653	}
654	else if ( strcmp( argv[i], "-core") == 0)
655	{
656#if !defined(WIN32) || !defined(__MINGW32__)
657	    struct rlimit   core_limit;
658	    getrlimit (RLIMIT_CORE, &core_limit);
659	    core_limit.rlim_cur = core_limit.rlim_max;
660	    setrlimit (RLIMIT_CORE, &core_limit);
661#endif
662	    CoreDump = TRUE;
663	}
664        else if ( strcmp( argv[i], "-nocursor") == 0)
665        {
666            EnableCursor = FALSE;
667        }
668        else if ( strcmp( argv[i], "-dpi") == 0)
669	{
670	    if(++i < argc)
671	        monitorResolution = atoi(argv[i]);
672	    else
673		UseMsg();
674	}
675#ifdef DPMSExtension
676	else if ( strcmp( argv[i], "dpms") == 0)
677	    /* ignored for compatibility */ ;
678	else if ( strcmp( argv[i], "-dpms") == 0)
679	    DPMSDisabledSwitch = TRUE;
680#endif
681	else if ( strcmp( argv[i], "-deferglyphs") == 0)
682	{
683	    if(++i >= argc || !ParseGlyphCachingMode(argv[i]))
684		UseMsg();
685	}
686	else if ( strcmp( argv[i], "-f") == 0)
687	{
688	    if(++i < argc)
689	        defaultKeyboardControl.bell = atoi(argv[i]);
690	    else
691		UseMsg();
692	}
693	else if ( strcmp( argv[i], "-fc") == 0)
694	{
695	    if(++i < argc)
696	        defaultCursorFont = argv[i];
697	    else
698		UseMsg();
699	}
700	else if ( strcmp( argv[i], "-fn") == 0)
701	{
702	    if(++i < argc)
703	        defaultTextFont = argv[i];
704	    else
705		UseMsg();
706	}
707	else if ( strcmp( argv[i], "-fp") == 0)
708	{
709	    if(++i < argc)
710	    {
711	        defaultFontPath = argv[i];
712	    }
713	    else
714		UseMsg();
715	}
716	else if ( strcmp( argv[i], "-help") == 0)
717	{
718	    UseMsg();
719	    exit(0);
720	}
721        else if ( (skip=XkbProcessArguments(argc,argv,i))!=0 ) {
722	    if (skip>0)
723		 i+= skip-1;
724	    else UseMsg();
725	}
726#ifdef RLIMIT_DATA
727	else if ( strcmp( argv[i], "-ld") == 0)
728	{
729	    if(++i < argc)
730	    {
731	        limitDataSpace = atoi(argv[i]);
732		if (limitDataSpace > 0)
733		    limitDataSpace *= 1024;
734	    }
735	    else
736		UseMsg();
737	}
738#endif
739#ifdef RLIMIT_NOFILE
740	else if ( strcmp( argv[i], "-lf") == 0)
741	{
742	    if(++i < argc)
743	        limitNoFile = atoi(argv[i]);
744	    else
745		UseMsg();
746	}
747#endif
748#ifdef RLIMIT_STACK
749	else if ( strcmp( argv[i], "-ls") == 0)
750	{
751	    if(++i < argc)
752	    {
753	        limitStackSpace = atoi(argv[i]);
754		if (limitStackSpace > 0)
755		    limitStackSpace *= 1024;
756	    }
757	    else
758		UseMsg();
759	}
760#endif
761	else if ( strcmp ( argv[i], "-nolock") == 0)
762	{
763#if !defined(WIN32) && !defined(__CYGWIN__)
764	  if (getuid() != 0)
765	    ErrorF("Warning: the -nolock option can only be used by root\n");
766	  else
767#endif
768	    nolock = TRUE;
769	}
770	else if ( strcmp( argv[i], "-nolisten") == 0)
771	{
772            if(++i < argc) {
773		if (_XSERVTransNoListen(argv[i]))
774		    FatalError ("Failed to disable listen for %s transport",
775				argv[i]);
776	   } else
777		UseMsg();
778	}
779	else if ( strcmp( argv[i], "-noreset") == 0)
780	{
781	    dispatchExceptionAtReset = 0;
782	}
783	else if ( strcmp( argv[i], "-reset") == 0)
784	{
785	    dispatchExceptionAtReset = DE_RESET;
786	}
787	else if ( strcmp( argv[i], "-p") == 0)
788	{
789	    if(++i < argc)
790	        defaultScreenSaverInterval = ((CARD32)atoi(argv[i])) *
791					     MILLI_PER_MIN;
792	    else
793		UseMsg();
794	}
795	else if (strcmp(argv[i], "-pogo") == 0)
796	{
797	    dispatchException = DE_TERMINATE;
798	}
799	else if ( strcmp( argv[i], "-pn") == 0)
800	    PartialNetwork = TRUE;
801	else if ( strcmp( argv[i], "-nopn") == 0)
802	    PartialNetwork = FALSE;
803	else if ( strcmp( argv[i], "r") == 0)
804	    defaultKeyboardControl.autoRepeat = TRUE;
805	else if ( strcmp( argv[i], "-r") == 0)
806	    defaultKeyboardControl.autoRepeat = FALSE;
807	else if ( strcmp( argv[i], "-retro") == 0)
808	    party_like_its_1989 = TRUE;
809	else if ( strcmp( argv[i], "-noretro") == 0)
810	    party_like_its_1989 = FALSE;
811	else if ( strcmp( argv[i], "-s") == 0)
812	{
813	    if(++i < argc)
814	        defaultScreenSaverTime = ((CARD32)atoi(argv[i])) *
815					 MILLI_PER_MIN;
816	    else
817		UseMsg();
818	}
819	else if ( strcmp( argv[i], "-t") == 0)
820	{
821	    if(++i < argc)
822	        defaultPointerControl.threshold = atoi(argv[i]);
823	    else
824		UseMsg();
825	}
826	else if ( strcmp( argv[i], "-terminate") == 0)
827	{
828	    dispatchExceptionAtReset = DE_TERMINATE;
829	}
830	else if ( strcmp( argv[i], "-to") == 0)
831	{
832	    if(++i < argc)
833		TimeOutValue = ((CARD32)atoi(argv[i])) * MILLI_PER_SECOND;
834	    else
835		UseMsg();
836	}
837	else if ( strcmp( argv[i], "-tst") == 0)
838	{
839	    noTestExtensions = TRUE;
840	}
841	else if ( strcmp( argv[i], "v") == 0)
842	    defaultScreenSaverBlanking = PreferBlanking;
843	else if ( strcmp( argv[i], "-v") == 0)
844	    defaultScreenSaverBlanking = DontPreferBlanking;
845	else if ( strcmp( argv[i], "-wm") == 0)
846	    defaultBackingStore = WhenMapped;
847        else if ( strcmp( argv[i], "-wr") == 0)
848            whiteRoot = TRUE;
849        else if ( strcmp( argv[i], "-background") == 0) {
850            if(++i < argc) {
851                if (!strcmp ( argv[i], "none"))
852                    bgNoneRoot = TRUE;
853                else
854                    UseMsg();
855            }
856        }
857        else if ( strcmp( argv[i], "-maxbigreqsize") == 0) {
858             if(++i < argc) {
859                 long reqSizeArg = atol(argv[i]);
860
861                 /* Request size > 128MB does not make much sense... */
862                 if( reqSizeArg > 0L && reqSizeArg < 128L ) {
863                     maxBigRequestSize = (reqSizeArg * 1048576L) - 1L;
864                 }
865                 else
866                 {
867                     UseMsg();
868                 }
869             }
870             else
871             {
872                 UseMsg();
873             }
874         }
875#ifdef PANORAMIX
876	else if ( strcmp( argv[i], "+xinerama") == 0){
877	    noPanoramiXExtension = FALSE;
878	}
879	else if ( strcmp( argv[i], "-xinerama") == 0){
880	    noPanoramiXExtension = TRUE;
881	}
882	else if ( strcmp( argv[i], "-disablexineramaextension") == 0){
883	    PanoramiXExtensionDisabledHack = TRUE;
884	}
885#endif
886	else if ( strcmp( argv[i], "-I") == 0)
887	{
888	    /* ignore all remaining arguments */
889	    break;
890	}
891	else if (strncmp (argv[i], "tty", 3) == 0)
892	{
893            /* init supplies us with this useless information */
894	}
895#ifdef XDMCP
896	else if ((skip = XdmcpOptions(argc, argv, i)) != i)
897	{
898	    i = skip - 1;
899	}
900#endif
901	else if ( strcmp( argv[i], "-dumbSched") == 0)
902	{
903	    SmartScheduleDisable = TRUE;
904	}
905	else if ( strcmp( argv[i], "-schedInterval") == 0)
906	{
907	    if (++i < argc)
908	    {
909		SmartScheduleInterval = atoi(argv[i]);
910		SmartScheduleSlice = SmartScheduleInterval;
911	    }
912	    else
913		UseMsg();
914	}
915	else if ( strcmp( argv[i], "-schedMax") == 0)
916	{
917	    if (++i < argc)
918	    {
919		SmartScheduleMaxSlice = atoi(argv[i]);
920	    }
921	    else
922		UseMsg();
923	}
924	else if ( strcmp( argv[i], "-render" ) == 0)
925	{
926	    if (++i < argc)
927	    {
928		int policy = PictureParseCmapPolicy (argv[i]);
929
930		if (policy != PictureCmapPolicyInvalid)
931		    PictureCmapPolicy = policy;
932		else
933		    UseMsg ();
934	    }
935	    else
936		UseMsg ();
937	}
938	else if ( strcmp( argv[i], "-sigstop") == 0)
939	{
940	    RunFromSigStopParent = TRUE;
941	}
942	else if ( strcmp( argv[i], "+extension") == 0)
943	{
944	    if (++i < argc)
945	    {
946		if (!EnableDisableExtension(argv[i], TRUE))
947		    EnableDisableExtensionError(argv[i], TRUE);
948	    }
949	    else
950		UseMsg();
951	}
952	else if ( strcmp( argv[i], "-extension") == 0)
953	{
954	    if (++i < argc)
955	    {
956		if (!EnableDisableExtension(argv[i], FALSE))
957		    EnableDisableExtensionError(argv[i], FALSE);
958	    }
959	    else
960		UseMsg();
961	}
962 	else
963 	{
964	    ErrorF("Unrecognized option: %s\n", argv[i]);
965	    UseMsg();
966	    FatalError("Unrecognized option: %s\n", argv[i]);
967        }
968    }
969}
970
971/* Implement a simple-minded font authorization scheme.  The authorization
972   name is "hp-hostname-1", the contents are simply the host name. */
973int
974set_font_authorizations(char **authorizations, int *authlen, pointer client)
975{
976#define AUTHORIZATION_NAME "hp-hostname-1"
977#if defined(TCPCONN) || defined(STREAMSCONN)
978    static char *result = NULL;
979    static char *p = NULL;
980
981    if (p == NULL)
982    {
983	char hname[1024], *hnameptr;
984	unsigned int len;
985#if defined(IPv6) && defined(AF_INET6)
986	struct addrinfo hints, *ai = NULL;
987#else
988	struct hostent *host;
989#ifdef XTHREADS_NEEDS_BYNAMEPARAMS
990	_Xgethostbynameparams hparams;
991#endif
992#endif
993
994	gethostname(hname, 1024);
995#if defined(IPv6) && defined(AF_INET6)
996	memset(&hints, 0, sizeof(hints));
997	hints.ai_flags = AI_CANONNAME;
998	if (getaddrinfo(hname, NULL, &hints, &ai) == 0) {
999	    hnameptr = ai->ai_canonname;
1000	} else {
1001	    hnameptr = hname;
1002	}
1003#else
1004	host = _XGethostbyname(hname, hparams);
1005	if (host == NULL)
1006	    hnameptr = hname;
1007	else
1008	    hnameptr = host->h_name;
1009#endif
1010
1011	len = strlen(hnameptr) + 1;
1012	result = malloc(len + sizeof(AUTHORIZATION_NAME) + 4);
1013
1014	p = result;
1015        *p++ = sizeof(AUTHORIZATION_NAME) >> 8;
1016        *p++ = sizeof(AUTHORIZATION_NAME) & 0xff;
1017        *p++ = (len) >> 8;
1018        *p++ = (len & 0xff);
1019
1020	memmove(p, AUTHORIZATION_NAME, sizeof(AUTHORIZATION_NAME));
1021	p += sizeof(AUTHORIZATION_NAME);
1022	memmove(p, hnameptr, len);
1023	p += len;
1024#if defined(IPv6) && defined(AF_INET6)
1025	if (ai) {
1026	    freeaddrinfo(ai);
1027	}
1028#endif
1029    }
1030    *authlen = p - result;
1031    *authorizations = result;
1032    return 1;
1033#else /* TCPCONN */
1034    return 0;
1035#endif /* TCPCONN */
1036}
1037
1038void *
1039Xalloc(unsigned long amount)
1040{
1041    /*
1042     * Xalloc used to return NULL when large amount of memory is requested. In
1043     * order to catch the buggy callers this warning has been added, slated to
1044     * removal by anyone who touches this code (or just looks at it) in 2011.
1045     *
1046     * -- Mikhail Gusarov
1047     */
1048    if ((long)amount <= 0)
1049	ErrorF("Warning: Xalloc: "
1050	       "requesting unpleasantly large amount of memory: %lu bytes.\n",
1051               amount);
1052
1053    return malloc(amount);
1054}
1055
1056void *
1057XNFalloc(unsigned long amount)
1058{
1059    void *ptr = malloc(amount);
1060    if (!ptr)
1061        FatalError("Out of memory");
1062    return ptr;
1063}
1064
1065void *
1066Xcalloc(unsigned long amount)
1067{
1068    return calloc(1, amount);
1069}
1070
1071void *
1072XNFcalloc(unsigned long amount)
1073{
1074    void *ret = calloc(1, amount);
1075    if (!ret)
1076        FatalError("XNFcalloc: Out of memory");
1077    return ret;
1078}
1079
1080void *
1081Xrealloc(void *ptr, unsigned long amount)
1082{
1083    /*
1084     * Xrealloc used to return NULL when large amount of memory is requested. In
1085     * order to catch the buggy callers this warning has been added, slated to
1086     * removal by anyone who touches this code (or just looks at it) in 2011.
1087     *
1088     * -- Mikhail Gusarov
1089     */
1090    if ((long)amount <= 0)
1091	ErrorF("Warning: Xrealloc: "
1092	       "requesting unpleasantly large amount of memory: %lu bytes.\n",
1093               amount);
1094
1095    return realloc(ptr, amount);
1096}
1097
1098void *
1099XNFrealloc(void *ptr, unsigned long amount)
1100{
1101    void *ret = realloc(ptr, amount);
1102    if (!ret)
1103	FatalError("XNFrealloc: Out of memory");
1104    return ret;
1105}
1106
1107void
1108Xfree(void *ptr)
1109{
1110    free(ptr);
1111}
1112
1113
1114char *
1115Xstrdup(const char *s)
1116{
1117    if (s == NULL)
1118	return NULL;
1119    return strdup(s);
1120}
1121
1122char *
1123XNFstrdup(const char *s)
1124{
1125    char *ret;
1126
1127    if (s == NULL)
1128	return NULL;
1129
1130    ret = strdup(s);
1131    if (!ret)
1132	FatalError("XNFstrdup: Out of memory");
1133    return ret;
1134}
1135
1136void
1137SmartScheduleStopTimer (void)
1138{
1139    struct itimerval	timer;
1140
1141    if (SmartScheduleDisable)
1142	return;
1143    timer.it_interval.tv_sec = 0;
1144    timer.it_interval.tv_usec = 0;
1145    timer.it_value.tv_sec = 0;
1146    timer.it_value.tv_usec = 0;
1147    (void) setitimer (ITIMER_REAL, &timer, 0);
1148}
1149
1150void
1151SmartScheduleStartTimer (void)
1152{
1153    struct itimerval	timer;
1154
1155    if (SmartScheduleDisable)
1156	return;
1157    timer.it_interval.tv_sec = 0;
1158    timer.it_interval.tv_usec = SmartScheduleInterval * 1000;
1159    timer.it_value.tv_sec = 0;
1160    timer.it_value.tv_usec = SmartScheduleInterval * 1000;
1161    setitimer (ITIMER_REAL, &timer, 0);
1162}
1163
1164static void
1165SmartScheduleTimer (int sig)
1166{
1167    SmartScheduleTime += SmartScheduleInterval;
1168}
1169
1170void
1171SmartScheduleInit (void)
1172{
1173    struct sigaction	act;
1174
1175    if (SmartScheduleDisable)
1176	return;
1177
1178    memset((char *) &act, 0, sizeof(struct sigaction));
1179
1180    /* Set up the timer signal function */
1181    act.sa_handler = SmartScheduleTimer;
1182    sigemptyset (&act.sa_mask);
1183    sigaddset (&act.sa_mask, SIGALRM);
1184    if (sigaction (SIGALRM, &act, 0) < 0)
1185    {
1186	perror ("sigaction for smart scheduler");
1187	SmartScheduleDisable = TRUE;
1188    }
1189}
1190
1191#ifdef SIG_BLOCK
1192static sigset_t	PreviousSignalMask;
1193static int	BlockedSignalCount;
1194#endif
1195
1196void
1197OsBlockSignals (void)
1198{
1199#ifdef SIG_BLOCK
1200    if (BlockedSignalCount++ == 0)
1201    {
1202	sigset_t    set;
1203
1204	sigemptyset (&set);
1205	sigaddset (&set, SIGALRM);
1206	sigaddset (&set, SIGVTALRM);
1207#ifdef SIGWINCH
1208	sigaddset (&set, SIGWINCH);
1209#endif
1210#ifdef SIGIO
1211	sigaddset (&set, SIGIO);
1212#endif
1213	sigaddset (&set, SIGTSTP);
1214	sigaddset (&set, SIGTTIN);
1215	sigaddset (&set, SIGTTOU);
1216	sigaddset (&set, SIGCHLD);
1217	sigprocmask (SIG_BLOCK, &set, &PreviousSignalMask);
1218    }
1219#endif
1220}
1221
1222void
1223OsReleaseSignals (void)
1224{
1225#ifdef SIG_BLOCK
1226    if (--BlockedSignalCount == 0)
1227    {
1228	sigprocmask (SIG_SETMASK, &PreviousSignalMask, 0);
1229    }
1230#endif
1231}
1232
1233/*
1234 * Pending signals may interfere with core dumping. Provide a
1235 * mechanism to block signals when aborting.
1236 */
1237
1238void
1239OsAbort (void)
1240{
1241#ifndef __APPLE__
1242    OsBlockSignals();
1243#endif
1244    abort();
1245}
1246
1247#if !defined(WIN32)
1248/*
1249 * "safer" versions of system(3), popen(3) and pclose(3) which give up
1250 * all privs before running a command.
1251 *
1252 * This is based on the code in FreeBSD 2.2 libc.
1253 *
1254 * XXX It'd be good to redirect stderr so that it ends up in the log file
1255 * as well.  As it is now, xkbcomp messages don't end up in the log file.
1256 */
1257
1258int
1259System(char *command)
1260{
1261    int pid, p;
1262    void (*csig)(int);
1263    int status;
1264
1265    if (!command)
1266	return 1;
1267
1268    csig = signal(SIGCHLD, SIG_DFL);
1269    if (csig == SIG_ERR) {
1270      perror("signal");
1271      return -1;
1272    }
1273    DebugF("System: `%s'\n", command);
1274
1275    switch (pid = fork()) {
1276    case -1:	/* error */
1277	p = -1;
1278    case 0:	/* child */
1279	if (setgid(getgid()) == -1)
1280	    _exit(127);
1281	if (setuid(getuid()) == -1)
1282	    _exit(127);
1283	execl("/bin/sh", "sh", "-c", command, (char *)NULL);
1284	_exit(127);
1285    default:	/* parent */
1286	do {
1287	    p = waitpid(pid, &status, 0);
1288	} while (p == -1 && errno == EINTR);
1289
1290    }
1291
1292    if (signal(SIGCHLD, csig) == SIG_ERR) {
1293      perror("signal");
1294      return -1;
1295    }
1296
1297    return p == -1 ? -1 : status;
1298}
1299
1300static struct pid {
1301    struct pid *next;
1302    FILE *fp;
1303    int pid;
1304} *pidlist;
1305
1306OsSigHandlerPtr old_alarm = NULL; /* XXX horrible awful hack */
1307
1308pointer
1309Popen(char *command, char *type)
1310{
1311    struct pid *cur;
1312    FILE *iop;
1313    int pdes[2], pid;
1314
1315    if (command == NULL || type == NULL)
1316	return NULL;
1317
1318    if ((*type != 'r' && *type != 'w') || type[1])
1319	return NULL;
1320
1321    if ((cur = malloc(sizeof(struct pid))) == NULL)
1322	return NULL;
1323
1324    if (pipe(pdes) < 0) {
1325	free(cur);
1326	return NULL;
1327    }
1328
1329    /* Ignore the smart scheduler while this is going on */
1330    old_alarm = OsSignal(SIGALRM, SIG_IGN);
1331    if (old_alarm == SIG_ERR) {
1332      close(pdes[0]);
1333      close(pdes[1]);
1334      free(cur);
1335      perror("signal");
1336      return NULL;
1337    }
1338
1339    switch (pid = fork()) {
1340    case -1: 	/* error */
1341	close(pdes[0]);
1342	close(pdes[1]);
1343	free(cur);
1344	if (OsSignal(SIGALRM, old_alarm) == SIG_ERR)
1345	  perror("signal");
1346	return NULL;
1347    case 0:	/* child */
1348	if (setgid(getgid()) == -1)
1349	    _exit(127);
1350	if (setuid(getuid()) == -1)
1351	    _exit(127);
1352	if (*type == 'r') {
1353	    if (pdes[1] != 1) {
1354		/* stdout */
1355		dup2(pdes[1], 1);
1356		close(pdes[1]);
1357	    }
1358	    close(pdes[0]);
1359	} else {
1360	    if (pdes[0] != 0) {
1361		/* stdin */
1362		dup2(pdes[0], 0);
1363		close(pdes[0]);
1364	    }
1365	    close(pdes[1]);
1366	}
1367	execl("/bin/sh", "sh", "-c", command, (char *)NULL);
1368	_exit(127);
1369    }
1370
1371    /* Avoid EINTR during stdio calls */
1372    OsBlockSignals ();
1373
1374    /* parent */
1375    if (*type == 'r') {
1376	iop = fdopen(pdes[0], type);
1377	close(pdes[1]);
1378    } else {
1379	iop = fdopen(pdes[1], type);
1380	close(pdes[0]);
1381    }
1382
1383    cur->fp = iop;
1384    cur->pid = pid;
1385    cur->next = pidlist;
1386    pidlist = cur;
1387
1388    DebugF("Popen: `%s', fp = %p\n", command, iop);
1389
1390    return iop;
1391}
1392
1393/* fopen that drops privileges */
1394pointer
1395Fopen(char *file, char *type)
1396{
1397    FILE *iop;
1398#ifndef HAS_SAVED_IDS_AND_SETEUID
1399    struct pid *cur;
1400    int pdes[2], pid;
1401
1402    if (file == NULL || type == NULL)
1403	return NULL;
1404
1405    if ((*type != 'r' && *type != 'w') || type[1])
1406	return NULL;
1407
1408    if ((cur = malloc(sizeof(struct pid))) == NULL)
1409	return NULL;
1410
1411    if (pipe(pdes) < 0) {
1412	free(cur);
1413	return NULL;
1414    }
1415
1416    switch (pid = fork()) {
1417    case -1: 	/* error */
1418	close(pdes[0]);
1419	close(pdes[1]);
1420	free(cur);
1421	return NULL;
1422    case 0:	/* child */
1423	if (setgid(getgid()) == -1)
1424	    _exit(127);
1425	if (setuid(getuid()) == -1)
1426	    _exit(127);
1427	if (*type == 'r') {
1428	    if (pdes[1] != 1) {
1429		/* stdout */
1430		dup2(pdes[1], 1);
1431		close(pdes[1]);
1432	    }
1433	    close(pdes[0]);
1434	} else {
1435	    if (pdes[0] != 0) {
1436		/* stdin */
1437		dup2(pdes[0], 0);
1438		close(pdes[0]);
1439	    }
1440	    close(pdes[1]);
1441	}
1442	execl("/bin/cat", "cat", file, (char *)NULL);
1443	_exit(127);
1444    }
1445
1446    /* Avoid EINTR during stdio calls */
1447    OsBlockSignals ();
1448
1449    /* parent */
1450    if (*type == 'r') {
1451	iop = fdopen(pdes[0], type);
1452	close(pdes[1]);
1453    } else {
1454	iop = fdopen(pdes[1], type);
1455	close(pdes[0]);
1456    }
1457
1458    cur->fp = iop;
1459    cur->pid = pid;
1460    cur->next = pidlist;
1461    pidlist = cur;
1462
1463    DebugF("Fopen(%s), fp = %p\n", file, iop);
1464
1465    return iop;
1466#else
1467    int ruid, euid;
1468
1469    ruid = getuid();
1470    euid = geteuid();
1471
1472    if (seteuid(ruid) == -1) {
1473	    return NULL;
1474    }
1475    iop = fopen(file, type);
1476
1477    if (seteuid(euid) == -1) {
1478	    fclose(iop);
1479	    return NULL;
1480    }
1481    return iop;
1482#endif /* HAS_SAVED_IDS_AND_SETEUID */
1483}
1484
1485int
1486Pclose(pointer iop)
1487{
1488    struct pid *cur, *last;
1489    int pstat;
1490    int pid;
1491
1492    DebugF("Pclose: fp = %p\n", iop);
1493    fclose(iop);
1494
1495    for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
1496	if (cur->fp == iop)
1497	    break;
1498    if (cur == NULL)
1499	return -1;
1500
1501    do {
1502	pid = waitpid(cur->pid, &pstat, 0);
1503    } while (pid == -1 && errno == EINTR);
1504
1505    if (last == NULL)
1506	pidlist = cur->next;
1507    else
1508	last->next = cur->next;
1509    free(cur);
1510
1511    /* allow EINTR again */
1512    OsReleaseSignals ();
1513
1514    if (old_alarm && OsSignal(SIGALRM, old_alarm) == SIG_ERR) {
1515      perror("signal");
1516      return -1;
1517    }
1518
1519    return pid == -1 ? -1 : pstat;
1520}
1521
1522int
1523Fclose(pointer iop)
1524{
1525#ifdef HAS_SAVED_IDS_AND_SETEUID
1526    return fclose(iop);
1527#else
1528    return Pclose(iop);
1529#endif
1530}
1531
1532#endif /* !WIN32 */
1533
1534
1535/*
1536 * CheckUserParameters: check for long command line arguments and long
1537 * environment variables.  By default, these checks are only done when
1538 * the server's euid != ruid.  In 3.3.x, these checks were done in an
1539 * external wrapper utility.
1540 */
1541
1542/* Consider LD* variables insecure? */
1543#ifndef REMOVE_ENV_LD
1544#define REMOVE_ENV_LD 1
1545#endif
1546
1547/* Remove long environment variables? */
1548#ifndef REMOVE_LONG_ENV
1549#define REMOVE_LONG_ENV 1
1550#endif
1551
1552/*
1553 * Disallow stdout or stderr as pipes?  It's possible to block the X server
1554 * when piping stdout+stderr to a pipe.
1555 *
1556 * Don't enable this because it looks like it's going to cause problems.
1557 */
1558#ifndef NO_OUTPUT_PIPES
1559#define NO_OUTPUT_PIPES 0
1560#endif
1561
1562
1563/* Check args and env only if running setuid (euid == 0 && euid != uid) ? */
1564#ifndef CHECK_EUID
1565#ifndef WIN32
1566#define CHECK_EUID 1
1567#else
1568#define CHECK_EUID 0
1569#endif
1570#endif
1571
1572/*
1573 * Maybe the locale can be faked to make isprint(3) report that everything
1574 * is printable?  Avoid it by default.
1575 */
1576#ifndef USE_ISPRINT
1577#define USE_ISPRINT 0
1578#endif
1579
1580#define MAX_ARG_LENGTH          128
1581#define MAX_ENV_LENGTH          256
1582#define MAX_ENV_PATH_LENGTH     2048	/* Limit for *PATH and TERMCAP */
1583
1584#if USE_ISPRINT
1585#include <ctype.h>
1586#define checkPrintable(c) isprint(c)
1587#else
1588#define checkPrintable(c) (((c) & 0x7f) >= 0x20 && ((c) & 0x7f) != 0x7f)
1589#endif
1590
1591enum BadCode {
1592    NotBad = 0,
1593    UnsafeArg,
1594    ArgTooLong,
1595    UnprintableArg,
1596    EnvTooLong,
1597    OutputIsPipe,
1598    InternalError
1599};
1600
1601#if defined(VENDORSUPPORT)
1602#define BUGADDRESS VENDORSUPPORT
1603#elif defined(BUILDERADDR)
1604#define BUGADDRESS BUILDERADDR
1605#else
1606#define BUGADDRESS "xorg@freedesktop.org"
1607#endif
1608
1609void
1610CheckUserParameters(int argc, char **argv, char **envp)
1611{
1612    enum BadCode bad = NotBad;
1613    int i = 0, j;
1614    char *a, *e = NULL;
1615
1616#if CHECK_EUID
1617    if (geteuid() == 0 && getuid() != geteuid())
1618#endif
1619    {
1620	/* Check each argv[] */
1621	for (i = 1; i < argc; i++) {
1622	    if (strcmp(argv[i], "-fp") == 0)
1623	    {
1624		i++; /* continue with next argument. skip the length check */
1625		if (i >= argc)
1626		    break;
1627	    } else
1628	    {
1629		if (strlen(argv[i]) > MAX_ARG_LENGTH) {
1630		    bad = ArgTooLong;
1631		    break;
1632		}
1633	    }
1634	    a = argv[i];
1635	    while (*a) {
1636		if (checkPrintable(*a) == 0) {
1637		    bad = UnprintableArg;
1638		    break;
1639		}
1640		a++;
1641	    }
1642	    if (bad)
1643		break;
1644	}
1645	if (!bad) {
1646	    /* Check each envp[] */
1647	    for (i = 0; envp[i]; i++) {
1648
1649		/* Check for bad environment variables and values */
1650#if REMOVE_ENV_LD
1651		while (envp[i] && (strncmp(envp[i], "LD", 2) == 0)) {
1652		    for (j = i; envp[j]; j++) {
1653			envp[j] = envp[j+1];
1654		    }
1655		}
1656#endif
1657		if (envp[i] && (strlen(envp[i]) > MAX_ENV_LENGTH)) {
1658#if REMOVE_LONG_ENV
1659		    for (j = i; envp[j]; j++) {
1660			envp[j] = envp[j+1];
1661		    }
1662		    i--;
1663#else
1664		    char *eq;
1665		    int len;
1666
1667		    eq = strchr(envp[i], '=');
1668		    if (!eq)
1669			continue;
1670		    len = eq - envp[i];
1671		    e = malloc(len + 1);
1672		    if (!e) {
1673			bad = InternalError;
1674			break;
1675		    }
1676		    strncpy(e, envp[i], len);
1677		    e[len] = 0;
1678		    if (len >= 4 &&
1679			(strcmp(e + len - 4, "PATH") == 0 ||
1680			 strcmp(e, "TERMCAP") == 0)) {
1681			if (strlen(envp[i]) > MAX_ENV_PATH_LENGTH) {
1682			    bad = EnvTooLong;
1683			    break;
1684			} else {
1685			    free(e);
1686			}
1687		    } else {
1688			bad = EnvTooLong;
1689			break;
1690		    }
1691#endif
1692		}
1693	    }
1694	}
1695#if NO_OUTPUT_PIPES
1696	if (!bad) {
1697	    struct stat buf;
1698
1699	    if (fstat(fileno(stdout), &buf) == 0 && S_ISFIFO(buf.st_mode))
1700		bad = OutputIsPipe;
1701	    if (fstat(fileno(stderr), &buf) == 0 && S_ISFIFO(buf.st_mode))
1702		bad = OutputIsPipe;
1703	}
1704#endif
1705    }
1706    switch (bad) {
1707    case NotBad:
1708	return;
1709    case UnsafeArg:
1710	ErrorF("Command line argument number %d is unsafe\n", i);
1711	break;
1712    case ArgTooLong:
1713	ErrorF("Command line argument number %d is too long\n", i);
1714	break;
1715    case UnprintableArg:
1716	ErrorF("Command line argument number %d contains unprintable"
1717		" characters\n", i);
1718	break;
1719    case EnvTooLong:
1720	ErrorF("Environment variable `%s' is too long\n", e);
1721	break;
1722    case OutputIsPipe:
1723	ErrorF("Stdout and/or stderr is a pipe\n");
1724	break;
1725    case InternalError:
1726	ErrorF("Internal Error\n");
1727	break;
1728    default:
1729	ErrorF("Unknown error\n");
1730	break;
1731    }
1732    FatalError("X server aborted because of unsafe environment\n");
1733}
1734
1735/*
1736 * CheckUserAuthorization: check if the user is allowed to start the
1737 * X server.  This usually means some sort of PAM checking, and it is
1738 * usually only done for setuid servers (uid != euid).
1739 */
1740
1741#ifdef USE_PAM
1742#include <security/pam_appl.h>
1743#include <security/pam_misc.h>
1744#include <pwd.h>
1745#endif /* USE_PAM */
1746
1747void
1748CheckUserAuthorization(void)
1749{
1750#ifdef USE_PAM
1751    static struct pam_conv conv = {
1752	misc_conv,
1753	NULL
1754    };
1755
1756    pam_handle_t *pamh = NULL;
1757    struct passwd *pw;
1758    int retval;
1759
1760    if (getuid() != geteuid()) {
1761	pw = getpwuid(getuid());
1762	if (pw == NULL)
1763	    FatalError("getpwuid() failed for uid %d\n", getuid());
1764
1765	retval = pam_start("xserver", pw->pw_name, &conv, &pamh);
1766	if (retval != PAM_SUCCESS)
1767	    FatalError("pam_start() failed.\n"
1768			"\tMissing or mangled PAM config file or module?\n");
1769
1770	retval = pam_authenticate(pamh, 0);
1771	if (retval != PAM_SUCCESS) {
1772	    pam_end(pamh, retval);
1773	    FatalError("PAM authentication failed, cannot start X server.\n"
1774			"\tPerhaps you do not have console ownership?\n");
1775	}
1776
1777	retval = pam_acct_mgmt(pamh, 0);
1778	if (retval != PAM_SUCCESS) {
1779	    pam_end(pamh, retval);
1780	    FatalError("PAM authentication failed, cannot start X server.\n"
1781			"\tPerhaps you do not have console ownership?\n");
1782	}
1783
1784	/* this is not a session, so do not do session management */
1785	pam_end(pamh, PAM_SUCCESS);
1786    }
1787#endif
1788}
1789
1790/*
1791 * Tokenize a string into a NULL terminated array of strings. Always returns
1792 * an allocated array unless an error occurs.
1793 */
1794char**
1795xstrtokenize(const char *str, const char *separators)
1796{
1797    char **list, **nlist;
1798    char *tok, *tmp;
1799    unsigned num = 0, n;
1800
1801    if (!str)
1802        return NULL;
1803    list = calloc(1, sizeof(*list));
1804    if (!list)
1805        return NULL;
1806    tmp = strdup(str);
1807    if (!tmp)
1808        goto error;
1809    for (tok = strtok(tmp, separators); tok; tok = strtok(NULL, separators)) {
1810        nlist = realloc(list, (num + 2) * sizeof(*list));
1811        if (!nlist)
1812            goto error;
1813        list = nlist;
1814        list[num] = strdup(tok);
1815        if (!list[num])
1816            goto error;
1817        list[++num] = NULL;
1818    }
1819    free(tmp);
1820    return list;
1821
1822error:
1823    free(tmp);
1824    for (n = 0; n < num; n++)
1825        free(list[n]);
1826    free(list);
1827    return NULL;
1828}
1829