📄 dbus-service.c
字号:
/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org> * * * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdio.h>#include <errno.h>#include <unistd.h>#include <stdlib.h>#include <dirent.h>#include <signal.h>#include <ctype.h>#include <sys/types.h>#include <sys/wait.h>#include <bluetooth/sdp.h>#include <glib.h>#include <dbus/dbus.h>#include "dbus.h"#include "dbus-helper.h"#include "hcid.h"#include "notify.h"#include "server.h"#include "dbus-common.h"#include "dbus-error.h"#include "error.h"#include "manager.h"#include "adapter.h"#include "dbus-service.h"#include "dbus-hci.h"#define SERVICE_INTERFACE "org.bluez.Service"#define STARTUP_TIMEOUT (10 * 1000) /* 10 seconds */#define SHUTDOWN_TIMEOUT (2 * 1000) /* 2 seconds */#define SERVICE_SUFFIX ".service"#define SERVICE_GROUP "Bluetooth Service"#define NAME_MATCH "interface=" DBUS_INTERFACE_DBUS ",member=NameOwnerChanged"static GSList *services = NULL;static GSList *removed = NULL;static void service_free(struct service *service){ if (!service) return; if (service->action) dbus_message_unref(service->action); g_free(service->bus_name); g_free(service->filename); g_free(service->object_path); g_free(service->name); g_free(service->descr); g_free(service->ident); g_free(service);}static void service_exit(const char *name, struct service *service){ DBusConnection *conn = get_dbus_connection(); debug("Service owner exited: %s", name); dbus_connection_emit_signal(conn, service->object_path, SERVICE_INTERFACE, "Stopped", DBUS_TYPE_INVALID); if (service->action) { DBusMessage *reply; reply = dbus_message_new_method_return(service->action); send_message_and_unref(conn, reply); dbus_message_unref(service->action); service->action = NULL; } g_free(service->bus_name); service->bus_name = NULL;}static DBusHandlerResult get_info(DBusConnection *conn, DBusMessage *msg, void *data){ struct service *service = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; dbus_bool_t running; reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_append_dict_entry(&dict, "identifier", DBUS_TYPE_STRING, &service->ident); dbus_message_iter_append_dict_entry(&dict, "name", DBUS_TYPE_STRING, &service->name); dbus_message_iter_append_dict_entry(&dict, "description", DBUS_TYPE_STRING, &service->descr); running = (service->external || service->bus_name) ? TRUE : FALSE; dbus_message_iter_append_dict_entry(&dict, "running", DBUS_TYPE_BOOLEAN, &running); dbus_message_iter_close_container(&iter, &dict); return send_message_and_unref(conn, reply);}static DBusHandlerResult get_identifier(DBusConnection *conn, DBusMessage *msg, void *data){ struct service *service = data; DBusMessage *reply; const char *identifier = ""; reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (service->ident) identifier = service->ident; dbus_message_append_args(reply, DBUS_TYPE_STRING, &identifier, DBUS_TYPE_INVALID); return send_message_and_unref(conn, reply);}static DBusHandlerResult get_name(DBusConnection *conn, DBusMessage *msg, void *data){ struct service *service = data; DBusMessage *reply; const char *name = ""; reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (service->name) name = service->name; dbus_message_append_args(reply, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); return send_message_and_unref(conn, reply);}static DBusHandlerResult get_description(DBusConnection *conn, DBusMessage *msg, void *data){ struct service *service = data; DBusMessage *reply; const char *description = ""; reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; if (service->descr) description = service->descr; dbus_message_append_args(reply, DBUS_TYPE_STRING, &description, DBUS_TYPE_INVALID); return send_message_and_unref(conn, reply);}static DBusHandlerResult get_bus_name(DBusConnection *conn, DBusMessage *msg, void *data){ struct service *service = data; DBusMessage *reply; if (!service->bus_name) return error_not_available(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; dbus_message_append_args(reply, DBUS_TYPE_STRING, &service->bus_name, DBUS_TYPE_INVALID); return send_message_and_unref(conn, reply);}static void service_setup(gpointer data){ /* struct service *service = data; */}static DBusHandlerResult service_filter(DBusConnection *conn, DBusMessage *msg, void *data){ DBusError err; struct service *service = data; const char *name, *old, *new; unsigned long pid; if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) { error("Invalid arguments for NameOwnerChanged signal"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } if (*new == '\0' || *old != '\0' || *new != ':') return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (!dbus_bus_get_unix_process_id(conn, new, &pid)) { error("Could not get PID of %s", new); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } if ((GPid) pid != service->pid) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; debug("Child PID %d got the unique bus name %s", service->pid, new); service->bus_name = g_strdup(new); dbus_error_init(&err); dbus_bus_remove_match(conn, NAME_MATCH, &err); if (dbus_error_is_set(&err)) { error("Remove match \"%s\" failed: %s" NAME_MATCH, err.message); dbus_error_free(&err); } dbus_connection_remove_filter(conn, service_filter, service); if (service->action) { msg = dbus_message_new_method_return(service->action); if (msg) { if (dbus_message_is_method_call(service->action, MANAGER_INTERFACE, "ActivateService")) dbus_message_append_args(msg, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID); send_message_and_unref(conn, msg); } dbus_message_unref(service->action); service->action = NULL; } if (service->startup_timer) { g_source_remove(service->startup_timer); service->startup_timer = 0; } else debug("service_filter: timeout was already removed!"); name_listener_add(conn, new, (name_cb_t) service_exit, service); dbus_connection_emit_signal(conn, service->object_path, SERVICE_INTERFACE, "Started", DBUS_TYPE_INVALID); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;}static void abort_startup(struct service *service, DBusConnection *conn, int ecode){ DBusError err; if (conn) { dbus_error_init(&err); dbus_bus_remove_match(conn, NAME_MATCH, &err); if (dbus_error_is_set(&err)) { error("Remove match \"%s\" failed: %s" NAME_MATCH, err.message); dbus_error_free(&err); } dbus_connection_remove_filter(conn, service_filter, service); } g_source_remove(service->startup_timer); service->startup_timer = 0; if (service->action) { if (conn) error_failed_errno(conn, service->action, ecode); dbus_message_unref(service->action); service->action = NULL; } if (service->pid > 0 && kill(service->pid, SIGKILL) < 0) error("kill(%d, SIGKILL): %s (%d)", service->pid, strerror(errno), errno);}static void service_died(GPid pid, gint status, gpointer data){ struct service *service = data; if (WIFEXITED(status)) debug("%s (%s) exited with status %d", service->name, service->ident, WEXITSTATUS(status)); else debug("%s (%s) was killed by signal %d", service->name, service->ident, WTERMSIG(status)); g_spawn_close_pid(pid); service->pid = 0; if (service->startup_timer) abort_startup(service, get_dbus_connection(), ECANCELED); if (service->shutdown_timer) { g_source_remove(service->shutdown_timer); service->shutdown_timer = 0; } if (g_slist_find(removed, service)) { removed = g_slist_remove(removed, service); service_free(service); }}static gboolean service_shutdown_timeout(gpointer data){ struct service *service = data; if (service->pid > 0) { debug("SIGKILL for \"%s\" (PID %d) since it didn't exit yet", service->name, service->pid); if (kill(service->pid, SIGKILL) < 0) error("kill(%d, SIGKILL): %s (%d)", service->pid, strerror(errno), errno); } service->shutdown_timer = 0; return FALSE;}static void stop_service(struct service *service, gboolean remove){ if (service->pid > 0 && kill(service->pid, SIGTERM) < 0) error("kill(%d, SIGTERM): %s (%d)", service->pid, strerror(errno), errno); service->shutdown_timer = g_timeout_add(SHUTDOWN_TIMEOUT, service_shutdown_timeout, service); if (remove) { services = g_slist_remove(services, service); removed = g_slist_append(removed, service); }}static gboolean service_startup_timeout(gpointer data){ struct service *service = data; debug("Killing \"%s\" (PID %d) because it did not connect to D-Bus in time", service->name, service->pid); abort_startup(service, get_dbus_connection(), ETIME); return FALSE;}int service_start(struct service *service, DBusConnection *conn){ DBusError derr; char *addr, *argv[2], *envp[2], command[PATH_MAX], address[256]; if (!dbus_connection_add_filter(conn, service_filter, service, NULL)) { error("Unable to add signal filter"); return -1; } dbus_error_init(&derr); dbus_bus_add_match(conn, NAME_MATCH, &derr); if (dbus_error_is_set(&derr)) { error("Add match \"%s\" failed: %s", derr.message); dbus_error_free(&derr); dbus_connection_remove_filter(conn, service_filter, service); return -1; } snprintf(command, sizeof(command) - 1, "%s/bluetoothd-service-%s", SERVICEDIR, service->ident); argv[0] = command; argv[1] = NULL; addr = get_local_server_address(); snprintf(address, sizeof(address) - 1, "BLUETOOTHD_ADDRESS=%s", addr); envp[0] = address; envp[1] = NULL; dbus_free(addr); if (!g_spawn_async(SERVICEDIR, argv, envp, G_SPAWN_DO_NOT_REAP_CHILD, service_setup, service, &service->pid, NULL)) { error("Unable to execute %s", argv[0]); dbus_connection_remove_filter(conn, service_filter, service); dbus_bus_remove_match(conn, NAME_MATCH, NULL); return -1; } g_child_watch_add(service->pid, service_died, service); debug("%s executed with PID %d", argv[0], service->pid); service->startup_timer = g_timeout_add(STARTUP_TIMEOUT, service_startup_timeout, service); return 0;}static DBusHandlerResult start(DBusConnection *conn, DBusMessage *msg, void *data){ struct service *service = data; if (service->external || service->pid) return error_failed_errno(conn, msg, EALREADY); if (service_start(service, conn) < 0) return error_failed_errno(conn, msg, ENOEXEC); service->action = dbus_message_ref(msg); return DBUS_HANDLER_RESULT_HANDLED;}static DBusHandlerResult stop(DBusConnection *conn, DBusMessage *msg, void *data){ struct service *service = data; if (service->external || !service->bus_name) return error_failed_errno(conn, msg, EPERM); stop_service(service, FALSE); service->action = dbus_message_ref(msg); return DBUS_HANDLER_RESULT_HANDLED;}static DBusHandlerResult is_running(DBusConnection *conn, DBusMessage *msg, void *data){ struct service *service = data; DBusMessage *reply; dbus_bool_t running; reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; running = (service->external || service->bus_name) ? TRUE : FALSE; dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &running, DBUS_TYPE_INVALID); return send_message_and_unref(conn, reply);}static DBusHandlerResult is_external(DBusConnection *conn, DBusMessage *msg, void *data){ struct service *service = data; DBusMessage *reply; reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &service->external, DBUS_TYPE_INVALID); return send_message_and_unref(conn, reply);}static DBusHandlerResult set_trusted(DBusConnection *conn, DBusMessage *msg, void *data){ struct service *service = data; DBusMessage *reply; const char *address; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg, NULL); if (check_address(address) < 0) return error_invalid_arguments(conn, msg, NULL); reply = dbus_message_new_method_return(msg);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -