aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2014-10-11 23:09:50 -0700
committerPaul Eggert2014-10-11 23:09:50 -0700
commit4c4c5b9121a550d006d1b57bc2ad97b0415cee9f (patch)
tree3b5921413aeaa73741d0a234572f151a05e6f7bd /src
parentc1ec59da4905015d841ffeba9bb53a674b3a44f4 (diff)
downloademacs-4c4c5b9121a550d006d1b57bc2ad97b0415cee9f.tar.gz
emacs-4c4c5b9121a550d006d1b57bc2ad97b0415cee9f.zip
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/ChangeLog38
-rw-r--r--src/dbusbind.c11
-rw-r--r--src/editfns.c359
-rw-r--r--src/emacs.c62
-rw-r--r--src/lisp.h3
-rw-r--r--src/systime.h16
-rw-r--r--src/xterm.c13
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 @@
12014-10-12 Paul Eggert <eggert@cs.ucla.edu>
2
3 Fix putenv race conditions with undefined behavior (Bug#8705).
4 Do all putenv calls before Emacs creates any threads.
5 Use a safer way to modify the TZ environment variable in the
6 presence of multiple threads. For further thread-safety,
7 prefer localtime_r and gmtime_r to localtime and gmtime,
8 and prefer struct tm's tm_gmtoff (if available) to calling
9 both localtime_r and gmtime_r.
10 * dbusbind.c (Fdbus__init_bus): Move xputenv call from here ...
11 (init_dbusbind): ... to this new function.
12 * emacs.c (main) [HAVE_DBUS]: Call it before creating threads.
13 * xterm.c (x_term_init): Move xputenv call from here ...
14 (init_xterm): ... to this new function.
15 * emacs.c (main) [USE_GTK]: Call it before creating threads.
16 * editfns.c (HAVE_TM_GMTOFF): Default to false.
17 (dump_tz_string): New constant.
18 (init_editfns): Use it. This centralizes the dump_tz stuff.
19 Call set_time_zone_rule here, so that its xputenv is done
20 before Emacs goes multithreaded.
21 (mktime_z) [!HAVE_TZALLOC]: New function, which is typically
22 thread-safe enough for Emacs.
23 (format_time_string, Fdecode_time, Fcurrent_time_string)
24 (Fcurrent_time_zone):
25 Prefer localtime_r and gmtime_r, which are more thread-safe, to
26 localtime and gmtime. Remove now-unnecessary calls to block_input.
27 (tm_gmtoff): New static function.
28 (Fdecode_time, Fcurrent_time_zone): Use it.
29 (Fencode_time): Use mktime_z, for better thread-safety.
30 (set_time_zone_rule): Now static. Rewrite to be mostly thread-safe,
31 i.e., not quite thread-safe but good enough for Emacs typical usage.
32 Do not reclaim storage that is in the environment; let it leak.
33 Always call tzset, since localtime_r does not.
34 * emacs.c (dump_tz, Fdump_emacs) [HAVE_TZSET]: Remove dump_tz stuff.
35 This is now done in init_editfns.
36 * systime.h (mktime_z, timezone_t, tzalloc, tzfree) [!HAVE_TZALLOC]:
37 New macros and declarations, for platforms lacking tzalloc & friends.
38
12014-10-09 Paul Eggert <eggert@cs.ucla.edu> 392014-10-09 Paul Eggert <eggert@cs.ucla.edu>
2 40
3 * lisp.h (USE_STACK_STRING): Now true only if USE_STACK CONS. 41 * 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)
1054 1054
1055 /* Unset session environment. */ 1055 /* Unset session environment. */
1056#if 0 1056#if 0
1057 /* This is buggy, since unsetenv is not thread-safe. */
1057 if (XSYMBOL (QCdbus_session_bus) == data) 1058 if (XSYMBOL (QCdbus_session_bus) == data)
1058 { 1059 {
1059 XD_DEBUG_MESSAGE ("unsetenv DBUS_SESSION_BUS_ADDRESS"); 1060 XD_DEBUG_MESSAGE ("unsetenv DBUS_SESSION_BUS_ADDRESS");
@@ -1219,9 +1220,6 @@ this connection to those buses. */)
1219 XSETFASTINT (val, (intptr_t) connection); 1220 XSETFASTINT (val, (intptr_t) connection);
1220 xd_registered_buses = Fcons (Fcons (bus, val), xd_registered_buses); 1221 xd_registered_buses = Fcons (Fcons (bus, val), xd_registered_buses);
1221 1222
1222 /* We do not want to abort. */
1223 xputenv ("DBUS_FATAL_WARNINGS=0");
1224
1225 /* Cleanup. */ 1223 /* Cleanup. */
1226 dbus_error_free (&derror); 1224 dbus_error_free (&derror);
1227 } 1225 }
@@ -1738,6 +1736,13 @@ xd_read_queued_messages (int fd, void *data)
1738 1736
1739 1737
1740void 1738void
1739init_dbusbind (void)
1740{
1741 /* We do not want to abort. */
1742 xputenv ("DBUS_FATAL_WARNINGS=0");
1743}
1744
1745void
1741syms_of_dbusbind (void) 1746syms_of_dbusbind (void)
1742{ 1747{
1743 1748
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/>. */
64extern Lisp_Object w32_get_internal_run_time (void); 64extern Lisp_Object w32_get_internal_run_time (void);
65#endif 65#endif
66 66
67static void set_time_zone_rule (char const *);
67static Lisp_Object format_time_string (char const *, ptrdiff_t, struct timespec, 68static Lisp_Object format_time_string (char const *, ptrdiff_t, struct timespec,
68 bool, struct tm *); 69 bool, struct tm *);
70static long int tm_gmtoff (struct tm *);
69static int tm_diff (struct tm *, struct tm *); 71static int tm_diff (struct tm *, struct tm *);
70static void update_buffer_properties (ptrdiff_t, ptrdiff_t); 72static void update_buffer_properties (ptrdiff_t, ptrdiff_t);
71 73
74#ifndef HAVE_TM_GMTOFF
75# define HAVE_TM_GMTOFF false
76#endif
77
72static Lisp_Object Qbuffer_access_fontify_functions; 78static Lisp_Object Qbuffer_access_fontify_functions;
73 79
74/* Symbol for the text property used to mark fields. */ 80/* Symbol for the text property used to mark fields. */
@@ -79,15 +85,12 @@ Lisp_Object Qfield;
79 85
80static Lisp_Object Qboundary; 86static Lisp_Object Qboundary;
81 87
82/* The startup value of the TZ environment variable so it can be 88/* The startup value of the TZ environment variable; null if unset. */
83 restored if the user calls set-time-zone-rule with a nil
84 argument. If null, the TZ environment variable was unset. */
85static char const *initial_tz; 89static char const *initial_tz;
86 90
87/* True if the static variable tzvalbuf (defined in 91/* A valid but unlikely setting for the TZ environment variable.
88 set_time_zone_rule) is part of 'environ'. */ 92 It is OK (though a bit slower) if the user chooses this value. */
89static bool tzvalbuf_in_environ; 93static char const dump_tz_string[] = "TZ=UtC0";
90
91 94
92void 95void
93init_editfns (void) 96init_editfns (void)
@@ -101,13 +104,38 @@ init_editfns (void)
101 init_system_name (); 104 init_system_name ();
102 105
103#ifndef CANNOT_DUMP 106#ifndef CANNOT_DUMP
104 /* Don't bother with this on initial start when just dumping out */ 107 /* When just dumping out, set the time zone to a known unlikely value
108 and skip the rest of this function. */
105 if (!initialized) 109 if (!initialized)
106 return; 110 {
107#endif /* not CANNOT_DUMP */ 111# ifdef HAVE_TZSET
112 xputenv ((char *) dump_tz_string);
113 tzset ();
114# endif
115 return;
116 }
117#endif
118
119 char *tz = getenv ("TZ");
120 initial_tz = tz;
108 121
109 initial_tz = getenv ("TZ"); 122#if !defined CANNOT_DUMP && defined HAVE_TZSET
110 tzvalbuf_in_environ = 0; 123 /* If the execution TZ happens to be the same as the dump TZ,
124 change it to some other value and then change it back,
125 to force the underlying implementation to reload the TZ info.
126 This is needed on implementations that load TZ info from files,
127 since the TZ file contents may differ between dump and execution. */
128 if (tz && strcmp (tz, &dump_tz_string[sizeof "TZ=" - 1]) == 0)
129 {
130 ++*tz;
131 tzset ();
132 --*tz;
133 }
134#endif
135
136 /* Call set_time_zone_rule now, so that its call to putenv is done
137 before multiple threads are active. */
138 set_time_zone_rule (tz);
111 139
112 pw = getpwuid (getuid ()); 140 pw = getpwuid (getuid ());
113#ifdef MSDOS 141#ifdef MSDOS
@@ -1373,6 +1401,30 @@ time_overflow (void)
1373 error ("Specified time is not representable"); 1401 error ("Specified time is not representable");
1374} 1402}
1375 1403
1404/* A substitute for mktime_z on platforms that lack it. It's not
1405 thread-safe, but should be good enough for Emacs in typical use. */
1406#ifndef HAVE_TZALLOC
1407time_t
1408mktime_z (timezone_t tz, struct tm *tm)
1409{
1410 char *oldtz = getenv ("TZ");
1411 USE_SAFE_ALLOCA;
1412 if (oldtz)
1413 {
1414 size_t oldtzsize = strlen (oldtz) + 1;
1415 char *oldtzcopy = SAFE_ALLOCA (oldtzsize);
1416 oldtz = strcpy (oldtzcopy, oldtz);
1417 }
1418 block_input ();
1419 set_time_zone_rule (tz);
1420 time_t t = mktime (tm);
1421 set_time_zone_rule (oldtz);
1422 unblock_input ();
1423 SAFE_FREE ();
1424 return t;
1425}
1426#endif
1427
1376/* Return the upper part of the time T (everything but the bottom 16 bits). */ 1428/* Return the upper part of the time T (everything but the bottom 16 bits). */
1377static EMACS_INT 1429static EMACS_INT
1378hi_time (time_t t) 1430hi_time (time_t t)
@@ -1768,39 +1820,28 @@ format_time_string (char const *format, ptrdiff_t formatlen,
1768 size_t len; 1820 size_t len;
1769 Lisp_Object bufstring; 1821 Lisp_Object bufstring;
1770 int ns = t.tv_nsec; 1822 int ns = t.tv_nsec;
1771 struct tm *tm;
1772 USE_SAFE_ALLOCA; 1823 USE_SAFE_ALLOCA;
1773 1824
1774 while (1) 1825 tmp = ut ? gmtime_r (&t.tv_sec, tmp) : localtime_r (&t.tv_sec, tmp);
1775 { 1826 if (! tmp)
1776 time_t *taddr = &t.tv_sec; 1827 time_overflow ();
1777 block_input (); 1828 synchronize_system_time_locale ();
1778
1779 synchronize_system_time_locale ();
1780
1781 tm = ut ? gmtime (taddr) : localtime (taddr);
1782 if (! tm)
1783 {
1784 unblock_input ();
1785 time_overflow ();
1786 }
1787 *tmp = *tm;
1788 1829
1830 while (true)
1831 {
1789 buf[0] = '\1'; 1832 buf[0] = '\1';
1790 len = emacs_nmemftime (buf, size, format, formatlen, tm, ut, ns); 1833 len = emacs_nmemftime (buf, size, format, formatlen, tmp, ut, ns);
1791 if ((0 < len && len < size) || (len == 0 && buf[0] == '\0')) 1834 if ((0 < len && len < size) || (len == 0 && buf[0] == '\0'))
1792 break; 1835 break;
1793 1836
1794 /* Buffer was too small, so make it bigger and try again. */ 1837 /* Buffer was too small, so make it bigger and try again. */
1795 len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tm, ut, ns); 1838 len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tmp, ut, ns);
1796 unblock_input ();
1797 if (STRING_BYTES_BOUND <= len) 1839 if (STRING_BYTES_BOUND <= len)
1798 string_overflow (); 1840 string_overflow ();
1799 size = len + 1; 1841 size = len + 1;
1800 buf = SAFE_ALLOCA (size); 1842 buf = SAFE_ALLOCA (size);
1801 } 1843 }
1802 1844
1803 unblock_input ();
1804 bufstring = make_unibyte_string (buf, len); 1845 bufstring = make_unibyte_string (buf, len);
1805 SAFE_FREE (); 1846 SAFE_FREE ();
1806 return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0); 1847 return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0);
@@ -1824,38 +1865,30 @@ DOW and ZONE.) */)
1824 (Lisp_Object specified_time) 1865 (Lisp_Object specified_time)
1825{ 1866{
1826 time_t time_spec = lisp_seconds_argument (specified_time); 1867 time_t time_spec = lisp_seconds_argument (specified_time);
1827 struct tm save_tm; 1868 struct tm local_tm, gmt_tm;
1828 struct tm *decoded_time;
1829 Lisp_Object list_args[9];
1830 1869
1831 block_input (); 1870 if (! (localtime_r (&time_spec, &local_tm)
1832 decoded_time = localtime (&time_spec); 1871 && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= local_tm.tm_year
1833 if (decoded_time) 1872 && local_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
1834 save_tm = *decoded_time;
1835 unblock_input ();
1836 if (! (decoded_time
1837 && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= save_tm.tm_year
1838 && save_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
1839 time_overflow (); 1873 time_overflow ();
1840 XSETFASTINT (list_args[0], save_tm.tm_sec);
1841 XSETFASTINT (list_args[1], save_tm.tm_min);
1842 XSETFASTINT (list_args[2], save_tm.tm_hour);
1843 XSETFASTINT (list_args[3], save_tm.tm_mday);
1844 XSETFASTINT (list_args[4], save_tm.tm_mon + 1);
1845 /* On 64-bit machines an int is narrower than EMACS_INT, thus the
1846 cast below avoids overflow in int arithmetics. */
1847 XSETINT (list_args[5], TM_YEAR_BASE + (EMACS_INT) save_tm.tm_year);
1848 XSETFASTINT (list_args[6], save_tm.tm_wday);
1849 list_args[7] = save_tm.tm_isdst ? Qt : Qnil;
1850 1874
1851 block_input (); 1875 /* Avoid overflow when INT_MAX < EMACS_INT_MAX. */
1852 decoded_time = gmtime (&time_spec); 1876 EMACS_INT tm_year_base = TM_YEAR_BASE;
1853 if (decoded_time == 0) 1877
1854 list_args[8] = Qnil; 1878 return Flist (9, ((Lisp_Object [])
1855 else 1879 {make_number (local_tm.tm_sec),
1856 XSETINT (list_args[8], tm_diff (&save_tm, decoded_time)); 1880 make_number (local_tm.tm_min),
1857 unblock_input (); 1881 make_number (local_tm.tm_hour),
1858 return Flist (9, list_args); 1882 make_number (local_tm.tm_mday),
1883 make_number (local_tm.tm_mon + 1),
1884 make_number (local_tm.tm_year + tm_year_base),
1885 make_number (local_tm.tm_wday),
1886 local_tm.tm_isdst ? Qt : Qnil,
1887 (HAVE_TM_GMTOFF
1888 ? make_number (tm_gmtoff (&local_tm))
1889 : gmtime_r (&time_spec, &gmt_tm)
1890 ? make_number (tm_diff (&local_tm, &gmt_tm))
1891 : Qnil)}));
1859} 1892}
1860 1893
1861/* Return OBJ - OFFSET, checking that OBJ is a valid fixnum and that 1894/* 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) */)
1911 if (CONSP (zone)) 1944 if (CONSP (zone))
1912 zone = XCAR (zone); 1945 zone = XCAR (zone);
1913 if (NILP (zone)) 1946 if (NILP (zone))
1914 { 1947 value = mktime (&tm);
1915 block_input ();
1916 value = mktime (&tm);
1917 unblock_input ();
1918 }
1919 else 1948 else
1920 { 1949 {
1921 static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d"; 1950 static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d";
1922 char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)]; 1951 char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)];
1923 char *old_tzstring;
1924 const char *tzstring; 1952 const char *tzstring;
1925 USE_SAFE_ALLOCA;
1926 1953
1927 if (EQ (zone, Qt)) 1954 if (EQ (zone, Qt))
1928 tzstring = "UTC0"; 1955 tzstring = "UTC0";
@@ -1939,29 +1966,13 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */)
1939 tzstring = tzbuf; 1966 tzstring = tzbuf;
1940 } 1967 }
1941 else 1968 else
1942 error ("Invalid time zone specification"); 1969 tzstring = 0;
1943 1970
1944 old_tzstring = getenv ("TZ"); 1971 timezone_t tz = tzstring ? tzalloc (tzstring) : 0;
1945 if (old_tzstring) 1972 if (! tz)
1946 { 1973 error ("Invalid time zone specification");
1947 char *buf = SAFE_ALLOCA (strlen (old_tzstring) + 1); 1974 value = mktime_z (tz, &tm);
1948 old_tzstring = strcpy (buf, old_tzstring); 1975 tzfree (tz);
1949 }
1950
1951 block_input ();
1952
1953 /* Set TZ before calling mktime; merely adjusting mktime's returned
1954 value doesn't suffice, since that would mishandle leap seconds. */
1955 set_time_zone_rule (tzstring);
1956
1957 value = mktime (&tm);
1958
1959 set_time_zone_rule (old_tzstring);
1960#ifdef LOCALTIME_CACHE
1961 tzset ();
1962#endif
1963 unblock_input ();
1964 SAFE_FREE ();
1965 } 1976 }
1966 1977
1967 if (value == (time_t) -1) 1978 if (value == (time_t) -1)
@@ -1987,34 +1998,27 @@ but this is considered obsolete. */)
1987 (Lisp_Object specified_time) 1998 (Lisp_Object specified_time)
1988{ 1999{
1989 time_t value = lisp_seconds_argument (specified_time); 2000 time_t value = lisp_seconds_argument (specified_time);
1990 struct tm *tm;
1991 char buf[sizeof "Mon Apr 30 12:49:17 " + INT_STRLEN_BOUND (int) + 1];
1992 int len IF_LINT (= 0);
1993 2001
1994 /* Convert to a string in ctime format, except without the trailing 2002 /* Convert to a string in ctime format, except without the trailing
1995 newline, and without the 4-digit year limit. Don't use asctime 2003 newline, and without the 4-digit year limit. Don't use asctime
1996 or ctime, as they might dump core if the year is outside the 2004 or ctime, as they might dump core if the year is outside the
1997 range -999 .. 9999. */ 2005 range -999 .. 9999. */
1998 block_input (); 2006 struct tm tm;
1999 tm = localtime (&value); 2007 if (! localtime_r (&value, &tm))
2000 if (tm)
2001 {
2002 static char const wday_name[][4] =
2003 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
2004 static char const mon_name[][4] =
2005 { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
2006 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
2007 printmax_t year_base = TM_YEAR_BASE;
2008
2009 len = sprintf (buf, "%s %s%3d %02d:%02d:%02d %"pMd,
2010 wday_name[tm->tm_wday], mon_name[tm->tm_mon], tm->tm_mday,
2011 tm->tm_hour, tm->tm_min, tm->tm_sec,
2012 tm->tm_year + year_base);
2013 }
2014 unblock_input ();
2015 if (! tm)
2016 time_overflow (); 2008 time_overflow ();
2017 2009
2010 static char const wday_name[][4] =
2011 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
2012 static char const mon_name[][4] =
2013 { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
2014 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
2015 printmax_t year_base = TM_YEAR_BASE;
2016 char buf[sizeof "Mon Apr 30 12:49:17 " + INT_STRLEN_BOUND (int) + 1];
2017 int len = sprintf (buf, "%s %s%3d %02d:%02d:%02d %"pMd,
2018 wday_name[tm.tm_wday], mon_name[tm.tm_mon], tm.tm_mday,
2019 tm.tm_hour, tm.tm_min, tm.tm_sec,
2020 tm.tm_year + year_base);
2021
2018 return make_unibyte_string (buf, len); 2022 return make_unibyte_string (buf, len);
2019} 2023}
2020 2024
@@ -2041,6 +2045,17 @@ tm_diff (struct tm *a, struct tm *b)
2041 + (a->tm_sec - b->tm_sec)); 2045 + (a->tm_sec - b->tm_sec));
2042} 2046}
2043 2047
2048/* Yield A's UTC offset, or an unspecified value if unknown. */
2049static long int
2050tm_gmtoff (struct tm *a)
2051{
2052#if HAVE_TM_GMTOFF
2053 return a->tm_gmtoff;
2054#else
2055 return 0;
2056#endif
2057}
2058
2044DEFUN ("current-time-zone", Fcurrent_time_zone, Scurrent_time_zone, 0, 1, 0, 2059DEFUN ("current-time-zone", Fcurrent_time_zone, Scurrent_time_zone, 0, 1, 0,
2045 doc: /* Return the offset and name for the local time zone. 2060 doc: /* Return the offset and name for the local time zone.
2046This returns a list of the form (OFFSET NAME). 2061This returns a list of the form (OFFSET NAME).
@@ -2059,32 +2074,30 @@ the data it can't find. */)
2059 (Lisp_Object specified_time) 2074 (Lisp_Object specified_time)
2060{ 2075{
2061 struct timespec value; 2076 struct timespec value;
2062 int offset; 2077 struct tm local_tm, gmt_tm;
2063 struct tm *t;
2064 struct tm localtm;
2065 Lisp_Object zone_offset, zone_name; 2078 Lisp_Object zone_offset, zone_name;
2066 2079
2067 zone_offset = Qnil; 2080 zone_offset = Qnil;
2068 value = make_timespec (lisp_seconds_argument (specified_time), 0); 2081 value = make_timespec (lisp_seconds_argument (specified_time), 0);
2069 zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &localtm); 2082 zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &local_tm);
2070 block_input ();
2071 t = gmtime (&value.tv_sec);
2072 if (t)
2073 offset = tm_diff (&localtm, t);
2074 unblock_input ();
2075 2083
2076 if (t) 2084 if (HAVE_TM_GMTOFF || gmtime_r (&value.tv_sec, &gmt_tm))
2077 { 2085 {
2086 long int offset = (HAVE_TM_GMTOFF
2087 ? tm_gmtoff (&local_tm)
2088 : tm_diff (&local_tm, &gmt_tm));
2078 zone_offset = make_number (offset); 2089 zone_offset = make_number (offset);
2079 if (SCHARS (zone_name) == 0) 2090 if (SCHARS (zone_name) == 0)
2080 { 2091 {
2081 /* No local time zone name is available; use "+-NNNN" instead. */ 2092 /* No local time zone name is available; use "+-NNNN" instead. */
2082 int m = offset / 60; 2093 long int m = offset / 60;
2083 int am = offset < 0 ? - m : m; 2094 long int am = offset < 0 ? - m : m;
2084 char buf[sizeof "+00" + INT_STRLEN_BOUND (int)]; 2095 long int hour = am / 60;
2085 zone_name = make_formatted_string (buf, "%c%02d%02d", 2096 int min = am % 60;
2097 char buf[sizeof "+00" + INT_STRLEN_BOUND (long int)];
2098 zone_name = make_formatted_string (buf, "%c%02ld%02d",
2086 (offset < 0 ? '-' : '+'), 2099 (offset < 0 ? '-' : '+'),
2087 am / 60, am % 60); 2100 hour, min);
2088 } 2101 }
2089 } 2102 }
2090 2103
@@ -2123,12 +2136,12 @@ only the former. */)
2123 2136
2124/* Set the local time zone rule to TZSTRING. 2137/* Set the local time zone rule to TZSTRING.
2125 2138
2126 This function is not thread-safe, partly because putenv, unsetenv 2139 This function is not thread-safe, in theory because putenv is not,
2127 and tzset are not, and partly because of the static storage it 2140 but mostly because of the static storage it updates. Other threads
2128 updates. Other threads that invoke localtime etc. may be adversely 2141 that invoke localtime etc. may be adversely affected while this
2129 affected while this function is executing. */ 2142 function is executing. */
2130 2143
2131void 2144static void
2132set_time_zone_rule (const char *tzstring) 2145set_time_zone_rule (const char *tzstring)
2133{ 2146{
2134 /* A buffer holding a string of the form "TZ=value", intended 2147 /* A buffer holding a string of the form "TZ=value", intended
@@ -2137,75 +2150,47 @@ set_time_zone_rule (const char *tzstring)
2137 static ptrdiff_t tzvalbufsize; 2150 static ptrdiff_t tzvalbufsize;
2138 2151
2139 int tzeqlen = sizeof "TZ=" - 1; 2152 int tzeqlen = sizeof "TZ=" - 1;
2153 ptrdiff_t tzstringlen = tzstring ? strlen (tzstring) : 0;
2154 char *tzval = tzvalbuf;
2155 bool new_tzvalbuf = tzvalbufsize <= tzeqlen + tzstringlen;
2140 2156
2141#ifdef LOCALTIME_CACHE 2157 if (new_tzvalbuf)
2142 /* These two values are known to load tz files in buggy implementations, 2158 {
2143 i.e., Solaris 1 executables running under either Solaris 1 or Solaris 2. 2159 /* Do not attempt to free the old tzvalbuf, since another thread
2144 Their values shouldn't matter in non-buggy implementations. 2160 may be using it. In practice, the first allocation is large
2145 We don't use string literals for these strings, 2161 enough and memory does not leak. */
2146 since if a string in the environment is in readonly 2162 tzval = xpalloc (NULL, &tzvalbufsize,
2147 storage, it runs afoul of bugs in SVR4 and Solaris 2.3. 2163 tzeqlen + tzstringlen - tzvalbufsize + 1, -1, 1);
2148 See Sun bugs 1113095 and 1114114, ``Timezone routines 2164 tzvalbuf = tzval;
2149 improperly modify environment''. */ 2165 tzval[1] = 'Z';
2150 2166 tzval[2] = '=';
2151 static char set_time_zone_rule_tz[][sizeof "TZ=GMT+0"] 2167 }
2152 = { "TZ=GMT+0", "TZ=GMT+1" };
2153
2154 /* In SunOS 4.1.3_U1 and 4.1.4, if TZ has a value like
2155 "US/Pacific" that loads a tz file, then changes to a value like
2156 "XXX0" that does not load a tz file, and then changes back to
2157 its original value, the last change is (incorrectly) ignored.
2158 Also, if TZ changes twice in succession to values that do
2159 not load a tz file, tzset can dump core (see Sun bug#1225179).
2160 The following code works around these bugs. */
2161 2168
2162 if (tzstring) 2169 if (tzstring)
2163 { 2170 {
2164 /* Temporarily set TZ to a value that loads a tz file 2171 /* Modify TZVAL in place. Although this is dicey in a
2165 and that differs from tzstring. */ 2172 multithreaded environment, we know of no portable alternative.
2166 bool eq0 = strcmp (tzstring, set_time_zone_rule_tz[0] + tzeqlen) == 0; 2173 Calling putenv or setenv could crash some other thread. */
2167 xputenv (set_time_zone_rule_tz[eq0]); 2174 tzval[0] = 'T';
2175 strcpy (tzval + tzeqlen, tzstring);
2168 } 2176 }
2169 else 2177 else
2170 { 2178 {
2171 /* The implied tzstring is unknown, so temporarily set TZ to 2179 /* Turn 'TZ=whatever' into an empty environment variable 'tZ='.
2172 two different values that each load a tz file. */ 2180 Although this is also dicey, calling unsetenv here can crash Emacs.
2173 xputenv (set_time_zone_rule_tz[0]); 2181 See Bug#8705. */
2174 tzset (); 2182 tzval[0] = 't';
2175 xputenv (set_time_zone_rule_tz[1]); 2183 tzval[tzeqlen] = 0;
2176 } 2184 }
2177 tzset ();
2178 tzvalbuf_in_environ = 0;
2179#endif
2180 2185
2181 if (!tzstring) 2186 if (new_tzvalbuf)
2182 {
2183 unsetenv ("TZ");
2184 tzvalbuf_in_environ = 0;
2185 }
2186 else
2187 { 2187 {
2188 ptrdiff_t tzstringlen = strlen (tzstring); 2188 /* Although this is not thread-safe, in practice this runs only
2189 2189 on startup when there is only one thread. */
2190 if (tzvalbufsize <= tzeqlen + tzstringlen) 2190 xputenv (tzval);
2191 {
2192 unsetenv ("TZ");
2193 tzvalbuf_in_environ = 0;
2194 tzvalbuf = xpalloc (tzvalbuf, &tzvalbufsize,
2195 tzeqlen + tzstringlen - tzvalbufsize + 1, -1, 1);
2196 memcpy (tzvalbuf, "TZ=", tzeqlen);
2197 }
2198
2199 strcpy (tzvalbuf + tzeqlen, tzstring);
2200
2201 if (!tzvalbuf_in_environ)
2202 {
2203 xputenv (tzvalbuf);
2204 tzvalbuf_in_environ = 1;
2205 }
2206 } 2191 }
2207 2192
2208#ifdef LOCALTIME_CACHE 2193#ifdef HAVE_TZSET
2209 tzset (); 2194 tzset ();
2210#endif 2195#endif
2211} 2196}
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,
578} 578}
579 579
580 580
581#ifdef HAVE_TZSET
582/* A valid but unlikely value for the TZ environment value.
583 It is OK (though a bit slower) if the user actually chooses this value. */
584static char const dump_tz[] = "UtC0";
585#endif
586
587/* Test whether the next argument in ARGV matches SSTR or a prefix of 581/* Test whether the next argument in ARGV matches SSTR or a prefix of
588 LSTR (at least MINLEN characters). If so, then if VALPTR is non-null 582 LSTR (at least MINLEN characters). If so, then if VALPTR is non-null
589 (the argument is supposed to have a value) store in *VALPTR either 583 (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
1548 1542
1549 init_charset (); 1543 init_charset ();
1550 1544
1551 init_editfns (); /* init_process_emacs uses Voperating_system_release. */ 1545 /* This calls putenv and so must precede init_process_emacs. Also,
1552 init_process_emacs (); /* init_display uses add_keyboard_wait_descriptor. */ 1546 it sets Voperating_system_release, which init_process_emacs uses. */
1547 init_editfns ();
1548
1549 /* These two call putenv. */
1550#ifdef HAVE_DBUS
1551 init_dbusbind ();
1552#endif
1553#ifdef USE_GTK
1554 init_xterm ();
1555#endif
1556
1557 /* This can create a thread that may call getenv, so it must follow
1558 all calls to putenv and setenv. Also, this sets up
1559 add_keyboard_wait_descriptor, which init_display uses. */
1560 init_process_emacs ();
1561
1553 init_keyboard (); /* This too must precede init_sys_modes. */ 1562 init_keyboard (); /* This too must precede init_sys_modes. */
1554 if (!noninteractive) 1563 if (!noninteractive)
1555 init_display (); /* Determine terminal type. Calls init_sys_modes. */ 1564 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
1586 build_string ("loadup.el")); 1595 build_string ("loadup.el"));
1587 } 1596 }
1588 1597
1589 if (initialized)
1590 {
1591#ifdef HAVE_TZSET
1592 {
1593 /* If the execution TZ happens to be the same as the dump TZ,
1594 change it to some other value and then change it back,
1595 to force the underlying implementation to reload the TZ info.
1596 This is needed on implementations that load TZ info from files,
1597 since the TZ file contents may differ between dump and execution. */
1598 char *tz = getenv ("TZ");
1599 if (tz && !strcmp (tz, dump_tz))
1600 {
1601 ++*tz;
1602 tzset ();
1603 --*tz;
1604 }
1605 }
1606#endif
1607 }
1608
1609 /* Set up for profiling. This is known to work on FreeBSD, 1598 /* Set up for profiling. This is known to work on FreeBSD,
1610 GNU/Linux and MinGW. It might work on some other systems too. 1599 GNU/Linux and MinGW. It might work on some other systems too.
1611 Give it a try and tell us if it works on your system. To compile 1600 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
1630 1619
1631 initialized = 1; 1620 initialized = 1;
1632 1621
1633#ifdef LOCALTIME_CACHE
1634 /* Some versions of localtime have a bug. They cache the value of the time
1635 zone rather than looking it up every time. Since localtime() is
1636 called to bolt the undumping time into the undumped emacs, this
1637 results in localtime ignoring the TZ environment variable.
1638 This flushes the new TZ value into localtime. */
1639 tzset ();
1640#endif /* defined (LOCALTIME_CACHE) */
1641
1642 /* Enter editor command loop. This never returns. */ 1622 /* Enter editor command loop. This never returns. */
1643 Frecursive_edit (); 1623 Frecursive_edit ();
1644 /* NOTREACHED */ 1624 /* NOTREACHED */
@@ -2119,14 +2099,6 @@ You must run Emacs in batch mode in order to dump it. */)
2119 tem = Vpurify_flag; 2099 tem = Vpurify_flag;
2120 Vpurify_flag = Qnil; 2100 Vpurify_flag = Qnil;
2121 2101
2122#ifdef HAVE_TZSET
2123 set_time_zone_rule (dump_tz);
2124#ifndef LOCALTIME_CACHE
2125 /* Force a tz reload, since set_time_zone_rule doesn't. */
2126 tzset ();
2127#endif
2128#endif
2129
2130 fflush (stdout); 2102 fflush (stdout);
2131 /* Tell malloc where start of impure now is. */ 2103 /* Tell malloc where start of impure now is. */
2132 /* Also arrange for warnings when nearly out of space. */ 2104 /* 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,
3990 ptrdiff_t, bool); 3990 ptrdiff_t, bool);
3991extern void init_editfns (void); 3991extern void init_editfns (void);
3992extern void syms_of_editfns (void); 3992extern void syms_of_editfns (void);
3993extern void set_time_zone_rule (const char *);
3994 3993
3995/* Defined in buffer.c. */ 3994/* Defined in buffer.c. */
3996extern bool mouse_face_overlay_overlaps (Lisp_Object); 3995extern bool mouse_face_overlay_overlaps (Lisp_Object);
@@ -4398,6 +4397,7 @@ extern void syms_of_xsmfns (void);
4398extern void syms_of_xselect (void); 4397extern void syms_of_xselect (void);
4399 4398
4400/* Defined in xterm.c. */ 4399/* Defined in xterm.c. */
4400extern void init_xterm (void);
4401extern void syms_of_xterm (void); 4401extern void syms_of_xterm (void);
4402#endif /* HAVE_X_WINDOWS */ 4402#endif /* HAVE_X_WINDOWS */
4403 4403
@@ -4419,6 +4419,7 @@ extern void syms_of_decompress (void);
4419 4419
4420#ifdef HAVE_DBUS 4420#ifdef HAVE_DBUS
4421/* Defined in dbusbind.c. */ 4421/* Defined in dbusbind.c. */
4422void init_dbusbind (void);
4422void syms_of_dbusbind (void); 4423void syms_of_dbusbind (void);
4423#endif 4424#endif
4424 4425
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,
93extern struct timespec lisp_time_argument (Lisp_Object); 93extern struct timespec lisp_time_argument (Lisp_Object);
94#endif 94#endif
95 95
96#ifndef HAVE_TZALLOC
97# undef mktime_z
98# undef timezone_t
99# undef tzalloc
100# undef tzfree
101# define mktime_z emacs_mktime_z
102# define timezone_t emacs_timezone_t
103# define tzalloc emacs_tzalloc
104# define tzfree emacs_tzfree
105typedef char const *timezone_t;
106INLINE timezone_t tzalloc (char const *name) { return name; }
107INLINE void tzfree (timezone_t tz) { }
108/* Defined in editfns.c. */
109extern time_t mktime_z (timezone_t, struct tm *);
110#endif
111
96INLINE_HEADER_END 112INLINE_HEADER_END
97 113
98#endif /* EMACS_SYSTIME_H */ 114#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)
10717 10717
10718 XSetLocaleModifiers (""); 10718 XSetLocaleModifiers ("");
10719 10719
10720 /* Emacs can only handle core input events, so make sure
10721 Gtk doesn't use Xinput or Xinput2 extensions. */
10722 xputenv ("GDK_CORE_DEVICE_EVENTS=1");
10723
10724 /* Work around GLib bug that outputs a faulty warning. See 10720 /* Work around GLib bug that outputs a faulty warning. See
10725 https://bugzilla.gnome.org/show_bug.cgi?id=563627. */ 10721 https://bugzilla.gnome.org/show_bug.cgi?id=563627. */
10726 id = g_log_set_handler ("GLib", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL 10722 id = g_log_set_handler ("GLib", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
@@ -11470,6 +11466,15 @@ x_initialize (void)
11470 XSetIOErrorHandler (x_io_error_quitter); 11466 XSetIOErrorHandler (x_io_error_quitter);
11471} 11467}
11472 11468
11469#ifdef USE_GTK
11470void
11471init_xterm (void)
11472{
11473 /* Emacs can handle only core input events, so make sure
11474 Gtk doesn't use Xinput or Xinput2 extensions. */
11475 xputenv ("GDK_CORE_DEVICE_EVENTS=1");
11476}
11477#endif
11473 11478
11474void 11479void
11475syms_of_xterm (void) 11480syms_of_xterm (void)