| #include <stdbool.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <dbus/dbus.h> |
| #include <dbus/dbus-glib-lowlevel.h> /* for glib main loop */ |
| |
| const char *version = "0.1"; |
| GMainLoop *mainloop; |
| |
| /* |
| * This is the XML string describing the interfaces, methods and |
| * signals implemented by our 'Server' object. It's used by the |
| * 'Introspect' method of 'org.freedesktop.DBus.Introspectable' |
| * interface. |
| */ |
| const char *server_introspection_xml = |
| DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE |
| "<node>\n" |
| |
| " <interface name='org.freedesktop.DBus.Introspectable'>\n" |
| " <method name='Introspect'>\n" |
| " <arg name='data' type='s' direction='out' />\n" |
| " </method>\n" |
| " </interface>\n" |
| |
| " <interface name='org.freedesktop.DBus.Properties'>\n" |
| " <method name='Get'>\n" |
| " <arg name='interface' type='s' direction='in' />\n" |
| " <arg name='property' type='s' direction='in' />\n" |
| " <arg name='value' type='s' direction='out' />\n" |
| " </method>\n" |
| " <method name='GetAll'>\n" |
| " <arg name='interface' type='s' direction='in'/>\n" |
| " <arg name='properties' type='a{sv}' direction='out'/>\n" |
| " </method>\n" |
| " </interface>\n" |
| |
| " <interface name='com.Intel.PrmDispatch'>\n" |
| " <property name='Version' type='s' access='read' />\n" |
| " <method name='InstallPrmPackage'>\n" |
| " <arg type='ay' direction='in' name='PrmPackage' />\n" |
| " <arg type='u' direction='out' name='EfiStatus' />\n" |
| " </method>\n" |
| " <method name='EnumerateHandlers'>\n" |
| " <arg type='u' direction='out' name='EfiStatus'>\n" |
| " <arg type='a{ss} direction='out' name='PrmHandlerInfo' />\n" |
| " <entry key='PlatformGuid' value='s'/>\n" |
| " <entry key='ModuleGuid' value='s'/>\n" |
| " <entry key='HandlerGuid' value='s'/>\n" |
| " </arg>\n" |
| " </method>\n" |
| " <method name='InvokeHandler'>\n" |
| " <arg type='s' direction='in' name='HandlerGuid'/>\n" |
| " <arg type='ay' direction='in' name='ParameterBuffer' optional='true'/>\n" |
| " <arg type='u' direction='out' name='PrmHandlerReturnStatus'/>\n" |
| " <arg type='u' direction='out' name='EfiStatus' " |
| " </method>\n" |
| " <method name='UninstallPrmModule'>\n" |
| " <arg type='s' direction='in' name='ModuleGuid'/>\n" |
| " <arg type='u' direction='out' name='EfiStatus'/>\n" |
| " </method>\n" |
| " <method name='GetHandlerDebugInfo'>\n" |
| " <arg type='s' direction='in' name='HandlerGuid'/>\n" |
| " <arg type='ay' direction='out' name='AcpiParameterBuffer'/>\n" |
| " <arg type='s' direction='out' name='HandlerName' />\n" |
| " <arg type='u' durection='out' name='EfiStatus'/>\n" |
| " </method>\n" |
| " <method name='Quit'>\n" |
| " </method>\n" |
| " </interface>\n" |
| |
| "</node>\n"; |
| |
| /* |
| * This implements 'Get' method of DBUS_INTERFACE_PROPERTIES so a |
| * client can inspect the properties/attributes of 'TestInterface'. |
| */ |
| DBusHandlerResult server_get_properties_handler(const char *property, DBusConnection *conn, DBusMessage *reply) |
| { |
| if (!strcmp(property, "Version")) |
| { |
| dbus_message_append_args(reply, |
| DBUS_TYPE_STRING, &version, |
| DBUS_TYPE_INVALID); |
| } |
| else |
| /* Unknown property */ |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
| if (!dbus_connection_send(conn, reply, NULL)) |
| return DBUS_HANDLER_RESULT_NEED_MEMORY; |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| /* |
| * This implements 'GetAll' method of DBUS_INTERFACE_PROPERTIES. This |
| * one seems required by g_dbus_proxy_get_cached_property(). |
| */ |
| DBusHandlerResult server_get_all_properties_handler(DBusConnection *conn, DBusMessage *reply) |
| { |
| DBusHandlerResult result; |
| DBusMessageIter array, dict, iter, variant; |
| const char *property = "Version"; |
| |
| /* |
| * All dbus functions used below might fail due to out of |
| * memory error. If one of them fails, we assume that all |
| * following functions will fail too, including |
| * dbus_connection_send(). |
| */ |
| result = DBUS_HANDLER_RESULT_NEED_MEMORY; |
| |
| dbus_message_iter_init_append(reply, &iter); |
| dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array); |
| |
| /* Append all properties name/value pairs */ |
| property = "Version"; |
| dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict); |
| dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &property); |
| dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &variant); |
| dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &version); |
| dbus_message_iter_close_container(&dict, &variant); |
| dbus_message_iter_close_container(&array, &dict); |
| dbus_message_iter_close_container(&iter, &array); |
| |
| if (dbus_connection_send(conn, reply, NULL)) |
| result = DBUS_HANDLER_RESULT_HANDLED; |
| return result; |
| } |
| |
| /* |
| * This function implements the 'TestInterface' interface for the |
| * 'Server' DBus object. |
| * |
| * It also implements 'Introspect' method of |
| * 'org.freedesktop.DBus.Introspectable' interface which returns the |
| * XML string describing the interfaces, methods, and signals |
| * implemented by 'Server' object. This also can be used by tools such |
| * as d-feet(1) and can be queried by: |
| * |
| * $ gdbus introspect --session --dest org.example.TestServer --object-path /org/example/TestObject |
| */ |
| DBusHandlerResult server_message_handler(DBusConnection *conn, DBusMessage *message, void *data) |
| { |
| DBusHandlerResult result; |
| DBusMessage *reply = NULL; |
| DBusError err; |
| bool quit = false; |
| |
| fprintf(stderr, "Got D-Bus request: %s.%s on %s\n", |
| dbus_message_get_interface(message), |
| dbus_message_get_member(message), |
| dbus_message_get_path(message)); |
| |
| /* |
| * Does not allocate any memory; the error only needs to be |
| * freed if it is set at some point. |
| */ |
| dbus_error_init(&err); |
| |
| if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) |
| { |
| |
| if (!(reply = dbus_message_new_method_return(message))) |
| goto fail; |
| |
| dbus_message_append_args(reply, |
| DBUS_TYPE_STRING, &server_introspection_xml, |
| DBUS_TYPE_INVALID); |
| } |
| else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get")) |
| { |
| const char *interface, *property; |
| |
| if (!dbus_message_get_args(message, &err, |
| DBUS_TYPE_STRING, &interface, |
| DBUS_TYPE_STRING, &property, |
| DBUS_TYPE_INVALID)) |
| goto fail; |
| |
| if (!(reply = dbus_message_new_method_return(message))) |
| goto fail; |
| |
| result = server_get_properties_handler(property, conn, reply); |
| dbus_message_unref(reply); |
| return result; |
| } |
| else if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll")) |
| { |
| |
| if (!(reply = dbus_message_new_method_return(message))) |
| goto fail; |
| |
| result = server_get_all_properties_handler(conn, reply); |
| dbus_message_unref(reply); |
| return result; |
| } |
| else if (dbus_message_is_method_call(message, "com.Intel.PrmDispatch", "Quit")) |
| { |
| /* |
| * Quit() has no return values but a METHOD_RETURN |
| * reply is required, so the caller will know the |
| * method was successfully processed. |
| */ |
| reply = dbus_message_new_method_return(message); |
| quit = true; |
| } |
| else |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
| fail: |
| if (dbus_error_is_set(&err)) |
| { |
| if (reply) |
| dbus_message_unref(reply); |
| reply = dbus_message_new_error(message, err.name, err.message); |
| dbus_error_free(&err); |
| } |
| |
| /* |
| * In any cases we should have allocated a reply otherwise it |
| * means that we failed to allocate one. |
| */ |
| if (!reply) |
| return DBUS_HANDLER_RESULT_NEED_MEMORY; |
| |
| /* Send the reply which might be an error one too. */ |
| result = DBUS_HANDLER_RESULT_HANDLED; |
| if (!dbus_connection_send(conn, reply, NULL)) |
| result = DBUS_HANDLER_RESULT_NEED_MEMORY; |
| dbus_message_unref(reply); |
| |
| if (quit) |
| { |
| fprintf(stderr, "Server exiting...\n"); |
| g_main_loop_quit(mainloop); |
| } |
| return result; |
| } |
| |
| |
| const DBusObjectPathVTable server_vtable = { |
| .message_function = server_message_handler |
| }; |
| |
| |
| int run_server(void) |
| { |
| DBusConnection *conn; |
| DBusError err; |
| int rv; |
| |
| dbus_error_init(&err); |
| |
| /* connect to the daemon bus */ |
| conn = dbus_bus_get(DBUS_BUS_SESSION, &err); |
| if (!conn) { |
| fprintf(stderr, "Failed to get a session DBus connection: %s\n", err.message); |
| goto fail; |
| } |
| |
| rv = dbus_bus_request_name(conn, "com.Intel.PrmDispatchServer", DBUS_NAME_FLAG_REPLACE_EXISTING , &err); |
| if (rv != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { |
| fprintf(stderr, "Failed to request name on bus: %s\n", err.message); |
| goto fail; |
| } |
| |
| if (!dbus_connection_register_object_path(conn, "/org/Intel/PrmDispatchAPIObject", &server_vtable, NULL)) { |
| fprintf(stderr, "Failed to register a object path for 'PrmDispatchAPIObject'\n"); |
| goto fail; |
| } |
| |
| /* |
| * For the sake of simplicity we're using glib event loop to |
| * handle DBus messages. This is the only place where glib is |
| * used. |
| */ |
| printf("Starting demo dbus server v%s\n", version); |
| mainloop = g_main_loop_new(NULL, false); |
| /* Set up the DBus connection to work in a GLib event loop */ |
| dbus_connection_setup_with_g_main(conn, NULL); |
| /* Start the glib event loop */ |
| g_main_loop_run(mainloop); |
| |
| return EXIT_SUCCESS; |
| fail: |
| dbus_error_free(&err); |
| return EXIT_FAILURE; |
| } |
| |
| int main(void){ |
| run_server(); |
| } |