core,error-helpers: new method to normalize GErrors
The ModemManager API should not expose or return error types out of the error domains it supports. Default to a generic core error "Failed", while keeping the original error description.
This commit is contained in:
+174
-21
@@ -15,11 +15,22 @@
|
||||
* Copyright (C) 2011 - 2012 Google, Inc.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "mm-error-helpers.h"
|
||||
#include "mm-errors-types.h"
|
||||
#include "mm-log.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#if defined WITH_QMI
|
||||
# include <libqmi-glib.h>
|
||||
#endif
|
||||
|
||||
#if defined WITH_MBIM
|
||||
# include <libmbim-glib.h>
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static gchar *
|
||||
@@ -40,6 +51,38 @@ normalize_error_string (const gchar *str)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Core errors */
|
||||
|
||||
/* Human friendly messages for each error type */
|
||||
static const gchar *core_error_messages[] = {
|
||||
[MM_CORE_ERROR_FAILED] = "Failed",
|
||||
[MM_CORE_ERROR_CANCELLED] = "Cancelled",
|
||||
[MM_CORE_ERROR_ABORTED] = "Aborted",
|
||||
[MM_CORE_ERROR_UNSUPPORTED] = "Unsupported",
|
||||
[MM_CORE_ERROR_NO_PLUGINS] = "No plugins",
|
||||
[MM_CORE_ERROR_UNAUTHORIZED] = "Unauthorized",
|
||||
[MM_CORE_ERROR_INVALID_ARGS] = "Invalid arguments",
|
||||
[MM_CORE_ERROR_IN_PROGRESS] = "In progress",
|
||||
[MM_CORE_ERROR_WRONG_STATE] = "Wrong state",
|
||||
[MM_CORE_ERROR_CONNECTED] = "Connected",
|
||||
[MM_CORE_ERROR_TOO_MANY] = "Too many",
|
||||
[MM_CORE_ERROR_NOT_FOUND] = "Not found",
|
||||
[MM_CORE_ERROR_RETRY] = "Retry",
|
||||
[MM_CORE_ERROR_EXISTS] = "Exists",
|
||||
[MM_CORE_ERROR_WRONG_SIM_STATE] = "Wrong SIM state",
|
||||
[MM_CORE_ERROR_RESET_AND_RETRY] = "Reset and retry",
|
||||
[MM_CORE_ERROR_TIMEOUT] = "Timed out",
|
||||
[MM_CORE_ERROR_PROTOCOL] = "Protocol failure",
|
||||
[MM_CORE_ERROR_THROTTLED] = "Throttled",
|
||||
};
|
||||
|
||||
static const gchar *
|
||||
core_error_get_string (MMCoreError code)
|
||||
{
|
||||
return (code < G_N_ELEMENTS (core_error_messages)) ? core_error_messages[code] : NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Connection errors */
|
||||
|
||||
@@ -52,17 +95,21 @@ static const gchar *connection_error_messages[] = {
|
||||
[MM_CONNECTION_ERROR_NO_ANSWER] = "No answer",
|
||||
};
|
||||
|
||||
static const gchar *
|
||||
connection_error_get_string (MMConnectionError code)
|
||||
{
|
||||
return (code < G_N_ELEMENTS (connection_error_messages)) ? connection_error_messages[code] : NULL;
|
||||
}
|
||||
|
||||
GError *
|
||||
mm_connection_error_for_code (MMConnectionError code,
|
||||
gpointer log_object)
|
||||
{
|
||||
if (code < G_N_ELEMENTS (connection_error_messages)) {
|
||||
const gchar *error_message;
|
||||
const gchar *description;
|
||||
|
||||
error_message = connection_error_messages[code];
|
||||
if (error_message)
|
||||
return g_error_new_literal (MM_CONNECTION_ERROR, code, error_message);
|
||||
}
|
||||
description = connection_error_get_string (code);
|
||||
if (description)
|
||||
return g_error_new_literal (MM_CONNECTION_ERROR, code, description);
|
||||
|
||||
/* Not found? Then, default to 'no carrier' */
|
||||
mm_obj_dbg (log_object, "unknown connection error: %u", code);
|
||||
@@ -244,17 +291,21 @@ static const gchar *me_error_messages[] = {
|
||||
/* All generic ME errors should be < 255, as those are the only reserved ones in the 3GPP spec */
|
||||
G_STATIC_ASSERT (G_N_ELEMENTS (me_error_messages) <= 256);
|
||||
|
||||
static const gchar *
|
||||
me_error_get_string (MMMobileEquipmentError code)
|
||||
{
|
||||
return (code < G_N_ELEMENTS (me_error_messages)) ? me_error_messages[code] : NULL;
|
||||
}
|
||||
|
||||
GError *
|
||||
mm_mobile_equipment_error_for_code (MMMobileEquipmentError code,
|
||||
gpointer log_object)
|
||||
{
|
||||
if (code < G_N_ELEMENTS (me_error_messages)) {
|
||||
const gchar *error_message;
|
||||
const gchar *description;
|
||||
|
||||
error_message = me_error_messages[code];
|
||||
if (error_message)
|
||||
return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, code, error_message);
|
||||
}
|
||||
description = me_error_get_string (code);
|
||||
if (description)
|
||||
return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, code, description);
|
||||
|
||||
/* Not found? Then, default */
|
||||
mm_obj_dbg (log_object, "unknown mobile equipment error: %u", code);
|
||||
@@ -263,6 +314,27 @@ mm_mobile_equipment_error_for_code (MMMobileEquipmentError code,
|
||||
"Unknown mobile equipment error: %u", code);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Serial errors */
|
||||
|
||||
static const gchar *serial_error_messages[] = {
|
||||
[MM_SERIAL_ERROR_UNKNOWN] = "Unknown",
|
||||
[MM_SERIAL_ERROR_OPEN_FAILED] = "Open failed",
|
||||
[MM_SERIAL_ERROR_SEND_FAILED] = "Send failed",
|
||||
[MM_SERIAL_ERROR_RESPONSE_TIMEOUT] = "Response timeout",
|
||||
[MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE] = "Open failed, no device",
|
||||
[MM_SERIAL_ERROR_FLASH_FAILED] = "Flash failed",
|
||||
[MM_SERIAL_ERROR_NOT_OPEN] = "Not open",
|
||||
[MM_SERIAL_ERROR_PARSE_FAILED] = "Parse failed",
|
||||
[MM_SERIAL_ERROR_FRAME_NOT_FOUND] = "Frame not found",
|
||||
};
|
||||
|
||||
static const gchar *
|
||||
serial_error_get_string (MMSerialError code)
|
||||
{
|
||||
return (code < G_N_ELEMENTS (serial_error_messages)) ? serial_error_messages[code] : NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Message errors
|
||||
*
|
||||
@@ -302,23 +374,26 @@ static const gchar *msg_error_messages[] = {
|
||||
/* All generic message errors should be <= 500 (500-common=200), as those are the only reserved ones in the 3GPP spec */
|
||||
G_STATIC_ASSERT (G_N_ELEMENTS (msg_error_messages) <= 201);
|
||||
|
||||
static const gchar *
|
||||
message_error_get_string (MMMessageError code)
|
||||
{
|
||||
return ((code >= MM_MESSAGE_ERROR_COMMON_OFFSET) && ((code - MM_MESSAGE_ERROR_COMMON_OFFSET) < G_N_ELEMENTS (msg_error_messages))) ?
|
||||
msg_error_messages[code - MM_MESSAGE_ERROR_COMMON_OFFSET] : NULL;
|
||||
}
|
||||
|
||||
GError *
|
||||
mm_message_error_for_code (MMMessageError code,
|
||||
gpointer log_object)
|
||||
{
|
||||
if ((code >= MM_MESSAGE_ERROR_COMMON_OFFSET) && ((code - MM_MESSAGE_ERROR_COMMON_OFFSET) < G_N_ELEMENTS (msg_error_messages))) {
|
||||
const gchar *error_message;
|
||||
const gchar *description;
|
||||
|
||||
error_message = msg_error_messages[code - MM_MESSAGE_ERROR_COMMON_OFFSET];
|
||||
if (error_message)
|
||||
return g_error_new_literal (MM_MESSAGE_ERROR, code, error_message);
|
||||
}
|
||||
description = message_error_get_string (code);
|
||||
if (description)
|
||||
return g_error_new_literal (MM_MESSAGE_ERROR, code, description);
|
||||
|
||||
/* Not found? Then, default */
|
||||
mm_obj_dbg (log_object, "unknown message error: %u", code);
|
||||
return g_error_new (MM_MESSAGE_ERROR,
|
||||
MM_MESSAGE_ERROR_UNKNOWN,
|
||||
"Unknown message error: %u", code);
|
||||
return g_error_new (MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN, "Unknown message error: %u", code);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -420,3 +495,81 @@ mm_message_error_for_string (const gchar *str,
|
||||
MM_MESSAGE_ERROR_UNKNOWN,
|
||||
"Unknown message error string: %s", str);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* CDMA activation errors */
|
||||
|
||||
static const gchar *cdma_activation_error_messages[] = {
|
||||
[MM_CDMA_ACTIVATION_ERROR_NONE] = "None",
|
||||
[MM_CDMA_ACTIVATION_ERROR_UNKNOWN] = "Unknown",
|
||||
[MM_CDMA_ACTIVATION_ERROR_ROAMING] = "Roaming",
|
||||
[MM_CDMA_ACTIVATION_ERROR_WRONG_RADIO_INTERFACE] = "Wrong radio interface",
|
||||
[MM_CDMA_ACTIVATION_ERROR_COULD_NOT_CONNECT] = "Could not connect",
|
||||
[MM_CDMA_ACTIVATION_ERROR_SECURITY_AUTHENTICATION_FAILED] = "Security authentication failed",
|
||||
[MM_CDMA_ACTIVATION_ERROR_PROVISIONING_FAILED] = "Provisioning failed",
|
||||
[MM_CDMA_ACTIVATION_ERROR_NO_SIGNAL] = "No signal",
|
||||
[MM_CDMA_ACTIVATION_ERROR_TIMED_OUT] = "Timed out",
|
||||
[MM_CDMA_ACTIVATION_ERROR_START_FAILED] = "Start failed",
|
||||
};
|
||||
|
||||
static const gchar *
|
||||
cdma_activation_error_get_string (MMCdmaActivationError code)
|
||||
{
|
||||
return (code < G_N_ELEMENTS (cdma_activation_error_messages)) ? cdma_activation_error_messages[code] : NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Takes a GError of any kind and ensures that the returned GError is a
|
||||
* MM-defined one. */
|
||||
|
||||
static GError *
|
||||
normalize_mm_error (const GError *error,
|
||||
const gchar *error_description,
|
||||
const gchar *error_type)
|
||||
{
|
||||
if (error_description) {
|
||||
GError *copy;
|
||||
|
||||
copy = g_error_copy (error);
|
||||
/* Add error type name only if it isn't already the same string */
|
||||
if (g_strcmp0 (copy->message, error_description) != 0)
|
||||
g_prefix_error (©, "%s: ", error_description);
|
||||
return copy;
|
||||
}
|
||||
|
||||
return g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"Unhandled %s error (%u): %s",
|
||||
error_type, error->code, error->message);
|
||||
}
|
||||
|
||||
GError *
|
||||
mm_normalize_error (const GError *error)
|
||||
{
|
||||
g_assert (error);
|
||||
|
||||
if (error->domain == G_IO_ERROR) {
|
||||
/* G_IO_ERROR_CANCELLED is an exception, because we map it to
|
||||
* MM_CORE_ERROR_CANCELLED implicitly when building the DBus error name. */
|
||||
if (error->code == G_IO_ERROR_CANCELLED)
|
||||
return g_error_copy (error);
|
||||
|
||||
return g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unhandled GIO error (%u): %s", error->code, error->message);
|
||||
}
|
||||
|
||||
/* Ensure all errors reported in the MM domains are known errors */
|
||||
if (error->domain == MM_CORE_ERROR)
|
||||
return normalize_mm_error (error, core_error_get_string (error->code), "core");
|
||||
if (error->domain == MM_MOBILE_EQUIPMENT_ERROR)
|
||||
return normalize_mm_error (error, me_error_get_string (error->code), "mobile equipment");
|
||||
if (error->domain == MM_CONNECTION_ERROR)
|
||||
return normalize_mm_error (error, connection_error_get_string (error->code), "connection");
|
||||
if (error->domain == MM_SERIAL_ERROR)
|
||||
return normalize_mm_error (error, serial_error_get_string (error->code), "serial");
|
||||
if (error->domain == MM_MESSAGE_ERROR)
|
||||
return normalize_mm_error (error, message_error_get_string (error->code), "message");
|
||||
if (error->domain == MM_CDMA_ACTIVATION_ERROR)
|
||||
return normalize_mm_error (error, cdma_activation_error_get_string (error->code), "CDMA activation");
|
||||
|
||||
/* Generic fallback */
|
||||
return g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "%s", error->message);
|
||||
}
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
#include <ModemManager.h>
|
||||
#include <libmm-glib.h>
|
||||
|
||||
GError *mm_connection_error_for_code (MMConnectionError code, gpointer log_object);
|
||||
GError *mm_mobile_equipment_error_for_code (MMMobileEquipmentError code, gpointer log_object);
|
||||
GError *mm_mobile_equipment_error_for_string (const gchar *str, gpointer log_object);
|
||||
GError *mm_message_error_for_code (MMMessageError code, gpointer log_object);
|
||||
GError *mm_message_error_for_string (const gchar *str, gpointer log_object);
|
||||
GError *mm_connection_error_for_code (MMConnectionError code, gpointer log_object);
|
||||
GError *mm_mobile_equipment_error_for_code (MMMobileEquipmentError code, gpointer log_object);
|
||||
GError *mm_mobile_equipment_error_for_string (const gchar *str, gpointer log_object);
|
||||
GError *mm_message_error_for_code (MMMessageError code, gpointer log_object);
|
||||
GError *mm_message_error_for_string (const gchar *str, gpointer log_object);
|
||||
GError *mm_normalize_error (const GError *error);
|
||||
|
||||
#endif /* MM_ERROR_HELPERS_H */
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
* Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <string.h>
|
||||
@@ -24,6 +26,14 @@
|
||||
#include "mm-error-helpers.h"
|
||||
#include "mm-log.h"
|
||||
|
||||
#if defined WITH_QMI
|
||||
# include <libqmi-glib.h>
|
||||
#endif
|
||||
|
||||
#if defined WITH_MBIM
|
||||
# include <libmbim-glib.h>
|
||||
#endif
|
||||
|
||||
#define TEST_ERROR_HELPER(ERROR_CAPS,ERROR_SMALL,ERROR_CAMEL) \
|
||||
static void \
|
||||
test_error_helpers_## ERROR_SMALL (void) \
|
||||
@@ -131,6 +141,78 @@ test_error_helpers_message_error_for_string (void)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
normalize_and_assert_mm_error (gboolean expect_fallback,
|
||||
GQuark domain,
|
||||
int code,
|
||||
const gchar *description)
|
||||
{
|
||||
g_autoptr(GError) input_error = NULL;
|
||||
g_autoptr(GError) normalized_error = NULL;
|
||||
|
||||
g_debug ("Checking normalized error: %s", description);
|
||||
|
||||
input_error = g_error_new_literal (domain, code, description);
|
||||
normalized_error = mm_normalize_error (input_error);
|
||||
|
||||
g_assert (normalized_error);
|
||||
g_assert ((normalized_error->domain == MM_CORE_ERROR) ||
|
||||
(normalized_error->domain == MM_MOBILE_EQUIPMENT_ERROR) ||
|
||||
(normalized_error->domain == MM_CONNECTION_ERROR) ||
|
||||
(normalized_error->domain == MM_SERIAL_ERROR) ||
|
||||
(normalized_error->domain == MM_MESSAGE_ERROR) ||
|
||||
(normalized_error->domain == MM_CDMA_ACTIVATION_ERROR) ||
|
||||
g_error_matches (normalized_error, G_IO_ERROR, G_IO_ERROR_CANCELLED));
|
||||
|
||||
g_assert (expect_fallback == g_error_matches (normalized_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED));
|
||||
}
|
||||
|
||||
static void
|
||||
test_error_helpers_normalize (void)
|
||||
{
|
||||
normalize_and_assert_mm_error (TRUE, G_IO_ERROR, G_IO_ERROR_FAILED, "GIO error");
|
||||
normalize_and_assert_mm_error (FALSE, G_IO_ERROR, G_IO_ERROR_CANCELLED, "GIO error cancelled");
|
||||
|
||||
normalize_and_assert_mm_error (TRUE, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Known core error"); /* This is exactly the fallback */
|
||||
normalize_and_assert_mm_error (TRUE, MM_CORE_ERROR, 123456, "Unknown core error");
|
||||
|
||||
normalize_and_assert_mm_error (FALSE, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK, "Known ME error");
|
||||
normalize_and_assert_mm_error (TRUE, MM_MOBILE_EQUIPMENT_ERROR, 123456789, "Unknown ME error");
|
||||
|
||||
normalize_and_assert_mm_error (FALSE, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER, "Known connection error");
|
||||
normalize_and_assert_mm_error (TRUE, MM_CONNECTION_ERROR, 123456, "Unknown connection error");
|
||||
|
||||
normalize_and_assert_mm_error (FALSE, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED, "Known serial error");
|
||||
normalize_and_assert_mm_error (TRUE, MM_SERIAL_ERROR, 123456, "Unknown serial error");
|
||||
|
||||
normalize_and_assert_mm_error (FALSE, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_ME_FAILURE, "Known message error");
|
||||
normalize_and_assert_mm_error (TRUE, MM_MESSAGE_ERROR, 123456, "Unknown message error");
|
||||
|
||||
normalize_and_assert_mm_error (FALSE, MM_CDMA_ACTIVATION_ERROR, MM_CDMA_ACTIVATION_ERROR_ROAMING, "Known CDMA activation error");
|
||||
normalize_and_assert_mm_error (TRUE, MM_CDMA_ACTIVATION_ERROR, 123456, "Unknown CDMA activation error");
|
||||
|
||||
#if defined WITH_QMI
|
||||
normalize_and_assert_mm_error (TRUE, QMI_CORE_ERROR, QMI_CORE_ERROR_TIMEOUT, "Known QMI core error");
|
||||
normalize_and_assert_mm_error (TRUE, QMI_CORE_ERROR, 123456, "Unknown QMI core error");
|
||||
|
||||
normalize_and_assert_mm_error (TRUE, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_ABORTED, "Known QMI protocol error");
|
||||
normalize_and_assert_mm_error (TRUE, QMI_PROTOCOL_ERROR, 123456, "Unknown QMI protocol error");
|
||||
#endif
|
||||
|
||||
#if defined WITH_MBIM
|
||||
normalize_and_assert_mm_error (TRUE, MBIM_CORE_ERROR, MBIM_CORE_ERROR_ABORTED, "Known MBIM core error");
|
||||
normalize_and_assert_mm_error (TRUE, MBIM_CORE_ERROR, 123456, "Unknown MBIM core error");
|
||||
|
||||
normalize_and_assert_mm_error (TRUE, MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_CANCEL, "Known MBIM protocol error");
|
||||
normalize_and_assert_mm_error (TRUE, MBIM_PROTOCOL_ERROR, 123456, "Unknown MBIM protocol error");
|
||||
|
||||
normalize_and_assert_mm_error (TRUE, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_BUSY, "Known MBIM status error");
|
||||
normalize_and_assert_mm_error (TRUE, MBIM_STATUS_ERROR, 123456, "Unknown MBIM status error");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
setlocale (LC_ALL, "");
|
||||
@@ -147,5 +229,7 @@ int main (int argc, char **argv)
|
||||
g_test_add_func ("/MM/error-helpers/mobile-equipment-error/for-string", test_error_helpers_mobile_equipment_error_for_string);
|
||||
g_test_add_func ("/MM/error-helpers/message-error/for-string", test_error_helpers_message_error_for_string);
|
||||
|
||||
g_test_add_func ("/MM/error-helpers/normalize", test_error_helpers_normalize);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user