/* * COPYRIGHT (c) 2012 Chris Johns * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. */ /** * @file * * @ingroup rtems_rap * * @brief RTEMS Application Loader * * This is the RAP implementation. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "rtl-find-file.h" /** * The global RAP data. This structure is allocated on the heap when the first * call to location an application and is never released. */ typedef struct rtems_rap_data_s { rtems_id lock; /**< The RAP lock id */ rtems_chain_control apps; /**< List if loaded application. */ int last_errno; /**< Last error number. */ char last_error[64]; /**< Last error string. */ } rtems_rap_data_t; /** * The RAP file data. This structure is allocated on the heap when a file is * loaded. */ typedef struct rtems_rap_app_s { rtems_chain_node node; /**< The node's link in the chain. */ const char* name; /**< The file name */ void* handle; /**< The dlopen handle. */ } rtems_rap_app_t; /** * Semaphore configuration to create a mutex. */ #define RTEMS_MUTEX_ATTRIBS \ (RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | \ RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL) /** * RTL entry. */ #if (RTL_GLUE(__USER_LABEL_PREFIX__, 1) == RTL_GLUE(_, 1)) #define RTL_ENTRY_POINT "_rtems" #else #define RTL_ENTRY_POINT "rtems" #endif /** * Static RAP data is returned to the user when the loader is locked. */ static rtems_rap_data_t rap_; /** * Verbose level for the RAP loader. */ static bool rap_verbose; /** * RAP entry call signature. */ typedef int (*rtems_rap_entry_t)(int argc, const char* argv[]); /** * Forward decl. */ static bool rtems_rap_unlock (void); static bool rtems_rap_data_init (void) { /* * Lock the RAP. We only create a lock if a call is made. First we test if a * lock is present. If one is present we lock it. If not the libio lock is * locked and we then test the lock again. If not present we create the lock * then release libio lock. */ if (!rap_.lock) { rtems_libio_lock (); if (!rap_.lock) { rtems_status_code sc; rtems_id lock; /* * Create the RAP lock. */ sc = rtems_semaphore_create (rtems_build_name ('R', 'A', 'P', '_'), 1, RTEMS_MUTEX_ATTRIBS, RTEMS_NO_PRIORITY, &lock); if (sc != RTEMS_SUCCESSFUL) return false; sc = rtems_semaphore_obtain (lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) { rtems_semaphore_delete (lock); return false; } rap_.lock = lock; /* * Initialise the objects list and create any required services. */ rtems_chain_initialize_empty (&rap_.apps); } rtems_libio_unlock (); rtems_rap_unlock (); } return true; } static rtems_rap_data_t* rtems_rap_lock (void) { rtems_status_code sc; if (!rtems_rap_data_init ()) return NULL; sc = rtems_semaphore_obtain (rap_.lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) { errno = EINVAL; return NULL; } return &rap_; } static bool rtems_rap_unlock (void) { /* * Not sure any error should be returned or an assert. */ rtems_status_code sc; sc = rtems_semaphore_release (rap_.lock); if ((sc != RTEMS_SUCCESSFUL) && (errno == 0)) { errno = EINVAL; return false; } return true; } static rtems_rap_app_t* rtems_rap_check_handle (void* handle) { rtems_rap_app_t* app; rtems_chain_node* node; app = handle; node = rtems_chain_first (&rap_.apps); while (!rtems_chain_is_tail (&rap_.apps, node)) { rtems_rap_app_t* check = (rtems_rap_app_t*) node; if (check == app) return app; node = rtems_chain_next (node); } return NULL; } static rtems_rap_app_t* rtems_rap_app_alloc (void) { rtems_rap_app_t* app = malloc (sizeof (rtems_rap_app_t)); memset (app, 0, sizeof (rtems_rap_app_t)); rtems_chain_append (&rap_.apps, &app->node); return app; } static void rtems_rap_app_free (rtems_rap_app_t* app) { if (app->handle) { dlclose (app->handle); app->handle = NULL; } if (!rtems_chain_is_node_off_chain (&app->node)) rtems_chain_extract (&app->node); } static bool rtems_rap_match_name (rtems_rap_app_t* app, const char* name) { const char* a; /* * Assume the app name is absolute, ie points to the file on disk. This means * there is at least one delimiter in the name. */ if (strncmp (app->name, name, strlen (name)) == 0) return true; a = app->name + strlen (app->name) - 1; while (a >= app->name) { if (rtems_filesystem_is_delimiter (*a)) { const char* n = name; ++a; while (*a && *n) { if (*a == '.') { if (*n == '\0') return true; } ++a; ++n; } return false; } --a; } return false; } static void rtems_rap_get_rtl_error (void) { rap_.last_errno = rtems_rtl_get_error (rap_.last_error, sizeof (rap_.last_error)); } static void rtems_rap_set_error (int error, const char* format, ...) { rtems_rap_data_t* rap = rtems_rap_lock (); va_list ap; va_start (ap, format); rap->last_errno = error; vsnprintf (rap->last_error, sizeof (rap->last_error), format, ap); rtems_rap_unlock (); va_end (ap); } bool rtems_rap_load (const char* name, int mode, int argc, const char* argv[]) { rtems_rap_data_t* rap = rtems_rap_lock (); if (!rap) return false; if (rap_verbose) printf ("rap: loading '%s'\n", name); /* * See if the app has already been loaded. */ if (!rtems_rap_find (name)) { rtems_rap_app_t* app; rtems_rap_entry_t init; rtems_rap_entry_t fini; size_t size = 0; int r; /* * Allocate a new application descriptor and attempt to load it. */ app = rtems_rap_app_alloc (); if (app == NULL) { rtems_rap_set_error (ENOMEM, "no memory for application"); rtems_rap_unlock (); return false; } /* * Find the file in the file system using the search path. */ if (!rtems_rtl_find_file (name, getenv ("PATH"), &app->name, &size)) { rtems_rap_set_error (ENOENT, "file not found"); rtems_rap_app_free (app); rtems_rap_unlock (); return false; } app->handle = dlopen (app->name, RTLD_NOW | mode); if (!app->handle) { rtems_rap_get_rtl_error (); rtems_rap_app_free (app); rtems_rap_unlock (); return false; } init = dlsym (app->handle, RTL_ENTRY_POINT); if (!init) { rtems_rap_get_rtl_error (); rtems_rap_app_free (app); rtems_rap_unlock (); return false; } fini = dlsym (app->handle, RTL_ENTRY_POINT); if (!fini) { rtems_rap_get_rtl_error (); rtems_rap_app_free (app); rtems_rap_unlock (); return false; } r = init (argc, argv); if (r != 0) { rtems_rap_set_error (r, "init call failure"); rtems_rap_app_free (app); rtems_rap_unlock (); return false; } } rtems_rap_unlock (); return true; } bool rtems_rap_unload (const char* name) { rtems_rap_app_t* app; rtems_rap_entry_t fini; int r; rtems_rap_lock (); app = rtems_rap_find (name); if (rap_verbose) printf ("rap: unloading '%s'\n", name); if (!app) { rtems_rap_set_error (ENOENT, "invalid handle"); rtems_rap_unlock (); return false; } fini = dlsym (app->handle, RTL_ENTRY_POINT); if (!fini) { rtems_rap_get_rtl_error (); rtems_rap_unlock (); return false; } r = fini (0, NULL); if (r != 0) { rtems_rap_set_error (r, "fini failure"); rtems_rap_unlock (); return false; } rtems_rap_app_free (app); rtems_rap_unlock (); return true; } void* rtems_rap_find (const char* name) { rtems_rap_data_t* rap = rtems_rap_lock (); rtems_chain_node* node; node = rtems_chain_first (&rap->apps); while (!rtems_chain_is_tail (&rap->apps, node)) { rtems_rap_app_t* app = (rtems_rap_app_t*) node; if (rtems_rap_match_name (app, name)) { rtems_rap_unlock (); return app; } node = rtems_chain_next (node); } rtems_rap_unlock (); return NULL; } bool rtems_rap_iterate (rtems_rap_iterator_t iterator) { rtems_rap_data_t* rap = rtems_rap_lock (); rtems_chain_node* node; bool result = true; node = rtems_chain_first (&rap->apps); while (!rtems_chain_is_tail (&rap->apps, node)) { rtems_rap_app_t* app = (rtems_rap_app_t*) node; result = iterator (app); if (!result) break; node = rtems_chain_next (node); } rtems_rap_unlock (); return result; } const char* rtems_rap_name (void* handle) { rtems_rap_app_t* app = rtems_rap_check_handle (handle); if (app) return app->name; return NULL; } void* rtems_rap_dl_handle (void* handle) { rtems_rap_app_t* app = rtems_rap_check_handle (handle); if (app) return app->handle; return NULL; } int rtems_rap_get_error (char* message, size_t max_message) { rtems_rap_data_t* rap = rtems_rap_lock (); int last_errno = rap->last_errno; strncpy (message, rap->last_error, sizeof (rap->last_error)); rtems_rap_unlock (); return last_errno; }