/* * MATE CPUFreq Applet * Copyright (C) 2008 Carlos Garcia Campos * * This library 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 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 * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "cpufreq-selector.h" #include "cpufreq-selector-factory.h" #include "cpufreq-selector-service.h" #include "cpufreq-selector-service-glue.h" #define MAX_CPUS 255 struct _CPUFreqSelectorService { GObject parent; CPUFreqSelector *selectors[MAX_CPUS]; gint selectors_max; DBusGConnection *system_bus; /* PolicyKit */ PolkitAuthority *authority; }; struct _CPUFreqSelectorServiceClass { GObjectClass parent_class; }; G_DEFINE_TYPE (CPUFreqSelectorService, cpufreq_selector_service, G_TYPE_OBJECT) #define BUS_NAME "org.mate.CPUFreqSelector" GType cpufreq_selector_service_error_get_type (void) { static GType etype = 0; if (G_UNLIKELY (etype == 0)) { static const GEnumValue values[] = { { SERVICE_ERROR_GENERAL, "SERVICE_ERROR_GENERAL", "GeneralError" }, { SERVICE_ERROR_DBUS, "SERVICE_ERROR_DBUS", "DBUSError" }, { SERVICE_ERROR_ALREADY_REGISTERED, "SERVICE_ERROR_ALREADY_REGISTERED", "AlreadyRegistered" }, { SERVICE_ERROR_NOT_AUTHORIZED, "SERVICE_ERROR_NOT_AUTHORIZED", "NotAuthorized"}, { 0, NULL, NULL} }; etype = g_enum_register_static ("CPUFreqSelectorServiceError", values); } return etype; } GQuark cpufreq_selector_service_error_quark (void) { static GQuark error_quark = 0; if (G_UNLIKELY (error_quark == 0)) error_quark = g_quark_from_static_string ("cpufreq-selector-service-error-quark"); return error_quark; } static void cpufreq_selector_service_finalize (GObject *object) { CPUFreqSelectorService *service = CPUFREQ_SELECTOR_SERVICE (object); gint i; service->system_bus = NULL; if (service->selectors_max >= 0) { for (i = 0; i < service->selectors_max; i++) { if (service->selectors[i]) { g_object_unref (service->selectors[i]); service->selectors[i] = NULL; } } service->selectors_max = -1; } if (service->authority) { g_object_unref (service->authority); service->authority = NULL; } G_OBJECT_CLASS (cpufreq_selector_service_parent_class)->finalize (object); } static void cpufreq_selector_service_class_init (CPUFreqSelectorServiceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = cpufreq_selector_service_finalize; } static void cpufreq_selector_service_init (CPUFreqSelectorService *service) { service->selectors_max = -1; } CPUFreqSelectorService * cpufreq_selector_service_get_instance (void) { static CPUFreqSelectorService *service = NULL; if (!service) service = CPUFREQ_SELECTOR_SERVICE (g_object_new (CPUFREQ_TYPE_SELECTOR_SERVICE, NULL)); return service; } static gboolean service_shutdown (gpointer user_data) { g_object_unref (SELECTOR_SERVICE); return FALSE; } static void reset_killtimer (void) { static guint timer_id = 0; if (timer_id > 0) g_source_remove (timer_id); timer_id = g_timeout_add_seconds (30, (GSourceFunc) service_shutdown, NULL); } gboolean cpufreq_selector_service_register (CPUFreqSelectorService *service, GError **error) { DBusGConnection *connection; DBusGProxy *bus_proxy; gboolean res; guint result; GError *err = NULL; if (service->system_bus) { g_set_error (error, CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_ALREADY_REGISTERED, "Service %s already registered", BUS_NAME); return FALSE; } connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err); if (!connection) { g_set_error (error, CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_DBUS, "Couldn't connect to system bus: %s", err->message); g_error_free (err); return FALSE; } bus_proxy = dbus_g_proxy_new_for_name (connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); if (!bus_proxy) { g_set_error (error, CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_DBUS, "Could not construct bus_proxy object"); return FALSE; } res = dbus_g_proxy_call (bus_proxy, "RequestName", &err, G_TYPE_STRING, BUS_NAME, G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE, G_TYPE_INVALID, G_TYPE_UINT, &result, G_TYPE_INVALID); g_object_unref (bus_proxy); if (!res) { if (err) { g_set_error (error, CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_DBUS, "Failed to acquire %s: %s", BUS_NAME, err->message); g_error_free (err); } else { g_set_error (error, CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_DBUS, "Failed to acquire %s", BUS_NAME); } return FALSE; } if (result == DBUS_REQUEST_NAME_REPLY_EXISTS) { g_set_error (error, CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_ALREADY_REGISTERED, "Service %s already registered", BUS_NAME); return FALSE; } service->authority = polkit_authority_get_sync (NULL, NULL); service->system_bus = connection; dbus_g_object_type_install_info (CPUFREQ_TYPE_SELECTOR_SERVICE, &dbus_glib_cpufreq_selector_service_object_info); dbus_g_connection_register_g_object (connection, "/org/mate/cpufreq_selector/selector", G_OBJECT (service)); dbus_g_error_domain_register (CPUFREQ_SELECTOR_SERVICE_ERROR, NULL, CPUFREQ_TYPE_SELECTOR_SERVICE_ERROR); reset_killtimer (); return TRUE; } static CPUFreqSelector * get_selector_for_cpu (CPUFreqSelectorService *service, guint cpu) { if (!service->selectors[cpu]) { service->selectors[cpu] = cpufreq_selector_factory_create_selector (cpu); if (!service->selectors[cpu]) return NULL; if (service->selectors_max < cpu) service->selectors_max = cpu; } return service->selectors[cpu]; } /* PolicyKit */ static gboolean cpufreq_selector_service_check_policy (CPUFreqSelectorService *service, DBusGMethodInvocation *context, GError **error) { PolkitSubject *subject; PolkitAuthorizationResult *result; gchar *sender; gboolean ret; sender = dbus_g_method_get_sender (context); subject = polkit_system_bus_name_new (sender); g_free (sender); result = polkit_authority_check_authorization_sync (service->authority, subject, "org.mate.cpufreqselector", NULL, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, NULL, error); g_object_unref (subject); if (*error) { g_warning ("Check policy: %s", (*error)->message); g_object_unref (result); return FALSE; } ret = polkit_authorization_result_get_is_authorized (result); if (!ret) { g_set_error (error, CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_NOT_AUTHORIZED, "Caller is not authorized"); } g_object_unref (result); return ret; } /* D-BUS interface */ gboolean cpufreq_selector_service_set_frequency (CPUFreqSelectorService *service, guint cpu, guint frequency, DBusGMethodInvocation *context) { CPUFreqSelector *selector; GError *error = NULL; reset_killtimer (); if (!cpufreq_selector_service_check_policy (service, context, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return FALSE; } if (cpu > MAX_CPUS) { GError *err; err = g_error_new (CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_DBUS, "Error setting frequency on cpu %d: Invalid cpu", cpu); dbus_g_method_return_error (context, err); g_error_free (err); return FALSE; } selector = get_selector_for_cpu (service, cpu); if (!selector) { GError *err; err = g_error_new (CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_DBUS, "Error setting frequency on cpu %d: No cpufreq support", cpu); dbus_g_method_return_error (context, err); g_error_free (err); return FALSE; } cpufreq_selector_set_frequency (selector, frequency, &error); if (error) { GError *err; err = g_error_new (CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_DBUS, "Error setting frequency %d on cpu %d: %s", frequency, cpu, error->message); dbus_g_method_return_error (context, err); g_error_free (err); g_error_free (error); return FALSE; } dbus_g_method_return (context); return TRUE; } gboolean cpufreq_selector_service_set_governor (CPUFreqSelectorService *service, guint cpu, const gchar *governor, DBusGMethodInvocation *context) { CPUFreqSelector *selector; GError *error = NULL; reset_killtimer (); if (!cpufreq_selector_service_check_policy (service, context, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return FALSE; } if (cpu > MAX_CPUS) { GError *err; err = g_error_new (CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_DBUS, "Error setting governor on cpu %d: Invalid cpu", cpu); dbus_g_method_return_error (context, err); g_error_free (err); return FALSE; } selector = get_selector_for_cpu (service, cpu); if (!selector) { GError *err; err = g_error_new (CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_DBUS, "Error setting governor on cpu %d: No cpufreq support", cpu); dbus_g_method_return_error (context, err); g_error_free (err); return FALSE; } cpufreq_selector_set_governor (selector, governor, &error); if (error) { GError *err; err = g_error_new (CPUFREQ_SELECTOR_SERVICE_ERROR, SERVICE_ERROR_DBUS, "Error setting governor %s on cpu %d: %s", governor, cpu, error->message); dbus_g_method_return_error (context, err); g_error_free (err); g_error_free (error); return FALSE; } dbus_g_method_return (context); return TRUE; } gboolean cpufreq_selector_service_can_set (CPUFreqSelectorService *service, DBusGMethodInvocation *context) { PolkitSubject *subject; PolkitAuthorizationResult *result; gchar *sender; gboolean ret; GError *error = NULL; reset_killtimer (); sender = dbus_g_method_get_sender (context); subject = polkit_system_bus_name_new (sender); g_free (sender); result = polkit_authority_check_authorization_sync (service->authority, subject, "org.mate.cpufreqselector", NULL, 0, NULL, &error); g_object_unref (subject); if (error) { dbus_g_method_return_error (context, error); g_error_free (error); return FALSE; } if (polkit_authorization_result_get_is_authorized (result)) { ret = TRUE; } else if (polkit_authorization_result_get_is_challenge (result)) { ret = TRUE; } else { ret = FALSE; } g_object_unref (result); dbus_g_method_return (context, ret); return TRUE; }