1/*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3/***
4  Copyright 2009 Lennart Poettering
5  Copyright 2010 David Henningsson <diwic@ubuntu.com>
6
7  Permission is hereby granted, free of charge, to any person
8  obtaining a copy of this software and associated documentation files
9  (the "Software"), to deal in the Software without restriction,
10  including without limitation the rights to use, copy, modify, merge,
11  publish, distribute, sublicense, and/or sell copies of the Software,
12  and to permit persons to whom the Software is furnished to do so,
13  subject to the following conditions:
14
15  The above copyright notice and this permission notice shall be
16  included in all copies or substantial portions of the Software.
17
18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  SOFTWARE.
26***/
27
28#include <errno.h>
29
30#include "rtkit.h"
31
32#if defined(__linux__) && !defined(__ANDROID__)
33
34#ifndef _GNU_SOURCE
35#define _GNU_SOURCE
36#endif
37
38#ifdef HAVE_CONFIG_H
39#include <config.h>
40#endif
41
42#include <string.h>
43#include <unistd.h>
44#include <sys/types.h>
45#include <sys/syscall.h>
46#include <pulsecore/core-util.h>
47
48static pid_t _gettid(void) {
49        return (pid_t) syscall(SYS_gettid);
50}
51
52static int translate_error(const char *name) {
53        if (pa_streq(name, DBUS_ERROR_NO_MEMORY))
54                return -ENOMEM;
55        if (pa_streq(name, DBUS_ERROR_SERVICE_UNKNOWN) ||
56            pa_streq(name, DBUS_ERROR_NAME_HAS_NO_OWNER))
57                return -ENOENT;
58        if (pa_streq(name, DBUS_ERROR_ACCESS_DENIED) ||
59            pa_streq(name, DBUS_ERROR_AUTH_FAILED))
60                return -EACCES;
61
62        return -EIO;
63}
64
65static long long rtkit_get_int_property(DBusConnection *connection, const char* propname, long long* propval) {
66        DBusMessage *m = NULL, *r = NULL;
67        DBusMessageIter iter, subiter;
68        dbus_int64_t i64;
69        dbus_int32_t i32;
70        DBusError error;
71        int current_type;
72        long long ret;
73        const char * interfacestr = "org.freedesktop.RealtimeKit1";
74
75        dbus_error_init(&error);
76
77        if (!(m = dbus_message_new_method_call(
78                              RTKIT_SERVICE_NAME,
79                              RTKIT_OBJECT_PATH,
80                              DBUS_INTERFACE_PROPERTIES,
81                              "Get"))) {
82                ret = -ENOMEM;
83                goto finish;
84        }
85
86        if (!dbus_message_append_args(
87                            m,
88                            DBUS_TYPE_STRING, &interfacestr,
89                            DBUS_TYPE_STRING, &propname,
90                            DBUS_TYPE_INVALID)) {
91                ret = -ENOMEM;
92                goto finish;
93        }
94
95        if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
96                ret = translate_error(error.name);
97                goto finish;
98        }
99
100        if (dbus_set_error_from_message(&error, r)) {
101                ret = translate_error(error.name);
102                goto finish;
103        }
104
105        ret = -EBADMSG;
106        dbus_message_iter_init(r, &iter);
107        while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) {
108
109                if (current_type == DBUS_TYPE_VARIANT) {
110                        dbus_message_iter_recurse(&iter, &subiter);
111
112                        while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) {
113
114                                if (current_type == DBUS_TYPE_INT32) {
115                                        dbus_message_iter_get_basic(&subiter, &i32);
116                                        *propval = i32;
117                                        ret = 0;
118                                }
119
120                                if (current_type == DBUS_TYPE_INT64) {
121                                        dbus_message_iter_get_basic(&subiter, &i64);
122                                        *propval = i64;
123                                        ret = 0;
124                                }
125
126                                dbus_message_iter_next (&subiter);
127                         }
128                }
129                dbus_message_iter_next (&iter);
130        }
131
132finish:
133
134        if (m)
135                dbus_message_unref(m);
136
137        if (r)
138                dbus_message_unref(r);
139
140        dbus_error_free(&error);
141
142        return ret;
143}
144
145int rtkit_get_max_realtime_priority(DBusConnection *connection) {
146        long long retval;
147        int err;
148
149        err = rtkit_get_int_property(connection, "MaxRealtimePriority", &retval);
150        return err < 0 ? err : retval;
151}
152
153int rtkit_get_min_nice_level(DBusConnection *connection, int* min_nice_level) {
154        long long retval;
155        int err;
156
157        err = rtkit_get_int_property(connection, "MinNiceLevel", &retval);
158        if (err >= 0)
159                *min_nice_level = retval;
160        return err;
161}
162
163long long rtkit_get_rttime_usec_max(DBusConnection *connection) {
164        long long retval;
165        int err;
166
167        err = rtkit_get_int_property(connection, "RTTimeUSecMax", &retval);
168        return err < 0 ? err : retval;
169}
170
171int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) {
172        DBusMessage *m = NULL, *r = NULL;
173        dbus_uint64_t u64;
174        dbus_uint32_t u32;
175        DBusError error;
176        int ret;
177
178        dbus_error_init(&error);
179
180        if (thread == 0)
181                thread = _gettid();
182
183        if (!(m = dbus_message_new_method_call(
184                              RTKIT_SERVICE_NAME,
185                              RTKIT_OBJECT_PATH,
186                              "org.freedesktop.RealtimeKit1",
187                              "MakeThreadRealtime"))) {
188                ret = -ENOMEM;
189                goto finish;
190        }
191
192        u64 = (dbus_uint64_t) thread;
193        u32 = (dbus_uint32_t) priority;
194
195        if (!dbus_message_append_args(
196                            m,
197                            DBUS_TYPE_UINT64, &u64,
198                            DBUS_TYPE_UINT32, &u32,
199                            DBUS_TYPE_INVALID)) {
200                ret = -ENOMEM;
201                goto finish;
202        }
203
204        if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
205                ret = translate_error(error.name);
206                goto finish;
207        }
208
209
210        if (dbus_set_error_from_message(&error, r)) {
211                ret = translate_error(error.name);
212                goto finish;
213        }
214
215        ret = 0;
216
217finish:
218
219        if (m)
220                dbus_message_unref(m);
221
222        if (r)
223                dbus_message_unref(r);
224
225        dbus_error_free(&error);
226
227        return ret;
228}
229
230int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) {
231        DBusMessage *m = NULL, *r = NULL;
232        dbus_uint64_t u64;
233        dbus_int32_t s32;
234        DBusError error;
235        int ret;
236
237        dbus_error_init(&error);
238
239        if (thread == 0)
240                thread = _gettid();
241
242        if (!(m = dbus_message_new_method_call(
243                              RTKIT_SERVICE_NAME,
244                              RTKIT_OBJECT_PATH,
245                              "org.freedesktop.RealtimeKit1",
246                              "MakeThreadHighPriority"))) {
247                ret = -ENOMEM;
248                goto finish;
249        }
250
251        u64 = (dbus_uint64_t) thread;
252        s32 = (dbus_int32_t) nice_level;
253
254        if (!dbus_message_append_args(
255                            m,
256                            DBUS_TYPE_UINT64, &u64,
257                            DBUS_TYPE_INT32, &s32,
258                            DBUS_TYPE_INVALID)) {
259                ret = -ENOMEM;
260                goto finish;
261        }
262
263
264
265        if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
266                ret = translate_error(error.name);
267                goto finish;
268        }
269
270
271        if (dbus_set_error_from_message(&error, r)) {
272                ret = translate_error(error.name);
273                goto finish;
274        }
275
276        ret = 0;
277
278finish:
279
280        if (m)
281                dbus_message_unref(m);
282
283        if (r)
284                dbus_message_unref(r);
285
286        dbus_error_free(&error);
287
288        return ret;
289}
290
291#else
292
293int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) {
294        return -ENOTSUP;
295}
296
297int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) {
298        return -ENOTSUP;
299}
300
301int rtkit_get_max_realtime_priority(DBusConnection *connection) {
302        return -ENOTSUP;
303}
304
305int rtkit_get_min_nice_level(DBusConnection *connection, int* min_nice_level) {
306        return -ENOTSUP;
307}
308
309long long rtkit_get_rttime_usec_max(DBusConnection *connection) {
310        return -ENOTSUP;
311}
312
313#endif
314