diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2014-10-11 23:09:50 -0700 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2014-10-11 23:09:50 -0700 |
commit | 4c4c5b9121a550d006d1b57bc2ad97b0415cee9f (patch) | |
tree | 3b5921413aeaa73741d0a234572f151a05e6f7bd /src | |
parent | c1ec59da4905015d841ffeba9bb53a674b3a44f4 (diff) | |
download | emacs-4c4c5b9121a550d006d1b57bc2ad97b0415cee9f.tar.gz |
Fix putenv race conditions with undefined behavior.
Do all putenv calls before Emacs creates any threads.
Use a safer way to modify the TZ environment variable in the
presence of multiple threads. For further thread-safety,
prefer localtime_r and gmtime_r to localtime and gmtime,
and prefer struct tm's tm_gmtoff (if available) to calling
both localtime_r and gmtime_r.
* configure.ac (LOCALTIME_CACHE): Remove.
We needn't worry about SunOS 4 any more; Sun dropped support in 2003.
All uses of LOCALTIME_CACHE removed. This simplifies the fix.
(tzalloc): Add check for this function.
* admin/merge-gnulib (GNULIB_MODULES): Add time_r, since Emacs now
calls localtime_r and gmtime_r directly.
* src/dbusbind.c (Fdbus__init_bus): Move xputenv call from here ...
(init_dbusbind): ... to this new function.
* src/emacs.c (main) [HAVE_DBUS]: Call it before creating threads.
* src/xterm.c (x_term_init): Move xputenv call from here ...
(init_xterm): ... to this new function.
* src/emacs.c (main) [USE_GTK]: Call it before creating threads.
* src/editfns.c (HAVE_TM_GMTOFF): Default to false.
(dump_tz_string): New constant.
(init_editfns): Use it. This centralizes the dump_tz stuff.
Call set_time_zone_rule here, so that its xputenv is done
before Emacs goes multithreaded.
(mktime_z) [!HAVE_TZALLOC]: New function, which is typically
thread-safe enough for Emacs.
(format_time_string, Fdecode_time, Fcurrent_time_string)
(Fcurrent_time_zone):
Prefer localtime_r and gmtime_r, which are more thread-safe, to
localtime and gmtime. Remove now-unnecessary calls to block_input.
(tm_gmtoff): New static function.
(Fdecode_time, Fcurrent_time_zone): Use it.
(Fencode_time): Use mktime_z, for better thread-safety.
(set_time_zone_rule): Now static. Rewrite to be mostly thread-safe,
i.e., not quite thread-safe but good enough for Emacs typical usage.
Do not reclaim storage that is in the environment; let it leak.
Always call tzset, since localtime_r does not.
* src/emacs.c (dump_tz, Fdump_emacs) [HAVE_TZSET]: Remove dump_tz stuff.
This is now done in init_editfns.
* src/systime.h (mktime_z, timezone_t, tzalloc, tzfree) [!HAVE_TZALLOC]:
New macros and declarations, for platforms lacking tzalloc & friends.
Fixes: debbugs:8705
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 38 | ||||
-rw-r--r-- | src/dbusbind.c | 11 | ||||
-rw-r--r-- | src/editfns.c | 359 | ||||
-rw-r--r-- | src/emacs.c | 62 | ||||
-rw-r--r-- | src/lisp.h | 3 | ||||
-rw-r--r-- | src/systime.h | 16 | ||||
-rw-r--r-- | src/xterm.c | 13 |
7 files changed, 262 insertions, 240 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index f511aa19a19..568022f478b 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,41 @@ +2014-10-12 Paul Eggert <eggert@cs.ucla.edu> + + Fix putenv race conditions with undefined behavior (Bug#8705). + Do all putenv calls before Emacs creates any threads. + Use a safer way to modify the TZ environment variable in the + presence of multiple threads. For further thread-safety, + prefer localtime_r and gmtime_r to localtime and gmtime, + and prefer struct tm's tm_gmtoff (if available) to calling + both localtime_r and gmtime_r. + * dbusbind.c (Fdbus__init_bus): Move xputenv call from here ... + (init_dbusbind): ... to this new function. + * emacs.c (main) [HAVE_DBUS]: Call it before creating threads. + * xterm.c (x_term_init): Move xputenv call from here ... + (init_xterm): ... to this new function. + * emacs.c (main) [USE_GTK]: Call it before creating threads. + * editfns.c (HAVE_TM_GMTOFF): Default to false. + (dump_tz_string): New constant. + (init_editfns): Use it. This centralizes the dump_tz stuff. + Call set_time_zone_rule here, so that its xputenv is done + before Emacs goes multithreaded. + (mktime_z) [!HAVE_TZALLOC]: New function, which is typically + thread-safe enough for Emacs. + (format_time_string, Fdecode_time, Fcurrent_time_string) + (Fcurrent_time_zone): + Prefer localtime_r and gmtime_r, which are more thread-safe, to + localtime and gmtime. Remove now-unnecessary calls to block_input. + (tm_gmtoff): New static function. + (Fdecode_time, Fcurrent_time_zone): Use it. + (Fencode_time): Use mktime_z, for better thread-safety. + (set_time_zone_rule): Now static. Rewrite to be mostly thread-safe, + i.e., not quite thread-safe but good enough for Emacs typical usage. + Do not reclaim storage that is in the environment; let it leak. + Always call tzset, since localtime_r does not. + * emacs.c (dump_tz, Fdump_emacs) [HAVE_TZSET]: Remove dump_tz stuff. + This is now done in init_editfns. + * systime.h (mktime_z, timezone_t, tzalloc, tzfree) [!HAVE_TZALLOC]: + New macros and declarations, for platforms lacking tzalloc & friends. + 2014-10-09 Paul Eggert <eggert@cs.ucla.edu> * lisp.h (USE_STACK_STRING): Now true only if USE_STACK CONS. diff --git a/src/dbusbind.c b/src/dbusbind.c index f81666ba7bd..4852739d8e4 100644 --- a/src/dbusbind.c +++ b/src/dbusbind.c @@ -1054,6 +1054,7 @@ xd_remove_watch (DBusWatch *watch, void *data) /* Unset session environment. */ #if 0 + /* This is buggy, since unsetenv is not thread-safe. */ if (XSYMBOL (QCdbus_session_bus) == data) { XD_DEBUG_MESSAGE ("unsetenv DBUS_SESSION_BUS_ADDRESS"); @@ -1219,9 +1220,6 @@ this connection to those buses. */) XSETFASTINT (val, (intptr_t) connection); xd_registered_buses = Fcons (Fcons (bus, val), xd_registered_buses); - /* We do not want to abort. */ - xputenv ("DBUS_FATAL_WARNINGS=0"); - /* Cleanup. */ dbus_error_free (&derror); } @@ -1738,6 +1736,13 @@ xd_read_queued_messages (int fd, void *data) void +init_dbusbind (void) +{ + /* We do not want to abort. */ + xputenv ("DBUS_FATAL_WARNINGS=0"); +} + +void syms_of_dbusbind (void) { diff --git a/src/editfns.c b/src/editfns.c index e7c960dfffe..d17d809c9ec 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -64,11 +64,17 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ extern Lisp_Object w32_get_internal_run_time (void); #endif +static void set_time_zone_rule (char const *); static Lisp_Object format_time_string (char const *, ptrdiff_t, struct timespec, bool, struct tm *); +static long int tm_gmtoff (struct tm *); static int tm_diff (struct tm *, struct tm *); static void update_buffer_properties (ptrdiff_t, ptrdiff_t); +#ifndef HAVE_TM_GMTOFF +# define HAVE_TM_GMTOFF false +#endif + static Lisp_Object Qbuffer_access_fontify_functions; /* Symbol for the text property used to mark fields. */ @@ -79,15 +85,12 @@ Lisp_Object Qfield; static Lisp_Object Qboundary; -/* The startup value of the TZ environment variable so it can be - restored if the user calls set-time-zone-rule with a nil - argument. If null, the TZ environment variable was unset. */ +/* The startup value of the TZ environment variable; null if unset. */ static char const *initial_tz; -/* True if the static variable tzvalbuf (defined in - set_time_zone_rule) is part of 'environ'. */ -static bool tzvalbuf_in_environ; - +/* A valid but unlikely setting for the TZ environment variable. + It is OK (though a bit slower) if the user chooses this value. */ +static char const dump_tz_string[] = "TZ=UtC0"; void init_editfns (void) @@ -101,13 +104,38 @@ init_editfns (void) init_system_name (); #ifndef CANNOT_DUMP - /* Don't bother with this on initial start when just dumping out */ + /* When just dumping out, set the time zone to a known unlikely value + and skip the rest of this function. */ if (!initialized) - return; -#endif /* not CANNOT_DUMP */ + { +# ifdef HAVE_TZSET + xputenv ((char *) dump_tz_string); + tzset (); +# endif + return; + } +#endif + + char *tz = getenv ("TZ"); + initial_tz = tz; - initial_tz = getenv ("TZ"); - tzvalbuf_in_environ = 0; +#if !defined CANNOT_DUMP && defined HAVE_TZSET + /* If the execution TZ happens to be the same as the dump TZ, + change it to some other value and then change it back, + to force the underlying implementation to reload the TZ info. + This is needed on implementations that load TZ info from files, + since the TZ file contents may differ between dump and execution. */ + if (tz && strcmp (tz, &dump_tz_string[sizeof "TZ=" - 1]) == 0) + { + ++*tz; + tzset (); + --*tz; + } +#endif + + /* Call set_time_zone_rule now, so that its call to putenv is done + before multiple threads are active. */ + set_time_zone_rule (tz); pw = getpwuid (getuid ()); #ifdef MSDOS @@ -1373,6 +1401,30 @@ time_overflow (void) error ("Specified time is not representable"); } +/* A substitute for mktime_z on platforms that lack it. It's not + thread-safe, but should be good enough for Emacs in typical use. */ +#ifndef HAVE_TZALLOC +time_t +mktime_z (timezone_t tz, struct tm *tm) +{ + char *oldtz = getenv ("TZ"); + USE_SAFE_ALLOCA; + if (oldtz) + { + size_t oldtzsize = strlen (oldtz) + 1; + char *oldtzcopy = SAFE_ALLOCA (oldtzsize); + oldtz = strcpy (oldtzcopy, oldtz); + } + block_input (); + set_time_zone_rule (tz); + time_t t = mktime (tm); + set_time_zone_rule (oldtz); + unblock_input (); + SAFE_FREE (); + return t; +} +#endif + /* Return the upper part of the time T (everything but the bottom 16 bits). */ static EMACS_INT hi_time (time_t t) @@ -1768,39 +1820,28 @@ format_time_string (char const *format, ptrdiff_t formatlen, size_t len; Lisp_Object bufstring; int ns = t.tv_nsec; - struct tm *tm; USE_SAFE_ALLOCA; - while (1) - { - time_t *taddr = &t.tv_sec; - block_input (); - - synchronize_system_time_locale (); - - tm = ut ? gmtime (taddr) : localtime (taddr); - if (! tm) - { - unblock_input (); - time_overflow (); - } - *tmp = *tm; + tmp = ut ? gmtime_r (&t.tv_sec, tmp) : localtime_r (&t.tv_sec, tmp); + if (! tmp) + time_overflow (); + synchronize_system_time_locale (); + while (true) + { buf[0] = '\1'; - len = emacs_nmemftime (buf, size, format, formatlen, tm, ut, ns); + len = emacs_nmemftime (buf, size, format, formatlen, tmp, ut, ns); if ((0 < len && len < size) || (len == 0 && buf[0] == '\0')) break; /* Buffer was too small, so make it bigger and try again. */ - len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tm, ut, ns); - unblock_input (); + len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tmp, ut, ns); if (STRING_BYTES_BOUND <= len) string_overflow (); size = len + 1; buf = SAFE_ALLOCA (size); } - unblock_input (); bufstring = make_unibyte_string (buf, len); SAFE_FREE (); return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0); @@ -1824,38 +1865,30 @@ DOW and ZONE.) */) (Lisp_Object specified_time) { time_t time_spec = lisp_seconds_argument (specified_time); - struct tm save_tm; - struct tm *decoded_time; - Lisp_Object list_args[9]; + struct tm local_tm, gmt_tm; - block_input (); - decoded_time = localtime (&time_spec); - if (decoded_time) - save_tm = *decoded_time; - unblock_input (); - if (! (decoded_time - && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= save_tm.tm_year - && save_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE)) + if (! (localtime_r (&time_spec, &local_tm) + && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= local_tm.tm_year + && local_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE)) time_overflow (); - XSETFASTINT (list_args[0], save_tm.tm_sec); - XSETFASTINT (list_args[1], save_tm.tm_min); - XSETFASTINT (list_args[2], save_tm.tm_hour); - XSETFASTINT (list_args[3], save_tm.tm_mday); - XSETFASTINT (list_args[4], save_tm.tm_mon + 1); - /* On 64-bit machines an int is narrower than EMACS_INT, thus the - cast below avoids overflow in int arithmetics. */ - XSETINT (list_args[5], TM_YEAR_BASE + (EMACS_INT) save_tm.tm_year); - XSETFASTINT (list_args[6], save_tm.tm_wday); - list_args[7] = save_tm.tm_isdst ? Qt : Qnil; - block_input (); - decoded_time = gmtime (&time_spec); - if (decoded_time == 0) - list_args[8] = Qnil; - else - XSETINT (list_args[8], tm_diff (&save_tm, decoded_time)); - unblock_input (); - return Flist (9, list_args); + /* Avoid overflow when INT_MAX < EMACS_INT_MAX. */ + EMACS_INT tm_year_base = TM_YEAR_BASE; + + return Flist (9, ((Lisp_Object []) + {make_number (local_tm.tm_sec), + make_number (local_tm.tm_min), + make_number (local_tm.tm_hour), + make_number (local_tm.tm_mday), + make_number (local_tm.tm_mon + 1), + make_number (local_tm.tm_year + tm_year_base), + make_number (local_tm.tm_wday), + local_tm.tm_isdst ? Qt : Qnil, + (HAVE_TM_GMTOFF + ? make_number (tm_gmtoff (&local_tm)) + : gmtime_r (&time_spec, &gmt_tm) + ? make_number (tm_diff (&local_tm, &gmt_tm)) + : Qnil)})); } /* Return OBJ - OFFSET, checking that OBJ is a valid fixnum and that @@ -1911,18 +1944,12 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */) if (CONSP (zone)) zone = XCAR (zone); if (NILP (zone)) - { - block_input (); - value = mktime (&tm); - unblock_input (); - } + value = mktime (&tm); else { static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d"; char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)]; - char *old_tzstring; const char *tzstring; - USE_SAFE_ALLOCA; if (EQ (zone, Qt)) tzstring = "UTC0"; @@ -1939,29 +1966,13 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */) tzstring = tzbuf; } else - error ("Invalid time zone specification"); + tzstring = 0; - old_tzstring = getenv ("TZ"); - if (old_tzstring) - { - char *buf = SAFE_ALLOCA (strlen (old_tzstring) + 1); - old_tzstring = strcpy (buf, old_tzstring); - } - - block_input (); - - /* Set TZ before calling mktime; merely adjusting mktime's returned - value doesn't suffice, since that would mishandle leap seconds. */ - set_time_zone_rule (tzstring); - - value = mktime (&tm); - - set_time_zone_rule (old_tzstring); -#ifdef LOCALTIME_CACHE - tzset (); -#endif - unblock_input (); - SAFE_FREE (); + timezone_t tz = tzstring ? tzalloc (tzstring) : 0; + if (! tz) + error ("Invalid time zone specification"); + value = mktime_z (tz, &tm); + tzfree (tz); } if (value == (time_t) -1) @@ -1987,34 +1998,27 @@ but this is considered obsolete. */) (Lisp_Object specified_time) { time_t value = lisp_seconds_argument (specified_time); - struct tm *tm; - char buf[sizeof "Mon Apr 30 12:49:17 " + INT_STRLEN_BOUND (int) + 1]; - int len IF_LINT (= 0); /* Convert to a string in ctime format, except without the trailing newline, and without the 4-digit year limit. Don't use asctime or ctime, as they might dump core if the year is outside the range -999 .. 9999. */ - block_input (); - tm = localtime (&value); - if (tm) - { - static char const wday_name[][4] = - { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - static char const mon_name[][4] = - { "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - printmax_t year_base = TM_YEAR_BASE; - - len = sprintf (buf, "%s %s%3d %02d:%02d:%02d %"pMd, - wday_name[tm->tm_wday], mon_name[tm->tm_mon], tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_year + year_base); - } - unblock_input (); - if (! tm) + struct tm tm; + if (! localtime_r (&value, &tm)) time_overflow (); + static char const wday_name[][4] = + { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static char const mon_name[][4] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + printmax_t year_base = TM_YEAR_BASE; + char buf[sizeof "Mon Apr 30 12:49:17 " + INT_STRLEN_BOUND (int) + 1]; + int len = sprintf (buf, "%s %s%3d %02d:%02d:%02d %"pMd, + wday_name[tm.tm_wday], mon_name[tm.tm_mon], tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + year_base); + return make_unibyte_string (buf, len); } @@ -2041,6 +2045,17 @@ tm_diff (struct tm *a, struct tm *b) + (a->tm_sec - b->tm_sec)); } +/* Yield A's UTC offset, or an unspecified value if unknown. */ +static long int +tm_gmtoff (struct tm *a) +{ +#if HAVE_TM_GMTOFF + return a->tm_gmtoff; +#else + return 0; +#endif +} + DEFUN ("current-time-zone", Fcurrent_time_zone, Scurrent_time_zone, 0, 1, 0, doc: /* Return the offset and name for the local time zone. This returns a list of the form (OFFSET NAME). @@ -2059,32 +2074,30 @@ the data it can't find. */) (Lisp_Object specified_time) { struct timespec value; - int offset; - struct tm *t; - struct tm localtm; + struct tm local_tm, gmt_tm; Lisp_Object zone_offset, zone_name; zone_offset = Qnil; value = make_timespec (lisp_seconds_argument (specified_time), 0); - zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &localtm); - block_input (); - t = gmtime (&value.tv_sec); - if (t) - offset = tm_diff (&localtm, t); - unblock_input (); + zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &local_tm); - if (t) + if (HAVE_TM_GMTOFF || gmtime_r (&value.tv_sec, &gmt_tm)) { + long int offset = (HAVE_TM_GMTOFF + ? tm_gmtoff (&local_tm) + : tm_diff (&local_tm, &gmt_tm)); zone_offset = make_number (offset); if (SCHARS (zone_name) == 0) { /* No local time zone name is available; use "+-NNNN" instead. */ - int m = offset / 60; - int am = offset < 0 ? - m : m; - char buf[sizeof "+00" + INT_STRLEN_BOUND (int)]; - zone_name = make_formatted_string (buf, "%c%02d%02d", + long int m = offset / 60; + long int am = offset < 0 ? - m : m; + long int hour = am / 60; + int min = am % 60; + char buf[sizeof "+00" + INT_STRLEN_BOUND (long int)]; + zone_name = make_formatted_string (buf, "%c%02ld%02d", (offset < 0 ? '-' : '+'), - am / 60, am % 60); + hour, min); } } @@ -2123,12 +2136,12 @@ only the former. */) /* Set the local time zone rule to TZSTRING. - This function is not thread-safe, partly because putenv, unsetenv - and tzset are not, and partly because of the static storage it - updates. Other threads that invoke localtime etc. may be adversely - affected while this function is executing. */ + This function is not thread-safe, in theory because putenv is not, + but mostly because of the static storage it updates. Other threads + that invoke localtime etc. may be adversely affected while this + function is executing. */ -void +static void set_time_zone_rule (const char *tzstring) { /* A buffer holding a string of the form "TZ=value", intended @@ -2137,75 +2150,47 @@ set_time_zone_rule (const char *tzstring) static ptrdiff_t tzvalbufsize; int tzeqlen = sizeof "TZ=" - 1; + ptrdiff_t tzstringlen = tzstring ? strlen (tzstring) : 0; + char *tzval = tzvalbuf; + bool new_tzvalbuf = tzvalbufsize <= tzeqlen + tzstringlen; -#ifdef LOCALTIME_CACHE - /* These two values are known to load tz files in buggy implementations, - i.e., Solaris 1 executables running under either Solaris 1 or Solaris 2. - Their values shouldn't matter in non-buggy implementations. - We don't use string literals for these strings, - since if a string in the environment is in readonly - storage, it runs afoul of bugs in SVR4 and Solaris 2.3. - See Sun bugs 1113095 and 1114114, ``Timezone routines - improperly modify environment''. */ - - static char set_time_zone_rule_tz[][sizeof "TZ=GMT+0"] - = { "TZ=GMT+0", "TZ=GMT+1" }; - - /* In SunOS 4.1.3_U1 and 4.1.4, if TZ has a value like - "US/Pacific" that loads a tz file, then changes to a value like - "XXX0" that does not load a tz file, and then changes back to - its original value, the last change is (incorrectly) ignored. - Also, if TZ changes twice in succession to values that do - not load a tz file, tzset can dump core (see Sun bug#1225179). - The following code works around these bugs. */ + if (new_tzvalbuf) + { + /* Do not attempt to free the old tzvalbuf, since another thread + may be using it. In practice, the first allocation is large + enough and memory does not leak. */ + tzval = xpalloc (NULL, &tzvalbufsize, + tzeqlen + tzstringlen - tzvalbufsize + 1, -1, 1); + tzvalbuf = tzval; + tzval[1] = 'Z'; + tzval[2] = '='; + } if (tzstring) { - /* Temporarily set TZ to a value that loads a tz file - and that differs from tzstring. */ - bool eq0 = strcmp (tzstring, set_time_zone_rule_tz[0] + tzeqlen) == 0; - xputenv (set_time_zone_rule_tz[eq0]); + /* Modify TZVAL in place. Although this is dicey in a + multithreaded environment, we know of no portable alternative. + Calling putenv or setenv could crash some other thread. */ + tzval[0] = 'T'; + strcpy (tzval + tzeqlen, tzstring); } else { - /* The implied tzstring is unknown, so temporarily set TZ to - two different values that each load a tz file. */ - xputenv (set_time_zone_rule_tz[0]); - tzset (); - xputenv (set_time_zone_rule_tz[1]); + /* Turn 'TZ=whatever' into an empty environment variable 'tZ='. + Although this is also dicey, calling unsetenv here can crash Emacs. + See Bug#8705. */ + tzval[0] = 't'; + tzval[tzeqlen] = 0; } - tzset (); - tzvalbuf_in_environ = 0; -#endif - if (!tzstring) - { - unsetenv ("TZ"); - tzvalbuf_in_environ = 0; - } - else + if (new_tzvalbuf) { - ptrdiff_t tzstringlen = strlen (tzstring); - - if (tzvalbufsize <= tzeqlen + tzstringlen) - { - unsetenv ("TZ"); - tzvalbuf_in_environ = 0; - tzvalbuf = xpalloc (tzvalbuf, &tzvalbufsize, - tzeqlen + tzstringlen - tzvalbufsize + 1, -1, 1); - memcpy (tzvalbuf, "TZ=", tzeqlen); - } - - strcpy (tzvalbuf + tzeqlen, tzstring); - - if (!tzvalbuf_in_environ) - { - xputenv (tzvalbuf); - tzvalbuf_in_environ = 1; - } + /* Although this is not thread-safe, in practice this runs only + on startup when there is only one thread. */ + xputenv (tzval); } -#ifdef LOCALTIME_CACHE +#ifdef HAVE_TZSET tzset (); #endif } diff --git a/src/emacs.c b/src/emacs.c index 60b67b5a902..90182e53e70 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -578,12 +578,6 @@ DEFUN ("invocation-directory", Finvocation_directory, Sinvocation_directory, } -#ifdef HAVE_TZSET -/* A valid but unlikely value for the TZ environment value. - It is OK (though a bit slower) if the user actually chooses this value. */ -static char const dump_tz[] = "UtC0"; -#endif - /* Test whether the next argument in ARGV matches SSTR or a prefix of LSTR (at least MINLEN characters). If so, then if VALPTR is non-null (the argument is supposed to have a value) store in *VALPTR either @@ -1548,8 +1542,23 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem init_charset (); - init_editfns (); /* init_process_emacs uses Voperating_system_release. */ - init_process_emacs (); /* init_display uses add_keyboard_wait_descriptor. */ + /* This calls putenv and so must precede init_process_emacs. Also, + it sets Voperating_system_release, which init_process_emacs uses. */ + init_editfns (); + + /* These two call putenv. */ +#ifdef HAVE_DBUS + init_dbusbind (); +#endif +#ifdef USE_GTK + init_xterm (); +#endif + + /* This can create a thread that may call getenv, so it must follow + all calls to putenv and setenv. Also, this sets up + add_keyboard_wait_descriptor, which init_display uses. */ + init_process_emacs (); + init_keyboard (); /* This too must precede init_sys_modes. */ if (!noninteractive) init_display (); /* Determine terminal type. Calls init_sys_modes. */ @@ -1586,26 +1595,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem build_string ("loadup.el")); } - if (initialized) - { -#ifdef HAVE_TZSET - { - /* If the execution TZ happens to be the same as the dump TZ, - change it to some other value and then change it back, - to force the underlying implementation to reload the TZ info. - This is needed on implementations that load TZ info from files, - since the TZ file contents may differ between dump and execution. */ - char *tz = getenv ("TZ"); - if (tz && !strcmp (tz, dump_tz)) - { - ++*tz; - tzset (); - --*tz; - } - } -#endif - } - /* Set up for profiling. This is known to work on FreeBSD, GNU/Linux and MinGW. It might work on some other systems too. Give it a try and tell us if it works on your system. To compile @@ -1630,15 +1619,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem initialized = 1; -#ifdef LOCALTIME_CACHE - /* Some versions of localtime have a bug. They cache the value of the time - zone rather than looking it up every time. Since localtime() is - called to bolt the undumping time into the undumped emacs, this - results in localtime ignoring the TZ environment variable. - This flushes the new TZ value into localtime. */ - tzset (); -#endif /* defined (LOCALTIME_CACHE) */ - /* Enter editor command loop. This never returns. */ Frecursive_edit (); /* NOTREACHED */ @@ -2119,14 +2099,6 @@ You must run Emacs in batch mode in order to dump it. */) tem = Vpurify_flag; Vpurify_flag = Qnil; -#ifdef HAVE_TZSET - set_time_zone_rule (dump_tz); -#ifndef LOCALTIME_CACHE - /* Force a tz reload, since set_time_zone_rule doesn't. */ - tzset (); -#endif -#endif - fflush (stdout); /* Tell malloc where start of impure now is. */ /* Also arrange for warnings when nearly out of space. */ diff --git a/src/lisp.h b/src/lisp.h index 9e4cc5fdc53..89f29ea268b 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3990,7 +3990,6 @@ extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, bool); extern void init_editfns (void); extern void syms_of_editfns (void); -extern void set_time_zone_rule (const char *); /* Defined in buffer.c. */ extern bool mouse_face_overlay_overlaps (Lisp_Object); @@ -4398,6 +4397,7 @@ extern void syms_of_xsmfns (void); extern void syms_of_xselect (void); /* Defined in xterm.c. */ +extern void init_xterm (void); extern void syms_of_xterm (void); #endif /* HAVE_X_WINDOWS */ @@ -4419,6 +4419,7 @@ extern void syms_of_decompress (void); #ifdef HAVE_DBUS /* Defined in dbusbind.c. */ +void init_dbusbind (void); void syms_of_dbusbind (void); #endif diff --git a/src/systime.h b/src/systime.h index a834bce76dc..8f018044660 100644 --- a/src/systime.h +++ b/src/systime.h @@ -93,6 +93,22 @@ extern bool decode_time_components (Lisp_Object, Lisp_Object, Lisp_Object, extern struct timespec lisp_time_argument (Lisp_Object); #endif +#ifndef HAVE_TZALLOC +# undef mktime_z +# undef timezone_t +# undef tzalloc +# undef tzfree +# define mktime_z emacs_mktime_z +# define timezone_t emacs_timezone_t +# define tzalloc emacs_tzalloc +# define tzfree emacs_tzfree +typedef char const *timezone_t; +INLINE timezone_t tzalloc (char const *name) { return name; } +INLINE void tzfree (timezone_t tz) { } +/* Defined in editfns.c. */ +extern time_t mktime_z (timezone_t, struct tm *); +#endif + INLINE_HEADER_END #endif /* EMACS_SYSTIME_H */ diff --git a/src/xterm.c b/src/xterm.c index aff57f6a17e..f32aea031f9 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -10717,10 +10717,6 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) XSetLocaleModifiers (""); - /* Emacs can only handle core input events, so make sure - Gtk doesn't use Xinput or Xinput2 extensions. */ - xputenv ("GDK_CORE_DEVICE_EVENTS=1"); - /* Work around GLib bug that outputs a faulty warning. See https://bugzilla.gnome.org/show_bug.cgi?id=563627. */ id = g_log_set_handler ("GLib", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL @@ -11470,6 +11466,15 @@ x_initialize (void) XSetIOErrorHandler (x_io_error_quitter); } +#ifdef USE_GTK +void +init_xterm (void) +{ + /* Emacs can handle only core input events, so make sure + Gtk doesn't use Xinput or Xinput2 extensions. */ + xputenv ("GDK_CORE_DEVICE_EVENTS=1"); +} +#endif void syms_of_xterm (void) |