/* * dhcpcd - DHCP client daemon * Copyright (c) 2006-2015 Roy Marples * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #define _INDEV #include "common.h" #include "dev.h" #include "eloop.h" #include "dhcpcd.h" int dev_initialized(struct dhcpcd_ctx *ctx, const char *ifname) { if (ctx->dev == NULL) return 1; return ctx->dev->initialized(ifname); } int dev_listening(struct dhcpcd_ctx *ctx) { if (ctx->dev == NULL) return 0; return ctx->dev->listening(); } static void dev_stop1(struct dhcpcd_ctx *ctx, int stop) { if (ctx->dev) { if (stop) logger(ctx, LOG_DEBUG, "dev: unloaded %s", ctx->dev->name); eloop_event_delete(ctx->eloop, ctx->dev_fd, 0); ctx->dev->stop(); free(ctx->dev); ctx->dev = NULL; ctx->dev_fd = -1; } if (ctx->dev_handle) { dlclose(ctx->dev_handle); ctx->dev_handle = NULL; } } void dev_stop(struct dhcpcd_ctx *ctx) { dev_stop1(ctx,!(ctx->options & DHCPCD_FORKED)); } static int dev_start2(struct dhcpcd_ctx *ctx, const char *name) { char file[PATH_MAX]; void *h; void (*fptr)(struct dev *, const struct dev_dhcpcd *); int r; struct dev_dhcpcd dev_dhcpcd; snprintf(file, sizeof(file), DEVDIR "/%s", name); h = dlopen(file, RTLD_LAZY); if (h == NULL) { logger(ctx, LOG_ERR, "dlopen: %s", dlerror()); return -1; } fptr = (void (*)(struct dev *, const struct dev_dhcpcd *)) dlsym(h, "dev_init"); if (fptr == NULL) { logger(ctx, LOG_ERR, "dlsym: %s", dlerror()); dlclose(h); return -1; } ctx->dev = calloc(1, sizeof(*ctx->dev)); dev_dhcpcd.handle_interface = &dhcpcd_handleinterface; fptr(ctx->dev, &dev_dhcpcd); if (ctx->dev->start == NULL || (r = ctx->dev->start()) == -1) { free(ctx->dev); ctx->dev = NULL; dlclose(h); return -1; } logger(ctx, LOG_INFO, "dev: loaded %s", ctx->dev->name); ctx->dev_handle = h; return r; } static int dev_start1(struct dhcpcd_ctx *ctx) { DIR *dp; struct dirent *d; int r; if (ctx->dev) { logger(ctx, LOG_ERR, "dev: already started %s", ctx->dev->name); return -1; } if (ctx->dev_load) return dev_start2(ctx, ctx->dev_load); dp = opendir(DEVDIR); if (dp == NULL) { logger(ctx, LOG_DEBUG, "dev: %s: %m", DEVDIR); return 0; } r = 0; while ((d = readdir(dp))) { if (d->d_name[0] == '.') continue; r = dev_start2(ctx, d->d_name); if (r != -1) break; } closedir(dp); return r; } static void dev_handle_data(void *arg) { struct dhcpcd_ctx *ctx; ctx = arg; if (ctx->dev->handle_device(arg) == -1) { /* XXX: an error occured. should we restart dev? */ } } int dev_start(struct dhcpcd_ctx *ctx) { if (ctx->dev_fd != -1) { logger(ctx, LOG_ERR, "%s: already started on fd %d", __func__, ctx->dev_fd); return ctx->dev_fd; } ctx->dev_fd = dev_start1(ctx); if (ctx->dev_fd != -1) { if (eloop_event_add(ctx->eloop, ctx->dev_fd, dev_handle_data, ctx, NULL, NULL) == -1) { logger(ctx, LOG_ERR, "%s: eloop_event_add: %m", __func__); dev_stop1(ctx, 1); return -1; } } return ctx->dev_fd; }