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+ }
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
293311static 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
380428int 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