Skip to content

Commit 7f26a84

Browse files
refactor(driver): ondemand load drivers (estkme-group#357)
1 parent 24b4af4 commit 7f26a84

1 file changed

Lines changed: 90 additions & 40 deletions

File tree

driver/driver.c

Lines changed: 90 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
# include <sys/sysctl.h>
2727
# include <sys/syslimits.h>
2828
#elif defined(__APPLE__) && defined(__MACH__)
29+
# include <dlfcn.h>
2930
# include <mach-o/dyld.h>
3031
# include <sys/syslimits.h>
3132
#elif defined(_WIN32)
@@ -106,6 +107,16 @@ struct euicc_drivers_list {
106107

107108
static LIST_HEAD(drivers);
108109

110+
#if defined(_WIN32)
111+
static const char *dynlib_suffix = ".dll";
112+
#elif defined(__APPLE__) && defined(__MACH__)
113+
static const char *dynlib_suffix = ".dylib";
114+
#elif defined(__unix__) || defined(__unix)
115+
static const char *dynlib_suffix = ".so";
116+
#else
117+
# error "Unsupported platform."
118+
#endif
119+
109120
#if defined(__unix__) || defined(__unix)
110121
static char *get_origin() {
111122
# if defined(__FreeBSD__)
@@ -209,54 +220,61 @@ static char *get_first_runpath(const char *runpath) {
209220
return strdup(strtok(runpath1, ":"));
210221
}
211222

212-
#if defined(__unix__) || defined(__unix)
213-
static bool init_driver_list() {
214-
_cleanup_free_ char *LPAC_DRIVER_HOME = get_first_runpath(get_runpath());
223+
static char *get_driver_path() {
224+
char *LPAC_DRIVER_HOME = get_first_runpath(get_runpath());
215225
if (LPAC_DRIVER_HOME == NULL)
216-
return false;
226+
return NULL;
217227
if (!strcmp(LPAC_DRIVER_HOME, "$ORIGIN")) {
218228
free(LPAC_DRIVER_HOME);
219229
LPAC_DRIVER_HOME = get_origin();
220230
if (LPAC_DRIVER_HOME == NULL)
221-
return false;
231+
return NULL;
222232
}
223233
char *tmp = realloc(LPAC_DRIVER_HOME, strlen(LPAC_DRIVER_HOME) + 8 + 1);
224234
if (tmp == NULL)
225-
return false;
235+
return NULL;
226236
LPAC_DRIVER_HOME = tmp;
227237
strcat(LPAC_DRIVER_HOME, "/drivers");
238+
return LPAC_DRIVER_HOME;
239+
}
240+
241+
static const struct euicc_driver *find_driver_by_path(const char *restrict dir, char *restrict name) {
242+
_cleanup_free_ char *driver_path = path_concat(dir, name);
243+
if (access(driver_path, R_OK) != 0) {
244+
return NULL;
245+
}
246+
void *handle = dlopen(driver_path, RTLD_NOW);
247+
if (handle == NULL) {
248+
return NULL;
249+
}
250+
_cleanup_free_ char *ifn = remove_suffix(name, dynlib_suffix);
251+
if (ifn == NULL) {
252+
return NULL;
253+
}
254+
struct euicc_driver *driver = dlsym(handle, ifn);
255+
if (driver == NULL) {
256+
dlclose(handle);
257+
}
258+
return driver;
259+
}
228260

261+
#if defined(__unix__) || defined(__unix)
262+
static bool init_driver_list() {
263+
if (list_empty(&drivers)) {
264+
return true;
265+
}
266+
_cleanup_free_ char *LPAC_DRIVER_HOME = get_driver_path();
267+
if (LPAC_DRIVER_HOME == NULL)
268+
return false;
229269
_cleanup_dir_ DIR *driver_dir = opendir(LPAC_DRIVER_HOME);
230270
if (driver_dir == NULL) {
231271
return true;
232272
}
233-
# if defined(_WIN32)
234-
const char *suffix = ".dll";
235-
# elif defined(__APPLE__) && defined(__MACH__)
236-
const char *suffix = ".dylib";
237-
# elif defined(__unix__) || defined(__unix)
238-
const char *suffix = ".so";
239-
# else
240-
# error "Unsupported platform."
241-
# endif
242273

243274
struct dirent *entry;
244275
while ((entry = readdir(driver_dir)) != NULL) {
245-
if ((entry->d_type & (DT_LNK | DT_REG | DT_UNKNOWN)) && ends_with(entry->d_name, suffix)) {
246-
_cleanup_free_ char *fullpath = path_concat(LPAC_DRIVER_HOME, entry->d_name);
247-
void *handle = dlopen(fullpath, RTLD_NOW);
248-
if (handle == NULL) {
249-
continue;
250-
}
251-
_cleanup_free_ char *ifn = remove_suffix(entry->d_name, suffix);
252-
if (ifn == NULL) {
253-
continue;
254-
}
255-
struct euicc_driver *driver = dlsym(handle, ifn);
256-
if (driver == NULL) {
257-
dlclose(handle);
258-
continue;
259-
}
276+
if ((entry->d_type & (DT_LNK | DT_REG | DT_UNKNOWN)) && ends_with(entry->d_name, dynlib_suffix)) {
277+
const struct euicc_driver *driver = find_driver_by_path(LPAC_DRIVER_HOME, entry->d_name);
260278
struct euicc_drivers_list *tmp = (struct euicc_drivers_list *)calloc(1, sizeof(struct euicc_drivers_list));
261279
if (tmp == NULL) {
262280
return false;
@@ -278,6 +296,9 @@ static bool init_driver_list() {
278296
}
279297
#else
280298
static bool init_driver_list() {
299+
if (list_empty(&drivers)) {
300+
return true;
301+
}
281302
for (int i = 0; builtin_drivers[i] != NULL; i++) {
282303
struct euicc_drivers_list *tmp = (struct euicc_drivers_list *)calloc(1, sizeof(struct euicc_drivers_list));
283304
if (tmp == NULL) {
@@ -291,14 +312,35 @@ static bool init_driver_list() {
291312
#endif
292313

293314
static const struct euicc_driver *find_driver_by_name(const enum euicc_driver_type type, const char *name) {
294-
list_for_each_entry_scoped(it, struct euicc_drivers_list, &drivers, list) {
295-
const struct euicc_driver *driver = it->driver;
296-
if (driver->type != type)
297-
continue;
298-
if (strcasecmp(driver->name, name) == 0)
299-
return driver;
315+
char *driver_type = NULL;
316+
if (type == DRIVER_APDU) {
317+
driver_type = "apdu";
318+
} else if (type == DRIVER_HTTP) {
319+
driver_type = "http";
320+
} else {
321+
return NULL;
300322
}
301-
return NULL;
323+
324+
_cleanup_free_ char *LPAC_DRIVER_HOME = get_driver_path();
325+
if (LPAC_DRIVER_HOME == NULL)
326+
return false;
327+
if (!strcmp(LPAC_DRIVER_HOME, "$ORIGIN")) {
328+
free(LPAC_DRIVER_HOME);
329+
LPAC_DRIVER_HOME = get_origin();
330+
if (LPAC_DRIVER_HOME == NULL)
331+
return false;
332+
}
333+
char *tmp = realloc(LPAC_DRIVER_HOME, strlen(LPAC_DRIVER_HOME) + 8 + 1);
334+
if (tmp == NULL)
335+
return NULL;
336+
LPAC_DRIVER_HOME = tmp;
337+
strcat(LPAC_DRIVER_HOME, "/drivers");
338+
339+
size_t driver_name_len = 7 + strlen(driver_type) + 1 + strlen(name) + strlen(dynlib_suffix) + 1;
340+
_cleanup_free_ char *driver_name = calloc(driver_name_len, sizeof(char));
341+
snprintf(driver_name, driver_name_len, "driver_%s_%s%s", driver_type, name, dynlib_suffix);
342+
343+
return find_driver_by_path(LPAC_DRIVER_HOME, driver_name);
302344
}
303345

304346
// If backend is not specified, find the certain driver in builtin order.
@@ -309,7 +351,6 @@ static const struct euicc_driver *find_driver_fallback(const enum euicc_driver_t
309351
static const char *http_fallback_order[] = {
310352
"winhttp",
311353
"curl",
312-
"stdio",
313354
NULL
314355
};
315356
static const char *apdu_fallback_order[] = {
@@ -319,7 +360,6 @@ static const struct euicc_driver *find_driver_fallback(const enum euicc_driver_t
319360
"qmi_qrtr",
320361
"pcsc",
321362
"at",
322-
"stdio",
323363
NULL
324364
};
325365
// clang-format on
@@ -331,12 +371,23 @@ static const struct euicc_driver *find_driver_fallback(const enum euicc_driver_t
331371
} else {
332372
return NULL;
333373
}
374+
// Try to find driver in fallback list.
334375
for (int i = 0; fallback_order[i] != NULL; i++) {
335376
const struct euicc_driver *driver = find_driver_by_name(type, fallback_order[i]);
336377
if (driver != NULL) {
337378
return driver;
338379
}
339380
}
381+
// Load all driver and try to find available one.
382+
// stdio are always builtin so it should fallback to it as last.
383+
if (init_driver_list()) {
384+
list_for_each_entry_scoped(it, struct euicc_drivers_list, &drivers, list) {
385+
const struct euicc_driver *driver = it->driver;
386+
if (driver->type == type) {
387+
return driver;
388+
}
389+
}
390+
}
340391
return NULL;
341392
}
342393

@@ -378,7 +429,6 @@ int euicc_driver_list(int argc, char **argv) {
378429
}
379430

380431
int euicc_driver_init(const char *apdu_driver_name, const char *http_driver_name) {
381-
init_driver_list();
382432
_driver_apdu = find_driver(DRIVER_APDU, apdu_driver_name);
383433
if (_driver_apdu == NULL) {
384434
fprintf(stderr, "No APDU driver found\n");

0 commit comments

Comments
 (0)