summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2009-06-04 01:46:12 +0200
committerLennart Poettering <lennart@poettering.net>2009-06-04 01:46:12 +0200
commitf4dd55fa9fcf2b4a018793a2bfe3fe42e9dbc663 (patch)
tree5862cf01a7303952c9fbe5ca79c2ad8a085ebdb4
Initial commit
-rw-r--r--.gitignore3
-rw-r--r--Makefile13
-rw-r--r--rtkit-daemon.c755
-rw-r--r--rtkit-test.c84
-rw-r--r--rtkit.c153
-rw-r--r--rtkit.h52
6 files changed, 1060 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f5bd368
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+rtkit-daemon
+rtkit-test
+*.o
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..85c6ab4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+CFLAGS=-Wextra -Wall -O0 -g `pkg-config --cflags dbus-1`
+LIBS= `pkg-config --libs dbus-1`
+
+all: rtkit-daemon rtkit-test
+
+rtkit-daemon: rtkit-daemon.o
+ $(CC) $(CFLAGS) $(LIBS) -o rtkit-daemon $^
+
+rtkit-test: rtkit-test.o rtkit.o rtkit.h
+ $(CC) $(CFLAGS) $(LIBS) -o rtkit-test $^
+
+clean:
+ rm -rf rtkit-daemon *.o
diff --git a/rtkit-daemon.c b/rtkit-daemon.c
new file mode 100644
index 0000000..6a9f4ff
--- /dev/null
+++ b/rtkit-daemon.c
@@ -0,0 +1,755 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#define _GNU_SOURCE
+
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <sched.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <dbus/dbus.h>
+#include <pwd.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+
+#include "rtkit.h"
+
+#ifndef __linux__
+#error "This stuff only works on Linux!"
+#endif
+
+#ifndef SCHED_RESET_ON_FORK
+#warning "Your libc lacks the definition of SCHED_RESET_ON_FORK. We'll now define it ourselves, however make sure your kernel is new enough!"
+#define SCHED_RESET_ON_FORK 0x40000000
+#endif
+
+#ifndef RLIMIT_RTTIME
+#define RLIMIT_RTTIME 15
+#endif
+
+#define OUR_RR_PRIORITY 30
+#define OUR_NICE_LEVEL 1
+
+#define USERNAME "rtkit"
+
+#define RTKIT_RTTIME_MAX_NS 200000000ULL /* 200 ms */
+
+#define INTROSPECT_XML \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>" \
+ " <interface name=\"org.freedesktop.RealtimeKit1\">" \
+ " <method name=\"MakeThreadRealtime\">" \
+ " <arg name=\"thread\" type=\"t\" direction=\"in\"/>" \
+ " <arg name=\"priority\" type=\"u\" direction=\"in\"/>" \
+ " </method>" \
+ " <method name=\"MakeHighPriority\">" \
+ " <arg name=\"thread\" type=\"t\" direction=\"in\"/>" \
+ " <arg name=\"priority\" type=\"i\" direction=\"in\"/>" \
+ " </method>" \
+ " </interface>" \
+ " <interface name=\"org.freedesktop.DBus.Introspectable\">" \
+ " <method name=\"Introspect\">" \
+ " <arg name=\"data\" type=\"s\" direction=\"out\"/>" \
+ " </method>" \
+ " </interface>" \
+ "</node>"
+
+/* Similar to assert(), but has side effects, and hence shall never be optimized away, regardless of NDEBUG */
+#define assert_se(expr) \
+ do { \
+ if (__builtin_expect(!(expr), 0)) { \
+ fprintf(stderr, "Asssertion %s failed at %s:%u, function %s(). Aborting.\n", #expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ abort(); \
+ } \
+ } while(0)
+
+#define ELEMENTSOF(x) (sizeof(x)/sizeof(x[0]))
+
+struct process {
+ pid_t pid;
+ uid_t uid;
+ unsigned long long starttime;
+};
+
+static int startswith(const char *s, const char *prefix) {
+ return strncmp(s, prefix, strlen(prefix)) == 0;
+}
+
+static int self_set_realtime(void) {
+ struct sched_param param;
+ int r;
+
+ memset(&param, 0, sizeof(param));
+ param.sched_priority = OUR_RR_PRIORITY;
+
+ if (sched_setscheduler(0, SCHED_RR|SCHED_RESET_ON_FORK, &param) < 0) {
+ r = -errno;
+ fprintf(stderr, "Failed to make ourselves SCHED_RR: %s\n", strerror(errno));
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ return r;
+}
+
+static void self_drop_realtime(void) {
+ struct sched_param param;
+
+ memset(&param, 0, sizeof(param));
+
+ if (sched_setscheduler(0, SCHED_OTHER, &param) < 0)
+ fprintf(stderr, "Warning: Failed to reset scheduling to SCHED_OTHER: %s\n", strerror(errno));
+
+ if (setpriority(PRIO_PROCESS, 0, OUR_NICE_LEVEL) < 0)
+ fprintf(stderr, "Warning: Failed to reset nice level to %u: %s\n", OUR_NICE_LEVEL, strerror(errno));
+}
+
+static int verify_rttime(struct process *p) {
+ char fn[128];
+ FILE *f;
+ int r, good = 0;
+
+ /* Verifies that RLIMIT_RTTIME is set for the specified process */
+
+ assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%llu/limits", (unsigned long long) p->pid) < (int) (sizeof(fn)-1));
+ fn[sizeof(fn)-1] = 0;
+
+ if (!(f = fopen(fn, "r"))) {
+ r = -errno;
+ fprintf(stderr, "Failed to open '%s': %s\n", fn, strerror(errno));
+ return r;
+ }
+
+ for (;;) {
+ char line[128];
+ char soft[32], hard[32];
+ unsigned long long rttime;
+ char *e = NULL;
+
+ if (!fgets(line, sizeof(line), f))
+ break;
+
+ if (!startswith(line, "Max realtime timeout"))
+ continue;
+
+ if (sscanf(line + 20, "%s %s", soft, hard) != 2)
+ break;
+
+ errno = 0;
+ rttime = strtoll(hard, &e, 10);
+
+ if (errno != 0 || !e || *e != 0)
+ break;
+
+ if (rttime <= RTKIT_RTTIME_MAX_NS)
+ good = 1;
+
+ break;
+ }
+
+ fclose(f);
+
+ return good ? 0 : -EPERM;
+}
+
+static int verify_user(struct process *p) {
+ char fn[128];
+ int r;
+ struct stat st;
+
+ assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%llu", (unsigned long long) p->pid) < (int) (sizeof(fn)-1));
+ fn[sizeof(fn)-1] = 0;
+
+ memset(&st, 0, sizeof(st));
+ if (stat(fn, &st) < 0) {
+ r = -errno;
+ fprintf(stderr, "Failed to stat() file '%s': %s\n", fn, strerror(errno));
+ return r;
+ }
+
+ return st.st_uid == p->uid ? 0 : -EPERM;
+}
+
+static int read_starttime(pid_t pid, unsigned long long *st) {
+ char fn[128];
+ int r;
+ FILE *f;
+
+ assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%llu/stat", (unsigned long long) pid) < (int) (sizeof(fn)-1));
+ fn[sizeof(fn)-1] = 0;
+
+ if (!(f = fopen(fn, "r"))) {
+ r = -errno;
+ fprintf(stderr, "Failed to open '%s': %s\n", fn, strerror(errno));
+ return r;
+ }
+
+ if (fscanf(f,
+ "%*d " /* pid */
+ "%*s " /* comm */
+ "%*c " /* state */
+ "%*d " /* ppid */
+ "%*d " /* pgrp */
+ "%*d " /* session */
+ "%*d " /* tty_nr */
+ "%*d " /* tpgid */
+ "%*u " /* flags */
+ "%*u " /* minflt */
+ "%*u " /* cminflt */
+ "%*u " /* majflt */
+ "%*u " /* cmajflt */
+ "%*u " /* utime */
+ "%*u " /* stime */
+ "%*d " /* cutime */
+ "%*d " /* cstime */
+ "%*d " /* priority */
+ "%*d " /* nice */
+ "%*d " /* num_threads */
+ "%*d " /* itrealvalue */
+ "%llu " /* starttime */,
+ st) != 1) {
+ fclose(f);
+ return -EIO;
+ }
+
+ fclose(f);
+ return 0;
+}
+
+static int verify_starttime(struct process *p) {
+ unsigned long long st;
+ int r;
+
+ if ((r = read_starttime(p->pid, &st)) < 0)
+ return r;
+
+ return st == p->starttime ? 0 : -EPERM;
+}
+
+static int verify_thread(struct process *p, pid_t thread) {
+ char fn[128];
+
+ /* Verifies that the specified thread exists in the specified
+ * process */
+
+ assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%llu/task/%llu", (unsigned long long) p->pid, (unsigned long long) thread) < (int) (sizeof(fn)-1));
+ fn[sizeof(fn)-1] = 0;
+
+ return access(fn, F_OK) == 0 ? 0 : -errno;
+}
+
+static void thread_reset(pid_t thread) {
+ struct sched_param param;
+
+ memset(&param, 0, sizeof(param));
+ param.sched_priority = 0;
+
+ if (sched_setscheduler(thread, SCHED_OTHER, &param) < 0)
+ fprintf(stderr, "Warning: Failed to reset scheduling to SCHED_OTHER for thread %llu: %s\n", (unsigned long long) thread, strerror(errno));
+
+ if (setpriority(PRIO_PROCESS, thread, 0) < 0)
+ fprintf(stderr, "Warning: Failed to reset nice level to 0 for thread %llu: %s\n", (unsigned long long) thread, strerror(errno));
+}
+
+static char* get_exe_name(pid_t pid, char *exe, size_t len) {
+ char fn[128];
+ ssize_t n;
+
+ assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%llu/exe", (unsigned long long) pid) < (int) (sizeof(fn)-1));
+ fn[sizeof(fn)-1] = 0;
+
+ if ((n = readlink(fn, exe, len-1)) < 0) {
+ snprintf(exe, len-1, "n/a");
+ exe[len-1] = 0;
+ } else
+ exe[n] = 0;
+
+ return exe;
+}
+
+static char* get_user_name(uid_t uid, char *user, size_t len) {
+ struct passwd *pw;
+
+ if ((pw = getpwuid(uid))) {
+ strncpy(user, pw->pw_name, len-1);
+ user[len-1] = 0;
+ return user;
+ }
+
+ snprintf(user, len-1, "%llu", (unsigned long long) uid);
+ user[len-1] = 0;
+ return user;
+}
+
+static int process_set_realtime(struct process *p, pid_t thread, unsigned priority) {
+ int r;
+ struct sched_param param;
+ char user[64], exe[128];
+
+ if (thread < 0)
+ return -EINVAL;
+
+ if ((int) priority < sched_get_priority_min(SCHED_RR) ||
+ (int) priority > sched_get_priority_max(SCHED_RR))
+ return -EINVAL;
+
+ if (priority >= OUR_RR_PRIORITY)
+ return -EPERM;
+
+ if (thread == 0)
+ thread = p->pid;
+
+ /* Temporarily become a realtime process. We do this to make
+ * sure that our verification code is not preempted by an evil
+ * client's code which might have gotten SCHED_RR through
+ * us. */
+ if ((r = self_set_realtime()) < 0)
+ return r;
+
+ /* Let's make sure that everything is alright before we make
+ * the process realtime */
+ if ((r = verify_user(p)) < 0 ||
+ (r = verify_starttime(p)) < 0 ||
+ (r = verify_rttime(p)) < 0 ||
+ (r = verify_thread(p, thread)) < 0)
+ goto finish;
+
+ /* Ok, everything seems to be in order, now, let's do it */
+ memset(&param, 0, sizeof(param));
+ param.sched_priority = (int) priority;
+ if (sched_setscheduler(thread, SCHED_RR|SCHED_RESET_ON_FORK, &param) < 0) {
+ r = -errno;
+ fprintf(stderr, "Failed to make thread %llu SCHED_RR: %s\n", (unsigned long long) thread, strerror(errno));
+ goto finish;
+ }
+
+ /* We do some sanity checks afterwards, to verify that the
+ * caller didn't play games with us and replaced the process
+ * behind the PID */
+ if ((r = verify_thread(p, thread)) < 0 ||
+ (r = verify_rttime(p)) < 0 ||
+ (r = verify_starttime(p)) < 0 ||
+ (r = verify_user(p)) < 0) {
+
+ thread_reset(thread);
+ goto finish;
+ }
+
+ fprintf(stderr, "Sucessfully made thread %llu of process %llu (%s) owned by '%s' SCHED_RR at priority %u.\n",
+ (unsigned long long) thread,
+ (unsigned long long) p->pid,
+ get_exe_name(p->pid, exe, sizeof(exe)),
+ get_user_name(p->uid, user, sizeof(user)),
+ priority);
+
+ r = 0;
+
+finish:
+ self_drop_realtime();
+
+ return r;
+}
+
+static int process_set_high_priority(struct process *p, pid_t thread, int priority) {
+ int r;
+ struct sched_param param;
+ char user[64], exe[128];
+
+ if (thread < 0)
+ return -EINVAL;
+
+ if (priority < -20 || priority > 19)
+ return -EINVAL;
+
+ if (thread == 0)
+ thread = p->pid;
+
+ /* Temporarily become a realtime process */
+ if ((r = self_set_realtime()) < 0)
+ return r;
+
+ /* Let's make sure that everything is alright before we make
+ * the process high priority */
+ if ((r = verify_user(p)) < 0 ||
+ (r = verify_starttime(p)) < 0 ||
+ (r = verify_thread(p, thread)) < 0)
+ goto finish;
+
+ /* Ok, everything seems to be in order, now, let's do it */
+ memset(&param, 0, sizeof(param));
+ param.sched_priority = 0;
+ if (sched_setscheduler(thread, SCHED_OTHER|SCHED_RESET_ON_FORK, &param) < 0) {
+ r = -errno;
+ fprintf(stderr, "Failed to make process %llu SCHED_NORMAL: %s\n", (unsigned long long) thread, strerror(errno));
+ goto finish;
+ }
+
+ if (setpriority(PRIO_PROCESS, thread, priority) < 0) {
+ r = -errno;
+ fprintf(stderr, "Failed to set nice level of process %llu to %i: %s\n", (unsigned long long) thread, priority, strerror(errno));
+ goto finish;
+ }
+
+ if ((r = verify_thread(p, thread)) < 0 ||
+ (r = verify_starttime(p)) < 0 ||
+ (r = verify_user(p)) < 0) {
+
+ thread_reset(thread);
+ goto finish;
+ }
+
+ fprintf(stderr, "Sucessfully made thread %llu of process %llu (%s) owned by '%s' high priority at nice level %i.\n",
+ (unsigned long long) thread,
+ (unsigned long long) p->pid,
+ get_exe_name(p->pid, exe, sizeof(exe)),
+ get_user_name(p->uid, user, sizeof(user)),
+ priority);
+
+ r = 0;
+
+finish:
+ self_drop_realtime();
+
+ return r;
+}
+
+/* This mimics dbus_bus_get_unix_user() */
+static unsigned long get_unix_process_id(
+ DBusConnection *connection,
+ const char *name,
+ DBusError *error) {
+
+ DBusMessage *m, *r;
+ uint32_t pid = (uint32_t) -1;
+
+ assert_se(m = dbus_message_new_method_call(
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetConnectionUnixProcessID"));
+
+ assert_se(dbus_message_append_args(
+ m,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID));
+
+ r = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
+ dbus_message_unref (m);
+
+ if (!r)
+ goto finish;
+
+ if (dbus_set_error_from_message(error, r))
+ goto finish;
+
+ if (!dbus_message_get_args(
+ r, error,
+ DBUS_TYPE_UINT32, &pid,
+ DBUS_TYPE_INVALID)) {
+ pid = (uint32_t) -1;
+ goto finish;
+ }
+
+finish:
+
+ if (r)
+ dbus_message_unref(r);
+
+ return (unsigned long) pid;
+}
+
+static int process_fill(struct process *p, DBusConnection *c, DBusMessage *m) {
+ DBusError error;
+ int r;
+ unsigned long pid, uid;
+
+ dbus_error_init(&error);
+
+ if ((uid = dbus_bus_get_unix_user(c, dbus_message_get_sender(m), &error)) == (unsigned long) -1) {
+ fprintf(stderr, "dbus_message_get_unix_user() failed: %s\n", error.message);
+ r = -EIO;
+ goto fail;
+ }
+
+ p->uid = (uid_t) uid;
+
+ if ((pid = get_unix_process_id(c, dbus_message_get_sender(m), &error)) == (unsigned long) -1) {
+ fprintf(stderr, "get_unix_process_id() failed: %s\n", error.message);
+ r = -EIO;
+ goto fail;
+ }
+
+ p->pid = (uid_t) pid;
+
+ if ((r = read_starttime(p->pid, &p->starttime)) < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static DBusHandlerResult dbus_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
+ DBusError error;
+ DBusMessage *r = NULL;
+
+ dbus_error_init(&error);
+
+ if (dbus_message_is_method_call(m, "org.freedesktop.RealtimeKit1", "MakeThreadRealtime")) {
+
+ uint64_t thread;
+ uint32_t priority;
+ struct process p;
+ int ret;
+
+ if (!dbus_message_get_args(m, &error,
+ DBUS_TYPE_UINT64, &thread,
+ DBUS_TYPE_UINT32, &priority,
+ DBUS_TYPE_INVALID)) {
+
+ fprintf(stderr, "Failed to parse MakeThreadRealtime() method call: %s\n", error.message);
+ assert_se(r = dbus_message_new_error(m, error.name, error.message));
+
+ goto finish;
+ }
+
+ if ((ret = process_fill(&p, c, m)) < 0) {
+ assert_se(r = dbus_message_new_error_printf(m, DBUS_ERROR_FAILED, strerror(-ret)));
+ goto finish;
+ }
+
+ if ((ret = process_set_realtime(&p, (pid_t) thread, priority))) {
+ assert_se(r = dbus_message_new_error_printf(m, DBUS_ERROR_FAILED, strerror(-ret)));
+ goto finish;
+ }
+
+ assert_se(r = dbus_message_new_method_return(m));
+
+ } else if (dbus_message_is_method_call(m, "org.freedesktop.RealtimeKit1", "MakeThreadHighPriority")) {
+
+ uint64_t thread;
+ int32_t priority;
+ struct process p;
+ int ret;
+
+ if (!dbus_message_get_args(m, &error,
+ DBUS_TYPE_UINT64, &thread,
+ DBUS_TYPE_INT32, &priority,
+ DBUS_TYPE_INVALID)) {
+
+ fprintf(stderr, "Failed to parse MakeThreadHighPriority() method call: %s\n", error.message);
+ assert_se(r = dbus_message_new_error(m, error.name, error.message));
+
+ goto finish;
+ }
+
+ if ((ret = process_fill(&p, c, m)) < 0) {
+ assert_se(r = dbus_message_new_error_printf(m, DBUS_ERROR_FAILED, strerror(-ret)));
+ goto finish;
+ }
+
+ if ((ret = process_set_high_priority(&p, (pid_t) thread, priority))) {
+ assert_se(r = dbus_message_new_error_printf(m, DBUS_ERROR_FAILED, strerror(-ret)));
+ goto finish;
+ }
+
+ assert_se(r = dbus_message_new_method_return(m));
+
+ } else if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+ const char *xml = INTROSPECT_XML;
+
+ assert_se(r = dbus_message_new_method_return(m));
+ assert_se(dbus_message_append_args(
+ r,
+ DBUS_TYPE_STRING, &xml,
+ DBUS_TYPE_INVALID));
+ } else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+finish:
+
+ if (r) {
+ assert_se(dbus_connection_send(c, r, NULL));
+ dbus_message_unref(r);
+ }
+
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static int setup_dbus(DBusConnection **c) {
+ static const DBusObjectPathVTable vtable = {
+ .message_function = dbus_handler,
+ };
+
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (!(*c = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
+ fprintf(stderr, "Failed to connect to system bus: %s\n", error.message);
+ goto fail;
+ }
+
+ if (dbus_bus_request_name(*c, RTKIT_SERVICE_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
+ fprintf(stderr, "Failed to register name on bus: %s\n", error.message);
+ goto fail;
+ }
+
+ assert_se(dbus_connection_register_object_path(*c, RTKIT_OBJECT_PATH, &vtable, NULL));
+
+ return 0;
+
+fail:
+ dbus_error_free(&error);
+ return -EIO;
+}
+
+static int drop_priviliges(void) {
+ struct passwd *pw;
+ int r;
+ cap_t caps;
+ const cap_value_t cap_values[] = {
+ CAP_SYS_NICE, /* Needed for obvious reasons */
+ CAP_DAC_READ_SEARCH, /* Needed so that we can verify resource limits */
+ CAP_SYS_PTRACE /* Needed so that we can read /proc/$$/exe. Linux is weird. */
+ };
+
+ if (!(pw = getpwnam(USERNAME))) {
+ fprintf(stderr, "Failed to find user '%s'.\n", USERNAME);
+ return -ENOENT;
+ }
+
+ /* First, say that we want to keep caps */
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ r = -errno;
+ fprintf(stderr, "PR_SET_KEEPCAPS failed: %s\n", strerror(errno));
+ return r;
+ }
+
+ /* Second, drop privs */
+ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0 ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0) {
+ r = -errno;
+ fprintf(stderr, "Failed to become %s: %s\n", USERNAME, strerror(errno));
+ return r;
+ }
+
+ /* Third, reset caps flag */
+ if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
+ r = -errno;
+ fprintf(stderr, "PR_SET_KEEPCAPS failed: %s\n", strerror(errno));
+ return r;
+ }
+
+ /* Fourth, reduce caps */
+ assert_se(caps = cap_init());
+ assert_se(cap_clear(caps) == 0);
+ assert_se(cap_set_flag(caps, CAP_EFFECTIVE, ELEMENTSOF(cap_values), cap_values, CAP_SET) == 0);
+ assert_se(cap_set_flag(caps, CAP_PERMITTED, ELEMENTSOF(cap_values), cap_values, CAP_SET) == 0);
+
+ if (cap_set_proc(caps) < 0) {
+ r = -errno;
+ fprintf(stderr, "cap_set_proc() failed: %s\n", strerror(errno));
+ return r;
+ }
+
+ /* Fifth, update environment */
+ setenv("USER", USERNAME, 1);
+ setenv("USERNAME", USERNAME, 1);
+ setenv("LOGNAME", USERNAME, 1);
+ setenv("HOME", pw->pw_dir, 1);
+
+ fprintf(stderr, "Sucessfully dropped priviliges.\n");
+
+ return 0;
+}
+
+static int set_resource_limits(void) {
+
+ static const struct {
+ int id;
+ const char *name;
+ rlim_t value;
+ } table[] = {
+ { .id = RLIMIT_FSIZE, .name = "RLIMIT_FSIZE", .value = 0 },
+ { .id = RLIMIT_MEMLOCK, .name = "RLIMIT_MEMLOCK", .value = 0 },
+ { .id = RLIMIT_MSGQUEUE, .name = "RLIMIT_MSGQUEUE", .value = 0 },
+ { .id = RLIMIT_NICE, .name = "RLIMIT_NICE", .value = 20 },
+ { .id = RLIMIT_NOFILE, .name = "RLIMIT_NOFILE", .value = 50 },
+ { .id = RLIMIT_NPROC, .name = "RLIMIT_NPROC", .value = 1 },
+ { .id = RLIMIT_RTPRIO, .name = "RLIMIT_RTPRIO", .value = 0 }, /* Since we have CAP_SYS_NICE we don't need this */
+ { .id = RLIMIT_RTTIME, .name = "RLIMIT_RTTIME", .value = RTKIT_RTTIME_MAX_NS } /* Do as I say AND do as I do */
+ };
+
+ unsigned u;
+ int r;
+
+ for (u = 0; u < ELEMENTSOF(table); u++) {
+ struct rlimit rlim;
+
+ if (getrlimit(table[u].id, &rlim) < 0) {
+ r = -errno;
+ fprintf(stderr, "Failed to get %s: %s\n", table[u].name, strerror(errno));
+ return r;
+ }
+
+ if (rlim.rlim_max < table[u].value)
+ continue;
+
+ rlim.rlim_cur = rlim.rlim_max = table[u].value;
+
+ if (setrlimit(table[u].id, &rlim) < 0) {
+ r = -errno;
+ fprintf(stderr, "Failed to set %s: %s\n", table[u].name, strerror(errno));
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ DBusConnection *bus = NULL;
+ int ret = 1;
+
+ self_drop_realtime();
+
+ if (drop_priviliges() < 0)
+ goto finish;
+
+ if (set_resource_limits() < 0)
+ goto finish;
+
+ assert_se(chdir("/") == 0);
+ umask(0777);
+
+ if (setup_dbus(&bus) < 0)
+ goto finish;
+
+ fprintf(stderr, "Running.\n");
+
+ while (dbus_connection_read_write_dispatch(bus, -1))
+ ;
+
+ ret = 0;
+
+finish:
+
+ if (bus)
+ dbus_connection_unref(bus);
+
+ return ret;
+}
diff --git a/rtkit-test.c b/rtkit-test.c
new file mode 100644
index 0000000..59244f8
--- /dev/null
+++ b/rtkit-test.c
@@ -0,0 +1,84 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#include <errno.h>
+#include <string.h>
+#include <sched.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "rtkit.h"
+
+#ifndef SCHED_RESET_ON_FORK
+#define SCHED_RESET_ON_FORK 0x40000000
+#endif
+
+static void print_status(const char *t) {
+ int ret;
+
+ if ((ret = sched_getscheduler(0)) < 0) {
+ fprintf(stderr, "sched_getscheduler() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ printf("%s:\n"
+ "\tSCHED_RESET_ON_FORK: %s\n",
+ t,
+ (ret & SCHED_RESET_ON_FORK) ? "yes" : "no");
+
+ if ((ret & ~SCHED_RESET_ON_FORK) == SCHED_RR) {
+ struct sched_param param;
+
+ if (sched_getparam(0, &param) < 0) {
+ fprintf(stderr, "sched_getschedparam() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ printf("\tSCHED_RR with priority %i\n", param.sched_priority);
+
+ } else if ((ret & ~SCHED_RESET_ON_FORK) == SCHED_OTHER) {
+ errno = 0;
+ ret = getpriority(PRIO_PROCESS, 0);
+ if (errno != 0) {
+ fprintf(stderr, "getpriority() failed: %s\n", strerror(errno));
+ return;
+ }
+
+ printf("\tSCHED_OTHER with nice level: %i\n", ret);
+
+ } else
+ fprintf(stderr, "Neither SCHED_RR nor SCHED_OTHER.\n");
+}
+
+int main(int argc, char *argv[]) {
+ DBusError error;
+ DBusConnection *bus;
+ int r;
+
+ dbus_error_init(&error);
+
+ if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
+ fprintf(stderr, "Failed to connect to system bus: %s\n", error.message);
+ return 1;
+ }
+
+ print_status("before");
+
+ if ((r = rtkit_make_high_priority(bus, 0, -10)) < 0)
+ fprintf(stderr, "Failed to become high priority: %s\n", strerror(-r));
+ else
+ printf("Sucessfully became high priority.\n");
+
+ print_status("after high priority");
+
+ if ((r = rtkit_make_realtime(bus, 0, 10)) < 0)
+ fprintf(stderr, "Failed to become realtime: %s\n", strerror(-r));
+ else
+ printf("Sucessfully became realtime.\n");
+
+ print_status("after realtime");
+
+ dbus_connection_unref(bus);
+
+ return 0;
+}
diff --git a/rtkit.c b/rtkit.c
new file mode 100644
index 0000000..67a44f7
--- /dev/null
+++ b/rtkit.c
@@ -0,0 +1,153 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+ Copyright 2009 Lennart Poettering
+
+ 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 <string.h>
+#include <errno.h>
+
+#include "rtkit.h"
+
+static 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_make_realtime(DBusConnection *connection, pid_t thread, int priority) {
+ DBusMessage *m = NULL, *r = NULL;
+ dbus_uint64_t u64;
+ dbus_uint32_t u32;
+ DBusError error;
+ int ret;
+
+ dbus_error_init(&error);
+
+ if (!(m = dbus_message_new_method_call(
+ RTKIT_SERVICE_NAME,
+ RTKIT_OBJECT_PATH,
+ "org.freedesktop.RealtimeKit1",
+ "MakeThreadRealtime"))) {
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ u64 = (dbus_uint64_t) thread;
+ u32 = (dbus_uint32_t) priority;
+
+ if (!dbus_message_append_args(
+ m,
+ DBUS_TYPE_UINT64, &u64,
+ DBUS_TYPE_UINT32, &u32,
+ DBUS_TYPE_INVALID)) {
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
+ ret = translate_error(error.name);
+ goto finish;
+ }
+
+
+ if (dbus_set_error_from_message(&error, r)) {
+ ret = translate_error(error.name);
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+
+ if (m)
+ dbus_message_unref(m);
+
+ if (r)
+ dbus_message_unref(r);
+
+ return ret;
+}
+
+int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) {
+ DBusMessage *m = NULL, *r = NULL;
+ dbus_uint64_t u64;
+ dbus_int32_t s32;
+ DBusError error;
+ int ret;
+
+ dbus_error_init(&error);
+
+ if (!(m = dbus_message_new_method_call(
+ RTKIT_SERVICE_NAME,
+ RTKIT_OBJECT_PATH,
+ "org.freedesktop.RealtimeKit1",
+ "MakeThreadHighPriority"))) {
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ u64 = (dbus_uint64_t) thread;
+ s32 = (dbus_int32_t) nice_level;
+
+ if (!dbus_message_append_args(
+ m,
+ DBUS_TYPE_UINT64, &u64,
+ DBUS_TYPE_INT32, &s32,
+ DBUS_TYPE_INVALID)) {
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+
+
+ if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
+ ret = translate_error(error.name);
+ goto finish;
+ }
+
+
+ if (dbus_set_error_from_message(&error, r)) {
+ ret = translate_error(error.name);
+ goto finish;
+ }
+
+ ret = 0;
+
+finish:
+
+ if (m)
+ dbus_message_unref(m);
+
+ if (r)
+ dbus_message_unref(r);
+
+ return ret;
+}
diff --git a/rtkit.h b/rtkit.h
new file mode 100644
index 0000000..5796ad8
--- /dev/null
+++ b/rtkit.h
@@ -0,0 +1,52 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foortkithfoo
+#define foortkithfoo
+
+/***
+ Copyright 2009 Lennart Poettering
+
+ 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 <sys/types.h>
+#include <dbus/dbus.h>
+
+/* This is the reference implementation for a client for
+ * RealtimeKit. You don't have to use this, but if do, just copy these
+ * sources into your repository */
+
+#define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1"
+#define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1"
+
+/* This is mostly equivalent to sched_setparam(thread, SCHED_RR, {
+ * .sched_priority = priority }). If thread is 0 the calling
+ * processe's main thread is used. The returned value is a negative
+ * errno error code, or 0 on success. */
+int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority);
+
+/* This is mostly equivalent to setpriority(PRIO_PROCESS, thread,
+ * nice_level). If thread is 0 the calling processe's main thread is
+ * used. The returned value is a negative errno error code, or 0 on
+ * success.*/
+int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level);
+
+#endif