2 * remote.c: XML-RPC based remote extension for libvsccli
4 * Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <xmlrpc-c/client.h>
25 #include <libvscmisc/assert.h>
26 #include <libvscmisc/error.h>
27 #include <libvscmisc/memory.h>
28 #include <libvscmisc/string.h>
30 #include <libvsccli/option.h>
32 #include "../include/libvscremote/libvscremote.h"
34 static int _initialized = FALSE;
35 static VscRemoteMethodLookupFunction _method_lookup_function = NULL;
36 static VscRemoteParameterNameLookupFunction _parameter_name_lookup_function = NULL;
37 static char *_remote_url = NULL;
38 static char *_username = NULL;
39 static char *_cookie = NULL;
42 _error_from_xmlrpc (struct VscError *error, const xmlrpc_env *env,
43 const char *file, const char *function, int line)
45 vsc_error_report (error, VSC__ERROR_CODE__XMLRPC_ERROR, file, line,
46 function, "%s (%d)", env->fault_string,
50 #define _ERROR_FROM_XMLRPC(error, env) \
51 _error_from_xmlrpc (error, env, __FILE__, __FUNCTION__, __LINE__)
54 _request_password (struct VscError *error, const char *username)
56 struct termios old, new;
57 char *password = NULL;
60 VSC__ASSERT (error != NULL);
61 VSC__ASSERT (! error->occured);
63 password = vsc_alloc (error, size);
66 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
70 if (tcgetattr (fileno (stdin), &old) != 0) {
71 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
79 if (tcsetattr (fileno (stdin), TCSAFLUSH, &new) != 0) {
80 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
85 printf ("Enter password for %s: ", username);
87 if (fgets (password, size, stdin) == NULL) {
91 if (password != NULL) {
92 size = strlen (password);
95 password[size - 1] = '\0';
99 (void) tcsetattr (fileno (stdin), TCSAFLUSH, &old);
106 static const char * /* method_name */
107 _lookup_method (struct VscError *error, const char *command_name,
108 VscRemotePackFunction *pack_function,
109 VscRemoteUnpackFunction *unpack_function, void *user_data)
111 const char *method_name = NULL;
113 VSC__ASSERT (error != NULL);
114 VSC__ASSERT (! error->occured);
115 VSC__ASSERT (_method_lookup_function != NULL);
116 VSC__ASSERT (pack_function != NULL);
117 VSC__ASSERT (unpack_function != NULL);
119 method_name = _method_lookup_function (error, command_name, pack_function,
120 unpack_function, user_data);
122 if (error->occured) {
123 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
127 if (method_name == NULL) {
128 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
133 if (strcmp (command_name, "login") != 0 &&
134 strcmp (command_name, "logout") != 0) {
135 if (*pack_function == NULL) {
136 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
141 if (*unpack_function == NULL) {
142 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
151 static const char * /* parameter_name */
152 _lookup_parameter_name (struct VscError *error, const char *command_name,
153 const char *option_long_name, void *user_data)
155 const char *argument_name = NULL;
157 VSC__ASSERT (error != NULL);
158 VSC__ASSERT (! error->occured);
159 VSC__ASSERT (_parameter_name_lookup_function != NULL);
161 argument_name = _parameter_name_lookup_function
162 (error, command_name, option_long_name, user_data);
164 if (error->occured) {
165 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
169 if (argument_name == NULL) {
170 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
175 return argument_name;
179 _decompose_response (struct VscError *error, xmlrpc_value *response, int *code,
180 char **message, xmlrpc_value **value)
183 xmlrpc_value *struct_value = NULL;
184 xmlrpc_value *temp_value = NULL;
186 VSC__ASSERT (error != NULL);
187 VSC__ASSERT (! error->occured);
188 VSC__ASSERT (response != NULL);
189 VSC__ASSERT (code != NULL);
190 VSC__ASSERT (message != NULL);
192 xmlrpc_env_init (&env);
194 xmlrpc_decompose_value (&env, response, "S", &struct_value);
196 if (env.fault_occurred) {
197 _ERROR_FROM_XMLRPC (error, &env);
201 xmlrpc_struct_find_value (&env, struct_value, "code", &temp_value);
203 if (env.fault_occurred) {
204 _ERROR_FROM_XMLRPC (error, &env);
208 if (temp_value == NULL) {
209 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
210 "Expecting XML-RPC response to contain a 'code' element");
214 xmlrpc_read_int (&env, temp_value, code);
215 xmlrpc_DECREF (temp_value);
217 if (env.fault_occurred) {
218 _ERROR_FROM_XMLRPC (error, &env);
223 xmlrpc_struct_find_value (&env, struct_value, "message", &temp_value);
225 if (env.fault_occurred) {
226 _ERROR_FROM_XMLRPC (error, &env);
230 if (temp_value == NULL) {
231 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
232 "Expecting XML-RPC response to contain a 'message' "
233 "element, because 'code' is non-zero");
237 xmlrpc_read_string (&env, temp_value, (const char **) message);
238 xmlrpc_DECREF (temp_value);
240 if (env.fault_occurred) {
241 _ERROR_FROM_XMLRPC (error, &env);
247 xmlrpc_struct_find_value (&env, struct_value, "value", value);
249 if (env.fault_occurred) {
250 _ERROR_FROM_XMLRPC (error, &env);
256 if (struct_value != NULL) {
257 xmlrpc_DECREF (struct_value);
260 xmlrpc_env_clean (&env);
268 _login (struct VscError *error, const struct VscCliCommandInfo *command_info,
269 const struct VscCliOption *option_list, void *user_data);
271 static const struct VscCliOptionInfo _login_option_infos[] = {
272 { -1, "username", "Username for the remote system",
273 VSC_CLI__OPTION_TYPE__STRING, FALSE, TRUE },
274 { -1, "password", "Password for the remote system",
275 VSC_CLI__OPTION_TYPE__STRING, FALSE, FALSE },
276 VSC_CLI__NULL__OPTION_INFO,
279 const struct VscCliCommandInfo vsc_remote_login_command_info = {
281 "Login to the remote system",
288 _login (struct VscError *error,
289 const struct VscCliCommandInfo *command_info VSC__ATTR__UNUSED,
290 const struct VscCliOption *option_list, void *user_data)
292 const char *method_name;
293 VscRemotePackFunction pack_function = NULL;
294 VscRemoteUnpackFunction unpack_function = NULL;
295 const char *username_name;
296 const char *password_name;
297 const char *username;
298 const char *password;
301 xmlrpc_value *response = NULL;
303 char *message = NULL;
304 xmlrpc_value *cookie_value = NULL;
306 VSC__ASSERT (error != NULL);
307 VSC__ASSERT (! error->occured);
309 vsc_remote_check (error, FALSE);
311 if (error->occured) {
312 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
316 method_name = _lookup_method (error, command_info->name, &pack_function,
317 &unpack_function, user_data);
319 if (error->occured) {
320 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
324 username_name = _lookup_parameter_name (error, command_info->name,
325 _login_option_infos[0].long_name,
328 if (error->occured) {
329 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
333 password_name = _lookup_parameter_name (error, command_info->name,
334 _login_option_infos[1].long_name,
337 if (error->occured) {
338 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
342 VSC__ASSERT (_username == NULL);
344 username = vsc_cli_option_list_lookup_string
345 (option_list, _login_option_infos[0].long_name);
346 password = vsc_cli_option_list_lookup_string
347 (option_list, _login_option_infos[1].long_name);
349 VSC__ASSERT (username != NULL);
351 if (password == NULL) {
352 password_ = _request_password (error, username);
354 if (password_ == NULL) {
355 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
356 "Could not read passowrd");
360 password_ = vsc_strdup (error, password);
362 if (error->occured) {
363 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
368 xmlrpc_env_init (&env);
370 response = xmlrpc_client_call (&env, _remote_url, method_name,
372 username_name, username,
373 password_name, password_);
375 if (env.fault_occurred) {
376 _ERROR_FROM_XMLRPC (error, &env);
381 _decompose_response (error, response, &code, &message, &cookie_value);
383 if (error->occured) {
384 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
389 VSC__ERROR2 (error, VSC__ERROR_CODE__REMOTE_ERROR,
390 "%s (%d)", message, code);
395 xmlrpc_read_string (&env, cookie_value, (const char **) &_cookie);
396 xmlrpc_DECREF (cookie_value);
398 if (env.fault_occurred) {
399 _ERROR_FROM_XMLRPC (error, &env);
404 _username = vsc_strdup (error, username);
406 if (error->occured) {
407 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
412 if (response != NULL) {
413 xmlrpc_DECREF (response);
416 vsc_free (&password_);
419 xmlrpc_env_clean (&env);
427 _logout (struct VscError *error, const struct VscCliCommandInfo *command_info,
428 const struct VscCliOption *option_list, void *user_data);
430 static const struct VscCliOptionInfo _logout_option_infos[] = {
431 VSC_CLI__NULL__OPTION_INFO,
434 const struct VscCliCommandInfo vsc_remote_logout_command_info = {
436 "Logout from the remote system",
439 _logout_option_infos,
443 _logout (struct VscError *error,
444 const struct VscCliCommandInfo *command_info VSC__ATTR__UNUSED,
445 const struct VscCliOption *option_list VSC__ATTR__UNUSED,
446 void *user_data VSC__ATTR__UNUSED)
448 const char *method_name = NULL;
449 VscRemotePackFunction pack_function = NULL;
450 VscRemoteUnpackFunction unpack_function = NULL;
451 const char *cookie_name = NULL;
453 xmlrpc_value *response = NULL;
455 char *message = NULL;
457 VSC__ASSERT (error != NULL);
458 VSC__ASSERT (! error->occured);
460 vsc_remote_check (error, TRUE);
462 if (error->occured) {
463 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
467 method_name = _lookup_method (error, command_info->name, &pack_function,
468 &unpack_function, user_data);
470 if (error->occured) {
471 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
475 cookie_name = _lookup_parameter_name (error, command_info->name, "cookie",
478 if (error->occured) {
479 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
483 xmlrpc_env_init (&env);
485 response = xmlrpc_client_call (&env, _remote_url, method_name,
487 cookie_name, _cookie);
489 if (env.fault_occurred) {
490 _ERROR_FROM_XMLRPC (error, &env);
495 _decompose_response (error, response, &code, &message, NULL);
497 if (error->occured) {
498 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
503 VSC__ERROR2 (error, VSC__ERROR_CODE__REMOTE_ERROR,
504 "%s (%d)", message, code);
509 vsc_free (&_username);
512 if (response != NULL) {
513 xmlrpc_DECREF (response);
518 xmlrpc_env_clean (&env);
526 vsc_remote_init (struct VscError *error, const char *name, const char *version,
527 const char *remote_url,
528 VscRemoteMethodLookupFunction method_lookup_function,
529 VscRemoteParameterNameLookupFunction parameter_name_lookup_function)
532 char user_agent[256];
533 struct xmlrpc_clientparms clientParms;
534 struct xmlrpc_curl_xportparms curlParms;
536 VSC__ASSERT (error != NULL);
537 VSC__ASSERT (! error->occured);
538 VSC__ASSERT (method_lookup_function != NULL);
539 VSC__ASSERT (parameter_name_lookup_function != NULL);
542 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
543 "Already initialized");
547 _method_lookup_function = method_lookup_function;
548 _parameter_name_lookup_function = parameter_name_lookup_function;
550 if (remote_url != NULL) {
551 _remote_url = vsc_strdup (error, remote_url);
553 if (error->occured) {
554 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
562 * Configure the XML-RPC-C Curl backend to send a User-Agent.
564 xmlrpc_env_init (&env);
566 memset (&clientParms, 0, sizeof (struct xmlrpc_clientparms));
567 memset (&curlParms, 0, sizeof (struct xmlrpc_curl_xportparms));
569 snprintf (user_agent, 256, "%s/%s", name, version);
571 curlParms.user_agent = user_agent;
573 clientParms.transport = "curl";
574 clientParms.transportparmsP = &curlParms;
575 clientParms.transportparm_size = XMLRPC_CXPSIZE (user_agent);
577 xmlrpc_client_init2 (&env, XMLRPC_CLIENT_NO_FLAGS, name, version,
578 &clientParms, XMLRPC_CPSIZE (transportparm_size));
580 if (env.fault_occurred) {
581 _ERROR_FROM_XMLRPC (error, &env);
588 xmlrpc_env_clean (&env);
592 vsc_remote_cleanup (void)
594 struct VscError error;
596 VSC__ASSERT (_initialized);
598 if (! _initialized) {
603 * FIXME: If an RPC error occured this call segfaults. This only happens if
604 * the XML-RPC-C library is compiled with -O3. If compiled with -O0
605 * everything is fine.
607 xmlrpc_client_cleanup();
609 if (_cookie != NULL) {
610 vsc_error_init (&error);
612 _logout (&error, &vsc_remote_logout_command_info, NULL, NULL);
614 vsc_error_cleanup (&error);
617 vsc_free (&_remote_url);
619 vsc_free (&_username);
621 _method_lookup_function = NULL;
622 _parameter_name_lookup_function = NULL;
624 _initialized = FALSE;
628 vsc_remote_check (struct VscError *error, int expected_login_state)
630 VSC__ASSERT (error != NULL);
631 VSC__ASSERT (! error->occured);
633 if (! _initialized) {
634 VSC__ERROR1 (error, VSC__ERROR_CODE__NOT_INITIALIZED,
639 if (_remote_url == NULL) {
640 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
641 "No remote URL specified");
645 if (expected_login_state == TRUE && _cookie == NULL) {
646 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL, "Not logged in");
650 if (expected_login_state == FALSE && _cookie != NULL) {
651 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
652 "Already logged in");
658 vsc_remote_get_cookie (struct VscError *error)
660 VSC__ASSERT (error != NULL);
661 VSC__ASSERT (! error->occured);
663 if (! _initialized) {
664 VSC__ERROR1 (error, VSC__ERROR_CODE__NOT_INITIALIZED,
669 if (_cookie == NULL) {
670 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_CALL,
671 "No cookie avaiable");
679 vsc_remote_get_username (struct VscError *error)
681 VSC__ASSERT (error != NULL);
682 VSC__ASSERT (! error->occured);
684 if (! _initialized) {
685 VSC__ERROR1 (error, VSC__ERROR_CODE__NOT_INITIALIZED,
698 vsc_remote_default_pack (struct VscError *error,
699 const struct VscCliCommandInfo *command_info,
700 const struct VscCliOption *option_list,
701 const char *cookie, xmlrpc_value *struct_value,
706 const struct VscCliOption *option;
707 const struct VscCliOptionInfo *option_info;
709 xmlrpc_value *value = NULL;
711 VSC__ASSERT (error != NULL);
712 VSC__ASSERT (! error->occured);
714 name = _lookup_parameter_name (error, command_info->name, "cookie",
717 if (error->occured) {
718 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
722 xmlrpc_env_init (&env);
724 value = xmlrpc_string_new (&env, cookie);
726 if (env.fault_occurred) {
727 _ERROR_FROM_XMLRPC (error, &env);
731 xmlrpc_struct_set_value (&env, struct_value, name, value);
733 xmlrpc_DECREF (value);
735 if (env.fault_occurred) {
736 _ERROR_FROM_XMLRPC (error, &env);
740 for (i = 0; command_info->option_infos[i].long_name != NULL; i++) {
741 option_info = &command_info->option_infos[i];
742 option = vsc_cli_option_list_lookup (option_list,
743 option_info->long_name);
744 name = _lookup_parameter_name (error, command_info->name,
745 option_info->long_name, user_data);
747 if (error->occured) {
748 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
752 if (option != NULL) {
753 switch (option_info->type) {
754 case VSC_CLI__OPTION_TYPE__BOOLEAN:
755 value = xmlrpc_bool_new (&env, TRUE);
758 case VSC_CLI__OPTION_TYPE__NUMBER:
759 value = xmlrpc_int_new (&env, option->number);
762 case VSC_CLI__OPTION_TYPE__STRING:
763 value = xmlrpc_string_new (&env, option->string);
767 VSC__ERROR1 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
771 } else if (option_info->type == VSC_CLI__OPTION_TYPE__BOOLEAN &&
772 option_info->required) {
773 value = xmlrpc_bool_new (&env, FALSE);
778 if (env.fault_occurred) {
779 _ERROR_FROM_XMLRPC (error, &env);
783 xmlrpc_struct_set_value (&env, struct_value, name, value);
785 xmlrpc_DECREF (value);
787 if (env.fault_occurred) {
788 _ERROR_FROM_XMLRPC (error, &env);
794 xmlrpc_env_clean (&env);
798 vsc_remote_default_command (struct VscError *error,
799 const struct VscCliCommandInfo *command_info,
800 const struct VscCliOption *option_list,
804 const char *method_name = NULL;
805 VscRemotePackFunction pack_function = NULL;
806 VscRemoteUnpackFunction unpack_function = NULL;
807 xmlrpc_value *struct_value = NULL;
808 xmlrpc_value *value = NULL;
809 xmlrpc_value *response = NULL;
811 char *message = NULL;
813 VSC__ASSERT (error != NULL);
814 VSC__ASSERT (! error->occured);
816 vsc_remote_check (error, TRUE);
818 if (error->occured) {
819 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
823 method_name = _lookup_method (error, command_info->name, &pack_function,
824 &unpack_function, user_data);
826 if (error->occured) {
827 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
831 xmlrpc_env_init (&env);
833 struct_value = xmlrpc_struct_new (&env);
835 if (env.fault_occurred) {
836 _ERROR_FROM_XMLRPC (error, &env);
844 pack_function (error, command_info, option_list, _cookie, struct_value,
847 if (error->occured) {
848 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
855 response = xmlrpc_client_call (&env, _remote_url, method_name,
856 "(S)", struct_value);
858 xmlrpc_DECREF (struct_value);
861 if (env.fault_occurred) {
862 _ERROR_FROM_XMLRPC (error, &env);
870 _decompose_response (error, response, &code, &message, &value);
872 if (error->occured) {
873 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
880 unpack_function (error, command_info, option_list, code, message, value,
882 xmlrpc_DECREF (value);
884 if (error->occured) {
885 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
890 if (struct_value != NULL) {
891 xmlrpc_DECREF (struct_value);
894 if (response != NULL) {
895 xmlrpc_DECREF (response);
900 xmlrpc_env_clean (&env);