diff options
Diffstat (limited to 'core/rtkit.cpp')
-rw-r--r-- | core/rtkit.cpp | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/core/rtkit.cpp b/core/rtkit.cpp new file mode 100644 index 00000000..ff944ebf --- /dev/null +++ b/core/rtkit.cpp @@ -0,0 +1,236 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + Copyright 2009 Lennart Poettering + Copyright 2010 David Henningsson <[email protected]> + Copyright 2021 Chris Robinson + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include "config.h" + +#include "rtkit.h" + +#include <errno.h> + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <memory> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef __linux__ +#include <sys/syscall.h> +#elif defined(__FreeBSD__) +#include <sys/thr.h> +#endif + + +namespace dbus { + +constexpr int TypeString{'s'}; +constexpr int TypeVariant{'v'}; +constexpr int TypeInt32{'i'}; +constexpr int TypeUInt32{'u'}; +constexpr int TypeInt64{'x'}; +constexpr int TypeUInt64{'t'}; +constexpr int TypeInvalid{'\0'}; + +struct MessageDeleter { + void operator()(DBusMessage *m) { dbus_message_unref(m); } +}; +using MessagePtr = std::unique_ptr<DBusMessage,MessageDeleter>; + +} // namespace dbus + +namespace { + +inline pid_t _gettid() +{ +#ifdef __linux__ + return static_cast<pid_t>(syscall(SYS_gettid)); +#elif defined(__FreeBSD__) + long pid{}; + thr_self(&pid); + return static_cast<pid_t>(pid); +#else +#warning gettid not available + return 0; +#endif +} + +int translate_error(const char *name) +{ + if(strcmp(name, DBUS_ERROR_NO_MEMORY) == 0) + return -ENOMEM; + if(strcmp(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 + || strcmp(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0) + return -ENOENT; + if(strcmp(name, DBUS_ERROR_ACCESS_DENIED) == 0 + || strcmp(name, DBUS_ERROR_AUTH_FAILED) == 0) + return -EACCES; + return -EIO; +} + +int rtkit_get_int_property(DBusConnection *connection, const char *propname, long long *propval) +{ + dbus::MessagePtr m{dbus_message_new_method_call(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, + "org.freedesktop.DBus.Properties", "Get")}; + if(!m) return -ENOMEM; + + const char *interfacestr = RTKIT_SERVICE_NAME; + auto ready = dbus_message_append_args(m.get(), + dbus::TypeString, &interfacestr, + dbus::TypeString, &propname, + dbus::TypeInvalid); + if(!ready) return -ENOMEM; + + dbus::Error error; + dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(connection, m.get(), -1, + &error.get())}; + if(!r) return translate_error(error->name); + + if(dbus_set_error_from_message(&error.get(), r.get())) + return translate_error(error->name); + + int ret{-EBADMSG}; + DBusMessageIter iter{}; + dbus_message_iter_init(r.get(), &iter); + while(int curtype{dbus_message_iter_get_arg_type(&iter)}) + { + if(curtype == dbus::TypeVariant) + { + DBusMessageIter subiter{}; + dbus_message_iter_recurse(&iter, &subiter); + + while((curtype=dbus_message_iter_get_arg_type(&subiter)) != dbus::TypeInvalid) + { + if(curtype == dbus::TypeInt32) + { + dbus_int32_t i32{}; + dbus_message_iter_get_basic(&subiter, &i32); + *propval = i32; + ret = 0; + } + + if(curtype == dbus::TypeInt64) + { + dbus_int64_t i64{}; + dbus_message_iter_get_basic(&subiter, &i64); + *propval = i64; + ret = 0; + } + + dbus_message_iter_next(&subiter); + } + } + dbus_message_iter_next(&iter); + } + + return ret; +} + +} // namespace + +int rtkit_get_max_realtime_priority(DBusConnection *connection) +{ + long long retval{}; + int err{rtkit_get_int_property(connection, "MaxRealtimePriority", &retval)}; + return err < 0 ? err : static_cast<int>(retval); +} + +int rtkit_get_min_nice_level(DBusConnection *connection, int *min_nice_level) +{ + long long retval{}; + int err{rtkit_get_int_property(connection, "MinNiceLevel", &retval)}; + if(err >= 0) *min_nice_level = static_cast<int>(retval); + return err; +} + +long long rtkit_get_rttime_usec_max(DBusConnection *connection) +{ + long long retval{}; + int err{rtkit_get_int_property(connection, "RTTimeUSecMax", &retval)}; + return err < 0 ? err : retval; +} + +int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) +{ + if(thread == 0) + thread = _gettid(); + if(thread == 0) + return -ENOTSUP; + + dbus::MessagePtr m{dbus_message_new_method_call(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, + "org.freedesktop.RealtimeKit1", "MakeThreadRealtime")}; + if(!m) return -ENOMEM; + + auto u64 = static_cast<dbus_uint64_t>(thread); + auto u32 = static_cast<dbus_uint32_t>(priority); + auto ready = dbus_message_append_args(m.get(), + dbus::TypeUInt64, &u64, + dbus::TypeUInt32, &u32, + dbus::TypeInvalid); + if(!ready) return -ENOMEM; + + dbus::Error error; + dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(connection, m.get(), -1, + &error.get())}; + if(!r) return translate_error(error->name); + + if(dbus_set_error_from_message(&error.get(), r.get())) + return translate_error(error->name); + + return 0; +} + +int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) +{ + if(thread == 0) + thread = _gettid(); + if(thread == 0) + return -ENOTSUP; + + dbus::MessagePtr m{dbus_message_new_method_call(RTKIT_SERVICE_NAME, RTKIT_OBJECT_PATH, + "org.freedesktop.RealtimeKit1", "MakeThreadHighPriority")}; + if(!m) return -ENOMEM; + + auto u64 = static_cast<dbus_uint64_t>(thread); + auto s32 = static_cast<dbus_int32_t>(nice_level); + auto ready = dbus_message_append_args(m.get(), + dbus::TypeUInt64, &u64, + dbus::TypeInt32, &s32, + dbus::TypeInvalid); + if(!ready) return -ENOMEM; + + dbus::Error error; + dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(connection, m.get(), -1, + &error.get())}; + if(!r) return translate_error(error->name); + + if(dbus_set_error_from_message(&error.get(), r.get())) + return translate_error(error->name); + + return 0; +} |