Skip to content

Commit 96cfeac

Browse files
refactor(driver): ondemand load drivers
Signed-off-by: Coelacanthus <uwu@coelacanthus.name>
1 parent 24b4af4 commit 96cfeac

1 file changed

Lines changed: 87 additions & 40 deletions

File tree

driver/driver.c

Lines changed: 87 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+
}
228240

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+
}
260+
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;
@@ -291,14 +309,35 @@ static bool init_driver_list() {
291309
#endif
292310

293311
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;
312+
char *driver_type = NULL;
313+
if (type == DRIVER_APDU) {
314+
driver_type = "apdu";
315+
} else if (type == DRIVER_HTTP) {
316+
driver_type = "http";
317+
} else {
318+
return NULL;
300319
}
301-
return NULL;
320+
321+
_cleanup_free_ char *LPAC_DRIVER_HOME = get_driver_path();
322+
if (LPAC_DRIVER_HOME == NULL)
323+
return false;
324+
if (!strcmp(LPAC_DRIVER_HOME, "$ORIGIN")) {
325+
free(LPAC_DRIVER_HOME);
326+
LPAC_DRIVER_HOME = get_origin();
327+
if (LPAC_DRIVER_HOME == NULL)
328+
return false;
329+
}
330+
char *tmp = realloc(LPAC_DRIVER_HOME, strlen(LPAC_DRIVER_HOME) + 8 + 1);
331+
if (tmp == NULL)
332+
return NULL;
333+
LPAC_DRIVER_HOME = tmp;
334+
strcat(LPAC_DRIVER_HOME, "/drivers");
335+
336+
size_t driver_name_len = 7 + strlen(driver_type) + 1 + strlen(name) + strlen(dynlib_suffix) + 1;
337+
_cleanup_free_ char *driver_name = calloc(driver_name_len, sizeof(char));
338+
snprintf(driver_name, driver_name_len, "driver_%s_%s%s", driver_type, name, dynlib_suffix);
339+
340+
return find_driver_by_path(LPAC_DRIVER_HOME, driver_name);
302341
}
303342

304343
// If backend is not specified, find the certain driver in builtin order.
@@ -309,7 +348,6 @@ static const struct euicc_driver *find_driver_fallback(const enum euicc_driver_t
309348
static const char *http_fallback_order[] = {
310349
"winhttp",
311350
"curl",
312-
"stdio",
313351
NULL
314352
};
315353
static const char *apdu_fallback_order[] = {
@@ -319,7 +357,6 @@ static const struct euicc_driver *find_driver_fallback(const enum euicc_driver_t
319357
"qmi_qrtr",
320358
"pcsc",
321359
"at",
322-
"stdio",
323360
NULL
324361
};
325362
// clang-format on
@@ -331,12 +368,23 @@ static const struct euicc_driver *find_driver_fallback(const enum euicc_driver_t
331368
} else {
332369
return NULL;
333370
}
371+
// Try to find driver in fallback list.
334372
for (int i = 0; fallback_order[i] != NULL; i++) {
335373
const struct euicc_driver *driver = find_driver_by_name(type, fallback_order[i]);
336374
if (driver != NULL) {
337375
return driver;
338376
}
339377
}
378+
// Load all driver and try to find available one.
379+
// stdio are always builtin so it should fallback to it as last.
380+
if (init_driver_list()) {
381+
list_for_each_entry_scoped(it, struct euicc_drivers_list, &drivers, list) {
382+
const struct euicc_driver *driver = it->driver;
383+
if (driver->type == type) {
384+
return driver;
385+
}
386+
}
387+
}
340388
return NULL;
341389
}
342390

@@ -378,7 +426,6 @@ int euicc_driver_list(int argc, char **argv) {
378426
}
379427

380428
int euicc_driver_init(const char *apdu_driver_name, const char *http_driver_name) {
381-
init_driver_list();
382429
_driver_apdu = find_driver(DRIVER_APDU, apdu_driver_name);
383430
if (_driver_apdu == NULL) {
384431
fprintf(stderr, "No APDU driver found\n");

0 commit comments

Comments
 (0)