1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-07 04:10:27 -08:00
emacs/src/xrdb.c
Paul Eggert 73dcdb9f30 Use faccessat, not access, when checking file permissions.
This fixes a bug that has been present in Emacs since its creation.
It was reported by Chris Torek in 1983 even before GNU Emacs existed,
which must set some sort of record.  (Torek's bug report was against
a predecessor of GNU Emacs, but GNU Emacs happened to have the
same common flaw.)  See Torek's Usenet posting
"setuid/setgid programs & Emacs" Article-I.D.: sri-arpa.858
Posted: Fri Apr  8 14:18:56 1983.
* .bzrignore: Add lib/fcntl.h.
* configure.ac (euidaccess): Remove check; gnulib does this for us now.
(gl_FCNTL_O_FLAGS): Define a dummy version.
* lib/at-func.c, lib/euidaccess.c, lib/faccessat.c, lib/fcntl.in.h:
* lib/getgroups.c, lib/group-member.c, lib/root-uid.h:
* lib/xalloc-oversized.h, m4/euidaccess.m4, m4/faccessat.m4:
* m4/fcntl_h.m4, m4/getgroups.m4, m4/group-member.m4:
New files, from gnulib.
* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
* admin/merge-gnulib (GNULIB_MODULES): Add faccessat.
(GNULIB_TOOL_FLAGS): Avoid at-internal, fchdir, malloc-posix,
openat-die, openat-h, save-cwd.  Do not avoid fcntl-h.
Omit gnulib's m4/fcntl-o.m4.
* nt/inc/ms-w32.h (AT_FDCWD, AT_EACCESS): New symbols.
(access): Remove.
(faccessat): New macro.
* src/Makefile.in (LIB_EACCESS): New macro.
(LIBES): Use it.
* src/callproc.c (init_callproc):
* src/charset.c (init_charset):
* src/fileio.c (check_existing, check_executable, check_writable)
(Ffile_readable_p):
* src/lread.c (openp, load_path_check):
* src/process.c (allocate_pty):
* src/xrdb.c (file_p):
Use effective UID when checking permissions, not real UID.
* src/callproc.c (init_callproc):
* src/charset.c (init_charset):
* src/lread.c (load_path_check, init_lread):
Test whether directories are accessible, not merely whether they exist.
* src/conf_post.h (GNULIB_SUPPORT_ONLY_AT_FDCWD): New macro.
* src/fileio.c (check_existing, check_executable, check_writable)
(Ffile_readable_p):
Use symbolic names instead of integers for the flags, as they're
portable now.
(check_writable): New arg AMODE.  All uses changed.
Set errno on failure.
(Ffile_readable_p): Use faccessat, not stat + open + close.
(Ffile_writable_p): No need to call check_existing + check_writable.
Just call check_writable and then look at errno.  This saves a syscall.
dir should never be nil; replace an unnecessary runtime check
with an eassert.  When checking the parent directory of a nonexistent
file, check that the directory is searchable as well as writable, as
we can't create files in unsearchable directories.
(file_directory_p): New function, which uses 'stat' on most platforms
but faccessat with D_OK (for efficiency) if WINDOWSNT.
(Ffile_directory_p, Fset_file_times): Use it.
(file_accessible_directory_p): New function, which uses a single
syscall for efficiency.
(Ffile_accessible_directory_p): Use it.
* src/xrdb.c (file_p): Use file_directory_p.
* src/lisp.h (file_directory_p, file_accessible_directory_p): New decls.
* src/lread.c (openp): When opening a file, use fstat rather than
stat, as that avoids a permissions race.  When not opening a file,
use file_directory_p rather than stat.
(dir_warning): First arg is now a usage string, not a format.
Use errno.  All uses changed.
* src/nsterm.m (ns_term_init): Remove unnecessary call to file-readable
that merely introduced a race.
* src/process.c, src/sysdep.c, src/term.c: All uses of '#ifdef O_NONBLOCK'
changed to '#if O_NONBLOCK', to accommodate gnulib O_* style,
and similarly for the other O_* flags.
* src/w32.c (sys_faccessat): Rename from sys_access and switch to
faccessat's API.  All uses changed.
* src/xrdb.c: Do not include <sys/stat.h>; no longer needed.
(magic_db): Rename from magic_file_p.
(magic_db, search_magic_path): Return an XrmDatabase rather than a
char *, so that we don't have to test for file existence
separately from opening the file for reading.  This removes a race
fixes a permission-checking problem, and simplifies the code.
All uses changed.
(file_p): Remove; no longer needed.

Fixes: debbugs:12632
2012-11-13 20:55:41 -08:00

718 lines
17 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Deal with the X Resource Manager.
Copyright (C) 1990, 1993-1994, 2000-2012 Free Software Foundation, Inc.
Author: Joseph Arceneaux
Created: 4/90
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <unistd.h>
#include <errno.h>
#include <epaths.h>
#include <stdlib.h>
#include <stdio.h>
#include "lisp.h"
/* This may include sys/types.h, and that somehow loses
if this is not done before the other system files. */
#include "xterm.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/X.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef USE_MOTIF
/* For Vdouble_click_time. */
#include "keyboard.h"
#endif
char *x_get_string_resource (XrmDatabase rdb, const char *name,
const char *class);
/* X file search path processing. */
/* The string which gets substituted for the %C escape in XFILESEARCHPATH
and friends, or zero if none was specified. */
static char *x_customization_string;
/* Return the value of the emacs.customization (Emacs.Customization)
resource, for later use in search path decoding. If we find no
such resource, return zero. */
static char *
x_get_customization_string (XrmDatabase db, const char *name,
const char *class)
{
char *full_name = alloca (strlen (name) + sizeof "customization" + 3);
char *full_class = alloca (strlen (class) + sizeof "Customization" + 3);
char *result;
sprintf (full_name, "%s.%s", name, "customization");
sprintf (full_class, "%s.%s", class, "Customization");
result = x_get_string_resource (db, full_name, full_class);
if (result)
{
char *copy = xmalloc (strlen (result) + 1);
strcpy (copy, result);
return copy;
}
else
return 0;
}
/* Expand all the Xt-style %-escapes in STRING, whose length is given
by STRING_LEN. Here are the escapes we're supposed to recognize:
%N The value of the application's class name
%T The value of the type parameter ("app-defaults" in this
context)
%S The value of the suffix parameter ("" in this context)
%L The language string associated with the specified display
(We use the "LANG" environment variable here, if it's set.)
%l The language part of the display's language string
(We treat this just like %L. If someone can tell us what
we're really supposed to do, dandy.)
%t The territory part of the display's language string
(This never gets used.)
%c The codeset part of the display's language string
(This never gets used either.)
%C The customization string retrieved from the resource
database associated with display.
(This is x_customization_string.)
Return the resource database if its file was read successfully, and
refers to %L only when the LANG environment variable is set, or
otherwise provided by X.
ESCAPED_SUFFIX is postpended to STRING if it is non-zero.
%-escapes in ESCAPED_SUFFIX are expanded.
Return NULL otherwise. */
static XrmDatabase
magic_db (const char *string, ptrdiff_t string_len, const char *class,
const char *escaped_suffix)
{
XrmDatabase db;
char *lang = getenv ("LANG");
ptrdiff_t path_size = 100;
char *path = xmalloc (path_size);
ptrdiff_t path_len = 0;
const char *p = string;
while (p < string + string_len)
{
/* The chunk we're about to stick on the end of result. */
const char *next = NULL;
ptrdiff_t next_len;
if (*p == '%')
{
p++;
if (p >= string + string_len)
next_len = 0;
else
switch (*p)
{
case '%':
next = "%";
next_len = 1;
break;
case 'C':
next = (x_customization_string
? x_customization_string
: "");
next_len = strlen (next);
break;
case 'N':
next = class;
next_len = strlen (class);
break;
case 'T':
next = "app-defaults";
next_len = strlen (next);
break;
default:
case 'S':
next_len = 0;
break;
case 'L':
case 'l':
if (! lang)
{
xfree (path);
return NULL;
}
next = lang;
next_len = strlen (next);
break;
case 't':
case 'c':
xfree (path);
return NULL;
}
}
else
next = p, next_len = 1;
/* Do we have room for this component followed by a '\0' ? */
if (path_size - path_len <= next_len)
{
if (min (PTRDIFF_MAX, SIZE_MAX) / 2 - 1 - path_len < next_len)
memory_full (SIZE_MAX);
path_size = (path_len + next_len + 1) * 2;
path = xrealloc (path, path_size);
}
memcpy (path + path_len, next, next_len);
path_len += next_len;
p++;
/* If we've reached the end of the string, append ESCAPED_SUFFIX. */
if (p >= string + string_len && escaped_suffix)
{
string = escaped_suffix;
string_len = strlen (string);
p = string;
escaped_suffix = NULL;
}
}
path[path_len] = '\0';
db = XrmGetFileDatabase (path);
xfree (path);
return db;
}
static char *
gethomedir (void)
{
struct passwd *pw;
char *ptr;
char *copy;
if ((ptr = getenv ("HOME")) == NULL)
{
if ((ptr = getenv ("LOGNAME")) != NULL
|| (ptr = getenv ("USER")) != NULL)
pw = getpwnam (ptr);
else
pw = getpwuid (getuid ());
if (pw)
ptr = pw->pw_dir;
}
if (ptr == NULL)
return xstrdup ("/");
copy = xmalloc (strlen (ptr) + 2);
strcpy (copy, ptr);
strcat (copy, "/");
return copy;
}
/* Find the first element of SEARCH_PATH which exists and is readable,
after expanding the %-escapes. Return 0 if we didn't find any, and
the path name of the one we found otherwise. */
static XrmDatabase
search_magic_path (const char *search_path, const char *class,
const char *escaped_suffix)
{
const char *s, *p;
for (s = search_path; *s; s = p)
{
for (p = s; *p && *p != ':'; p++)
;
if (p > s)
{
XrmDatabase db = magic_db (s, p - s, class, escaped_suffix);
if (db)
return db;
}
else if (*p == ':')
{
static char const ns[] = "%N%S";
XrmDatabase db = magic_db (ns, strlen (ns), class, escaped_suffix);
if (db)
return db;
}
if (*p == ':')
p++;
}
return 0;
}
/* Producing databases for individual sources. */
static XrmDatabase
get_system_app (const char *class)
{
const char *path;
path = getenv ("XFILESEARCHPATH");
if (! path) path = PATH_X_DEFAULTS;
return search_magic_path (path, class, 0);
}
static XrmDatabase
get_fallback (Display *display)
{
return NULL;
}
static XrmDatabase
get_user_app (const char *class)
{
XrmDatabase db = 0;
const char *path;
/* Check for XUSERFILESEARCHPATH. It is a path of complete file
names, not directories. */
path = getenv ("XUSERFILESEARCHPATH");
if (path)
db = search_magic_path (path, class, 0);
if (! db)
{
/* Check for APPLRESDIR; it is a path of directories. In each,
we have to search for LANG/CLASS and then CLASS. */
path = getenv ("XAPPLRESDIR");
if (path)
{
db = search_magic_path (path, class, "/%L/%N");
if (!db)
db = search_magic_path (path, class, "/%N");
}
}
if (! db)
{
/* Check in the home directory. This is a bit of a hack; let's
hope one's home directory doesn't contain any %-escapes. */
char *home = gethomedir ();
db = search_magic_path (home, class, "%L/%N");
if (! db)
db = search_magic_path (home, class, "%N");
xfree (home);
}
return db;
}
static XrmDatabase
get_user_db (Display *display)
{
XrmDatabase db;
char *xdefs;
#ifdef PBaseSize /* Cheap way to test for X11R4 or later. */
xdefs = XResourceManagerString (display);
#else
xdefs = display->xdefaults;
#endif
if (xdefs != NULL)
db = XrmGetStringDatabase (xdefs);
else
{
char *home;
char *xdefault;
home = gethomedir ();
xdefault = xmalloc (strlen (home) + sizeof ".Xdefaults");
strcpy (xdefault, home);
strcat (xdefault, ".Xdefaults");
db = XrmGetFileDatabase (xdefault);
xfree (home);
xfree (xdefault);
}
#ifdef HAVE_XSCREENRESOURCESTRING
/* Get the screen-specific resources too. */
xdefs = XScreenResourceString (DefaultScreenOfDisplay (display));
if (xdefs != NULL)
{
XrmMergeDatabases (XrmGetStringDatabase (xdefs), &db);
XFree (xdefs);
}
#endif
return db;
}
static XrmDatabase
get_environ_db (void)
{
XrmDatabase db;
char *p;
char *path = 0;
if ((p = getenv ("XENVIRONMENT")) == NULL)
{
static char const xdefaults[] = ".Xdefaults-";
char *home = gethomedir ();
char const *host = SSDATA (Vsystem_name);
ptrdiff_t pathsize = (strlen (home) + sizeof xdefaults
+ SBYTES (Vsystem_name));
path = xrealloc (home, pathsize);
strcat (strcat (path, xdefaults), host);
p = path;
}
db = XrmGetFileDatabase (p);
xfree (path);
return db;
}
/* External interface. */
/* Types of values that we can find in a database */
#define XrmStringType "String" /* String representation */
static XrmRepresentation x_rm_string; /* Quark representation */
/* Load X resources based on the display and a possible -xrm option. */
XrmDatabase
x_load_resources (Display *display, const char *xrm_string,
const char *myname, const char *myclass)
{
XrmDatabase user_database;
XrmDatabase rdb;
XrmDatabase db;
char line[256];
#if defined USE_MOTIF || !defined HAVE_XFT || !defined USE_LUCID
const char *helv = "-*-helvetica-medium-r-*--*-120-*-*-*-*-iso8859-1";
#endif
#ifdef USE_MOTIF
const char *courier = "-*-courier-medium-r-*-*-*-120-*-*-*-*-iso8859-1";
#endif
x_rm_string = XrmStringToQuark (XrmStringType);
#ifndef USE_X_TOOLKIT
/* pmr@osf.org says this shouldn't be done if USE_X_TOOLKIT.
I suspect it's because the toolkit version does this elsewhere. */
XrmInitialize ();
#endif
rdb = XrmGetStringDatabase ("");
/* Add some font defaults. If the font `helv' doesn't exist, widgets
will use some other default font. */
#ifdef USE_MOTIF
sprintf (line, "%s.pane.background: grey75", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fontList: %s", myclass, helv);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*menu*background: grey75", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*menubar*background: grey75", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*verticalScrollBar.background: grey75", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*verticalScrollBar.troughColor: grey75", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s.dialog*.background: grey75", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fsb.Text.background: white", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fsb.FilterText.background: white", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fsb*DirList.background: white", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fsb*ItemsList.background: white", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fsb*background: grey75", myclass);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fsb.Text.fontList: %s", myclass, courier);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fsb.FilterText.fontList: %s", myclass, courier);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fsb*ItemsList.fontList: %s", myclass, courier);
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fsb*DirList.fontList: %s", myclass, courier);
XrmPutLineResource (&rdb, line);
/* Set double click time of list boxes in the file selection
dialog from `double-click-time'. */
if (INTEGERP (Vdouble_click_time) && XINT (Vdouble_click_time) > 0)
{
sprintf (line, "%s*fsb*DirList.doubleClickInterval: %"pI"d",
myclass, XFASTINT (Vdouble_click_time));
XrmPutLineResource (&rdb, line);
sprintf (line, "%s*fsb*ItemsList.doubleClickInterval: %"pI"d",
myclass, XFASTINT (Vdouble_click_time));
XrmPutLineResource (&rdb, line);
}
#else /* not USE_MOTIF */
sprintf (line, "Emacs.dialog*.background: grey75");
XrmPutLineResource (&rdb, line);
#if !defined (HAVE_XFT) || !defined (USE_LUCID)
sprintf (line, "Emacs.dialog*.font: %s", helv);
XrmPutLineResource (&rdb, line);
sprintf (line, "*XlwMenu*font: %s", helv);
XrmPutLineResource (&rdb, line);
#endif
sprintf (line, "*XlwMenu*background: grey75");
XrmPutLineResource (&rdb, line);
sprintf (line, "Emacs*verticalScrollBar.background: grey75");
XrmPutLineResource (&rdb, line);
#endif /* not USE_MOTIF */
user_database = get_user_db (display);
/* Figure out what the "customization string" is, so we can use it
to decode paths. */
xfree (x_customization_string);
x_customization_string
= x_get_customization_string (user_database, myname, myclass);
/* Get application system defaults */
db = get_system_app (myclass);
if (db != NULL)
XrmMergeDatabases (db, &rdb);
/* Get Fallback resources */
db = get_fallback (display);
if (db != NULL)
XrmMergeDatabases (db, &rdb);
/* Get application user defaults */
db = get_user_app (myclass);
if (db != NULL)
XrmMergeDatabases (db, &rdb);
/* get User defaults */
if (user_database != NULL)
XrmMergeDatabases (user_database, &rdb);
/* Get Environment defaults. */
db = get_environ_db ();
if (db != NULL)
XrmMergeDatabases (db, &rdb);
/* Last, merge in any specification from the command line. */
if (xrm_string != NULL)
{
db = XrmGetStringDatabase (xrm_string);
if (db != NULL)
XrmMergeDatabases (db, &rdb);
}
return rdb;
}
/* Retrieve the value of the resource specified by NAME with class CLASS
and of type TYPE from database RDB. The value is returned in RET_VALUE. */
static int
x_get_resource (XrmDatabase rdb, const char *name, const char *class,
XrmRepresentation expected_type, XrmValue *ret_value)
{
XrmValue value;
XrmName namelist[100];
XrmClass classlist[100];
XrmRepresentation type;
XrmStringToNameList (name, namelist);
XrmStringToClassList (class, classlist);
if (XrmQGetResource (rdb, namelist, classlist, &type, &value) == True
&& (type == expected_type))
{
if (type == x_rm_string)
ret_value->addr = (char *) value.addr;
else
memcpy (ret_value->addr, value.addr, ret_value->size);
return value.size;
}
return 0;
}
/* Retrieve the string resource specified by NAME with CLASS from
database RDB. */
char *
x_get_string_resource (XrmDatabase rdb, const char *name, const char *class)
{
XrmValue value;
if (inhibit_x_resources)
/* --quick was passed, so this is a no-op. */
return NULL;
if (x_get_resource (rdb, name, class, x_rm_string, &value))
return (char *) value.addr;
return (char *) 0;
}
/* Stand-alone test facilities. */
#ifdef TESTRM
typedef char **List;
#define arg_listify(len, list) (list)
#define car(list) (*(list))
#define cdr(list) (list + 1)
#define NIL(list) (! *(list))
#define free_arglist(list)
static List
member (char *elt, List list)
{
List p;
for (p = list; ! NIL (p); p = cdr (p))
if (! strcmp (elt, car (p)))
return p;
return p;
}
static void
fatal (char *msg, char *prog)
{
if (errno)
perror (prog);
(void) fprintf (stderr, msg, prog);
exit (1);
}
int
main (int argc, char **argv)
{
Display *display;
char *displayname, *resource_string, *class, *name;
XrmDatabase xdb;
List arg_list, lp;
arg_list = arg_listify (argc, argv);
lp = member ("-d", arg_list);
if (!NIL (lp))
displayname = car (cdr (lp));
else
displayname = "localhost:0.0";
lp = member ("-xrm", arg_list);
if (! NIL (lp))
resource_string = car (cdr (lp));
else
resource_string = (char *) 0;
lp = member ("-c", arg_list);
if (! NIL (lp))
class = car (cdr (lp));
else
class = "Emacs";
lp = member ("-n", arg_list);
if (! NIL (lp))
name = car (cdr (lp));
else
name = "emacs";
free_arglist (arg_list);
if (!(display = XOpenDisplay (displayname)))
fatal ("Can't open display '%s'\n", XDisplayName (displayname));
xdb = x_load_resources (display, resource_string, name, class);
/* In a real program, you'd want to also do this: */
display->db = xdb;
while (1)
{
char query_name[90];
char query_class[90];
printf ("Name: ");
gets (query_name);
if (strlen (query_name))
{
char *value;
printf ("Class: ");
gets (query_class);
value = x_get_string_resource (xdb, query_name, query_class);
if (value != NULL)
printf ("\t%s(%s): %s\n\n", query_name, query_class, value);
else
printf ("\tNo Value.\n\n");
}
else
break;
}
printf ("\tExit.\n\n");
XCloseDisplay (display);
return 0;
}
#endif /* TESTRM */