nspluginwrapper/tests/test-rpc-common.c

330 lines
7.8 KiB
C

/*
* test-rpc-common.c - Common RPC test code
*
* nspluginwrapper (C) 2005-2009 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "sysdeps.h"
#include "test-rpc-common.h"
#include <signal.h>
#define DEBUG 1
#include "debug.h"
#ifdef BUILD_WRAPPER
#define BUILD_CLIENT 1
#endif
#ifdef BUILD_VIEWER
#define BUILD_SERVER 1
#endif
typedef struct _RPCTest RPCTest;
struct _RPCTest
{
RPCTestFuncs funcs;
gpointer user_data;
};
static RPCTest g_test;
static rpc_connection_t *g_connection;
static const gchar *g_connection_path;
static GMainLoop *g_main_loop;
#ifdef BUILD_CLIENT
static GPid g_child_pid;
static guint g_child_watch_id;
#endif
static gint g_exit_status;
void
rpc_test_set_funcs (const RPCTestFuncs *funcs, gpointer user_data)
{
g_test.user_data = user_data;
if (funcs && funcs->pre_dispatch_hook)
g_test.funcs.pre_dispatch_hook = funcs->pre_dispatch_hook;
if (funcs && funcs->post_dispatch_hook)
g_test.funcs.post_dispatch_hook = funcs->post_dispatch_hook;
}
rpc_connection_t *
rpc_test_get_connection (void)
{
return g_connection;
}
#ifdef BUILD_CLIENT
static void
child_exited_cb (GPid pid, gint status, gpointer user_data)
{
g_print ("child_exited_cb(), pid %d, status %d\n", pid, status);
if (status)
g_exit_status = status;
g_main_loop_quit (g_main_loop);
if (g_child_watch_id)
g_source_remove (g_child_watch_id);
g_spawn_close_pid (pid);
}
static void
kill_child_now (void)
{
if (g_child_watch_id)
g_source_remove (g_child_watch_id);
if (g_child_pid)
{
g_spawn_close_pid (g_child_pid);
kill (g_child_pid, SIGTERM);
}
}
static void
urgent_exit_sig (int sig)
{
kill_child_now ();
}
#endif
static gboolean
rpc_event_prepare (GSource *source, gint *timeout)
{
*timeout = -1;
return FALSE;
}
static gboolean
rpc_event_check (GSource *source)
{
return rpc_wait_dispatch (g_connection, 0) > 0;
}
static gboolean
rpc_event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
{
gint rc;
if (g_test.funcs.pre_dispatch_hook)
{
if (!g_test.funcs.pre_dispatch_hook (g_test.user_data))
return TRUE;
}
rc = rpc_dispatch (g_connection);
if (g_test.funcs.post_dispatch_hook)
g_test.funcs.post_dispatch_hook (g_test.user_data);
return rc != RPC_ERROR_CONNECTION_CLOSED;
}
typedef gboolean (*GSourcePrepare) (GSource *, gint *);
typedef gboolean (*GSourceCheckFunc) (GSource *);
typedef gboolean (*GSourceDispatchFunc) (GSource *, GSourceFunc, gpointer);
typedef void (*GSourceFinalizeFunc) (GSource *);
static GSourceFuncs rpc_event_funcs =
{
rpc_event_prepare,
rpc_event_check,
rpc_event_dispatch,
(GSourceFinalizeFunc)g_free,
(GSourceFunc)NULL,
(GSourceDummyMarshal)NULL
};
static gboolean
rpc_test_execute_cb (gpointer user_data)
{
int rc = RPC_TEST_EXECUTE_SUCCESS;
if (rpc_test_execute)
rc = rpc_test_execute (g_test.user_data);
RPC_TEST_ENSURE ((rc & ~RPC_TEST_EXECUTE_DONT_QUIT) == RPC_TEST_EXECUTE_SUCCESS);
#ifdef BUILD_CLIENT
if ((rc & RPC_TEST_EXECUTE_DONT_QUIT) == 0)
rpc_test_exit_full (0);
#endif
return FALSE;
}
static gchar **
clone_args (gchar **args)
{
gchar **new_args;
#ifdef BUILD_CLIENT
gint i, n = 0;
#endif
if ((new_args = g_strdupv (args)) == NULL)
g_error ("could not duplicate the program arguments");
#ifdef BUILD_CLIENT
n = g_strv_length (new_args);
/* first arg was path to server program, skip it */
RPC_TEST_ENSURE (n >= 2);
g_free (new_args[1]);
for (i = 1; i < n - 1; i++)
new_args[i] = new_args[i + 1];
new_args[i] = NULL;
#endif
return new_args;
}
static void
rpc_test_init_invoke (gchar **program_args)
{
gchar **args = NULL;
gint n, rc = 0;
if ((args = clone_args (program_args)) == NULL)
g_error ("could not clone program arguments");
n = g_strv_length (args);
if (rpc_test_init)
rc = rpc_test_init (n, args);
g_strfreev (args);
RPC_TEST_ENSURE (rc == 0);
g_timeout_add (0, rpc_test_execute_cb, NULL);
}
void
rpc_test_exit (int status)
{
if (status)
g_exit_status = status;
g_main_loop_quit (g_main_loop);
}
void
rpc_test_exit_full (int status)
{
#ifdef BUILD_CLIENT
/* Tell server to quit, don't expect any reply from him */
rpc_method_invoke (g_connection,
RPC_TEST_METHOD_EXIT,
RPC_TYPE_INT32, status,
RPC_TYPE_INVALID);
#endif
rpc_test_exit (status);
}
static int
handle_rpc_test_exit (rpc_connection_t *connection)
{
int error;
gint32 status;
error = rpc_method_get_args (connection,
RPC_TYPE_INT32, &status,
RPC_TYPE_INVALID);
if (error == RPC_ERROR_NO_ERROR)
rpc_test_exit (status);
return error;
}
int
main (int argc, char *argv[])
{
if (rpc_test_get_connection_path)
g_connection_path = rpc_test_get_connection_path ();
else
g_connection_path = NPW_CONNECTION_PATH "/Test.RPC";
#ifdef BUILD_CLIENT
gchar **child_args;
if (argc < 2)
g_error ("no server program provided on command line");
signal (SIGSEGV, urgent_exit_sig);
signal (SIGBUS, urgent_exit_sig);
signal (SIGINT, urgent_exit_sig);
signal (SIGABRT, urgent_exit_sig);
if ((child_args = clone_args (argv)) == NULL)
g_error ("could not create server program arguments\n");
g_free (child_args[0]);
child_args[0] = g_strdup (argv[1]);
if (!g_spawn_async (NULL,
child_args,
NULL,
G_SPAWN_DO_NOT_REAP_CHILD,
NULL,
NULL,
&g_child_pid,
NULL))
g_error ("could not start server program '%s'", child_args[0]);
g_strfreev (child_args);
if ((g_connection = rpc_init_client (g_connection_path)) == NULL)
g_error ("failed to initialize RPC client connection");
#endif
#ifdef BUILD_SERVER
if ((g_connection = rpc_init_server (g_connection_path)) == NULL)
g_error ("failed to initialize RPC server connection");
#endif
int fd = -1;
GSource *rpc_source = NULL;
guint rpc_source_id = 0;
GPollFD rpc_event_poll_fd;
#ifdef BUILD_CLIENT
fd = rpc_socket (g_connection);
#endif
#ifdef BUILD_SERVER
fd = rpc_listen_socket (g_connection);
#endif
RPC_TEST_ENSURE (fd >= 0);
if ((rpc_source = g_source_new (&rpc_event_funcs, sizeof (GSource))) == NULL)
g_error ("failed to initialize RPC source");
rpc_source_id = g_source_attach (rpc_source, NULL);
memset (&rpc_event_poll_fd, 0, sizeof (rpc_event_poll_fd));
rpc_event_poll_fd.fd = fd;
rpc_event_poll_fd.events = G_IO_IN;
rpc_event_poll_fd.revents = 0;
g_source_add_poll (rpc_source, &rpc_event_poll_fd);
static const rpc_method_descriptor_t vtable[] = {
{ RPC_TEST_METHOD_EXIT, handle_rpc_test_exit }
};
if (rpc_connection_add_method_descriptor (g_connection, &vtable[0]) < 0)
g_error ("could not add method descriptor for TEST_RPC_METHOD_EXIT");
g_main_loop = g_main_loop_new (NULL, TRUE);
#ifdef BUILD_CLIENT
g_child_watch_id = g_child_watch_add (g_child_pid, child_exited_cb, NULL);
#endif
rpc_test_init_invoke (argv);
g_main_loop_run (g_main_loop);
if (rpc_source_id)
g_source_remove (rpc_source_id);
if (g_connection)
rpc_exit (g_connection);
return g_exit_status;
}