diff --git a/src/dhcp.c b/src/dhcp.c index 078f6927..f2ade480 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 73ac5e9c..e17a9077 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 07ffeb50..c5d71dfb 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 01af741c..fb3c73bf 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 70090551..0cdc4d47 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 acd183b0..f4646fb5 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 2464643f..ba7e1cae 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 b40574c7..3ad2092d 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 *);