[remote] Add generic command and pack system to simplify user code.
authorMatthias Bolte <matthias.bolte@googlemail.com>
Sat, 18 Apr 2009 01:38:43 +0000 (03:38 +0200)
committerMatthias Bolte <matthias.bolte@googlemail.com>
Sat, 18 Apr 2009 01:38:43 +0000 (03:38 +0200)
  * XML-RPC calls can be automatically generate from CLI commands.
  * Only an unpack function must be provided to handle the response
    of an XML-RPC call.

Signed-off-by: Matthias Bolte <matthias.bolte@googlemail.com>

include/libvscremote/libvscremote.h
remote/remote.c

index 429c6ee..9b85961 100644 (file)
 #ifndef __VSC_REMOTE_H__
 #define __VSC_REMOTE_H__
 
+#include <xmlrpc-c/base.h>
+
 #include <libvsccli/types.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-typedef const char * (*VscRemoteMethodNameLookupFunction)
-   (struct VscError *error, const char *command_name);
+typedef void (*VscRemotePackFunction)
+   (struct VscError *error, const struct VscCliCommandInfo *command_info,
+    const struct VscCliOption *option_list, const char *cookie,
+    xmlrpc_value *struct_value, void *user_data);
+
+typedef void (*VscRemoteUnpackFunction)
+   (struct VscError *error, const struct VscCliCommandInfo *command_info,
+    const struct VscCliOption *option_list, int code, const char *message,
+    xmlrpc_value *value, void *user_data);
+
+typedef const char * (*VscRemoteMethodLookupFunction)
+   (struct VscError *error, const char *command_name,
+    VscRemotePackFunction *pack_function,
+    VscRemoteUnpackFunction *unpack_function, void *user_data);
 
-typedef const char * (*VscRemoteArgumentNameLookupFunction)
+typedef const char * (*VscRemoteParameterNameLookupFunction)
    (struct VscError *error, const char *command_name,
-    const char *option_long_name);
+    const char *option_long_name, void *user_data);
 
 extern const struct VscCliCommandInfo vsc_remote_login_command_info;
 extern const struct VscCliCommandInfo vsc_remote_logout_command_info;
@@ -40,8 +54,8 @@ extern const struct VscCliCommandInfo vsc_remote_logout_command_info;
 void
 vsc_remote_init (struct VscError *error, const char *name, const char *version,
                  const char *remote_url,
-                 VscRemoteMethodNameLookupFunction method_name_lookup_function,
-                 VscRemoteArgumentNameLookupFunction argument_name_lookup_function);
+                 VscRemoteMethodLookupFunction method_lookup_function,
+                 VscRemoteParameterNameLookupFunction parameter_name_lookup_function);
 
 void
 vsc_remote_cleanup (void);
@@ -55,6 +69,19 @@ vsc_remote_get_cookie (struct VscError *error);
 const char *
 vsc_remote_get_username (struct VscError *error);
 
+void
+vsc_remote_default_pack (struct VscError *error,
+                         const struct VscCliCommandInfo *command_info,
+                         const struct VscCliOption *option_list,
+                         const char *cookie, xmlrpc_value *struct_value,
+                         void *user_data);
+
+void
+vsc_remote_default_command (struct VscError *error,
+                            const struct VscCliCommandInfo *command_info,
+                            const struct VscCliOption *option_list,
+                            void *user_data);
+
 #ifdef __cplusplus
 }
 #endif
index 63be377..fa8caa3 100644 (file)
@@ -20,7 +20,6 @@
 
 #include <termios.h>
 
-#include <xmlrpc-c/base.h>
 #include <xmlrpc-c/client.h>
 
 #include <libvscmisc/assert.h>
@@ -33,8 +32,8 @@
 #include "../include/libvscremote/libvscremote.h"
 
 static int _initialized = FALSE;
-static VscRemoteMethodNameLookupFunction _method_name_lookup_function = NULL;
-static VscRemoteArgumentNameLookupFunction _argument_name_lookup_function = NULL;
+static VscRemoteMethodLookupFunction _method_lookup_function = NULL;
+static VscRemoteParameterNameLookupFunction _parameter_name_lookup_function = NULL;
 static char *_remote_url = NULL;
 static char *_username = NULL;
 static char *_cookie = NULL;
@@ -104,16 +103,21 @@ _request_password (struct VscError *error, const char *username)
        return password;
 }
 
-static const char *
-_lookup_method_name (struct VscError *error, const char *command_name)
+static const char * /* method_name */
+_lookup_method (struct VscError *error, const char *command_name,
+                VscRemotePackFunction *pack_function,
+                VscRemoteUnpackFunction *unpack_function, void *user_data)
 {
        const char *method_name = NULL;
 
        VSC__ASSERT (error != NULL);
        VSC__ASSERT (! error->occured);
-       VSC__ASSERT (_method_name_lookup_function != NULL);
+       VSC__ASSERT (_method_lookup_function != NULL);
+       VSC__ASSERT (pack_function != NULL);
+       VSC__ASSERT (unpack_function != NULL);
 
-       method_name = _method_name_lookup_function (error, command_name);
+       method_name = _method_lookup_function (error, command_name, pack_function,
+                                           unpack_function, user_data);
 
        if (error->occured) {
                VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
@@ -126,21 +130,36 @@ _lookup_method_name (struct VscError *error, const char *command_name)
                return NULL;
        }
 
+       if (strcmp (command_name, "login") != 0 &&
+           strcmp (command_name, "logout") != 0) {
+               if (*pack_function == NULL) {
+                       VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
+                                    "Internal error");
+                       return NULL;
+               }
+
+               if (*unpack_function == NULL) {
+                       VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
+                                    "Internal error");
+                       return NULL;
+               }
+       }
+
        return method_name;
 }
 
 static const char *
-_lookup_argument_name (struct VscError *error, const char *command_name,
-                       const char *option_long_name)
+_lookup_parameter_name (struct VscError *error, const char *command_name,
+                       const char *option_long_name, void *user_data)
 {
        const char *argument_name = NULL;
 
        VSC__ASSERT (error != NULL);
        VSC__ASSERT (! error->occured);
-       VSC__ASSERT (_argument_name_lookup_function != NULL);
+       VSC__ASSERT (_parameter_name_lookup_function != NULL);
 
-       argument_name = _argument_name_lookup_function (error, command_name,
-                                                       option_long_name);
+       argument_name = _parameter_name_lookup_function
+                          (error, command_name, option_long_name, user_data);
 
        if (error->occured) {
                VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
@@ -156,6 +175,91 @@ _lookup_argument_name (struct VscError *error, const char *command_name,
        return argument_name;
 }
 
+static void
+_decompose_response (struct VscError *error, xmlrpc_value *response, int *code,
+                     char **message, xmlrpc_value **value)
+{
+       xmlrpc_env env;
+       xmlrpc_value *struct_value = NULL;
+       xmlrpc_value *temp_value = NULL;
+
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (response != NULL);
+       VSC__ASSERT (code != NULL);
+       VSC__ASSERT (message != NULL);
+
+       xmlrpc_env_init (&env);
+
+       xmlrpc_decompose_value (&env, response, "S", &struct_value);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               goto cleanup;
+       }
+
+       xmlrpc_struct_find_value (&env, struct_value, "code", &temp_value);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               goto cleanup;
+       }
+
+       if (temp_value == NULL) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
+                            "Expecting XML-RPC response to contain a 'code' element");
+               goto cleanup;
+       }
+
+       xmlrpc_read_int (&env, temp_value, code);
+       xmlrpc_DECREF (temp_value);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               goto cleanup;
+       }
+
+       if (code != 0) {
+               xmlrpc_struct_find_value (&env, struct_value, "message", &temp_value);
+
+               if (env.fault_occurred) {
+                       _ERROR_FROM_XMLRPC (error, &env);
+                       goto cleanup;
+               }
+
+               if (temp_value == NULL) {
+                       VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
+                                    "Expecting XML-RPC response to contain a 'message' "
+                                    "element, because 'code' is non-zero");
+                       goto cleanup;
+               }
+
+               xmlrpc_read_string (&env, temp_value, (const char **) message);
+               xmlrpc_DECREF (temp_value);
+
+               if (env.fault_occurred) {
+                       _ERROR_FROM_XMLRPC (error, &env);
+                       goto cleanup;
+               }
+       }
+
+       if (value != NULL) {
+               xmlrpc_struct_find_value (&env, struct_value, "value", value);
+
+               if (env.fault_occurred) {
+                       _ERROR_FROM_XMLRPC (error, &env);
+                       goto cleanup;
+               }
+       }
+
+cleanup:
+       if (struct_value != NULL) {
+               xmlrpc_DECREF (struct_value);
+       }
+
+       xmlrpc_env_clean (&env);
+}
+
 /*
  * Login
  */
@@ -165,16 +269,16 @@ _login (struct VscError *error, const struct VscCliCommandInfo *command_info,
         const struct VscCliOption *option_list, void *user_data);
 
 static const struct VscCliOptionInfo _login_option_infos[] = {
-       { -1, "username", "username for the remote system",
+       { -1, "username", "Username for the remote system",
          VSC_CLI__OPTION_TYPE__STRING, FALSE, TRUE },
-       { -1, "password", "password for the remote system",
+       { -1, "password", "Password for the remote system",
          VSC_CLI__OPTION_TYPE__STRING, FALSE, FALSE },
        VSC_CLI__NULL__OPTION_INFO,
 };
 
 const struct VscCliCommandInfo vsc_remote_login_command_info = {
        "login",
-       "login to the remote system",
+       "Login to the remote system",
        "",
        _login,
        _login_option_infos,
@@ -183,19 +287,21 @@ const struct VscCliCommandInfo vsc_remote_login_command_info = {
 static void
 _login (struct VscError *error,
         const struct VscCliCommandInfo *command_info VSC__ATTR__UNUSED,
-        const struct VscCliOption *option_list,
-        void *user_data VSC__ATTR__UNUSED)
+        const struct VscCliOption *option_list, void *user_data)
 {
        const char *method_name;
+       VscRemotePackFunction pack_function = NULL;
+       VscRemoteUnpackFunction unpack_function = NULL;
        const char *username_name;
        const char *password_name;
        const char *username;
        const char *password;
        char *password_;
        xmlrpc_env env;
-       xmlrpc_value *result = NULL;
+       xmlrpc_value *response = NULL;
        int code;
        char *message = NULL;
+       xmlrpc_value *cookie_value = NULL;
 
        VSC__ASSERT (error != NULL);
        VSC__ASSERT (! error->occured);
@@ -207,21 +313,26 @@ _login (struct VscError *error,
                return;
        }
 
-       method_name = _lookup_method_name (error, "login");
+       method_name = _lookup_method (error, command_info->name, &pack_function,
+                                     &unpack_function, user_data);
 
        if (error->occured) {
                VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
                return;
        }
 
-       username_name = _lookup_argument_name (error, "login", "username");
+       username_name = _lookup_parameter_name (error, command_info->name,
+                                               _login_option_infos[0].long_name,
+                                               user_data);
 
        if (error->occured) {
                VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
                return;
        }
 
-       password_name = _lookup_argument_name (error, "login", "password");
+       password_name = _lookup_parameter_name (error, command_info->name,
+                                               _login_option_infos[1].long_name,
+                                               user_data);
 
        if (error->occured) {
                VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
@@ -230,8 +341,10 @@ _login (struct VscError *error,
 
        VSC__ASSERT (_username == NULL);
 
-       username = vsc_cli_option_get_string (option_list, "username");
-       password = vsc_cli_option_get_string (option_list, "password");
+       username = vsc_cli_option_list_lookup_string
+                     (option_list, _login_option_infos[0].long_name);
+       password = vsc_cli_option_list_lookup_string
+                     (option_list, _login_option_infos[1].long_name);
 
        VSC__ASSERT (username != NULL);
 
@@ -254,25 +367,21 @@ _login (struct VscError *error,
 
        xmlrpc_env_init (&env);
 
-       result = xmlrpc_client_call (&env, _remote_url, method_name,
-                                    "({s:s,s:s})",
-                                    username_name, username,
-                                    password_name, password_);
+       response = xmlrpc_client_call (&env, _remote_url, method_name,
+                                      "({s:s,s:s})",
+                                      username_name, username,
+                                      password_name, password_);
 
        if (env.fault_occurred) {
                _ERROR_FROM_XMLRPC (error, &env);
-               result = NULL;
+               response = NULL;
                goto cleanup;
        }
 
-       xmlrpc_decompose_value (&env, result, "{s:i,s:s,s:s,*}",
-                               "code", &code,
-                               "message", &message,
-                               "value", &_cookie);
+       _decompose_response (error, response, &code, &message, &cookie_value);
 
-       if (env.fault_occurred) {
-               _ERROR_FROM_XMLRPC (error, &env);
-               message = NULL;
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
                goto cleanup;
        }
 
@@ -283,6 +392,15 @@ _login (struct VscError *error,
                goto cleanup;
        }
 
+       xmlrpc_read_string (&env, cookie_value, (const char **) &_cookie);
+       xmlrpc_DECREF (cookie_value);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               _cookie = NULL;
+               goto cleanup;
+       }
+
        _username = vsc_strdup (error, username);
 
        if (error->occured) {
@@ -291,8 +409,8 @@ _login (struct VscError *error,
        }
 
 cleanup:
-       if (result != NULL) {
-               xmlrpc_DECREF (result);
+       if (response != NULL) {
+               xmlrpc_DECREF (response);
        }
 
        vsc_free (&password_);
@@ -315,7 +433,7 @@ static const struct VscCliOptionInfo _logout_option_infos[] = {
 
 const struct VscCliCommandInfo vsc_remote_logout_command_info = {
        "logout",
-       "logout from the remote system",
+       "Logout from the remote system",
        "",
        _logout,
        _logout_option_infos,
@@ -328,9 +446,11 @@ _logout (struct VscError *error,
          void *user_data VSC__ATTR__UNUSED)
 {
        const char *method_name = NULL;
+       VscRemotePackFunction pack_function = NULL;
+       VscRemoteUnpackFunction unpack_function = NULL;
        const char *cookie_name = NULL;
        xmlrpc_env env;
-       xmlrpc_value *result = NULL;
+       xmlrpc_value *response = NULL;
        int code;
        char *message = NULL;
 
@@ -344,14 +464,16 @@ _logout (struct VscError *error,
                return;
        }
 
-       method_name = _lookup_method_name (error, "logout");
+       method_name = _lookup_method (error, command_info->name, &pack_function,
+                                     &unpack_function, user_data);
 
        if (error->occured) {
                VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
                return;
        }
 
-       cookie_name = _lookup_argument_name (error, "logout", "cookie");
+       cookie_name = _lookup_parameter_name (error, command_info->name, "cookie",
+                                             user_data);
 
        if (error->occured) {
                VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
@@ -360,23 +482,20 @@ _logout (struct VscError *error,
 
        xmlrpc_env_init (&env);
 
-       result = xmlrpc_client_call (&env, _remote_url, method_name,
-                                    "({s:s})",
-                                    cookie_name, _cookie);
+       response = xmlrpc_client_call (&env, _remote_url, method_name,
+                                      "({s:s})",
+                                      cookie_name, _cookie);
 
        if (env.fault_occurred) {
                _ERROR_FROM_XMLRPC (error, &env);
-               result = NULL;
+               response = NULL;
                goto cleanup;
        }
 
-       xmlrpc_decompose_value (&env, result, "{s:i,s:s,*}",
-                               "code", &code,
-                               "message", &message);
+       _decompose_response (error, response, &code, &message, NULL);
 
-       if (env.fault_occurred) {
-               _ERROR_FROM_XMLRPC (error, &env);
-               message = NULL;
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
                goto cleanup;
        }
 
@@ -390,8 +509,8 @@ _logout (struct VscError *error,
        vsc_free (&_username);
 
 cleanup:
-       if (result != NULL) {
-               xmlrpc_DECREF (result);
+       if (response != NULL) {
+               xmlrpc_DECREF (response);
        }
 
        vsc_free (&message);
@@ -406,8 +525,8 @@ cleanup:
 void
 vsc_remote_init (struct VscError *error, const char *name, const char *version,
                  const char *remote_url,
-                 VscRemoteMethodNameLookupFunction method_name_lookup_function,
-                 VscRemoteArgumentNameLookupFunction argument_name_lookup_function)
+                 VscRemoteMethodLookupFunction method_lookup_function,
+                 VscRemoteParameterNameLookupFunction parameter_name_lookup_function)
 {
        xmlrpc_env env;
        char user_agent[256];
@@ -416,8 +535,8 @@ vsc_remote_init (struct VscError *error, const char *name, const char *version,
 
        VSC__ASSERT (error != NULL);
        VSC__ASSERT (! error->occured);
-       VSC__ASSERT (method_name_lookup_function != NULL);
-       VSC__ASSERT (argument_name_lookup_function != NULL);
+       VSC__ASSERT (method_lookup_function != NULL);
+       VSC__ASSERT (parameter_name_lookup_function != NULL);
 
        if (_initialized) {
                VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
@@ -425,8 +544,8 @@ vsc_remote_init (struct VscError *error, const char *name, const char *version,
                return;
        }
 
-       _method_name_lookup_function = method_name_lookup_function;
-       _argument_name_lookup_function = argument_name_lookup_function;
+       _method_lookup_function = method_lookup_function;
+       _parameter_name_lookup_function = parameter_name_lookup_function;
 
        if (remote_url != NULL) {
                _remote_url = vsc_strdup (error, remote_url);
@@ -490,7 +609,7 @@ vsc_remote_cleanup (void)
        if (_cookie != NULL) {
                vsc_error_init (&error);
 
-               _logout (&error, NULL, NULL, NULL);
+               _logout (&error, &vsc_remote_logout_command_info, NULL, NULL);
 
                vsc_error_cleanup (&error);
        }
@@ -499,8 +618,8 @@ vsc_remote_cleanup (void)
        vsc_free (&_cookie);
        vsc_free (&_username);
 
-       _method_name_lookup_function = NULL;
-       _argument_name_lookup_function = NULL;
+       _method_lookup_function = NULL;
+       _parameter_name_lookup_function = NULL;
 
        _initialized = FALSE;
 }
@@ -570,3 +689,213 @@ vsc_remote_get_username (struct VscError *error)
 
        return _username;
 }
+
+/*
+ * Command
+ */
+
+void
+vsc_remote_default_pack (struct VscError *error,
+                         const struct VscCliCommandInfo *command_info,
+                         const struct VscCliOption *option_list,
+                         const char *cookie, xmlrpc_value *struct_value,
+                         void *user_data)
+{
+       int i;
+       xmlrpc_env env;
+       const struct VscCliOption *option;
+       const struct VscCliOptionInfo *option_info;
+       const char *name;
+       xmlrpc_value *value = NULL;
+
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+
+       name = _lookup_parameter_name (error, command_info->name, "cookie",
+                                      user_data);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       xmlrpc_env_init (&env);
+
+       value = xmlrpc_string_new (&env, cookie);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               goto cleanup;
+       }
+
+       xmlrpc_struct_set_value (&env, struct_value, name, value);
+
+       xmlrpc_DECREF (value);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               goto cleanup;
+       }
+
+       for (i = 0; command_info->option_infos[i].long_name != NULL; i++) {
+               option_info = &command_info->option_infos[i];
+               option = vsc_cli_option_list_lookup (option_list,
+                                                    option_info->long_name);
+               name = _lookup_parameter_name (error, command_info->name,
+                                              option_info->long_name, user_data);
+
+               if (error->occured) {
+                       VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                       goto cleanup;
+               }
+
+               if (option != NULL) {
+                       switch (option_info->type) {
+                       case VSC_CLI__OPTION_TYPE__BOOLEAN:
+                               value = xmlrpc_bool_new (&env, TRUE);
+                               break;
+
+                       case VSC_CLI__OPTION_TYPE__NUMBER:
+                               value = xmlrpc_int_new (&env, option->number);
+                               break;
+
+                       case VSC_CLI__OPTION_TYPE__STRING:
+                               value = xmlrpc_string_new (&env, option->string);
+                               break;
+
+                       default:
+                               VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
+                                            "Internal error");
+                               goto cleanup;
+                       }
+               } else if (option_info->type == VSC_CLI__OPTION_TYPE__BOOLEAN &&
+                          option_info->required) {
+                       value = xmlrpc_bool_new (&env, FALSE);
+               } else {
+                       continue;
+               }
+
+               if (env.fault_occurred) {
+                       _ERROR_FROM_XMLRPC (error, &env);
+                       goto cleanup;
+               }
+
+               xmlrpc_struct_set_value (&env, struct_value, name, value);
+
+               xmlrpc_DECREF (value);
+
+               if (env.fault_occurred) {
+                       _ERROR_FROM_XMLRPC (error, &env);
+                       goto cleanup;
+               }
+       }
+
+cleanup:
+       xmlrpc_env_clean (&env);
+}
+
+void
+vsc_remote_default_command (struct VscError *error,
+                            const struct VscCliCommandInfo *command_info,
+                            const struct VscCliOption *option_list,
+                            void *user_data)
+{
+       xmlrpc_env env;
+       const char *method_name = NULL;
+       VscRemotePackFunction pack_function = NULL;
+       VscRemoteUnpackFunction unpack_function = NULL;
+       xmlrpc_value *struct_value = NULL;
+       xmlrpc_value *value = NULL;
+       xmlrpc_value *response = NULL;
+       int code = 0;
+       char *message = NULL;
+
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+
+       vsc_remote_check (error, TRUE);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       method_name = _lookup_method (error, command_info->name, &pack_function,
+                                     &unpack_function, user_data);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       xmlrpc_env_init (&env);
+
+       struct_value = xmlrpc_struct_new (&env);
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               struct_value = NULL;
+               goto cleanup;
+       }
+
+       /*
+        * Pack
+        */
+       pack_function (error, command_info, option_list, _cookie, struct_value,
+                      user_data);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               goto cleanup;
+       }
+
+       /*
+        * Call
+        */
+       response = xmlrpc_client_call (&env, _remote_url, method_name,
+                                      "(S)", struct_value);
+
+       xmlrpc_DECREF (struct_value);
+       struct_value = NULL;
+
+       if (env.fault_occurred) {
+               _ERROR_FROM_XMLRPC (error, &env);
+               response = NULL;
+               goto cleanup;
+       }
+
+       /*
+        * Decompose
+        */
+       _decompose_response (error, response, &code, &message, &value);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               goto cleanup;
+       }
+
+       /*
+        * Unpack
+        */
+       unpack_function (error, command_info, option_list, code, message, value,
+                        user_data);
+       xmlrpc_DECREF (value);
+
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               goto cleanup;
+       }
+
+cleanup:
+       if (struct_value != NULL) {
+               xmlrpc_DECREF (struct_value);
+       }
+
+       if (response != NULL) {
+               xmlrpc_DECREF (response);
+       }
+
+       vsc_free (&message);
+
+       xmlrpc_env_clean (&env);
+}