From 0d58dd469ac73d4ad40496dcae3a7fdbfab91786 Mon Sep 17 00:00:00 2001 From: Colin McInnes Date: Thu, 1 Oct 2026 18:01:27 -0600 Subject: [PATCH 1/4] Export next renew/rebind/expire times when dumping leases Add eloop_timeout_remaining and script_envtime so DHCPv4/DHCPv6 dump-lease output can include next_renewal_time, next_rebind_time, and next_expire_time derived from the active eloop timeouts. --- src/dhcp.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/dhcp.h | 3 ++ src/dhcp6.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dhcp6.h | 3 ++ src/eloop.c | 22 ++++++++++++++ src/eloop.h | 8 +++++ src/script.c | 28 +++++++++++++++++ src/script.h | 3 ++ 8 files changed, 237 insertions(+) diff --git a/src/dhcp.c b/src/dhcp.c index 078f6927e..f2ade480e 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #define ELOOP_QUEUE ELOOP_DHCP @@ -4390,6 +4391,89 @@ dhcp_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid) } #ifndef SMALL +static int +dhcp_dump_lease_time(FILE *fp, const char *name, struct eloop *eloop, + void (*callback)(void *), void *arg, time_t *now, uint32_t lifetime, + bool privsep) +{ + unsigned int seconds; + int has; + time_t when; + struct tm tm; + char buf[32]; + char *ts; + + if (lifetime == DHCP_INFINITE_LIFETIME) + return script_envtime(fp, name, "never"); + + has = eloop_timeout_remaining(eloop, callback, arg, &seconds); + if (has == -1) { + logerrx("Could not get remaining dhcp4 lease time"); + return -1; + } + if (has == 0) + return script_envtime(fp, name, "expired"); + + when = *now + (time_t)seconds; + if (!privsep) { + ts = ctime(&when); + if (ts == NULL) { + logerrx("Could not convert dhcp4 lease time"); + return -1; + } + return script_envtime(fp, name, ts); + } + /* if using privsep, use gmtime and convert the time to a string in the + * UTC */ + if (gmtime_r(&when, &tm) == NULL) { + logerrx("Could not convert dhcp4 lease time"); + return -1; + } + if (strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Y UTC", &tm) == 0) { + logerrx("Could not format dhcp4 lease time"); + return -1; + } + return script_envtime(fp, name, buf); +} + +int +dhcp_dump_lease_times(FILE *fp, const struct interface *ifp) +{ + const struct dhcp_state *state; + const struct dhcp_lease *lease; + void *arg; + time_t now; + + state = D_CSTATE(ifp); + if (state == NULL) + return 0; + if (state->state != DHS_BOUND && state->state != DHS_RENEW && + state->state != DHS_REBIND) + return 0; + + now = time(NULL); + if (now == (time_t)-1) { + logerrx("Could not get current time"); + return -1; + } + + lease = &state->lease; + arg = UNCONST(ifp); + if (dhcp_dump_lease_time(fp, "next_renewal_time", ifp->ctx->eloop, + dhcp_startrenew, arg, &now, lease->renewaltime, + IN_PRIVSEP(ifp->ctx)) == -1) + return -1; + if (dhcp_dump_lease_time(fp, "next_rebind_time", ifp->ctx->eloop, + dhcp_rebind, arg, &now, lease->rebindtime, + IN_PRIVSEP(ifp->ctx)) == -1) + return -1; + if (dhcp_dump_lease_time(fp, "next_expire_time", ifp->ctx->eloop, + dhcp_expire, arg, &now, lease->leasetime, + IN_PRIVSEP(ifp->ctx)) == -1) + return -1; + return 0; +} + int dhcp_dump(struct interface *ifp) { diff --git a/src/dhcp.h b/src/dhcp.h index 73ac5e9ce..e17a90779 100644 --- a/src/dhcp.h +++ b/src/dhcp.h @@ -263,6 +263,9 @@ uint16_t dhcp_get_mtu(const struct interface *); int dhcp_get_routes(rb_tree_t *, struct interface *); ssize_t dhcp_env(FILE *, const char *, const struct interface *, const struct bootp *, size_t); +#ifndef SMALL +int dhcp_dump_lease_times(FILE *, const struct interface *); +#endif struct ipv4_addr *dhcp_handleifa(int, struct ipv4_addr *, pid_t pid); void dhcp_drop(struct interface *, const char *); diff --git a/src/dhcp6.c b/src/dhcp6.c index 07ffeb501..c5d71dfb4 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #define ELOOP_QUEUE ELOOP_DHCP6 @@ -4511,6 +4512,91 @@ dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp, return 1; } + +#ifndef SMALL +static int +dhcp6_dump_lease_time(FILE *fp, const char *name, struct eloop *eloop, + void (*callback)(void *), void *arg, time_t *now, uint32_t lifetime, + bool privsep) +{ + unsigned int seconds; + int has; + time_t when; + struct tm tm; + char buf[32]; + char *ts; + + if (lifetime == ND6_INFINITE_LIFETIME) + return script_envtime(fp, name, "never"); + + has = eloop_timeout_remaining(eloop, callback, arg, &seconds); + if (has == -1) { + logerrx("Could not get remaining dhcp6 lease time"); + return -1; + } + if (has == 0) + return script_envtime(fp, name, "expired"); + + when = *now + (time_t)seconds; + if (!privsep) { + /* if not using privsep, we can take advantage of ctime + * formatting */ + ts = ctime(&when); + if (ts == NULL) { + logerrx("Could not convert dhcp6 lease time"); + return -1; + } + return script_envtime(fp, name, ts); + } + /* if using privsep, use gmtime and convert the time to a string in the + * UTC */ + if (gmtime_r(&when, &tm) == NULL) { + logerrx("Could not convert dhcp6 lease time"); + return -1; + } + if (strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Y UTC", &tm) == 0) { + logerrx("Could not format dhcp6 lease time"); + return -1; + } + return script_envtime(fp, name, buf); +} + +int +dhcp6_dump_lease_times(FILE *fp, const struct interface *ifp) +{ + const struct dhcp6_state *state; + void *arg; + time_t now; + + state = D6_CSTATE(ifp); + if (state == NULL) + return 0; + if (state->state != DH6S_BOUND && state->state != DH6S_RENEW && + state->state != DH6S_REBIND) + return 0; + + now = time(NULL); + if (now == (time_t)-1) { + logerrx("Could not get current time"); + return -1; + } + + arg = UNCONST(ifp); + if (dhcp6_dump_lease_time(fp, "next_renewal_time", ifp->ctx->eloop, + dhcp6_startrenew, arg, &now, state->renew, + IN_PRIVSEP(ifp->ctx)) == -1) + return -1; + if (dhcp6_dump_lease_time(fp, "next_rebind_time", ifp->ctx->eloop, + dhcp6_startrebind, arg, &now, state->rebind, + IN_PRIVSEP(ifp->ctx)) == -1) + return -1; + if (dhcp6_dump_lease_time(fp, "next_expire_time", ifp->ctx->eloop, + dhcp6_startexpire, arg, &now, state->expire, + IN_PRIVSEP(ifp->ctx)) == -1) + return -1; + return 0; +} +#endif #endif #ifndef SMALL diff --git a/src/dhcp6.h b/src/dhcp6.h index 01af741ce..fb3c73bf0 100644 --- a/src/dhcp6.h +++ b/src/dhcp6.h @@ -245,6 +245,9 @@ void dhcp6_reboot(struct interface *); void dhcp6_renew(struct interface *); ssize_t dhcp6_env(FILE *, const char *, const struct interface *, const struct dhcp6_message *, size_t); +#ifndef SMALL +int dhcp6_dump_lease_times(FILE *, const struct interface *); +#endif void dhcp6_free(struct interface *); void dhcp6_handleifa(int, struct ipv6_addr *, pid_t); bool dhcp6_dadcompleted(const struct interface *); diff --git a/src/eloop.c b/src/eloop.c index 700905519..0cdc4d47f 100644 --- a/src/eloop.c +++ b/src/eloop.c @@ -606,6 +606,28 @@ eloop_q_timeout_delete(struct eloop *eloop, int queue, void (*callback)(void *), return n; } +#ifndef SMALL +int +eloop_q_timeout_remaining(struct eloop *eloop, int queue, + void (*callback)(void *), void *arg, unsigned int *seconds) +{ + struct eloop_timeout *t; + + if (eloop_reduce_timers(eloop) == -1) + return -1; + + TAILQ_FOREACH(t, &eloop->timeouts, next) { + if ((queue == 0 || t->queue == queue) && + t->callback == callback && t->arg == arg) { + if (seconds != NULL) + *seconds = t->seconds; + return 1; + } + } + return 0; +} +#endif + void eloop_exit(struct eloop *eloop, int code) { diff --git a/src/eloop.h b/src/eloop.h index acd183b00..f4646fb5d 100644 --- a/src/eloop.h +++ b/src/eloop.h @@ -81,6 +81,10 @@ unsigned long long eloop_timespec_diff(const struct timespec *tsp, eloop_q_timeout_add_msec((eloop), ELOOP_QUEUE, (ms), (cb), (ctx)) #define eloop_timeout_delete(eloop, cb, ctx) \ eloop_q_timeout_delete((eloop), ELOOP_QUEUE, (cb), (ctx)) +#ifndef SMALL +#define eloop_timeout_remaining(eloop, cb, ctx, sec) \ + eloop_q_timeout_remaining((eloop), ELOOP_QUEUE, (cb), (ctx), (sec)) +#endif int eloop_q_timeout_add_tv(struct eloop *, int, const struct timespec *, void (*)(void *), void *); int eloop_q_timeout_add_sec(struct eloop *, int, unsigned int, void (*)(void *), @@ -88,6 +92,10 @@ int eloop_q_timeout_add_sec(struct eloop *, int, unsigned int, void (*)(void *), int eloop_q_timeout_add_msec(struct eloop *, int, unsigned long, void (*)(void *), void *); int eloop_q_timeout_delete(struct eloop *, int, void (*)(void *), void *); +#ifndef SMALL +int eloop_q_timeout_remaining(struct eloop *, int, void (*)(void *), void *, + unsigned int *); +#endif int eloop_signal_set_cb(struct eloop *, const int *, size_t, void (*)(int, void *), void *); diff --git a/src/script.c b/src/script.c index 2464643f0..ba7e1caeb 100644 --- a/src/script.c +++ b/src/script.c @@ -158,6 +158,24 @@ efprintf(FILE *fp, const char *fmt, ...) return r; } +#ifndef SMALL +int +script_envtime(FILE *fp, const char *name, const char *timestr) +{ + char buf[32]; + char *nl; + + if (strlcpy(buf, timestr, sizeof(buf)) >= sizeof(buf)) { + errno = ENAMETOOLONG; + return -1; + } + nl = strchr(buf, '\n'); + if (nl != NULL) + *nl = '\0'; + return efprintf(fp, "%s=%s", name, buf); +} +#endif + char ** script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len) { @@ -524,6 +542,11 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp, if (append_config(fp, "new", (const char *const *)ifo->config) == -1) goto eexit; +#ifndef SMALL + if (ifp->ctx->options & DHCPCD_DUMPLEASE && + dhcp_dump_lease_times(fp, ifp) == -1) + goto eexit; +#endif } #endif #ifdef INET6 @@ -536,6 +559,11 @@ make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp, if (dhcp6_env(fp, "new", ifp, d6_state->new, d6_state->new_len) == -1) goto eexit; +#ifndef SMALL + if (ifp->ctx->options & DHCPCD_DUMPLEASE && + dhcp6_dump_lease_times(fp, ifp) == -1) + goto eexit; +#endif } #endif if (protocol == PROTO_RA) { diff --git a/src/script.h b/src/script.h index b40574c77..3ad2092da 100644 --- a/src/script.h +++ b/src/script.h @@ -32,6 +32,9 @@ #include "control.h" __printflike(2, 3) int efprintf(FILE *, const char *, ...); +#ifndef SMALL +int script_envtime(FILE *, const char *, const char *); +#endif void if_printoptions(void); char **script_buftoenv(struct dhcpcd_ctx *, char *, size_t); pid_t script_exec(char *const *, char *const *); From 608922de68b0a69093ca5b0a88b394da74b56953 Mon Sep 17 00:00:00 2001 From: Colin McInnes Date: Wed, 14 Oct 2026 16:35:50 -0600 Subject: [PATCH 2/4] Change to seconds left until renew/rebind/expire --- src/dhcp.c | 67 ++++++++++++++------------------------------------- src/dhcp6.c | 68 +++++++++++++--------------------------------------- src/eloop.c | 31 +++++++++++++++++++----- src/eloop.h | 9 ++++--- src/script.c | 18 -------------- src/script.h | 3 --- 6 files changed, 66 insertions(+), 130 deletions(-) diff --git a/src/dhcp.c b/src/dhcp.c index f2ade480e..8ca573c84 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -4393,47 +4393,18 @@ dhcp_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid) #ifndef SMALL static int dhcp_dump_lease_time(FILE *fp, const char *name, struct eloop *eloop, - void (*callback)(void *), void *arg, time_t *now, uint32_t lifetime, - bool privsep) + void (*callback)(void *), void *arg, unsigned long long esecs, + unsigned int ensecs, uint32_t lifetime) { unsigned int seconds; - int has; - time_t when; - struct tm tm; - char buf[32]; - char *ts; if (lifetime == DHCP_INFINITE_LIFETIME) - return script_envtime(fp, name, "never"); + return efprintf(fp, "%s=never", name); - has = eloop_timeout_remaining(eloop, callback, arg, &seconds); - if (has == -1) { - logerrx("Could not get remaining dhcp4 lease time"); - return -1; - } - if (has == 0) - return script_envtime(fp, name, "expired"); - - when = *now + (time_t)seconds; - if (!privsep) { - ts = ctime(&when); - if (ts == NULL) { - logerrx("Could not convert dhcp4 lease time"); - return -1; - } - return script_envtime(fp, name, ts); - } - /* if using privsep, use gmtime and convert the time to a string in the - * UTC */ - if (gmtime_r(&when, &tm) == NULL) { - logerrx("Could not convert dhcp4 lease time"); - return -1; - } - if (strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Y UTC", &tm) == 0) { - logerrx("Could not format dhcp4 lease time"); - return -1; - } - return script_envtime(fp, name, buf); + if (eloop_timeout_remaining(eloop, callback, arg, esecs, ensecs, + &seconds) == 0) + seconds = 0; + return efprintf(fp, "%s=%u", name, seconds); } int @@ -4442,7 +4413,8 @@ dhcp_dump_lease_times(FILE *fp, const struct interface *ifp) const struct dhcp_state *state; const struct dhcp_lease *lease; void *arg; - time_t now; + unsigned long long esecs; + unsigned int ensecs; state = D_CSTATE(ifp); if (state == NULL) @@ -4451,25 +4423,22 @@ dhcp_dump_lease_times(FILE *fp, const struct interface *ifp) state->state != DHS_REBIND) return 0; - now = time(NULL); - if (now == (time_t)-1) { - logerrx("Could not get current time"); + if (eloop_monotonic_elapsed(ifp->ctx->eloop, &esecs, &ensecs) == -1) { + logerr("eloop_monotonic_elapsed"); return -1; } lease = &state->lease; arg = UNCONST(ifp); - if (dhcp_dump_lease_time(fp, "next_renewal_time", ifp->ctx->eloop, - dhcp_startrenew, arg, &now, lease->renewaltime, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp_dump_lease_time(fp, "time_to_renew", ifp->ctx->eloop, + dhcp_startrenew, arg, esecs, ensecs, + lease->renewaltime) == -1) return -1; - if (dhcp_dump_lease_time(fp, "next_rebind_time", ifp->ctx->eloop, - dhcp_rebind, arg, &now, lease->rebindtime, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp_dump_lease_time(fp, "time_to_rebind", ifp->ctx->eloop, + dhcp_rebind, arg, esecs, ensecs, lease->rebindtime) == -1) return -1; - if (dhcp_dump_lease_time(fp, "next_expire_time", ifp->ctx->eloop, - dhcp_expire, arg, &now, lease->leasetime, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp_dump_lease_time(fp, "time_to_expire", ifp->ctx->eloop, + dhcp_expire, arg, esecs, ensecs, lease->leasetime) == -1) return -1; return 0; } diff --git a/src/dhcp6.c b/src/dhcp6.c index c5d71dfb4..6e6cde9a6 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -4516,49 +4516,18 @@ dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp, #ifndef SMALL static int dhcp6_dump_lease_time(FILE *fp, const char *name, struct eloop *eloop, - void (*callback)(void *), void *arg, time_t *now, uint32_t lifetime, - bool privsep) + void (*callback)(void *), void *arg, unsigned long long esecs, + unsigned int ensecs, uint32_t lifetime) { unsigned int seconds; - int has; - time_t when; - struct tm tm; - char buf[32]; - char *ts; if (lifetime == ND6_INFINITE_LIFETIME) - return script_envtime(fp, name, "never"); + return efprintf(fp, "%s=never", name); - has = eloop_timeout_remaining(eloop, callback, arg, &seconds); - if (has == -1) { - logerrx("Could not get remaining dhcp6 lease time"); - return -1; - } - if (has == 0) - return script_envtime(fp, name, "expired"); - - when = *now + (time_t)seconds; - if (!privsep) { - /* if not using privsep, we can take advantage of ctime - * formatting */ - ts = ctime(&when); - if (ts == NULL) { - logerrx("Could not convert dhcp6 lease time"); - return -1; - } - return script_envtime(fp, name, ts); - } - /* if using privsep, use gmtime and convert the time to a string in the - * UTC */ - if (gmtime_r(&when, &tm) == NULL) { - logerrx("Could not convert dhcp6 lease time"); - return -1; - } - if (strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Y UTC", &tm) == 0) { - logerrx("Could not format dhcp6 lease time"); - return -1; - } - return script_envtime(fp, name, buf); + if (eloop_timeout_remaining(eloop, callback, arg, esecs, ensecs, + &seconds) == 0) + seconds = 0; + return efprintf(fp, "%s=%u", name, seconds); } int @@ -4566,7 +4535,8 @@ dhcp6_dump_lease_times(FILE *fp, const struct interface *ifp) { const struct dhcp6_state *state; void *arg; - time_t now; + unsigned long long esecs; + unsigned int ensecs; state = D6_CSTATE(ifp); if (state == NULL) @@ -4575,24 +4545,20 @@ dhcp6_dump_lease_times(FILE *fp, const struct interface *ifp) state->state != DH6S_REBIND) return 0; - now = time(NULL); - if (now == (time_t)-1) { - logerrx("Could not get current time"); + if (eloop_monotonic_elapsed(ifp->ctx->eloop, &esecs, &ensecs) == -1) { + logerr("eloop_monotonic_elapsed"); return -1; } arg = UNCONST(ifp); - if (dhcp6_dump_lease_time(fp, "next_renewal_time", ifp->ctx->eloop, - dhcp6_startrenew, arg, &now, state->renew, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp6_dump_lease_time(fp, "time_to_renew", ifp->ctx->eloop, + dhcp6_startrenew, arg, esecs, ensecs, state->renew) == -1) return -1; - if (dhcp6_dump_lease_time(fp, "next_rebind_time", ifp->ctx->eloop, - dhcp6_startrebind, arg, &now, state->rebind, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp6_dump_lease_time(fp, "time_to_rebind", ifp->ctx->eloop, + dhcp6_startrebind, arg, esecs, ensecs, state->rebind) == -1) return -1; - if (dhcp6_dump_lease_time(fp, "next_expire_time", ifp->ctx->eloop, - dhcp6_startexpire, arg, &now, state->expire, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp6_dump_lease_time(fp, "time_to_expire", ifp->ctx->eloop, + dhcp6_startexpire, arg, esecs, ensecs, state->expire) == -1) return -1; return 0; } diff --git a/src/eloop.c b/src/eloop.c index 0cdc4d47f..ffaa7b28b 100644 --- a/src/eloop.c +++ b/src/eloop.c @@ -608,19 +608,38 @@ eloop_q_timeout_delete(struct eloop *eloop, int queue, void (*callback)(void *), #ifndef SMALL int -eloop_q_timeout_remaining(struct eloop *eloop, int queue, - void (*callback)(void *), void *arg, unsigned int *seconds) +eloop_monotonic_elapsed(struct eloop *eloop, unsigned long long *secs, + unsigned int *nsecs) { - struct eloop_timeout *t; + struct timespec now; - if (eloop_reduce_timers(eloop) == -1) + if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) return -1; + *secs = eloop_timespec_diff(&now, &eloop->now, nsecs); + return 0; +} + +int +eloop_q_timeout_remaining(struct eloop *eloop, int queue, + void (*callback)(void *), void *arg, unsigned long long secs, + unsigned int nsecs, unsigned int *seconds) +{ + struct eloop_timeout *t; + unsigned int left; TAILQ_FOREACH(t, &eloop->timeouts, next) { if ((queue == 0 || t->queue == queue) && t->callback == callback && t->arg == arg) { - if (seconds != NULL) - *seconds = t->seconds; + if (seconds == NULL) + return 1; + if (secs > t->seconds) + left = 0; + else { + left = t->seconds - (unsigned int)secs; + if (nsecs > t->nseconds && left > 0) + left--; + } + *seconds = left; return 1; } } diff --git a/src/eloop.h b/src/eloop.h index f4646fb5d..c03f962da 100644 --- a/src/eloop.h +++ b/src/eloop.h @@ -82,8 +82,11 @@ unsigned long long eloop_timespec_diff(const struct timespec *tsp, #define eloop_timeout_delete(eloop, cb, ctx) \ eloop_q_timeout_delete((eloop), ELOOP_QUEUE, (cb), (ctx)) #ifndef SMALL -#define eloop_timeout_remaining(eloop, cb, ctx, sec) \ - eloop_q_timeout_remaining((eloop), ELOOP_QUEUE, (cb), (ctx), (sec)) +int eloop_monotonic_elapsed(struct eloop *, unsigned long long *, + unsigned int *); +#define eloop_timeout_remaining(eloop, cb, ctx, esecs, ensecs, sec) \ + eloop_q_timeout_remaining((eloop), ELOOP_QUEUE, (cb), (ctx), \ + (esecs), (ensecs), (sec)) #endif int eloop_q_timeout_add_tv(struct eloop *, int, const struct timespec *, void (*)(void *), void *); @@ -94,7 +97,7 @@ int eloop_q_timeout_add_msec(struct eloop *, int, unsigned long, int eloop_q_timeout_delete(struct eloop *, int, void (*)(void *), void *); #ifndef SMALL int eloop_q_timeout_remaining(struct eloop *, int, void (*)(void *), void *, - unsigned int *); + unsigned long long, unsigned int, unsigned int *); #endif int eloop_signal_set_cb(struct eloop *, const int *, size_t, diff --git a/src/script.c b/src/script.c index ba7e1caeb..156d0c62c 100644 --- a/src/script.c +++ b/src/script.c @@ -158,24 +158,6 @@ efprintf(FILE *fp, const char *fmt, ...) return r; } -#ifndef SMALL -int -script_envtime(FILE *fp, const char *name, const char *timestr) -{ - char buf[32]; - char *nl; - - if (strlcpy(buf, timestr, sizeof(buf)) >= sizeof(buf)) { - errno = ENAMETOOLONG; - return -1; - } - nl = strchr(buf, '\n'); - if (nl != NULL) - *nl = '\0'; - return efprintf(fp, "%s=%s", name, buf); -} -#endif - char ** script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len) { diff --git a/src/script.h b/src/script.h index 3ad2092da..b40574c77 100644 --- a/src/script.h +++ b/src/script.h @@ -32,9 +32,6 @@ #include "control.h" __printflike(2, 3) int efprintf(FILE *, const char *, ...); -#ifndef SMALL -int script_envtime(FILE *, const char *, const char *); -#endif void if_printoptions(void); char **script_buftoenv(struct dhcpcd_ctx *, char *, size_t); pid_t script_exec(char *const *, char *const *); From 8a548bbd897844720608a3da1775ff523d553c4d Mon Sep 17 00:00:00 2001 From: Colin McInnes Date: Wed, 14 Oct 2026 16:35:50 -0600 Subject: [PATCH 3/4] Change to seconds left until renew/rebind/expire Ensure clang formatting --- src/dhcp.c | 66 +++++++++++++------------------------------------- src/dhcp6.c | 68 +++++++++++++--------------------------------------- src/eloop.c | 31 +++++++++++++++++++----- src/eloop.h | 9 ++++--- src/script.c | 18 -------------- src/script.h | 3 --- 6 files changed, 65 insertions(+), 130 deletions(-) diff --git a/src/dhcp.c b/src/dhcp.c index f2ade480e..0e05db931 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -4393,47 +4393,18 @@ dhcp_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid) #ifndef SMALL static int dhcp_dump_lease_time(FILE *fp, const char *name, struct eloop *eloop, - void (*callback)(void *), void *arg, time_t *now, uint32_t lifetime, - bool privsep) + void (*callback)(void *), void *arg, unsigned long long esecs, + unsigned int ensecs, uint32_t lifetime) { unsigned int seconds; - int has; - time_t when; - struct tm tm; - char buf[32]; - char *ts; if (lifetime == DHCP_INFINITE_LIFETIME) - return script_envtime(fp, name, "never"); + return efprintf(fp, "%s=never", name); - has = eloop_timeout_remaining(eloop, callback, arg, &seconds); - if (has == -1) { - logerrx("Could not get remaining dhcp4 lease time"); - return -1; - } - if (has == 0) - return script_envtime(fp, name, "expired"); - - when = *now + (time_t)seconds; - if (!privsep) { - ts = ctime(&when); - if (ts == NULL) { - logerrx("Could not convert dhcp4 lease time"); - return -1; - } - return script_envtime(fp, name, ts); - } - /* if using privsep, use gmtime and convert the time to a string in the - * UTC */ - if (gmtime_r(&when, &tm) == NULL) { - logerrx("Could not convert dhcp4 lease time"); - return -1; - } - if (strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Y UTC", &tm) == 0) { - logerrx("Could not format dhcp4 lease time"); - return -1; - } - return script_envtime(fp, name, buf); + if (eloop_timeout_remaining(eloop, callback, arg, esecs, ensecs, + &seconds) == 0) + seconds = 0; + return efprintf(fp, "%s=%u", name, seconds); } int @@ -4442,7 +4413,8 @@ dhcp_dump_lease_times(FILE *fp, const struct interface *ifp) const struct dhcp_state *state; const struct dhcp_lease *lease; void *arg; - time_t now; + unsigned long long esecs; + unsigned int ensecs; state = D_CSTATE(ifp); if (state == NULL) @@ -4451,25 +4423,21 @@ dhcp_dump_lease_times(FILE *fp, const struct interface *ifp) state->state != DHS_REBIND) return 0; - now = time(NULL); - if (now == (time_t)-1) { - logerrx("Could not get current time"); + if (eloop_monotonic_elapsed(ifp->ctx->eloop, &esecs, &ensecs) == -1) { + logerr("eloop_monotonic_elapsed"); return -1; } lease = &state->lease; arg = UNCONST(ifp); - if (dhcp_dump_lease_time(fp, "next_renewal_time", ifp->ctx->eloop, - dhcp_startrenew, arg, &now, lease->renewaltime, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp_dump_lease_time(fp, "time_to_renew", ifp->ctx->eloop, + dhcp_startrenew, arg, esecs, ensecs, lease->renewaltime) == -1) return -1; - if (dhcp_dump_lease_time(fp, "next_rebind_time", ifp->ctx->eloop, - dhcp_rebind, arg, &now, lease->rebindtime, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp_dump_lease_time(fp, "time_to_rebind", ifp->ctx->eloop, + dhcp_rebind, arg, esecs, ensecs, lease->rebindtime) == -1) return -1; - if (dhcp_dump_lease_time(fp, "next_expire_time", ifp->ctx->eloop, - dhcp_expire, arg, &now, lease->leasetime, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp_dump_lease_time(fp, "time_to_expire", ifp->ctx->eloop, + dhcp_expire, arg, esecs, ensecs, lease->leasetime) == -1) return -1; return 0; } diff --git a/src/dhcp6.c b/src/dhcp6.c index c5d71dfb4..06ba63ff1 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -4516,49 +4516,18 @@ dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp, #ifndef SMALL static int dhcp6_dump_lease_time(FILE *fp, const char *name, struct eloop *eloop, - void (*callback)(void *), void *arg, time_t *now, uint32_t lifetime, - bool privsep) + void (*callback)(void *), void *arg, unsigned long long esecs, + unsigned int ensecs, uint32_t lifetime) { unsigned int seconds; - int has; - time_t when; - struct tm tm; - char buf[32]; - char *ts; if (lifetime == ND6_INFINITE_LIFETIME) - return script_envtime(fp, name, "never"); + return efprintf(fp, "%s=never", name); - has = eloop_timeout_remaining(eloop, callback, arg, &seconds); - if (has == -1) { - logerrx("Could not get remaining dhcp6 lease time"); - return -1; - } - if (has == 0) - return script_envtime(fp, name, "expired"); - - when = *now + (time_t)seconds; - if (!privsep) { - /* if not using privsep, we can take advantage of ctime - * formatting */ - ts = ctime(&when); - if (ts == NULL) { - logerrx("Could not convert dhcp6 lease time"); - return -1; - } - return script_envtime(fp, name, ts); - } - /* if using privsep, use gmtime and convert the time to a string in the - * UTC */ - if (gmtime_r(&when, &tm) == NULL) { - logerrx("Could not convert dhcp6 lease time"); - return -1; - } - if (strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Y UTC", &tm) == 0) { - logerrx("Could not format dhcp6 lease time"); - return -1; - } - return script_envtime(fp, name, buf); + if (eloop_timeout_remaining(eloop, callback, arg, esecs, ensecs, + &seconds) == 0) + seconds = 0; + return efprintf(fp, "%s=%u", name, seconds); } int @@ -4566,7 +4535,8 @@ dhcp6_dump_lease_times(FILE *fp, const struct interface *ifp) { const struct dhcp6_state *state; void *arg; - time_t now; + unsigned long long esecs; + unsigned int ensecs; state = D6_CSTATE(ifp); if (state == NULL) @@ -4575,24 +4545,20 @@ dhcp6_dump_lease_times(FILE *fp, const struct interface *ifp) state->state != DH6S_REBIND) return 0; - now = time(NULL); - if (now == (time_t)-1) { - logerrx("Could not get current time"); + if (eloop_monotonic_elapsed(ifp->ctx->eloop, &esecs, &ensecs) == -1) { + logerr("eloop_monotonic_elapsed"); return -1; } arg = UNCONST(ifp); - if (dhcp6_dump_lease_time(fp, "next_renewal_time", ifp->ctx->eloop, - dhcp6_startrenew, arg, &now, state->renew, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp6_dump_lease_time(fp, "time_to_renew", ifp->ctx->eloop, + dhcp6_startrenew, arg, esecs, ensecs, state->renew) == -1) return -1; - if (dhcp6_dump_lease_time(fp, "next_rebind_time", ifp->ctx->eloop, - dhcp6_startrebind, arg, &now, state->rebind, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp6_dump_lease_time(fp, "time_to_rebind", ifp->ctx->eloop, + dhcp6_startrebind, arg, esecs, ensecs, state->rebind) == -1) return -1; - if (dhcp6_dump_lease_time(fp, "next_expire_time", ifp->ctx->eloop, - dhcp6_startexpire, arg, &now, state->expire, - IN_PRIVSEP(ifp->ctx)) == -1) + if (dhcp6_dump_lease_time(fp, "time_to_expire", ifp->ctx->eloop, + dhcp6_startexpire, arg, esecs, ensecs, state->expire) == -1) return -1; return 0; } diff --git a/src/eloop.c b/src/eloop.c index 0cdc4d47f..ffaa7b28b 100644 --- a/src/eloop.c +++ b/src/eloop.c @@ -608,19 +608,38 @@ eloop_q_timeout_delete(struct eloop *eloop, int queue, void (*callback)(void *), #ifndef SMALL int -eloop_q_timeout_remaining(struct eloop *eloop, int queue, - void (*callback)(void *), void *arg, unsigned int *seconds) +eloop_monotonic_elapsed(struct eloop *eloop, unsigned long long *secs, + unsigned int *nsecs) { - struct eloop_timeout *t; + struct timespec now; - if (eloop_reduce_timers(eloop) == -1) + if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) return -1; + *secs = eloop_timespec_diff(&now, &eloop->now, nsecs); + return 0; +} + +int +eloop_q_timeout_remaining(struct eloop *eloop, int queue, + void (*callback)(void *), void *arg, unsigned long long secs, + unsigned int nsecs, unsigned int *seconds) +{ + struct eloop_timeout *t; + unsigned int left; TAILQ_FOREACH(t, &eloop->timeouts, next) { if ((queue == 0 || t->queue == queue) && t->callback == callback && t->arg == arg) { - if (seconds != NULL) - *seconds = t->seconds; + if (seconds == NULL) + return 1; + if (secs > t->seconds) + left = 0; + else { + left = t->seconds - (unsigned int)secs; + if (nsecs > t->nseconds && left > 0) + left--; + } + *seconds = left; return 1; } } diff --git a/src/eloop.h b/src/eloop.h index f4646fb5d..d0e56c942 100644 --- a/src/eloop.h +++ b/src/eloop.h @@ -82,8 +82,11 @@ unsigned long long eloop_timespec_diff(const struct timespec *tsp, #define eloop_timeout_delete(eloop, cb, ctx) \ eloop_q_timeout_delete((eloop), ELOOP_QUEUE, (cb), (ctx)) #ifndef SMALL -#define eloop_timeout_remaining(eloop, cb, ctx, sec) \ - eloop_q_timeout_remaining((eloop), ELOOP_QUEUE, (cb), (ctx), (sec)) +int eloop_monotonic_elapsed(struct eloop *, unsigned long long *, + unsigned int *); +#define eloop_timeout_remaining(eloop, cb, ctx, esecs, ensecs, sec) \ + eloop_q_timeout_remaining((eloop), ELOOP_QUEUE, (cb), (ctx), (esecs), \ + (ensecs), (sec)) #endif int eloop_q_timeout_add_tv(struct eloop *, int, const struct timespec *, void (*)(void *), void *); @@ -94,7 +97,7 @@ int eloop_q_timeout_add_msec(struct eloop *, int, unsigned long, int eloop_q_timeout_delete(struct eloop *, int, void (*)(void *), void *); #ifndef SMALL int eloop_q_timeout_remaining(struct eloop *, int, void (*)(void *), void *, - unsigned int *); + unsigned long long, unsigned int, unsigned int *); #endif int eloop_signal_set_cb(struct eloop *, const int *, size_t, diff --git a/src/script.c b/src/script.c index ba7e1caeb..156d0c62c 100644 --- a/src/script.c +++ b/src/script.c @@ -158,24 +158,6 @@ efprintf(FILE *fp, const char *fmt, ...) return r; } -#ifndef SMALL -int -script_envtime(FILE *fp, const char *name, const char *timestr) -{ - char buf[32]; - char *nl; - - if (strlcpy(buf, timestr, sizeof(buf)) >= sizeof(buf)) { - errno = ENAMETOOLONG; - return -1; - } - nl = strchr(buf, '\n'); - if (nl != NULL) - *nl = '\0'; - return efprintf(fp, "%s=%s", name, buf); -} -#endif - char ** script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len) { diff --git a/src/script.h b/src/script.h index 3ad2092da..b40574c77 100644 --- a/src/script.h +++ b/src/script.h @@ -32,9 +32,6 @@ #include "control.h" __printflike(2, 3) int efprintf(FILE *, const char *, ...); -#ifndef SMALL -int script_envtime(FILE *, const char *, const char *); -#endif void if_printoptions(void); char **script_buftoenv(struct dhcpcd_ctx *, char *, size_t); pid_t script_exec(char *const *, char *const *); From 1360095db582c27e7dfb1b4e80bc65e3274f07e6 Mon Sep 17 00:00:00 2001 From: Colin McInnes Date: Wed, 14 Oct 2026 17:04:41 -0600 Subject: [PATCH 4/4] clang cleanup --- src/dhcp.c | 6 ++---- src/dhcp6.c | 3 +-- src/eloop.h | 6 +++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/dhcp.c b/src/dhcp.c index ad1c7c957..0e05db931 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -4402,8 +4402,7 @@ dhcp_dump_lease_time(FILE *fp, const char *name, struct eloop *eloop, return efprintf(fp, "%s=never", name); if (eloop_timeout_remaining(eloop, callback, arg, esecs, ensecs, - - &seconds) == 0) + &seconds) == 0) seconds = 0; return efprintf(fp, "%s=%u", name, seconds); } @@ -4432,8 +4431,7 @@ dhcp_dump_lease_times(FILE *fp, const struct interface *ifp) lease = &state->lease; arg = UNCONST(ifp); if (dhcp_dump_lease_time(fp, "time_to_renew", ifp->ctx->eloop, - dhcp_startrenew, arg, esecs, ensecs, - lease->renewaltime) == -1) + dhcp_startrenew, arg, esecs, ensecs, lease->renewaltime) == -1) return -1; if (dhcp_dump_lease_time(fp, "time_to_rebind", ifp->ctx->eloop, dhcp_rebind, arg, esecs, ensecs, lease->rebindtime) == -1) diff --git a/src/dhcp6.c b/src/dhcp6.c index bb7bd8c67..06ba63ff1 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -4525,8 +4525,7 @@ dhcp6_dump_lease_time(FILE *fp, const char *name, struct eloop *eloop, return efprintf(fp, "%s=never", name); if (eloop_timeout_remaining(eloop, callback, arg, esecs, ensecs, - - &seconds) == 0) + &seconds) == 0) seconds = 0; return efprintf(fp, "%s=%u", name, seconds); } diff --git a/src/eloop.h b/src/eloop.h index c03f962da..d0e56c942 100644 --- a/src/eloop.h +++ b/src/eloop.h @@ -84,9 +84,9 @@ unsigned long long eloop_timespec_diff(const struct timespec *tsp, #ifndef SMALL int eloop_monotonic_elapsed(struct eloop *, unsigned long long *, unsigned int *); -#define eloop_timeout_remaining(eloop, cb, ctx, esecs, ensecs, sec) \ - eloop_q_timeout_remaining((eloop), ELOOP_QUEUE, (cb), (ctx), \ - (esecs), (ensecs), (sec)) +#define eloop_timeout_remaining(eloop, cb, ctx, esecs, ensecs, sec) \ + eloop_q_timeout_remaining((eloop), ELOOP_QUEUE, (cb), (ctx), (esecs), \ + (ensecs), (sec)) #endif int eloop_q_timeout_add_tv(struct eloop *, int, const struct timespec *, void (*)(void *), void *);