send_request(ifp);
}
+static void
+dhcp_deconfigure(void *arg)
+{
+ struct interface *ifp = arg;
+ struct dhcp_state *state = D_STATE(ifp);
+ struct if_options *ifo = ifp->options;
+
+#ifdef AUTH
+ dhcp_auth_reset(&state->auth);
+#endif
+
+ state->state = DHS_NONE;
+ free(state->offer);
+ state->offer = NULL;
+ state->offer_len = 0;
+ free(state->old);
+ state->old = state->new;
+ state->old_len = state->new_len;
+ state->new = NULL;
+ state->new_len = 0;
+ if (ifo->options & DHCPCD_CONFIGURE)
+ ipv4_applyaddr(ifp);
+ else {
+ state->addr = NULL;
+ state->added = 0;
+ script_runreason(ifp, state->reason);
+ }
+ free(state->old);
+ state->old = NULL;
+ state->old_len = 0;
+ state->lease.addr.s_addr = 0;
+ ifo->options &= ~(DHCPCD_CSR_WARNED | DHCPCD_ROUTER_HOST_ROUTE_WARNED);
+
+ dhcp_free(ifp);
+ dhcpcd_dropped(ifp);
+}
+
void
dhcp_drop(struct interface *ifp, const char *reason)
{
* but we do have a timeout, so punt it. */
if (state == NULL || state->state == DHS_NONE) {
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+ dhcpcd_dropped(ifp);
return;
}
#ifdef ARPING
state->arping_index = -1;
#endif
+ state->reason = reason;
if (ifo->options & DHCPCD_RELEASE && !(ifo->options & DHCPCD_INFORM)) {
/* Failure to send the release may cause this function to
state->new != NULL &&
state->lease.server.s_addr != INADDR_ANY)
{
+ /* We need to delay removal of the IP address so the
+ * message can be sent.
+ * Unlike DHCPv6, there is no acknowledgement. */
+ const struct timespec delay = {
+ .tv_sec = 1,
+ };
+
loginfox("%s: releasing lease of %s",
ifp->name, inet_ntoa(state->lease.addr));
dhcp_new_xid(ifp);
send_message(ifp, DHCP_RELEASE, NULL);
+ eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+ eloop_timeout_add_tv(ifp->ctx->eloop,
+ &delay, dhcp_deconfigure, ifp);
+ return;
}
}
#ifdef AUTH
#endif
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-#ifdef AUTH
- dhcp_auth_reset(&state->auth);
-#endif
-
- state->state = DHS_NONE;
- free(state->offer);
- state->offer = NULL;
- state->offer_len = 0;
- free(state->old);
- state->old = state->new;
- state->old_len = state->new_len;
- state->new = NULL;
- state->new_len = 0;
- state->reason = reason;
- if (ifo->options & DHCPCD_CONFIGURE)
- ipv4_applyaddr(ifp);
- else {
- state->addr = NULL;
- state->added = 0;
- script_runreason(ifp, state->reason);
- }
- free(state->old);
- state->old = NULL;
- state->old_len = 0;
- state->lease.addr.s_addr = 0;
- ifo->options &= ~(DHCPCD_CSR_WARNED | DHCPCD_ROUTER_HOST_ROUTE_WARNED);
-
- /* Close DHCP ports so a changed interface family is picked
- * up by a new BPF state. */
- dhcp_close(ifp);
+ dhcp_deconfigure(ifp);
}
static int
#define IS_STATE_ACTIVE(s) ((s)-state != DHS_NONE && \
(s)->state != DHS_INIT && (s)->state != DHS_BOUND)
+ /* Don't do anything if the user hasn't configured it. */
+ if (ifp->active != IF_ACTIVE_USER ||
+ ifp->options->options & DHCPCD_STOPPING ||
+ !(ifp->options->options & DHCPCD_DHCP))
+ return;
+
if (bootp->op != BOOTREPLY) {
if (IS_STATE_ACTIVE(state))
logdebugx("%s: op (%d) is not BOOTREPLY",
free(state->offer);
free(state->clientid);
free(state);
+ ifp->if_data[IF_DATA_DHCP] = NULL;
}
ctx = ifp->ctx;
dhcpcd_drop_af(ifp, stop, AF_UNSPEC);
}
-static void
-stop_interface(struct interface *ifp, const char *reason)
+static bool
+dhcpcd_ifrunning(struct interface *ifp)
{
- struct dhcpcd_ctx *ctx;
- ctx = ifp->ctx;
- loginfox("%s: removing interface", ifp->name);
- ifp->options->options |= DHCPCD_STOPPING;
+#ifdef INET
+ if (D_CSTATE(ifp) != NULL)
+ return true;
+#ifdef IPV4LL
+ if (IPV4LL_CSTATE(ifp) != NULL)
+ return true;
+#endif
+#endif
+#ifdef DHCP6
+ if (D6_CSTATE(ifp) != NULL)
+ return true;
+#endif
+ return false;
+}
- dhcpcd_drop(ifp, 1);
- script_runreason(ifp, reason == NULL ? "STOPPED" : reason);
+void
+dhcpcd_dropped(struct interface *ifp)
+{
+ struct dhcpcd_ctx *ctx = ifp->ctx;
- /* Delete all timeouts for the interfaces */
- eloop_q_timeout_delete(ctx->eloop, ELOOP_QUEUE_ALL, NULL, ifp);
+ if (dhcpcd_ifrunning(ifp))
+ return;
/* De-activate the interface */
- ifp->active = IF_INACTIVE;
- ifp->options->options &= ~DHCPCD_STOPPING;
+ if (ifp->active) {
+ ifp->active = IF_INACTIVE;
+ ifp->options->options &= ~DHCPCD_STOPPING;
+ script_runreason(ifp, "STOPPED");
+ }
- if (!(ctx->options & (DHCPCD_MANAGER | DHCPCD_TEST)))
- eloop_exit(ctx->eloop, EXIT_FAILURE);
+ if (!(ctx->options & DHCPCD_EXITING))
+ return;
+
+ TAILQ_FOREACH(ifp, ctx->ifaces, next) {
+ if (dhcpcd_ifrunning(ifp))
+ break;
+ }
+
+ /* All interfaces have stopped, we can exit */
+ if (ifp == NULL)
+ eloop_exit(ctx->eloop, EXIT_SUCCESS);
+}
+
+static void
+stop_interface(struct interface *ifp)
+{
+
+ loginfox("%s: removing interface", ifp->name);
+ ifp->options->options |= DHCPCD_STOPPING;
+ dhcpcd_drop(ifp, 1);
}
static void
ifp->carrier = carrier;
ifp->flags = flags;
+ if (ifp->options->options & DHCPCD_STOPPING)
+ return;
+
if (!if_is_link_up(ifp)) {
if (!ifp->active || (!was_link_up && !was_roaming))
return;
}
if (ifp->active) {
logdebugx("%s: interface departed", ifp->name);
- stop_interface(ifp, "DEPARTED");
+ stop_interface(ifp);
}
TAILQ_REMOVE(ctx->ifaces, ifp, next);
if_free(ifp);
return 0;
}
+ if (ctx->options & DHCPCD_EXITING)
+ return 0;
+
ifs = if_discover(ctx, &ifaddrs, -1, UNCONST(argv));
if (ifs == NULL) {
logerr(__func__);
{
struct dhcpcd_ctx *ctx = arg;
+ if (ctx->options & DHCPCD_EXITING)
+ return;
+
if (events != ELE_READ)
logerrx("%s: unexpected event 0x%04x", __func__, events);
}
}
-static void
+static bool
stop_all_interfaces(struct dhcpcd_ctx *ctx, unsigned long long opts)
{
struct interface *ifp;
+ bool anystopped = false;
ctx->options |= opts;
if (ctx->ifaces == NULL)
- return;
+ return anystopped;
if (ctx->options & DHCPCD_RELEASE)
ctx->options &= ~DHCPCD_PERSISTENT;
if (ifp->options->options & DHCPCD_RELEASE)
ifp->options->options &= ~DHCPCD_PERSISTENT;
ifp->options->options |= DHCPCD_EXITING;
- stop_interface(ifp, NULL);
+ anystopped = true;
+ stop_interface(ifp);
}
+ return anystopped;
}
static void
#ifdef USE_SIGNALS
#define sigmsg "received %s, %s"
-static volatile bool dhcpcd_exiting = false;
void
dhcpcd_signal_cb(int sig, void *arg)
{
* During teardown we don't want to process SIGTERM or SIGINT again,
* as that could trigger memory issues.
*/
- if (dhcpcd_exiting)
+ if (ctx->options & DHCPCD_EXITING)
+ return;
+
+ ctx->options |= DHCPCD_EXITING;
+ if (!(ctx->options & DHCPCD_TEST) &&
+ stop_all_interfaces(ctx, opts))
+ {
+ /* We stopped something, we will exit once that is done. */
return;
+ }
- dhcpcd_exiting = true;
- if (!(ctx->options & DHCPCD_TEST))
- stop_all_interfaces(ctx, opts);
eloop_exit(ctx->eloop, exit_code);
- dhcpcd_exiting = false;
}
#endif
if (opts & (DHCPCD_EXITING | DHCPCD_RELEASE)) {
if (oifind == argc && af == AF_UNSPEC) {
- stop_all_interfaces(ctx, opts);
- eloop_exit(ctx->eloop, EXIT_SUCCESS);
+ if (stop_all_interfaces(ctx, opts) == false)
+ eloop_exit(ctx->eloop, EXIT_SUCCESS);
+ /* We did stop an interface, it will notify us once
+ * dropped so we can exit. */
return 0;
}
if (af != AF_UNSPEC)
dhcpcd_drop_af(ifp, 1, af);
else
- stop_interface(ifp, NULL);
+ stop_interface(ifp);
ifo->options = orig_opts;
}
return 0;
pid_t pid;
pid = pidfile_read(ctx->pidfile);
-
- if(pid == -1)
+ if (pid == -1)
eloop_exit(ctx->eloop, EXIT_SUCCESS);
- else if (++ctx->duid_len >= 100) { /* overload duid_len */
- logerrx("pid %d failed to exit", (int)pid);
- eloop_exit(ctx->eloop, EXIT_FAILURE);
- } else
+ else
eloop_timeout_add_msec(ctx->eloop, 100,
dhcpcd_pidfile_timeout, ctx);
}
+static void
+dhcpcd_exit_timeout(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+ pid_t pid;
+
+ pid = pidfile_read(ctx->pidfile);
+ logwarnx("pid %lld failed to exit", (long long)pid);
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+}
+
static int dup_null(int fd)
{
int fd_null = open(_PATH_DEVNULL, O_WRONLY);
/* Spin until it exits */
loginfox("waiting for pid %d to exit", (int)pid);
dhcpcd_pidfile_timeout(&ctx);
+ eloop_timeout_add_sec(ctx.eloop, 50,
+ dhcpcd_exit_timeout, &ctx);
goto run_loop;
}
}