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
107108static 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 )
110121static 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
280298static 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
293314static 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
380431int 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