1 /*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <dirent.h>
28 #include <dlfcn.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #define _INDEV
34 #include "common.h"
35 #include "dev.h"
36 #include "eloop.h"
37 #include "dhcpcd.h"
38
39 int
dev_initialized(struct dhcpcd_ctx * ctx,const char * ifname)40 dev_initialized(struct dhcpcd_ctx *ctx, const char *ifname)
41 {
42
43 if (ctx->dev == NULL)
44 return 1;
45 return ctx->dev->initialized(ifname);
46 }
47
48 int
dev_listening(struct dhcpcd_ctx * ctx)49 dev_listening(struct dhcpcd_ctx *ctx)
50 {
51
52 if (ctx->dev == NULL)
53 return 0;
54 return ctx->dev->listening();
55 }
56
57 static void
dev_stop1(struct dhcpcd_ctx * ctx,int stop)58 dev_stop1(struct dhcpcd_ctx *ctx, int stop)
59 {
60
61 if (ctx->dev) {
62 if (stop)
63 logger(ctx, LOG_DEBUG,
64 "dev: unloaded %s", ctx->dev->name);
65 eloop_event_delete(ctx->eloop, ctx->dev_fd, 0);
66 ctx->dev->stop();
67 free(ctx->dev);
68 ctx->dev = NULL;
69 ctx->dev_fd = -1;
70 }
71 if (ctx->dev_handle) {
72 dlclose(ctx->dev_handle);
73 ctx->dev_handle = NULL;
74 }
75 }
76
77 void
dev_stop(struct dhcpcd_ctx * ctx)78 dev_stop(struct dhcpcd_ctx *ctx)
79 {
80
81 dev_stop1(ctx,!(ctx->options & DHCPCD_FORKED));
82 }
83
84 static int
dev_start2(struct dhcpcd_ctx * ctx,const char * name)85 dev_start2(struct dhcpcd_ctx *ctx, const char *name)
86 {
87 char file[PATH_MAX];
88 void *h;
89 void (*fptr)(struct dev *, const struct dev_dhcpcd *);
90 int r;
91 struct dev_dhcpcd dev_dhcpcd;
92
93 snprintf(file, sizeof(file), DEVDIR "/%s", name);
94 h = dlopen(file, RTLD_LAZY);
95 if (h == NULL) {
96 logger(ctx, LOG_ERR, "dlopen: %s", dlerror());
97 return -1;
98 }
99 fptr = (void (*)(struct dev *, const struct dev_dhcpcd *))
100 dlsym(h, "dev_init");
101 if (fptr == NULL) {
102 logger(ctx, LOG_ERR, "dlsym: %s", dlerror());
103 dlclose(h);
104 return -1;
105 }
106 ctx->dev = calloc(1, sizeof(*ctx->dev));
107 dev_dhcpcd.handle_interface = &dhcpcd_handleinterface;
108 fptr(ctx->dev, &dev_dhcpcd);
109 if (ctx->dev->start == NULL || (r = ctx->dev->start()) == -1) {
110 free(ctx->dev);
111 ctx->dev = NULL;
112 dlclose(h);
113 return -1;
114 }
115 logger(ctx, LOG_INFO, "dev: loaded %s", ctx->dev->name);
116 ctx->dev_handle = h;
117 return r;
118 }
119
120 static int
dev_start1(struct dhcpcd_ctx * ctx)121 dev_start1(struct dhcpcd_ctx *ctx)
122 {
123 DIR *dp;
124 struct dirent *d;
125 int r;
126
127 if (ctx->dev) {
128 logger(ctx, LOG_ERR, "dev: already started %s", ctx->dev->name);
129 return -1;
130 }
131
132 if (ctx->dev_load)
133 return dev_start2(ctx, ctx->dev_load);
134
135 dp = opendir(DEVDIR);
136 if (dp == NULL) {
137 logger(ctx, LOG_DEBUG, "dev: %s: %m", DEVDIR);
138 return 0;
139 }
140
141 r = 0;
142 while ((d = readdir(dp))) {
143 if (d->d_name[0] == '.')
144 continue;
145
146 r = dev_start2(ctx, d->d_name);
147 if (r != -1)
148 break;
149 }
150 closedir(dp);
151 return r;
152 }
153
154 static void
dev_handle_data(void * arg)155 dev_handle_data(void *arg)
156 {
157 struct dhcpcd_ctx *ctx;
158
159 ctx = arg;
160 if (ctx->dev->handle_device(arg) == -1) {
161 /* XXX: an error occured. should we restart dev? */
162 }
163 }
164
165 int
dev_start(struct dhcpcd_ctx * ctx)166 dev_start(struct dhcpcd_ctx *ctx)
167 {
168
169 if (ctx->dev_fd != -1) {
170 logger(ctx, LOG_ERR, "%s: already started on fd %d", __func__,
171 ctx->dev_fd);
172 return ctx->dev_fd;
173 }
174
175 ctx->dev_fd = dev_start1(ctx);
176 if (ctx->dev_fd != -1) {
177 if (eloop_event_add(ctx->eloop,
178 ctx->dev_fd, dev_handle_data, ctx, NULL, NULL) == -1)
179 {
180 logger(ctx, LOG_ERR,
181 "%s: eloop_event_add: %m", __func__);
182 dev_stop1(ctx, 1);
183 return -1;
184 }
185 }
186
187 return ctx->dev_fd;
188 }
189