Skip to content

Instantly share code, notes, and snippets.

@esmil
Created May 29, 2012 14:36
Show Gist options
  • Save esmil/2828779 to your computer and use it in GitHub Desktop.
Save esmil/2828779 to your computer and use it in GitHub Desktop.
Experiments with GIO and DBus which ended in forth-like interpreter..
#include <stdlib.h>
#include <string.h>
#include <gio/gio.h>
static GMainLoop *mainloop;
static GDBusConnection *bus;
static int exitcode = EXIT_FAILURE;
#define AGENT_PATH "/agent"
#define STACK_SIZE 20
static void
request_input(GVariant *par, GDBusMethodInvocation *inv)
{
GVariantIter iter;
GVariant *obj = g_variant_get_child_value(par, 0);
GVariant *fields = g_variant_get_child_value(par, 1);
gchar *key;
GVariant *dict;
g_variant_unref(par);
g_print("%s:\n", g_variant_get_string(obj, NULL));
g_variant_unref(obj);
g_variant_iter_init(&iter, fields);
while (g_variant_iter_next(&iter, "{sv}", &key, &dict)) {
g_print("%s\n", key);
g_free(key);
g_variant_unref(dict);
}
g_variant_unref(fields);
}
static void
agent_method_call(GDBusConnection *bus,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *par,
GDBusMethodInvocation *inv,
gpointer data)
{
(void)bus;
(void)sender;
(void)object_path;
(void)interface_name;
(void)data;
g_print("agent_method_call\n");
if (g_strcmp0(method_name, "RequestInput") == 0)
return request_input(par, inv);
}
#if 0
static GVariant *
agent_get_property(GDBusConnection *bus,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
g_print("agent_get_property\n");
return NULL;
}
static gboolean
agent_set_property(GDBusConnection *bus,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GVariant *value,
GError **error,
gpointer user_data)
{
g_print("agent_set_property\n");
return FALSE;
}
#endif
static GDBusArgInfo service_arg = {
.ref_count = -1,
.name = "service",
.signature = "o",
};
/*
* Release()
*/
static GDBusMethodInfo release_info = {
.ref_count = -1,
.name = "Release",
};
/*
* ReportError()
*/
static GDBusArgInfo reporterror_in2 = {
.ref_count = -1,
.name = "error",
.signature = "s",
};
static GDBusArgInfo *reporterror_in[] = {
&service_arg,
&reporterror_in2,
NULL
};
static GDBusMethodInfo reporterror_info = {
.ref_count = -1,
.name = "ReportError",
.in_args = reporterror_in,
};
/*
* RequestBrowser()
*/
static GDBusArgInfo requestbrowser_in2 = {
.ref_count = -1,
.name = "url",
.signature = "s",
};
static GDBusArgInfo *requestbrowser_in[] = {
&service_arg,
&requestbrowser_in2,
NULL
};
static GDBusMethodInfo requestbrowser_info = {
.ref_count = -1,
.name = "RequestBrowser",
.in_args = requestbrowser_in,
};
/*
* RequestInput()
*/
static GDBusArgInfo requestinput_in2 = {
.ref_count = -1,
.name = "fields",
.signature = "a{sv}",
};
static GDBusArgInfo *requestinput_in[] = {
&service_arg,
&requestinput_in2,
NULL
};
static GDBusArgInfo requestinput_out1 = {
.ref_count = -1,
.signature = "a{sv}",
};
static GDBusArgInfo *requestinput_out[] = {
&requestinput_out1,
NULL
};
static GDBusMethodInfo requestinput_info = {
.ref_count = -1,
.name = "RequestInput",
.in_args = requestinput_in,
.out_args = requestinput_out,
};
/*
* Cancel()
*/
static GDBusMethodInfo cancel_info = {
.ref_count = -1,
.name = "Cancel",
};
/*
* Agent
*/
static GDBusMethodInfo *agent_methods[] = {
&release_info,
&reporterror_info,
&requestbrowser_info,
&requestinput_info,
&cancel_info,
NULL
};
static GDBusInterfaceInfo agent_interface_info = {
.ref_count = -1,
.name = "net.connman.Agent",
.methods = agent_methods,
};
static const GDBusInterfaceVTable agent_vtable = {
.method_call = agent_method_call,
/*
.get_property = agent_get_property,
.set_property = agent_set_property,
*/
};
enum stack_type {
STACK_EMPTY,
STACK_GUINT,
STACK_STRING,
STACK_GSTRING,
STACK_GVAR,
};
struct stack_elem {
enum stack_type type;
union {
guint u;
const char *str;
gchar *gstr;
GVariant *gv;
};
void (*free)(struct stack_elem *e);
};
static struct stack_elem stack[STACK_SIZE];
static guint stack_top;
static struct stack_elem *
stack_push(void)
{
stack_top++;
g_assert(stack_top < STACK_SIZE);
return &stack[stack_top];
}
static struct stack_elem *
stack_pop(void)
{
g_assert(stack_top > 0);
return &stack[stack_top--];
}
static void
stack_free(void)
{
guint i;
for (i = stack_top; i > 0; i--) {
struct stack_elem *e = &stack[i];
e->free(e);
}
}
#if 0
static gchar *stack_type_name[] = {
"EMPTY",
"GUINT",
"STRING",
"GSTRING",
"GVAR",
};
static void
stack_dump(void)
{
guint i;
for (i = stack_top; i > 0; i--)
g_print("%s\n", stack_type_name[stack[i].type]);
}
#endif
static void
stack_free_noop(struct stack_elem *e)
{
(void)e; /* noop */
}
static void
stack_guint_push(guint u)
{
struct stack_elem *e = stack_push();
e->type = STACK_GUINT;
e->u = u;
e->free = stack_free_noop;
}
static guint
stack_guint_pop(void)
{
struct stack_elem *e = stack_pop();
g_assert(e->type == STACK_GUINT);
return e->u;
}
static void
stack_string_push(char *str)
{
struct stack_elem *e = stack_push();
e->type = STACK_STRING;
e->str = str;
e->free = stack_free_noop;
}
static const char *
stack_string_pop(void)
{
struct stack_elem *e = stack_pop();
g_assert(e->type == STACK_STRING);
return e->str;
}
static void
stack_gstring_free(struct stack_elem *e)
{
g_free(e->gstr);
}
static void
stack_gstring_push(gchar *gstr)
{
struct stack_elem *e = stack_push();
e->type = STACK_GSTRING;
e->gstr = gstr;
e->free = stack_gstring_free;
}
static gchar *
stack_gstring_pop(void)
{
struct stack_elem *e = stack_pop();
g_assert(e->type == STACK_GSTRING);
return e->gstr;
}
static void
stack_gvar_free(struct stack_elem *e)
{
g_variant_unref(e->gv);
}
static void
stack_gvar_push(GVariant *gv)
{
struct stack_elem *e = stack_push();
e->type = STACK_GVAR;
e->gv = gv;
e->free = stack_gvar_free;
}
static GVariant *
stack_gvar_pop(void)
{
struct stack_elem *e = stack_pop();
g_assert(e->type == STACK_GVAR);
return e->gv;
}
static void
bus_quit_cb(GObject *source, GAsyncResult *res, gpointer data)
{
GError *err = NULL;
(void)source;
(void)data;
if (!g_dbus_connection_close_finish(bus, res, &err)) {
g_printerr("%s\n", err->message);
g_error_free(err);
exitcode = EXIT_FAILURE;
}
g_main_loop_quit(mainloop);
}
static void
quit(int ret)
{
exitcode = ret;
stack_free();
g_dbus_connection_close(bus,
NULL,
bus_quit_cb,
NULL);
}
enum ins_return {
INS_DONE,
INS_YIELD,
INS_ERROR,
};
typedef enum ins_return (*ins_t)(void);
static const ins_t *ins_next;
static void
ins_run(void)
{
do {
switch ((*ins_next++)()) {
case INS_DONE:
break;
case INS_YIELD:
return;
case INS_ERROR:
quit(EXIT_FAILURE);
return;
}
} while (*ins_next);
quit(EXIT_SUCCESS);
}
#if 0
/*
* X -> ()
*/
static enum ins_return
ins_pop(void)
{
struct stack_elem *e = stack_pop();
e->free(e);
return INS_DONE;
}
#endif
/*
* () -> GSTRING
*/
static enum ins_return
ins_push_wifi_path(void)
{
stack_string_push("/net/connman/technology/wifi");
return INS_DONE;
}
/*
* () -> GSTRING
*/
static enum ins_return
ins_push_ethernet_path(void)
{
stack_string_push("/net/connman/technology/ethernet");
return INS_DONE;
}
static void
print_error(GError *err)
{
g_printerr("%s\n", err->message);
g_error_free(err);
}
static void
bus_call_cb(GObject *source, GAsyncResult *res, gpointer data)
{
GVariant *ret;
GError *err = NULL;
(void)source;
(void)data;
ret = g_dbus_connection_call_finish(bus, res, &err);
if (err != NULL) {
print_error(err);
quit(EXIT_FAILURE);
return;
}
if (g_variant_is_of_type(ret, G_VARIANT_TYPE_TUPLE) &&
g_variant_n_children(ret) == 1) {
GVariant *child = g_variant_get_child_value(ret, 0);
g_variant_unref(ret);
ret = child;
}
stack_gvar_push(ret);
return ins_run();
}
static void
bus_call(const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
const GVariantType *reply_type)
{
g_dbus_connection_call(bus,
"net.connman",
object_path,
interface_name,
method_name,
parameters,
reply_type,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
bus_call_cb,
NULL);
}
static GVariant *
service_get(GVariant *array, guint index)
{
GVariant *entry;
g_assert(g_variant_is_of_type(array, G_VARIANT_TYPE_ARRAY));
if (index > g_variant_n_children(array)) {
g_printerr("Invalid service index %u\n", index);
return NULL;
}
entry = g_variant_get_child_value(array, index-1);
g_variant_unref(array);
g_assert(g_variant_is_of_type(entry, G_VARIANT_TYPE_TUPLE));
return entry;
}
static gchar *
service_get_path(GVariant *array, guint index)
{
GVariant *entry = service_get(array, index);
GVariant *obj;
gchar *ret;
if (entry == NULL)
return NULL;
obj = g_variant_get_child_value(entry, 0);
g_variant_unref(entry);
g_assert(g_variant_is_of_type(obj, G_VARIANT_TYPE_OBJECT_PATH));
ret = g_variant_dup_string(obj, NULL);
g_variant_unref(obj);
return ret;
}
static GVariant *
service_get_dict(GVariant *array, guint index)
{
GVariant *entry = service_get(array, index);
GVariant *dict;
if (entry == NULL)
return NULL;
dict = g_variant_get_child_value(entry, 1);
g_variant_unref(entry);
g_assert(g_variant_is_of_type(dict, G_VARIANT_TYPE_VARDICT));
return dict;
}
/*
* () -> GVAR
*/
static enum ins_return
ins_call_manager_getservices(void)
{
bus_call("/",
"net.connman.Manager",
"GetServices",
NULL,
G_VARIANT_TYPE("(a(oa{sv}))"));
return INS_YIELD;
}
/*
* STRING -> GVAR
*/
static enum ins_return
ins_call_technology_getproperties(void)
{
const char *obj_path = stack_string_pop();
bus_call(obj_path,
"net.connman.Technology",
"GetProperties",
NULL,
G_VARIANT_TYPE("(a{sv})"));
return INS_YIELD;
}
/*
* STRING -> GVAR
*/
static enum ins_return
ins_call_technology_scan(void)
{
const char *obj_path = stack_string_pop();
bus_call(obj_path,
"net.connman.Technology",
"Scan",
NULL,
G_VARIANT_TYPE("(a{sv})"));
return INS_YIELD;
}
/*
* () -> GVAR
*/
static enum ins_return
ins_register_agent(void)
{
GError *err = NULL;
GVariant *children[1];
g_print("Registering agent.. ");
g_dbus_connection_register_object(bus,
AGENT_PATH,
&agent_interface_info,
&agent_vtable,
NULL,
NULL,
&err);
if (err != NULL) {
print_error(err);
return INS_ERROR;
}
children[0] = g_variant_new_object_path(AGENT_PATH);
bus_call("/",
"net.connman.Manager",
"RegisterAgent",
g_variant_new_tuple(children, 1),
NULL);
return INS_YIELD;
}
/*
* GVAR GUINT -> GVAR
*/
static enum ins_return
ins_call_service_connect(void)
{
GVariant *array = stack_gvar_pop();
guint index = stack_guint_pop();
gchar *obj_path = service_get_path(array, index);
if (obj_path == NULL)
return INS_ERROR;
g_print("Connecting to service.. ");
bus_call(obj_path,
"net.connman.Service",
"Connect",
NULL,
NULL);
g_free(obj_path);
return INS_YIELD;
}
/*
* GVAR GUINT -> GVAR
*/
static enum ins_return
ins_call_service_remove(void)
{
GVariant *array = stack_gvar_pop();
guint index = stack_guint_pop();
gchar *obj_path = service_get_path(array, index);
if (obj_path == NULL)
return INS_ERROR;
g_print("Removing service.. ");
bus_call(obj_path,
"net.connman.Service",
"Remove",
NULL,
NULL);
g_free(obj_path);
return INS_YIELD;
}
static void
print_key_boolean(GVariant *dict, const gchar *key)
{
GVariant *val = g_variant_lookup_value(dict, key, G_VARIANT_TYPE("b"));
g_print("%s: %s\n", key, g_variant_get_boolean(val) ? "true" : "false");
g_variant_unref(val);
}
static void
print_key_string(GVariant *dict, const gchar *key)
{
GVariant *val = g_variant_lookup_value(dict, key, G_VARIANT_TYPE("s"));
g_print("%s: %s\n", key, g_variant_get_string(val, NULL));
g_variant_unref(val);
}
/*
* GVAR -> ()
*/
static enum ins_return
ins_print_ok(void)
{
GVariant *ret = stack_gvar_pop();
g_variant_unref(ret);
g_print("OK\n");
return INS_DONE;
}
/*
* GVAR -> ()
*/
static enum ins_return
ins_print_service_list(void)
{
GVariant *array = stack_gvar_pop();
GVariant *entry;
GVariantIter iter;
guint n;
g_variant_iter_init(&iter, array);
n = 1;
while ((entry = g_variant_iter_next_value(&iter))) {
GVariant *dict = g_variant_get_child_value(entry, 1);
GVariant *fav = g_variant_lookup_value(dict,
"Favorite",
G_VARIANT_TYPE("b"));
GVariant *name = g_variant_lookup_value(dict,
"Name",
G_VARIANT_TYPE("s"));
GVariant *type = g_variant_lookup_value(dict,
"Type",
G_VARIANT_TYPE("s"));
GVariant *state = g_variant_lookup_value(dict,
"State",
G_VARIANT_TYPE("s"));
g_print("%3u: %c %s [%s,%s]\n", n++,
g_variant_get_boolean(fav) ? '*' : ' ',
name ? g_variant_get_string(name, NULL) : "<no name>",
g_variant_get_string(type, NULL),
g_variant_get_string(state, NULL));
g_variant_unref(fav);
if (name)
g_variant_unref(name);
g_variant_unref(type);
g_variant_unref(state);
g_variant_unref(dict);
g_variant_unref(entry);
}
g_variant_unref(array);
return INS_DONE;
}
/*
* GVAR GUINT -> ()
*/
static enum ins_return
ins_print_service(void)
{
GVariant *array = stack_gvar_pop();
guint index = stack_guint_pop();
GVariant *dict = service_get_dict(array, index);
print_key_string(dict, "Name");
print_key_string(dict, "State");
print_key_boolean(dict, "AutoConnect");
g_variant_unref(dict);
return INS_DONE;
}
/*
* GVAR -> ()
*/
static enum ins_return
ins_print_technology(void)
{
GVariant *dict = stack_gvar_pop();
print_key_boolean(dict, "Powered");
print_key_boolean(dict, "Connected");
print_key_boolean(dict, "Tethering");
g_variant_unref(dict);
return INS_DONE;
}
static const ins_t root[] = {
ins_call_manager_getservices,
ins_print_service_list,
NULL
};
static const ins_t service[] = {
ins_call_manager_getservices,
ins_print_service,
NULL
};
static const ins_t service_connect[] = {
ins_call_manager_getservices,
ins_register_agent,
ins_print_ok,
ins_call_service_connect,
ins_print_ok,
NULL
};
static const ins_t service_remove[] = {
ins_call_manager_getservices,
ins_call_service_remove,
ins_print_ok,
NULL
};
static const ins_t ethernet[] = {
ins_push_ethernet_path,
ins_call_technology_getproperties,
ins_print_technology,
NULL
};
static const ins_t ethernet_scan[] = {
ins_push_ethernet_path,
ins_call_technology_scan,
ins_print_ok,
NULL
};
static const ins_t wifi[] = {
ins_push_wifi_path,
ins_call_technology_getproperties,
ins_print_technology,
NULL
};
static const ins_t wifi_scan[] = {
ins_push_wifi_path,
ins_call_technology_scan,
ins_print_ok,
NULL
};
static void
bus_get_cb(GObject *source, GAsyncResult *res, gpointer data)
{
GError *err = NULL;
(void)source;
(void)data;
bus = g_bus_get_finish(res, &err);
if (bus != NULL)
return ins_run();
g_printerr("%s\n", err->message);
g_error_free(err);
exitcode = EXIT_FAILURE;
g_main_loop_quit(mainloop);
}
static int
loop(void)
{
mainloop = g_main_loop_new(NULL, FALSE);
g_bus_get(G_BUS_TYPE_SYSTEM,
NULL,
bus_get_cb,
NULL);
g_main_loop_run(mainloop);
g_main_loop_unref(mainloop);
return exitcode;
}
static int
help(void)
{
g_print("Hello, World!\n");
return EXIT_SUCCESS;
}
struct cmd_node;
struct cmd_list {
const char *cmd;
const struct cmd_node *node;
};
struct cmd_node {
union {
const ins_t *program;
int (*func)(void);
};
gboolean noloop;
const struct cmd_node *num;
struct cmd_list cmds[];
};
static const struct cmd_node service_connect_cmds = {
.program = service_connect,
.cmds = { { NULL } }
};
static const struct cmd_node service_remove_cmds = {
.program = service_remove,
.cmds = { { NULL } },
};
static const struct cmd_node service_cmds = {
.program = service,
.cmds = {
{ "connect", &service_connect_cmds },
{ "remove", &service_remove_cmds },
{ NULL },
}
};
static const struct cmd_node ethernet_scan_cmds = {
.program = ethernet_scan,
.cmds = { { NULL } }
};
static const struct cmd_node ethernet_cmds = {
.program = ethernet,
.cmds = {
{ "scan", &ethernet_scan_cmds },
{ NULL }
}
};
static const struct cmd_node wifi_scan_cmds = {
.program = wifi_scan,
.cmds = { { NULL } }
};
static const struct cmd_node wifi_cmds = {
.program = wifi,
.cmds = {
{ "scan", &wifi_scan_cmds },
{ NULL }
}
};
static const struct cmd_node help_cmds = {
.func = help,
.noloop = TRUE,
.cmds = { { NULL } }
};
static const struct cmd_node root_cmds = {
.program = root,
.num = &service_cmds,
.cmds = {
{ "ethernet", &ethernet_cmds },
{ "wifi", &wifi_cmds },
{ "help", &help_cmds },
{ NULL },
}
};
static gboolean
str_to_index(const char *str)
{
guint d;
if (*str < '1' || *str > '9')
return FALSE;
for (d = *str++ - '0'; *str >= '0' && *str <= '9'; str++) {
d *= 10;
d += *str - '0';
}
if (*str != '\0')
return FALSE;
stack_guint_push(d);
return TRUE;
}
int
main(int argc, char **argv)
{
const struct cmd_node *node = &root_cmds;
int i;
g_type_init();
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
unsigned int len;
const struct cmd_list *c;
if (node->num && str_to_index(arg)) {
node = node->num;
continue;
}
len = strlen(arg);
for (c = node->cmds;; c++) {
if (c->cmd == NULL) {
g_printerr("Unknown command \"%s\", "
"try \"cmi help\".\n", arg);
return EXIT_FAILURE;
}
if (len > strlen(c->cmd))
continue;
if (memcmp(arg, c->cmd, len) == 0) {
node = c->node;
break;
}
}
}
if (node->noloop)
return node->func();
ins_next = node->program;
return loop();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment