/* * Copyright (c) 2011- Osmo Antero. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License (GPL3), or any later version. * * This library 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 Library General Public License 3 for more details. * * You should have received a copy of the GNU Library General Public * License 3 along with this program; if not, see /usr/share/common-licenses/GPL file * or . */ #include "log.h" #include "support.h" #include "about.h" #include "rec-manager.h" #include // This module creates a DBus-server for this program. // Other programs can control the recorder by calling methods via this DBus interface. // // Audio-recorder itself can send messages to the running instance of this program. // See audio-recorder --help for more information. // // Exported DBus methods are: // get_state(), returns current recording state: "not running" | "on" | off" | "paused". // // set_state(state), set new state. The state can be one of: "start"|"stop"|"pause"|"hide"|"show"|"quit". // returns "OK" if success. NULL if error. // // Ref: https://developer.gnome.org/gio/stable/GDBusServer.html // // Notice: This module has nothing to do with dbus-player.[ch], dbus-mpris2.[ch] modules. // #define R_DBUS_SERVER_ADDRESS "unix:abstract=audiorecorder" #define R_DBUS_OBJECT_PATH "/org/gnome/API/AudioRecorder" #define R_DBUS_INTERFACE_NAME "org.gnome.API.AudioRecorderInterface" static GDBusServer *g_dbus_server = NULL; static GDBusNodeInfo *g_introspection_data = NULL; // Signatures for the methods we are exporting. // DBus clients can get information or control the recorder by calling these functions. static const gchar g_introspection_xml[] = "" " " " " " " // Returns current recording state: "not running"|"on"|off"|"paused" " " " " " " // Set new state. Input argument:"start"|"stop"|"pause"|"hide"|"show"|"quit". " " // Returns "OK" or NULL if error. " " " " ""; static gboolean dbus_service_start(); static void dbus_service_set_state(gchar *new_state); // ----------------------------------------------------------------------------------- void dbus_service_module_init() { LOG_DEBUG("Init dbus_service.c\n"); g_dbus_server = NULL; // Start service dbus_service_start(); } void dbus_service_module_exit() { LOG_DEBUG("Clean up dbus_service.c.\n"); if (g_introspection_data) { g_dbus_node_info_unref(g_introspection_data); } g_introspection_data = NULL; if (g_dbus_server) { g_dbus_server_stop(g_dbus_server); g_object_unref(g_dbus_server); } g_dbus_server = NULL; } static void handle_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { // DBus method call: get_state. // Returns "not running" | "on" | "off" | "paused" if (g_strcmp0(method_name, "get_state") == 0) { // Get recording state gint state = -1; gint pending = -1; rec_manager_get_state(&state, &pending); const gchar *state_str = NULL; switch (state) { case GST_STATE_PAUSED: state_str = "paused"; break; case GST_STATE_PLAYING: state_str = "on"; break; default: state_str = "off"; } g_dbus_method_invocation_return_value(invocation, g_variant_new ("(s)", state_str)); LOG_DEBUG("Audio recorder (DBus-server) executed method get_state().\n"); } // DBus method call: set_state(new_state). // new_state can be: "start" | "stop" | "pause" | "show" | "hide" | "quit". // Returns "OK" | NULL else if (g_strcmp0 (method_name, "set_state") == 0) { gchar *response = g_strdup("OK"); gchar *new_state = NULL; g_variant_get(parameters, "(&s)", &new_state); g_dbus_method_invocation_return_value(invocation, g_variant_new ("(s)", response)); g_free(response); LOG_DEBUG("Audio recorder (Dbus-server) executed method set_state(%s).\n", new_state); // Set recorder to new_state dbus_service_set_state(new_state); } } static void dbus_service_set_state(gchar *new_state) { // Set recorder to new_state // $ audio-recorder --command start if (!g_strcmp0(new_state, "start")) { rec_manager_start_recording(); } // $ audio-recorder --command stop else if (!g_strcmp0(new_state, "stop")) { rec_manager_stop_recording(); } // $ audio-recorder --command pause else if (!g_strcmp0(new_state, "pause")) { rec_manager_pause_recording(); } // $ audio-recorder --command show else if (!g_strcmp0(new_state, "show")) { rec_manager_show_window(TRUE); } // $ audio-recorder --command hide else if (!g_strcmp0(new_state, "hide")) { rec_manager_show_window(FALSE); } // $ audio-recorder --command quit else if (!g_strcmp0(new_state, "quit")) { rec_manager_quit_application(); } } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, NULL, NULL, }; // --------------------------------------------------------------------------------- static gboolean on_new_connection(GDBusServer *server, GDBusConnection *connection, gpointer user_data) { /* GCredentials *credentials = NULL; credentials = g_dbus_connection_get_peer_credentials(connection); gchar *s = NULL; if (credentials == NULL) s = g_strdup ("(no credentials received)"); else s = g_credentials_to_string(credentials); LOG_DEBUG("Client connected.\n" "Peer credentials: %s\n" "Negotiated capabilities: unix-fd-passing=%d\n", s, g_dbus_connection_get_capabilities(connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); */ g_object_ref(connection); gint registration_id = g_dbus_connection_register_object(connection, R_DBUS_OBJECT_PATH, g_introspection_data->interfaces[0], &interface_vtable, NULL, /* user_data */ NULL, /* user_data_free_func */ NULL); /* GError */ return (registration_id > 0); } static gboolean dbus_service_start() { // Start DBus-server for this application. // This will receive method calls from external programs. // Build introspection data from XML g_introspection_data = g_dbus_node_info_new_for_xml(g_introspection_xml, NULL); // Server flags GDBusServerFlags server_flags = G_DBUS_SERVER_FLAGS_NONE; server_flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS; gchar *guid = g_dbus_generate_guid(); // Create a new D-Bus server that listens on R_DBUS_SERVER_ADDRESS GError *error = NULL; g_dbus_server = g_dbus_server_new_sync(R_DBUS_SERVER_ADDRESS, server_flags, guid, NULL, /* GDBusAuthObserver */ NULL, /* GCancellable */ &error); g_dbus_server_start(g_dbus_server); g_free(guid); if (g_dbus_server == NULL) { LOG_ERROR("Cannot create server address %s for DBus. %s\n", R_DBUS_SERVER_ADDRESS, error->message); g_error_free(error); return FALSE; } LOG_DEBUG("This Audio Recorder is listening on DBus at: %s\n", g_dbus_server_get_client_address(g_dbus_server)); // Set up callback method (for client calls) g_signal_connect(g_dbus_server, "new-connection", G_CALLBACK (on_new_connection), NULL); return (g_dbus_server != NULL); } gchar *dbus_service_client_request(gchar *method_name, gchar *arg) { // Execute method call on the server. // Audio-recorder itself can call methods on the server. // DBus-server will most likely return a value. We will pass it to the calling function. GError *error = NULL; GDBusConnection *connection = g_dbus_connection_new_for_address_sync(R_DBUS_SERVER_ADDRESS, G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, NULL, // GDBusAuthObserver NULL, // GCancellable &error); if (!connection) { LOG_DEBUG("Cannot connect to DBus address %s. %s\n", R_DBUS_SERVER_ADDRESS, error->message); g_error_free(error); return NULL; } // Optional argument GVariant *argument = NULL; if (arg) { argument = g_variant_new("(s)", arg); } GVariant *value = NULL; value = g_dbus_connection_call_sync(connection, NULL, // bus_name R_DBUS_OBJECT_PATH, R_DBUS_INTERFACE_NAME, method_name, // method_name argument, // input parm G_VARIANT_TYPE ("(s)"), // return value (one string) G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!value) { LOG_ERROR("Error invoking %s(%s). %s\n", method_name, arg, error->message); g_error_free(error); g_object_unref(connection); return NULL; } gchar *server_response = NULL; g_variant_get(value, "(&s)", &server_response); // Take copy of the value gchar *ret = g_strdup(server_response); if (value) { g_variant_unref(value); } //Notice: "argument" is floating and consumed by g_dbus_connection_call_sync(). //if (argument) // g_variant_unref(argument); //} g_object_unref(connection); // Return ret return ret; }