From dcf0671d905200c449f92ead6cf43c184637a0d5 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Mon, 26 Aug 1996 10:28:45 +0000 Subject: handle password file locking. --- NEWS | 8 +- configure.in | 2 + elf/dl-error.c | 9 +- elf/dl-open.c | 8 +- elf/dlerror.c | 8 +- elf/rtld.c | 15 ++- misc/syslog.c | 6 +- resolv/res_debug.c | 12 +-- shadow/Makefile | 3 +- shadow/lckpwdf.c | 181 +++++++++++++++++++++++++++++++++++ shadow/shadow.h | 13 ++- sysdeps/generic/ftime.c | 13 ++- sysdeps/generic/setfpucw.c | 5 +- sysdeps/i386/fpu_control.h | 4 +- sysdeps/m68k/dl-machine.h | 2 +- sysdeps/posix/gettimeofday.c | 21 ++-- sysdeps/unix/sysv/linux/init-first.c | 31 ++++-- time/gmtime.c | 12 ++- time/strftime.c | 40 +++++--- 19 files changed, 321 insertions(+), 72 deletions(-) create mode 100644 shadow/lckpwdf.c diff --git a/NEWS b/NEWS index edb07b8330..76a4272569 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -GNU C Library NEWS -- history of user-visible changes. 24 June 1996 +GNU C Library NEWS -- history of user-visible changes. 25 August 1996 Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. See the end for copying conditions. @@ -20,8 +20,8 @@ Version 2.0 of many files which contained only symbol aliases, reducing the size of the source and the compiled library; many other files were renamed to less cryptic names previously occupied by the symbol alias files. - There is a new header file and new library `-lelf' for - programs which operate on files in the ELF format. + There is a new header file for programs which operate on + files in the ELF format. * Converted to Autoconf version 2, so `configure' has more options. Run `configure --help' to see the details. @@ -104,7 +104,7 @@ Version 2.0 * The new header file and suite of functions simplify programs that operate on directory trees. This code comes from 4.4 BSD. -* The resolver code has been updated from the BIND 4.9.4-T3B release. +* The resolver code has been updated from the BIND 4.9.5-T1A release. * The new function `malloc_find_object_address' finds the starting address of a malloc'd block, given any address within the block; diff --git a/configure.in b/configure.in index 631a035e92..a2668b9ff9 100644 --- a/configure.in +++ b/configure.in @@ -483,6 +483,8 @@ fi AC_CACHE_CHECK(for ld --no-whole-archive, libc_cv_ld_no_whole_archive, [dnl cat > conftest.c <<\EOF _start () {} +int __eh_pc; +__throw () {} EOF dnl No \ in command here because it ends up inside ''. if AC_TRY_COMMAND([${CC-cc} $CFLAGS diff --git a/elf/dl-error.c b/elf/dl-error.c index 737bba7421..2eaa7e03d1 100644 --- a/elf/dl-error.c +++ b/elf/dl-error.c @@ -46,8 +46,13 @@ _dl_signal_error (int errcode, if (catch) { - /* We are inside _dl_catch_error. Return to it. */ - catch->errstring = errstring; + /* We are inside _dl_catch_error. Return to it. We have to + duplicate the error string since it might be allocated on the + stack. */ + size_t len = strlen (errstring) + 1; + catch->errstring = malloc (len); + if (catch->errstring != NULL) + memcpy (catch->errstring, errstring, len); catch->objname = objname; longjmp (catch->env, errcode ?: -1); } diff --git a/elf/dl-open.c b/elf/dl-open.c index 40b5224725..76f6329762 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -27,6 +27,11 @@ extern void _dl_start (void); weak_extern (_dl_start) extern int __libc_multiple_libcs; /* Defined in init-first.c. */ +extern int __libc_argc; +extern char **__libc_argv; +extern char **__libc_envp; + + size_t _dl_global_scope_alloc; struct link_map * @@ -136,7 +141,8 @@ _dl_open (const char *file, int mode) /* Run the initializer functions of new objects. */ while (init = _dl_init_next (new)) - (*(void (*) (void)) init) (); + (*(void (*) (int, char **, char **)) init) (__libc_argc, __libc_argv, + __libc_envp); if (dl_start_ptr == NULL) /* We must be the static _dl_open in libc.a because ld.so.1 is not diff --git a/elf/dlerror.c b/elf/dlerror.c index 4ec5037de4..663207d708 100644 --- a/elf/dlerror.c +++ b/elf/dlerror.c @@ -1,5 +1,5 @@ /* dlerror -- Return error detail for failing functions. -Copyright (C) 1995 Free Software Foundation, Inc. +Copyright (C) 1995, 1996 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -58,6 +58,7 @@ dlerror (void) ? NULL : buf); /* Reset the error indicator. */ + free (last_errstring); last_errstring = NULL; return ret; } @@ -65,6 +66,11 @@ dlerror (void) int _dlerror_run (void (*operate) (void)) { + if (last_errstring != NULL) + /* Free the error string from the last failed command. This can + happen if `dlerror' was not run after an error was found. */ + free (last_errstring); + last_errcode = _dl_catch_error (&last_errstring, &last_object_name, operate); return last_errstring != NULL; diff --git a/elf/rtld.c b/elf/rtld.c index 9f13124207..be71e88c3c 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -204,12 +204,15 @@ of this helper program; chances are you did not intend to run this program.\n", { l = _dl_map_object (NULL, _dl_argv[0], lt_library); } - const char *err_str = NULL; + char *err_str = NULL; const char *obj_name __attribute__ ((unused)); (void) _dl_catch_error (&err_str, &obj_name, doit); if (err_str != NULL) - _exit (EXIT_FAILURE); + { + free (err_str); + _exit (EXIT_FAILURE); + } } else l = _dl_map_object (NULL, _dl_argv[0], lt_library); @@ -395,7 +398,8 @@ of this helper program; chances are you did not intend to run this program.\n", const ElfW(Sym) *ref = NULL; ElfW(Addr) loadbase = _dl_lookup_symbol (_dl_argv[i], &ref, &_dl_default_scope[2], - "argument", 0); + "argument", + DL_LOOKUP_NOPLT); char buf[20], *bp; buf[sizeof buf - 1] = '\0'; bp = _itoa (ref->st_value, &buf[sizeof buf - 1], 16, 0); @@ -488,8 +492,9 @@ of this helper program; chances are you did not intend to run this program.\n", dynamic linker. There is no additional initialization required for the ABI-compliant dynamic linker. */ - (*(void (*) (void)) (_dl_rtld_map.l_addr + - _dl_rtld_map.l_info[DT_INIT]->d_un.d_ptr)) (); + (*(void (*) (int, char **, char**)) + (_dl_rtld_map.l_addr + _dl_rtld_map.l_info[DT_INIT]->d_un.d_ptr)) + (0, NULL, NULL); /* Clear the field so a future dlopen won't run it again. */ _dl_rtld_map.l_info[DT_INIT] = NULL; diff --git a/misc/syslog.c b/misc/syslog.c index 3a39c4317a..de159da9ee 100644 --- a/misc/syslog.c +++ b/misc/syslog.c @@ -96,6 +96,7 @@ vsyslog(pri, fmt, ap) register const char *fmt; va_list ap; { + struct tm now_tm; time_t now; int fd; FILE *f; @@ -126,10 +127,11 @@ vsyslog(pri, fmt, ap) #ifdef USE_IN_LIBIO f->_IO_write_ptr += strftime (f->_IO_write_ptr, f->_IO_write_end - f->_IO_write_ptr, - "%h %e %T ", localtime (&now)); + "%h %e %T ", + __localtime_r (&now, &now_tm)); #else f->__bufp += strftime (f->__bufp, f->__put_limit - f->__bufp, - "%h %e %T ", localtime (&now)); + "%h %e %T ", __localtime_r (&now, &mow_tm)); #endif msgoff = ftell (f); if (LogTag == NULL) diff --git a/resolv/res_debug.c b/resolv/res_debug.c index dfb3b4706f..bb314bcaa0 100644 --- a/resolv/res_debug.c +++ b/resolv/res_debug.c @@ -1491,13 +1491,13 @@ __p_secstodate (secs) { static char output[15]; /* YYYYMMDDHHMMSS and null */ time_t clock = secs; - struct tm *time; + struct tm time; - time = gmtime(&clock); - time->tm_year += 1900; - time->tm_mon += 1; + __gmtime_r(&clock, &time); + time.tm_year += 1900; + time.tm_mon += 1; sprintf(output, "%04d%02d%02d%02d%02d%02d", - time->tm_year, time->tm_mon, time->tm_mday, - time->tm_hour, time->tm_min, time->tm_sec); + time.tm_year, time.tm_mon, time.tm_mday, + time.tm_hour, time.tm_min, time.tm_sec); return (output); } diff --git a/shadow/Makefile b/shadow/Makefile index 943881f1f1..614f7afdd4 100644 --- a/shadow/Makefile +++ b/shadow/Makefile @@ -23,7 +23,8 @@ subdir := shadow headers = shadow.h routines = getspent getspnam sgetspent fgetspent putspent \ - getspent_r getspnam_r sgetspent_r fgetspent_r + getspent_r getspnam_r sgetspent_r fgetspent_r \ + lckpwdf include ../Rules diff --git a/shadow/lckpwdf.c b/shadow/lckpwdf.c new file mode 100644 index 0000000000..fb1c4b2e5e --- /dev/null +++ b/shadow/lckpwdf.c @@ -0,0 +1,181 @@ +/* lckpwdf - handle locking of password file. +Copyright (C) 1996 Free Software Foundation, Inc. +This file is part of the GNU C Library. +Contributed by Ulrich Drepper , 1996. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#include +#include + + +/* Name of the lock file. */ +#define PWD_LOCKFILE "/var/lock/lock.pwd" + +/* How long to wait for getting the lock before returning with an + error. */ +#define TIMEOUT 15 /* sec */ + + +/* File descriptor for lock file. */ +static int lock_fd = -1; + +/* Prevent problems in multithreaded program by using mutex. */ +__libc_lock_define_initialized (static, lock) + + +/* Prototypes for local functions. */ +static void noop_handler __P ((int __sig)); + + +/* We cannot simply return in error cases. We have to close the file + and perhaps restore the signal handler. */ +#define RETURN_CLOSE_FD(code) \ + do { \ + if ((code) < 0 && lock_fd >= 0) \ + { \ + close (lock_fd); \ + lock_fd = -1; \ + } \ + __libc_lock_unlock (lock); \ + return (code); \ + } while (0) + +#define RETURN_RESTORE_HANDLER(code) \ + do { \ + /* Restore old action handler for alarm. We don't need to know \ + about the current one. */ \ + sigaction (SIGALRM, &saved_act, NULL); \ + RETURN_CLOSE_FD (code); \ + } while (0) + +#define RETURN_CLEAR_ALARM(code) \ + do { \ + /* Clear alarm. */ \ + alarm (0); \ + /* Restore old set of handled signals. We don't need to know \ + about the current one.*/ \ + sigprocmask (SIG_SETMASK, &saved_set, NULL); \ + RETURN_RESTORE_HANDLER (code); \ + } while (0) + + +int +__lckpwdf () +{ + int flags; + sigset_t saved_set; /* Saved set of caught signals. */ + struct sigaction saved_act; /* Saved signal action. */ + sigset_t new_set; /* New set of caught signals. */ + struct sigaction new_act; /* New signal action. */ + int result; + + if (lock_fd != -1) + /* Still locked by own process. */ + return -1; + + /* Prevent problems caused by multiple threads. */ + __libc_lock_lock (lock); + + lock_fd = open (PWD_LOCKFILE, O_WRONLY | O_CREAT, 0600); + if (lock_fd == -1) + /* Cannot create lock file. */ + RETURN_CLOSE_FD (-1); + + /* Make sure file gets correctly closed when process finished. */ + flags = fcntl (lock_fd, F_GETFD, 0); + if (flags == -1) + /* Cannot get file flags. */ + RETURN_CLOSE_FD (-1); + flags |= FD_CLOEXEC; /* Close on exit. */ + if (fcntl (lock_fd, F_SETFD, flags) < 0) + /* Cannot set new flags. */ + RETURN_CLOSE_FD (-1); + + /* Now we have to get exclusive write access. Since multiple + process could try this we won't stop when it first fails. + Instead we set a timeout for the system call. Once the timer + expires it is likely that there are some problems which cannot be + resolved by waiting. + + It is important that we don't change the signal state. We must + restore the old signal behaviour. */ + memset (&new_act, '\0', sizeof (struct sigaction)); + new_act.sa_handler = noop_handler; + sigfillset (&new_act.sa_mask); + new_act.sa_flags = 0ul; + + /* Install new action handler for alarm and save old. */ + if (sigaction (SIGALRM, &new_act, &saved_act) < 0) + /* Cannot install signal handler. */ + RETURN_CLOSE_FD (-1); + + /* Now make sure the alarm signal is not blocked. */ + sigemptyset (&new_set); + sigaddset (&new_set, SIGALRM); + if (sigprocmask (SIG_UNBLOCK, &new_set, &saved_set) < 0) + RETURN_RESTORE_HANDLER (-1); + + /* Start timer. If we cannot get the lock in the specified time we + get a signal. */ + alarm (TIMEOUT); + + /* Try to get the lock. */ + result = flock (lock_fd, LOCK_EX); + + RETURN_CLEAR_ALARM (result); +} +weak_alias (__lckpwdf, lckpwdf) + + +int +__ulckpwdf () +{ + int result; + + if (lock_fd == -1) + /* There is no lock set. */ + result = -1; + else + { + /* Prevent problems caused by multiple threads. */ + __libc_lock_lock (&lock); + + result = close (lock_fd); + + /* Mark descriptor as unused. */ + lock_fd = -1; + + /* Clear mutex. */ + __libc_lock_unlock (lock); + } + + return result; +} +weak_alias (__ulckpwdf, ulckpwdf) + + +static void +noop_handler (sig) + int sig; +{ + /* We simply return which makes the `flock' call return with an error. */ +} diff --git a/shadow/shadow.h b/shadow/shadow.h index 88199a9856..b1a4c82b4e 100644 --- a/shadow/shadow.h +++ b/shadow/shadow.h @@ -46,8 +46,8 @@ struct spwd the password. */ __time_t sp_inact; /* Number of days the account may be inactive. */ - __time_t sp_expire; /* Number of days since 700101 until account - expires. */ + __time_t sp_expire; /* Number of days since 1970-01-01 until + account expires. */ unsigned long int sp_flag; /* Reserved. */ }; @@ -103,6 +103,15 @@ extern struct spwd *fgetspent_r __P ((FILE *__stream, char *__buffer, int __buflen)); #endif /* reentrant */ + +/* Protect password file against multi writers. */ +extern int __lckpwdf __P ((void)); +extern int lckpwdf __P ((void)); + +/* Unlock password file. */ +extern int __ulckpwdf __P ((void)); +extern int ulckpwdf __P ((void)); + __END_DECLS #endif /* shadow.h */ diff --git a/sysdeps/generic/ftime.c b/sysdeps/generic/ftime.c index 76e9276483..600e959245 100644 --- a/sysdeps/generic/ftime.c +++ b/sysdeps/generic/ftime.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1994 Free Software Foundation, Inc. +/* Copyright (C) 1994, 1996 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -25,19 +25,18 @@ ftime (timebuf) struct timeb *timebuf; { int save = errno; - struct tm *tp; + struct tm tp; errno = 0; if (time (&timebuf->time) == (time_t) -1 && errno != 0) return -1; timebuf->millitm = 0; - - tp = localtime (&timebuf->time); - if (tp == NULL) + + if (__localtime_r (&timebuf->time, &tp) == NULL) return -1; - timebuf->timezone = tp->tm_gmtoff / 60; - timebuf->dstflag = tp->tm_isdst; + timebuf->timezone = tp.tm_gmtoff / 60; + timebuf->dstflag = tp.tm_isdst; errno = save; return 0; diff --git a/sysdeps/generic/setfpucw.c b/sysdeps/generic/setfpucw.c index 7b09a68b55..5654c942b0 100644 --- a/sysdeps/generic/setfpucw.c +++ b/sysdeps/generic/setfpucw.c @@ -29,5 +29,8 @@ __setfpucw (fpu_control_t set) /* Preserve the reserved bits, and set the rest as the user specified (or the default, if the user gave zero). */ - _FPU_SETCW ((cw & _FPU_RESERVED) | (set & ~_FPU_RESERVED)); + cw &= _FPU_RESERVED; + cw |= set & ~_FPU_RESERVED; + + _FPU_SETCW (cw); } diff --git a/sysdeps/i386/fpu_control.h b/sysdeps/i386/fpu_control.h index 706dea3d6d..7944b1a5ee 100644 --- a/sysdeps/i386/fpu_control.h +++ b/sysdeps/i386/fpu_control.h @@ -89,8 +89,8 @@ Boston, MA 02111-1307, USA. */ typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__HI__))); /* Macros for accessing the hardware control word. */ -#define _FPU_GETCW(cw) __asm__ ("fnstcw %0" : "=m" (cw)) -#define _FPU_SETCW(cw) __asm__ ("fldcw %0" : : "m" (cw)) +#define _FPU_GETCW(cw) __asm__ ("fnstcw %0" : "=m" (*&cw)) +#define _FPU_SETCW(cw) __asm__ ("fldcw %0" : : "m" (*&cw)) /* Default control word set at startup. */ extern fpu_control_t __fpu_control; diff --git a/sysdeps/m68k/dl-machine.h b/sysdeps/m68k/dl-machine.h index f36b9ce2f4..8b9872c15a 100644 --- a/sysdeps/m68k/dl-machine.h +++ b/sysdeps/m68k/dl-machine.h @@ -175,7 +175,7 @@ _dl_start_user: | Loop to call _dl_init_next for the next initializer. jra 0b 1: | Clear the startup flag. - move.l #0, _dl_starting_up@GOT(%a5) + clr.l _dl_starting_up@GOT(%a5) | Pass our finalizer function to the user in %a1. move.l _dl_fini@GOT(%a5), %a1 | Initialize %fp with the stack pointer. diff --git a/sysdeps/posix/gettimeofday.c b/sysdeps/posix/gettimeofday.c index a4bb38a41c..c3b8108258 100644 --- a/sysdeps/posix/gettimeofday.c +++ b/sysdeps/posix/gettimeofday.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991, 1992, 1994, 1995 Free Software Foundation, Inc. +/* Copyright (C) 1991, 92, 94, 95, 96 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -16,7 +16,6 @@ License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include #include #include @@ -32,8 +31,9 @@ Cambridge, MA 02139, USA. */ putting it into *TV and *TZ. If TZ is NULL, *TZ is not filled. Returns 0 on success, -1 on errors. */ int -DEFUN(__gettimeofday, (tv, tz), - struct timeval *tv AND struct timezone *tz) +__gettimeofday (tv, tz) + struct timeval *tv; + struct timezone *tz; { if (tv == NULL) { @@ -46,16 +46,17 @@ DEFUN(__gettimeofday, (tv, tz), if (tz != NULL) { - CONST time_t timer = tv->tv_sec; - CONST struct tm *tm; + const time_t timer = tv->tv_sec; + struct tm tm; + const struct tm *tmp; - CONST long int save_timezone = __timezone; - CONST long int save_daylight = __daylight; + const long int save_timezone = __timezone; + const long int save_daylight = __daylight; char *save_tzname[2]; save_tzname[0] = __tzname[0]; save_tzname[1] = __tzname[1]; - tm = localtime (&timer); + tmp = localtime (&timer, &tm); tz->tz_minuteswest = __timezone / 60; tz->tz_dsttime = __daylight; @@ -65,7 +66,7 @@ DEFUN(__gettimeofday, (tv, tz), __tzname[0] = save_tzname[0]; __tzname[1] = save_tzname[1]; - if (tm == NULL) + if (tmp == NULL) return -1; } diff --git a/sysdeps/unix/sysv/linux/init-first.c b/sysdeps/unix/sysv/linux/init-first.c index feff028a28..ae163bcd49 100644 --- a/sysdeps/unix/sysv/linux/init-first.c +++ b/sysdeps/unix/sysv/linux/init-first.c @@ -36,21 +36,30 @@ weak_extern (_dl_starting_up) used in the process. Safe assumption if initializer never runs. */ int __libc_multiple_libcs = 1; +/* Remember the command line argument and enviroment contents for + later calls of initializers for dynamic libraries. */ +int __libc_argc; +char **__libc_argv; +char **__libc_envp; + + static void init (void *data) { extern int __personality (int); - int argc = *(long *)data; - char **argv = (char **)data + 1; - char **envp = &argv[argc + 1]; - - __libc_multiple_libcs = &_dl_starting_up && ! _dl_starting_up; + /* We must not call `personality' twice. */ if (!__libc_multiple_libcs) { + /* The argument we got points to the values describing the + command line argument etc. */ + __libc_argc = *(int *)data; + __libc_argv = (char **)data + 1; + __libc_envp = &__libc_argv[__libc_argc + 1]; + /* The `personality' system call takes one argument that chooses the "personality", i.e. the set of system calls and such. We must make this call first thing to disable emulation of some @@ -61,9 +70,17 @@ init (void *data) /* Set the FPU control word to the proper default value. */ __setfpucw (__fpu_control); } + else + { + /* The argument we got points to the values describing the + command line argument etc. */ + __libc_argc = *((int *)data)++; + __libc_argv = *((char ***)data)++; + __libc_envp = *(char ***)data; + } - __environ = envp; - __libc_init (argc, argv, envp); + __environ = __libc_envp; + __libc_init (__libc_argc, __libc_argv, __libc_envp); #ifdef PIC __libc_global_ctors (); diff --git a/time/gmtime.c b/time/gmtime.c index 93fba659be..364b4c9262 100644 --- a/time/gmtime.c +++ b/time/gmtime.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991, 1993, 1995 Free Software Foundation, Inc. +/* Copyright (C) 1991, 1993, 1995, 1996 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -16,7 +16,6 @@ License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include #include @@ -25,16 +24,19 @@ extern struct tm _tmbuf; /* Return the `struct tm' representation of *T in UTC. */ struct tm * -DEFUN(gmtime, (t), CONST time_t *t) +gmtime (t) + const time_t *t; { return __gmtime_r (t, &_tmbuf); } + /* Return the `struct tm' representation of *T in UTC, using *TP to store the result. */ struct tm * -DEFUN(__gmtime_r, (t, tp), - CONST time_t *t AND struct tm *tp) +__gmtime_r (t, tp) + const time_t *t; + struct tm *tp; { __offtime (t, 0L, tp); diff --git a/time/strftime.c b/time/strftime.c index 214f82f488..129fd1412c 100644 --- a/time/strftime.c +++ b/time/strftime.c @@ -470,27 +470,37 @@ strftime (s, maxsize, format, tp) case 'z': { struct tm tml = *tp; - time_t t = mktime (&tml); struct tm tmg; + time_t t; + time_t offset = 0; int diff; - tml = *localtime (&t); /* Canonicalize the local time. */ - tmg = *gmtime (&t); + t = __mktime_internal (&tml, __localtime_r, &offset); - /* Compute the difference. */ - diff = tml.tm_min - tmg.tm_min; - diff += 60 * (tml.tm_hour - tmg.tm_hour); - - if (tml.tm_mon != tmg.tm_mon) + /* Canonicalize the local time. */ + if (t == (time_t) -1 || __localtime_r (&t, &tml) == NULL) + /* We didn't managed to get the local time. Assume it + GMT as a reasonable default value. */ + diff = 0; + else { - /* We assume no timezone differs from UTC by more than - +- 23 hours. This should be safe. */ - if (tmg.tm_mday == 1) - tml.tm_mday = 0; - else /* tml.tm_mday == 1 */ - tmg.tm_mday = 0; + __gmtime_r (&t, &tmg); + + /* Compute the difference. */ + diff = tml.tm_min - tmg.tm_min; + diff += 60 * (tml.tm_hour - tmg.tm_hour); + + if (tml.tm_mon != tmg.tm_mon) + { + /* We assume no timezone differs from UTC by more + than +- 23 hours. This should be safe. */ + if (tmg.tm_mday == 1) + tml.tm_mday = 0; + else /* tml.tm_mday == 1 */ + tmg.tm_mday = 0; + } + diff += 1440 * (tml.tm_mday - tmg.tm_mday); } - diff += 1440 * (tml.tm_mday - tmg.tm_mday); if (diff < 0) { -- cgit v1.2.3