diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -18,7 +18,7 @@ login_class.c login_crypt.c login_ok.c login_times.c login_tty.c \ mntopts.c \ pidfile.c property.c pty.c pw_scan.c pw_util.c quotafile.c \ - realhostname.c stub.c trimdomain.c uucplock.c + realhostname.c source_date_epoch.c stub.c trimdomain.c uucplock.c INCS= libutil.h login_cap.h mntopts.h CFLAGS+= -DNO__SCCSID diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h --- a/lib/libutil/libutil.h +++ b/lib/libutil/libutil.h @@ -63,6 +63,11 @@ #define _SIZE_T_DECLARED #endif +#ifndef _TIME_T_DECLARED +typedef __time_t time_t; +#define _TIME_T_DECLARED +#endif + #ifndef _UID_T_DECLARED typedef __uid_t uid_t; #define _UID_T_DECLARED @@ -133,6 +138,7 @@ int realhostname_sa(char *_host, size_t _hsize, struct sockaddr *_addr, int _addrlen); int _secure_path(const char *_path, uid_t _uid, gid_t _gid); +int source_date_epoch(time_t *tp); void trimdomain(char *_fullhost, int _hostsize); const char * uu_lockerr(int _uu_lockresult); diff --git a/lib/libutil/source_date_epoch.c b/lib/libutil/source_date_epoch.c new file mode 100644 --- /dev/null +++ b/lib/libutil/source_date_epoch.c @@ -0,0 +1,49 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 The FreeBSD Foundation + * + * This software was developed by Klara, Inc. under sponsorship from the FreeBSD + * Foundation and the Sovereign Tech Agency. + */ + +#include + +#include +#include +#include +#include +#include + +int +source_date_epoch(time_t *tp) +{ + intmax_t val; + char *end, *env; + + env = getenv("SOURCE_DATE_EPOCH"); + if (env == NULL) + return (1); + + errno = 0; + val = strtoimax(env, &end, 10); + if (errno != 0) + return (-1); + if (end == env || *end != '\0') { + errno = EINVAL; + return (-1); + } + + /* Check for truncation. Unfortunately there is no TIME_T_MAX. */ + _Static_assert(sizeof(time_t) == sizeof(int32_t) || + sizeof(time_t) == sizeof(int64_t), + "time_t must be 32 or 64 bits"); + if (val < 0 || (sizeof(time_t) == sizeof(int32_t) && val > INT32_MAX) || + (sizeof(time_t) == sizeof(int64_t) && val > INT64_MAX)) { + errno = ERANGE; + return (-1); + } + + *tp = (time_t)val; + return (0); +}