├── config.h.meson
├── .editorconfig
├── LICENSE
├── .gitignore
├── org.freedesktop.RealtimeKit1.service.in
├── rtkit-daemon.service.in
├── meson_options.txt
├── org.freedesktop.RealtimeKit1.conf
├── rtkitctl.8
├── autogen.sh
├── org.freedesktop.RealtimeKit1.policy
├── org.freedesktop.RealtimeKit1.xml
├── Makefile.am
├── rtkit.h
├── meson.build
├── rtkit-test.c
├── configure.ac
├── rtkitctl.c
├── README
├── rtkit.c
├── m4
├── attributes.m4
└── acx_pthread.m4
├── GPL
└── rtkit-daemon.c
/config.h.meson:
--------------------------------------------------------------------------------
1 | #ifndef fooconfighfoo
2 | #define fooconfighfoo
3 |
4 | #mesondefine PACKAGE_VERSION
5 |
6 | #mesondefine HAVE_LIBSYSTEMD
7 |
8 | #endif
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | trim_trailing_whitespace = true
7 | charset = utf-8
8 | indent_style = space
9 | indent_size = 8
10 |
11 | [Makefile.am]
12 | indent_style = tab
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | LICENSES
2 |
3 | The daemon itself is GPL3 licensed, for more information see the GPL
4 | file.
5 |
6 | The client library is BSD licensed, for more information see the
7 | headers of the appropriate files.
8 |
9 | Lennart Poettering, 2009
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.cache
2 | *.o
3 | .deps/
4 | .libs/
5 | /*.tar.gz
6 | /*.tar.xz
7 | /Makefile
8 | /Makefile.in
9 | /aclocal.m4
10 | /build-aux/
11 | /build/
12 | /compile
13 | /compile_commands.json
14 | /config.guess
15 | /config.h
16 | /config.h.in
17 | /config.h.in~
18 | /config.log
19 | /config.status
20 | /config.sub
21 | /configure
22 | /depcomp
23 | /install-sh
24 | /missing
25 | /org.freedesktop.RealtimeKit1.service
26 | /rtkit-daemon
27 | /rtkit-daemon.service
28 | /rtkit-test
29 | /rtkitctl
30 | /xml-introspection.h
31 | stamp*
32 |
--------------------------------------------------------------------------------
/org.freedesktop.RealtimeKit1.service.in:
--------------------------------------------------------------------------------
1 | # This file is part of RealtimeKit.
2 | #
3 | # Copyright 2009 Lennart Poettering
4 | #
5 | # RealtimeKit is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # RealtimeKit is distributed in the hope that it will be useful, but
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | # General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with RealtimeKit. If not, see .
17 |
18 | [D-BUS Service]
19 | Name=org.freedesktop.RealtimeKit1
20 | Exec=@LIBEXECDIR@/rtkit-daemon
21 | SystemdService=rtkit-daemon.service
22 | User=root
23 |
--------------------------------------------------------------------------------
/rtkit-daemon.service.in:
--------------------------------------------------------------------------------
1 | # This file is part of RealtimeKit.
2 | #
3 | # Copyright 2010 Lennart Poettering
4 | #
5 | # RealtimeKit is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # RealtimeKit is distributed in the hope that it will be useful, but
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | # General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with RealtimeKit. If not, see .
17 |
18 | [Unit]
19 | Description=RealtimeKit Scheduling Policy Service
20 |
21 | [Service]
22 | ExecStart=@LIBEXECDIR@/rtkit-daemon
23 | Type=dbus
24 | BusName=org.freedesktop.RealtimeKit1
25 | NotifyAccess=main
26 | CapabilityBoundingSet=CAP_SYS_NICE CAP_DAC_READ_SEARCH CAP_SYS_CHROOT CAP_SETGID CAP_SETUID
27 | PrivateNetwork=yes
28 |
29 | [Install]
30 | WantedBy=multi-user.target
31 |
--------------------------------------------------------------------------------
/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'dbus_interfacedir',
3 | type: 'string',
4 | value: '',
5 | description: 'Directory for D-Bus interface definitions',
6 | )
7 | option(
8 | 'dbus_rulesdir',
9 | type: 'string',
10 | value: '',
11 | description: 'Directory for D-Bus system bus policy rules',
12 | )
13 | option(
14 | 'dbus_systemservicedir',
15 | type: 'string',
16 | value: '',
17 | description: 'Directory for D-Bus system services',
18 | )
19 | option(
20 | 'libsystemd',
21 | type: 'feature',
22 | value: 'auto',
23 | description: 'Use the libsystemd sd-daemon API to communicate with systemd'
24 | )
25 | option(
26 | 'polkit_actiondir',
27 | type: 'string',
28 | value: '',
29 | description: 'Directory for Polkit actions',
30 | )
31 | option(
32 | 'systemd_systemunitdir',
33 | type: 'string',
34 | value: '',
35 | description: 'Directory for systemd system units',
36 | )
37 | option(
38 | 'installed_tests',
39 | type: 'boolean',
40 | value: true,
41 | description: 'Install tests'
42 | )
43 |
--------------------------------------------------------------------------------
/org.freedesktop.RealtimeKit1.conf:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
23 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/rtkitctl.8:
--------------------------------------------------------------------------------
1 | .\" Hey, EMACS: -*- nroff -*-
2 | .\" First parameter, NAME, should be all caps
3 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
4 | .\" other parameters are allowed: see man(7), man(1)
5 | .TH RTKITCTL 8 "July 1, 2009"
6 | .\" Please adjust this date whenever revising the manpage.
7 | .\"
8 | .\" Some roff macros, for reference:
9 | .\" .nh disable hyphenation
10 | .\" .hy enable hyphenation
11 | .\" .ad l left justify
12 | .\" .ad b justify to both left and right margins
13 | .\" .nf disable filling
14 | .\" .fi enable filling
15 | .\" .br insert line break
16 | .\" .sp insert n+1 empty lines
17 | .\" for manpage-specific macros, see man(7)
18 | .SH NAME
19 | rtkitctl \- Realtime Policy and Watchdog daemon control
20 | .SH SYNOPSIS
21 | .B rtkitctl
22 | .RI [ options ]
23 | .SH DESCRIPTION
24 | .B RealtimeKit
25 | is a D-Bus system service that changes the scheduling policy of user processes/threads to SCHED_RR (i.e. realtime scheduling mode) on request. It is intended to be used as a secure mechanism to allow real-time scheduling to be used by normal user processes.
26 | .SH OPTIONS
27 | .TP
28 | .B \-h, \-\-help
29 | Show a summary of options.
30 | .TP
31 | .B \-\-version
32 | Show version.
33 | .TP
34 | .B \-\-reset\-known
35 | Reset real-time status of known threads.
36 | .TP
37 | .B \-\-reset\-all
38 | Reset real-time status of all threads.
39 | .TP
40 | .B \-\-start
41 | Start RealtimeKit if it is not running already.
42 | .TP
43 | .B \-k, \-\-exit
44 | Terminate running RealtimeKit daemon.
45 | .SH AUTHOR
46 | RealtimeKit was written by Lennart Poettering .
47 |
--------------------------------------------------------------------------------
/autogen.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This file is part of RealtimeKit.
4 | #
5 | # Copyright 2009 Lennart Poettering
6 | #
7 | # RealtimeKit is free software: you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # RealtimeKit is distributed in the hope that it will be useful, but
13 | # WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 | # General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with RealtimeKit. If not, see .
19 |
20 | if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then
21 | cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \
22 | chmod +x .git/hooks/pre-commit && \
23 | echo "Activated pre-commit hook."
24 | fi
25 |
26 | autoreconf --force --install --symlink
27 |
28 | libdir() {
29 | echo $(cd $1/$(gcc -print-multi-os-directory); pwd)
30 | }
31 |
32 | args="\
33 | --sysconfdir=/etc \
34 | --localstatedir=/var \
35 | --libdir=$(libdir /usr/lib) \
36 | --libexecdir=/usr/lib"
37 |
38 | if [ "x$1" == "xc" ]; then
39 | ./configure CFLAGS='-g -O0 -Wp,-U_FORTIFY_SOURCE' $args
40 | make clean
41 | else
42 | echo
43 | echo "----------------------------------------------------------------"
44 | echo "Initialized build system. For a common configuration please run:"
45 | echo "----------------------------------------------------------------"
46 | echo
47 | echo "./configure CFLAGS='-g -O0 -Wp,-U_FORTIFY_SOURCE' $args"
48 | echo
49 | fi
50 |
--------------------------------------------------------------------------------
/org.freedesktop.RealtimeKit1.policy:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Lennart Poettering
7 |
8 |
9 | Grant high priority scheduling to a user process
10 | Bir sürece yüksek öncelikli çalışabilme yetkisi ver
11 | Authentication is required to grant an application high priority scheduling
12 | Sürecin yüksek öncelikli çalıştırılabilmesi için yetki gerekiyor
13 |
14 | no
15 | yes
16 | yes
17 |
18 |
19 |
20 |
21 | Grant realtime scheduling to a user process
22 | Bir sürece gerçek zamanlı çalışabilme yetkisi ver
23 | Authentication is required to grant an application realtime scheduling
24 | Sürecin gerçek zamanlı çalıştırılabilmesi için yetki gerekiyor
25 |
26 | no
27 | yes
28 | yes
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/org.freedesktop.RealtimeKit1.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Makefile.am:
--------------------------------------------------------------------------------
1 | # This file is part of RealtimeKit.
2 | #
3 | # Copyright 2009 Lennart Poettering
4 | #
5 | # RealtimeKit is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # RealtimeKit is distributed in the hope that it will be useful, but
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | # General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with RealtimeKit. If not, see .
17 |
18 | AM_CFLAGS = $(WARNINGFLAGS) $(PTHREAD_CFLAGS)
19 | AM_LDFLAGS = $(GCLDFLAGS)
20 |
21 | ACLOCAL_AMFLAGS = -I m4
22 |
23 | policykitdir = $(datadir)/polkit-1/actions/
24 | dbussystemdir = $(datadir)/dbus-1/system.d/
25 | dbusservicedir = $(datadir)/dbus-1/system-services/
26 | dbusinterfacedir = $(datadir)/dbus-1/interfaces/
27 | installedtestsdir = $(libexecdir)/installed-tests/rtkit/
28 |
29 | EXTRA_DIST = \
30 | README \
31 | GPL \
32 | LICENSE \
33 | config.h.meson \
34 | meson.build \
35 | meson_options.txt \
36 | org.freedesktop.RealtimeKit1.policy \
37 | org.freedesktop.RealtimeKit1.conf \
38 | org.freedesktop.RealtimeKit1.service.in \
39 | org.freedesktop.RealtimeKit1.xml \
40 | rtkit-daemon.service.in
41 |
42 | CLEANFILES = \
43 | org.freedesktop.RealtimeKit1.service \
44 | rtkit-daemon.service
45 |
46 | libexec_PROGRAMS = \
47 | rtkit-daemon
48 | sbin_PROGRAMS = \
49 | rtkitctl
50 | installedtests_PROGRAMS = \
51 | rtkit-test
52 | policykit_DATA = \
53 | org.freedesktop.RealtimeKit1.policy
54 | dbussystem_DATA = \
55 | org.freedesktop.RealtimeKit1.conf
56 | dbusservice_DATA = \
57 | org.freedesktop.RealtimeKit1.service
58 | dbusinterface_DATA = \
59 | org.freedesktop.RealtimeKit1.xml
60 |
61 | if HAVE_SYSTEMD
62 | systemdsystemunit_DATA = \
63 | rtkit-daemon.service
64 | endif
65 |
66 | rtkit_daemon_SOURCES = \
67 | rtkit-daemon.c \
68 | rtkit.h \
69 | xml-introspection.h
70 | rtkit_daemon_LDADD = \
71 | $(DBUS_LIBS) \
72 | $(LIBSYSTEMD_LIBS)
73 | rtkit_daemon_CFLAGS = \
74 | $(AM_CFLAGS) \
75 | $(DBUS_CFLAGS) \
76 | $(LIBSYSTEMD_CFLAGS)
77 |
78 | rtkitctl_SOURCES = \
79 | rtkitctl.c rtkit.h
80 | rtkitctl_LDADD = \
81 | $(DBUS_LIBS)
82 | rtkitctl_CFLAGS = \
83 | $(AM_CFLAGS) \
84 | $(DBUS_CFLAGS)
85 |
86 | rtkit_test_SOURCES = \
87 | rtkit-test.c rtkit.c rtkit.h
88 | rtkit_test_LDADD = \
89 | $(DBUS_LIBS)
90 | rtkit_test_CFLAGS = \
91 | $(AM_CFLAGS) \
92 | $(DBUS_CFLAGS)
93 |
94 | org.freedesktop.RealtimeKit1.service: org.freedesktop.RealtimeKit1.service.in
95 | $(AM_V_GEN)$(SED) -e 's,@LIBEXECDIR\@,$(libexecdir),g' < $< > $@
96 |
97 | rtkit-daemon.service: rtkit-daemon.service.in
98 | $(AM_V_GEN)$(SED) -e 's,@LIBEXECDIR\@,$(libexecdir),g' < $< > $@
99 |
100 | xml-introspection.h: org.freedesktop.RealtimeKit1.xml
101 | $(AM_V_GEN)$(XXD) -i < $< > $@
102 |
103 | BUILT_SOURCES = \
104 | xml-introspection.h
105 |
106 | man8_MANS = rtkitctl.8
107 |
108 | EXTRA_DIST += \
109 | $(man8_MANS)
110 |
111 | DISTCHECK_CONFIGURE_FLAGS = \
112 | --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
113 |
--------------------------------------------------------------------------------
/rtkit.h:
--------------------------------------------------------------------------------
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/
2 |
3 | #ifndef foortkithfoo
4 | #define foortkithfoo
5 |
6 | /***
7 | Copyright 2009 Lennart Poettering
8 | Copyright 2010 David Henningsson
9 |
10 | Permission is hereby granted, free of charge, to any person
11 | obtaining a copy of this software and associated documentation files
12 | (the "Software"), to deal in the Software without restriction,
13 | including without limitation the rights to use, copy, modify, merge,
14 | publish, distribute, sublicense, and/or sell copies of the Software,
15 | and to permit persons to whom the Software is furnished to do so,
16 | subject to the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be
19 | included in all copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
25 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
26 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | SOFTWARE.
29 | ***/
30 |
31 | #include
32 | #include
33 |
34 | #ifdef __cplusplus
35 | extern "C" {
36 | #endif
37 |
38 | /* This is the reference implementation for a client for
39 | * RealtimeKit. You don't have to use this, but if do, just copy these
40 | * sources into your repository */
41 |
42 | #define RTKIT_SERVICE_NAME "org.freedesktop.RealtimeKit1"
43 | #define RTKIT_OBJECT_PATH "/org/freedesktop/RealtimeKit1"
44 |
45 | /* This is mostly equivalent to sched_setparam(thread, SCHED_RR, {
46 | * .sched_priority = priority }). 'thread' needs to be a kernel thread
47 | * id as returned by gettid(), not a pthread_t! If 'thread' is 0 the
48 | * current thread is used. The returned value is a negative errno
49 | * style error code, or 0 on success. */
50 | int rtkit_make_realtime(DBusConnection *system_bus, pid_t thread, int priority);
51 |
52 | /* This is mostly equivalent to setpriority(PRIO_PROCESS, thread,
53 | * nice_level). 'thread' needs to be a kernel thread id as returned by
54 | * gettid(), not a pthread_t! If 'thread' is 0 the current thread is
55 | * used. The returned value is a negative errno style error code, or 0
56 | * on success.*/
57 | int rtkit_make_high_priority(DBusConnection *system_bus, pid_t thread, int nice_level);
58 |
59 | /* Return the maximum value of realtime priority available. Realtime requests
60 | * above this value will fail. A negative value is an errno style error code.
61 | */
62 | int rtkit_get_max_realtime_priority(DBusConnection *system_bus);
63 |
64 | /* Retreive the minimum value of nice level available. High prio requests
65 | * below this value will fail. The returned value is a negative errno
66 | * style error code, or 0 on success.*/
67 | int rtkit_get_min_nice_level(DBusConnection *system_bus, int* min_nice_level);
68 |
69 | /* Return the maximum value of RLIMIT_RTTIME to set before attempting a
70 | * realtime request. A negative value is an errno style error code.
71 | */
72 | long long rtkit_get_rttime_usec_max(DBusConnection *system_bus);
73 |
74 |
75 | #ifdef __cplusplus
76 | }
77 | #endif
78 |
79 | #endif
80 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'rtkit', 'c',
3 | version: '0.13',
4 | license: 'GPL3',
5 | meson_version: '>=0.49.0',
6 | default_options: ['c_std=c99', 'warning_level=2'],
7 | )
8 |
9 | add_project_arguments(
10 | '-D_GNU_SOURCE', '-DHAVE_CONFIG_H',
11 | language: 'c'
12 | )
13 |
14 | cc = meson.get_compiler('c')
15 | sh = find_program('sh')
16 | xxd = find_program('xxd')
17 |
18 | dbus_dep = dependency('dbus-1')
19 | libcap_dep = dependency('libcap')
20 | libsystemd_dep = dependency('libsystemd', required: get_option('libsystemd'))
21 | polkit_dep = dependency('polkit-gobject-1', required: false)
22 | systemd_dep = dependency('systemd', required: false)
23 | thread_dep = dependency('threads')
24 |
25 | librt_dep = cc.find_library('z')
26 | cc.check_header('sched.h', dependencies: librt_dep)
27 | cc.has_function('sched_setscheduler', dependencies: librt_dep)
28 |
29 | libexecdir = get_option('libexecdir')
30 | testsdir = get_option('libexecdir') / 'installed-tests' / meson.project_name()
31 |
32 | busservicedir = get_option('dbus_systemservicedir')
33 | if busservicedir == ''
34 | busservicedir = dbus_dep.get_pkgconfig_variable(
35 | 'system_bus_services_dir',
36 | default: get_option('datadir') / 'dbus-1' / 'system-services',
37 | )
38 | endif
39 |
40 | interfacedir = get_option('dbus_interfacedir')
41 | if interfacedir == ''
42 | interfacedir = dbus_dep.get_pkgconfig_variable(
43 | 'interfaces_dir',
44 | default: get_option('datadir') / 'dbus-1' / 'interfaces',
45 | )
46 | endif
47 |
48 | rulesdir = get_option('dbus_rulesdir')
49 | if rulesdir == ''
50 | rulesdir = get_option('datadir') / 'dbus-1' / 'system.d'
51 | endif
52 |
53 | policydir = get_option('polkit_actiondir')
54 | if policydir == '' and polkit_dep.found()
55 | policydir = polkit_dep.get_pkgconfig_variable('actiondir', default: '')
56 | endif
57 | if policydir == ''
58 | policydir = get_option('datadir') / 'polkit-1' / 'actions'
59 | endif
60 |
61 | systemunitdir = ''
62 | if systemunitdir == '' and systemd_dep.found()
63 | systemunitdir = systemd_dep.get_pkgconfig_variable(
64 | 'systemdsystemunitdir',
65 | default: '',
66 | )
67 | endif
68 | if systemunitdir == ''
69 | systemunitdir = get_option('libdir') / 'systemd' / 'system'
70 | endif
71 |
72 | config = configuration_data()
73 | config.set('LIBEXECDIR', get_option('prefix') / libexecdir)
74 | config.set_quoted('PACKAGE_VERSION', meson.project_version())
75 | config.set('HAVE_LIBSYSTEMD', libsystemd_dep.found())
76 |
77 | config_h = configure_file(
78 | input: 'config.h.meson',
79 | output: 'config.h',
80 | configuration: config,
81 | )
82 |
83 | xml_introspection_h = configure_file(
84 | input: 'org.freedesktop.RealtimeKit1.xml',
85 | output: 'xml-introspection.h',
86 | command: [
87 | sh, '-c', '"$1" -i < "$2" > "$3"', sh,
88 | xxd, '@INPUT@', '@OUTPUT@'
89 | ],
90 | )
91 |
92 | executable(
93 | 'rtkit-daemon',
94 | 'rtkit-daemon.c', 'rtkit.c', 'rtkit.h', config_h, xml_introspection_h,
95 | dependencies: [
96 | dbus_dep,
97 | libcap_dep,
98 | librt_dep,
99 | libsystemd_dep,
100 | thread_dep
101 | ],
102 | install: true,
103 | install_dir: libexecdir,
104 | )
105 |
106 | executable(
107 | 'rtkit-test',
108 | 'rtkit-test.c', 'rtkit.c', 'rtkit.h', config_h,
109 | dependencies: [dbus_dep, librt_dep],
110 | install: get_option('installed_tests'),
111 | install_dir: testsdir,
112 | )
113 |
114 | executable(
115 | 'rtkitctl',
116 | 'rtkitctl.c', 'rtkit.h', config_h,
117 | install: true,
118 | install_dir: get_option('sbindir'),
119 | dependencies: [dbus_dep],
120 | )
121 |
122 | install_man('rtkitctl.8')
123 | install_data('org.freedesktop.RealtimeKit1.xml', install_dir: interfacedir)
124 | install_data('org.freedesktop.RealtimeKit1.conf', install_dir: rulesdir)
125 | install_data('org.freedesktop.RealtimeKit1.policy', install_dir: policydir)
126 |
127 | configure_file(
128 | input: 'org.freedesktop.RealtimeKit1.service.in',
129 | output: 'org.freedesktop.RealtimeKit1.service',
130 | configuration: config,
131 | install_dir: busservicedir,
132 | )
133 |
134 | configure_file(
135 | input: 'rtkit-daemon.service.in',
136 | output: 'rtkit-daemon.service',
137 | configuration: config,
138 | install_dir: systemunitdir,
139 | )
140 |
--------------------------------------------------------------------------------
/rtkit-test.c:
--------------------------------------------------------------------------------
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/
2 |
3 | /***
4 | This file is part of RealtimeKit.
5 |
6 | Copyright 2009 Lennart Poettering
7 |
8 | RealtimeKit is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | RealtimeKit is distributed in the hope that it will be useful, but
14 | WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 | General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with RealtimeKit. If not, see .
20 | ***/
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include "rtkit.h"
30 |
31 | #ifndef SCHED_RESET_ON_FORK
32 | #define SCHED_RESET_ON_FORK 0x40000000
33 | #endif
34 |
35 | #ifndef RLIMIT_RTTIME
36 | #define RLIMIT_RTTIME 15
37 | #endif
38 |
39 | static void print_status(const char *t) {
40 | int ret;
41 |
42 | if ((ret = sched_getscheduler(0)) < 0) {
43 | fprintf(stderr, "sched_getscheduler() failed: %s\n", strerror(errno));
44 | return;
45 | }
46 |
47 | printf("%s:\n"
48 | "\tSCHED_RESET_ON_FORK: %s\n",
49 | t,
50 | (ret & SCHED_RESET_ON_FORK) ? "yes" : "no");
51 |
52 | if ((ret & ~SCHED_RESET_ON_FORK) == SCHED_RR) {
53 | struct sched_param param;
54 |
55 | if (sched_getparam(0, ¶m) < 0) {
56 | fprintf(stderr, "sched_getschedparam() failed: %s\n", strerror(errno));
57 | return;
58 | }
59 |
60 | printf("\tSCHED_RR with priority %i\n", param.sched_priority);
61 |
62 | } else if ((ret & ~SCHED_RESET_ON_FORK) == SCHED_OTHER) {
63 | errno = 0;
64 | ret = getpriority(PRIO_PROCESS, 0);
65 | if (errno != 0) {
66 | fprintf(stderr, "getpriority() failed: %s\n", strerror(errno));
67 | return;
68 | }
69 |
70 | printf("\tSCHED_OTHER with nice level: %i\n", ret);
71 |
72 | } else
73 | fprintf(stderr, "Neither SCHED_RR nor SCHED_OTHER.\n");
74 | }
75 |
76 | int main(int argc, char *argv[]) {
77 | DBusError error;
78 | DBusConnection *bus;
79 | int r, max_realtime_priority, min_nice_level;
80 | long long rttime_usec_max;
81 | struct rlimit rlim;
82 |
83 | dbus_error_init(&error);
84 |
85 | if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
86 | fprintf(stderr, "Failed to connect to system bus: %s\n", error.message);
87 | return 1;
88 | }
89 |
90 | if ((max_realtime_priority = rtkit_get_max_realtime_priority(bus)) < 0)
91 | fprintf(stderr, "Failed to retrieve max realtime priority: %s\n", strerror(-max_realtime_priority));
92 | else
93 | printf("Max realtime priority is: %d\n", max_realtime_priority);
94 |
95 | if ((r = rtkit_get_min_nice_level(bus, &min_nice_level)))
96 | fprintf(stderr, "Failed to retrieve min nice level: %s\n", strerror(-r));
97 | else
98 | printf("Min nice level is: %d\n", min_nice_level);
99 |
100 | if ((rttime_usec_max = rtkit_get_rttime_usec_max(bus)) < 0)
101 | fprintf(stderr, "Failed to retrieve rttime limit: %s\n", strerror(-rttime_usec_max));
102 | else
103 | printf("Rttime limit is: %lld ns\n", rttime_usec_max);
104 |
105 | memset(&rlim, 0, sizeof(rlim));
106 | rlim.rlim_cur = rlim.rlim_max = 100000ULL; /* 100ms */
107 | if ((setrlimit(RLIMIT_RTTIME, &rlim) < 0))
108 | fprintf(stderr, "Failed to set RLIMIT_RTTIME: %s\n", strerror(errno));
109 |
110 | print_status("before");
111 |
112 | if ((r = rtkit_make_high_priority(bus, 0, -10)) < 0)
113 | fprintf(stderr, "Failed to become high priority: %s\n", strerror(-r));
114 | else
115 | printf("Successfully became high priority.\n");
116 |
117 | print_status("after high priority");
118 |
119 | if ((r = rtkit_make_realtime(bus, 0, 10)) < 0)
120 | fprintf(stderr, "Failed to become realtime: %s\n", strerror(-r));
121 | else
122 | printf("Successfully became realtime.\n");
123 |
124 | print_status("after realtime");
125 |
126 | dbus_connection_unref(bus);
127 |
128 | return 0;
129 | }
130 |
--------------------------------------------------------------------------------
/configure.ac:
--------------------------------------------------------------------------------
1 | # -*- Autoconf -*-
2 | # Process this file with autoconf to produce a configure script.
3 |
4 | # This file is part of RealtimeKit.
5 | #
6 | # Copyright 2009 Lennart Poettering
7 | #
8 | # RealtimeKit is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # RealtimeKit is distributed in the hope that it will be useful, but
14 | # WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 | # General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with RealtimeKit. If not, see .
20 |
21 | AC_PREREQ(2.68)
22 |
23 | AC_INIT([rtkit],
24 | [0.13],
25 | [mzegxvg (at) 0pointer (dot) net],
26 | [rtkit],
27 | [http://git.0pointer.de/?p=rtkit.git])
28 |
29 | AC_CONFIG_SRCDIR([rtkit-daemon.c])
30 | AC_CONFIG_HEADERS([config.h])
31 | AC_CONFIG_MACRO_DIR(m4)
32 | AC_CONFIG_AUX_DIR([build-aux])
33 |
34 | AM_INIT_AUTOMAKE([foreign 1.11 -Wno-portability silent-rules -Wall tar-pax no-dist-gzip dist-xz])
35 | AM_SILENT_RULES([yes])
36 |
37 | AC_CANONICAL_HOST
38 |
39 | if type -p stow > /dev/null && test -d /usr/local/stow ; then
40 | AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***])
41 | ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}"
42 | fi
43 |
44 | #### Checks for programs. ####
45 |
46 | # CC
47 |
48 | AC_PROG_CC
49 | AC_PROG_CC_C99
50 | AM_PROG_CC_C_O
51 | AC_PROG_GCC_TRADITIONAL
52 | AC_USE_SYSTEM_EXTENSIONS
53 | AC_SYS_LARGEFILE
54 |
55 | # GCC flags
56 |
57 | CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\
58 | -pipe \
59 | -Wall \
60 | -W \
61 | -Wextra \
62 | -Winline \
63 | -Wvla \
64 | -Wundef \
65 | -Wformat=2 \
66 | -Wlogical-op \
67 | -Wsign-compare \
68 | -Wformat-security \
69 | -Wmissing-include-dirs \
70 | -Wformat-nonliteral \
71 | -Wold-style-definition \
72 | -Wpointer-arith \
73 | -Winit-self \
74 | -Wdeclaration-after-statement \
75 | -Wfloat-equal \
76 | -Wmissing-prototypes \
77 | -Wstrict-prototypes \
78 | -Wredundant-decls \
79 | -Wmissing-declarations \
80 | -Wmissing-noreturn \
81 | -Wshadow \
82 | -Wendif-labels \
83 | -Wcast-align \
84 | -Wstrict-aliasing=2 \
85 | -Wwrite-strings \
86 | -Wno-long-long \
87 | -Wno-overlength-strings \
88 | -Wno-unused-parameter \
89 | -Wno-missing-field-initializers \
90 | -Wno-unused-result \
91 | -Wunsafe-loop-optimizations \
92 | -Wpacked \
93 | -Werror=overflow \
94 | -Wp,-D_FORTIFY_SOURCE=2 \
95 | -ffast-math \
96 | -fno-common \
97 | -fdiagnostics-show-option \
98 | -fno-strict-aliasing \
99 | -ffunction-sections \
100 | -fdata-sections])
101 | AC_SUBST([WARNINGFLAGS], $with_cflags)
102 |
103 | CC_CHECK_FLAGS_APPEND([with_ldflags], [LDFLAGS], [\
104 | -Wl,--as-needed \
105 | -Wl,--gc-sections])
106 | AC_SUBST([GCLDFLAGS], $with_ldflags)
107 |
108 | ACX_PTHREAD
109 | LIBS="$PTHREAD_LIBS $LIBS"
110 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
111 | CC="$PTHREAD_CC"
112 |
113 | AC_SEARCH_LIBS([sched_setscheduler], [rt])
114 | AC_SEARCH_LIBS([mq_open], [rt])
115 | AC_SEARCH_LIBS([cap_init], [cap])
116 |
117 | PKG_CHECK_MODULES(DBUS, dbus-1)
118 |
119 | AC_ARG_ENABLE([libsystemd],
120 | AS_HELP_STRING([--enable-libsystemd], [use the libsystemd sd-daemon API to communicate with systemd (default: automatic)]))
121 | AS_IF([test "x${enable_libsystemd}" != "xno"],
122 | [PKG_CHECK_MODULES(LIBSYSTEMD, libsystemd, [have_libsystemd=yes], [have_libsystemd=no])],
123 | [have_libsystemd=no])
124 | AS_IF([test "x${have_libsystemd}" = "xyes"],
125 | [AC_DEFINE([HAVE_LIBSYSTEMD], [1], [Define to 1 if you have libsystemd and its header files])],
126 | [AS_IF([test "x${enable_libsystemd}" = "xyes"],
127 | [AC_MSG_ERROR([sd-daemon support requested but libsystemd not found])
128 | ])
129 | ])
130 |
131 | AC_ARG_WITH([systemdsystemunitdir],
132 | AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
133 | [],
134 | [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
135 | AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
136 | AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir"])
137 |
138 | AC_PROG_SED
139 | AC_PATH_PROG([XXD], [xxd], [xxd])
140 | if test "$XXD" = xxd; then
141 | AC_MSG_WARN([xxd not found, cannot compile introspection XML])
142 | fi
143 |
144 | ###################################
145 | # Output #
146 | ###################################
147 |
148 | AC_CONFIG_FILES([
149 | Makefile
150 | ])
151 | AC_OUTPUT
152 |
153 | echo "
154 | ---{ $PACKAGE_NAME $VERSION }---
155 |
156 | prefix: ${prefix}
157 | sysconfdir: ${sysconfdir}
158 | localstatedir: ${localstatedir}
159 | Compiler: ${CC}
160 | CFLAGS: ${CFLAGS}
161 | systemd unit directory: ${systemdsystemunitdir}
162 | use sd-daemon API: ${have_libsystemd}
163 | "
164 |
--------------------------------------------------------------------------------
/rtkitctl.c:
--------------------------------------------------------------------------------
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/
2 |
3 | /***
4 | This file is part of RealtimeKit.
5 |
6 | Copyright 2009 Lennart Poettering
7 |
8 | RealtimeKit is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | RealtimeKit is distributed in the hope that it will be useful, but
14 | WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 | General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with RealtimeKit. If not, see .
20 | ***/
21 |
22 | #ifdef HAVE_CONFIG_H
23 | #include
24 | #endif
25 |
26 | #include
27 | #include
28 | #include
29 |
30 | #include "rtkit.h"
31 |
32 | static char* get_file_name(const char *p) {
33 | char *e;
34 |
35 | if ((e = strrchr(p, '/')))
36 | return e + 1;
37 | else
38 | return (char*) p;
39 | }
40 |
41 | static void show_help(const char *exe) {
42 |
43 | printf("%s [options]\n\n"
44 | " -h, --help Show this help\n"
45 | " --version Show version\n\n"
46 | " --reset-known Reset real-time status of known threads\n"
47 | " --reset-all Reset real-time status of all threads\n"
48 | " --start Start RealtimeKit if it is not running already\n"
49 | " -k, --exit Terminate running RealtimeKit daemon\n",
50 | exe);
51 | }
52 |
53 | int main (int argc, char*argv[]) {
54 | enum {
55 | ARG_HELP = 256,
56 | ARG_VERSION,
57 | ARG_START,
58 | ARG_EXIT,
59 | ARG_RESET_KNOWN,
60 | ARG_RESET_ALL,
61 | };
62 |
63 | static const struct option long_options[] = {
64 | { "help", no_argument, 0, ARG_HELP },
65 | { "version", no_argument, 0, ARG_VERSION },
66 | { "start", no_argument, 0, ARG_START },
67 | { "exit", no_argument, 0, ARG_EXIT },
68 | { "reset-known", no_argument, 0, ARG_RESET_KNOWN },
69 | { "reset-all", no_argument, 0, ARG_RESET_ALL },
70 | { NULL, 0, 0, 0}
71 | };
72 |
73 | enum {
74 | OPERATION_START,
75 | OPERATION_EXIT,
76 | OPERATION_RESET_KNOWN,
77 | OPERATION_RESET_ALL,
78 | _OPERATION_MAX
79 | };
80 |
81 | static const char *method[_OPERATION_MAX] = {
82 | [OPERATION_START] = "StartServiceByName",
83 | [OPERATION_EXIT] = "Exit",
84 | [OPERATION_RESET_KNOWN] = "ResetKnown",
85 | [OPERATION_RESET_ALL] = "ResetAll"
86 | };
87 |
88 | DBusError error;
89 | DBusConnection *bus = NULL;
90 | int operation = -1;
91 | int ret = 1;
92 | DBusMessage *m = NULL, *r = NULL;
93 | int c;
94 |
95 | dbus_error_init(&error);
96 |
97 | while ((c = getopt_long(argc, argv, "hk", long_options, NULL)) >= 0) {
98 |
99 | switch (c) {
100 | case 'h':
101 | case ARG_HELP:
102 | show_help(get_file_name(argv[0]));
103 | ret = 0;
104 | goto finish;
105 |
106 | case ARG_VERSION:
107 | printf("%s " PACKAGE_VERSION "\n", get_file_name(argv[0]));
108 | ret = 0;
109 | goto finish;
110 |
111 | case ARG_START:
112 | operation = OPERATION_START;
113 | break;
114 |
115 | case 'k':
116 | case ARG_EXIT:
117 | operation = OPERATION_EXIT;
118 | break;
119 |
120 | case ARG_RESET_KNOWN:
121 | operation = OPERATION_RESET_KNOWN;
122 | break;
123 |
124 | case ARG_RESET_ALL:
125 | operation = OPERATION_RESET_ALL;
126 | break;
127 |
128 | case '?':
129 | default:
130 | fprintf(stderr, "Unknown command.\n");
131 | goto finish;
132 | }
133 | }
134 |
135 | if (optind < argc) {
136 | fprintf(stderr, "Too many arguments.\n");
137 | goto finish;
138 | }
139 |
140 | if (operation < 0) {
141 | fprintf(stderr, "Need to specify operation.\n");
142 | goto finish;
143 | }
144 |
145 | if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
146 | fprintf(stderr, "Failed to connect to system bus: %s\n", error.message);
147 | goto finish;
148 | }
149 |
150 | if (operation == OPERATION_START) {
151 | const char *service = RTKIT_SERVICE_NAME;
152 | dbus_uint32_t flags = 0;
153 |
154 | if (!(m = dbus_message_new_method_call(
155 | DBUS_SERVICE_DBUS,
156 | DBUS_PATH_DBUS,
157 | DBUS_INTERFACE_DBUS,
158 | method[operation]))) {
159 | fprintf(stderr, "Failed to allocated message.\n");
160 | goto finish;
161 | }
162 |
163 | if (!(dbus_message_append_args(
164 | m,
165 | DBUS_TYPE_STRING, &service,
166 | DBUS_TYPE_UINT32, &flags,
167 | DBUS_TYPE_INVALID))) {
168 | fprintf(stderr, "Failed to append to message.\n");
169 | goto finish;
170 | }
171 | } else {
172 | if (!(m = dbus_message_new_method_call(
173 | RTKIT_SERVICE_NAME,
174 | RTKIT_OBJECT_PATH,
175 | "org.freedesktop.RealtimeKit1",
176 | method[operation]))) {
177 | fprintf(stderr, "Failed to allocated message.\n");
178 | goto finish;
179 | }
180 | }
181 |
182 | if (!(r = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
183 | fprintf(stderr, "Failed to send %s request: %s\n", method[operation], error.message);
184 | goto finish;
185 | }
186 |
187 |
188 | if (dbus_set_error_from_message(&error, r)) {
189 | fprintf(stderr, "%s request failed: %s\n", method[operation], error.message);
190 | goto finish;
191 | }
192 |
193 | ret = 0;
194 |
195 | finish:
196 | if (m)
197 | dbus_message_unref(m);
198 |
199 | if (r)
200 | dbus_message_unref(r);
201 |
202 | if (bus)
203 | dbus_connection_unref(bus);
204 |
205 | return ret;
206 | }
207 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | REALTIMEKIT Realtime Policy and Watchdog Daemon
2 |
3 | GIT:
4 | https://github.com/heftig/rtkit
5 |
6 | NOTES:
7 | RealtimeKit is a D-Bus system service that changes the
8 | scheduling policy of user processes/threads to SCHED_RR
9 | (i.e. realtime scheduling mode) on request. It is intended to
10 | be used as a secure mechanism to allow real-time scheduling to
11 | be used by normal user processes.
12 |
13 | RealtimeKit enforces strict policies when handing out
14 | real-time security to user threads:
15 |
16 | * Only clients with RLIMIT_RTTIME set will get RT scheduling
17 |
18 | * RT scheduling will only be handed out to processes with
19 | SCHED_RESET_ON_FORK set to guarantee that the scheduling
20 | settings cannot 'leak' to child processes, thus making sure
21 | that 'RT fork bombs' cannot be used to bypass RLIMIT_RTTIME
22 | and take the system down.
23 |
24 | * Limits are enforced on all user controllable resources, only
25 | a maximum number of users, processes, threads can request RT
26 | scheduling at the same time.
27 |
28 | * Only a limited number of threads may be made RT in a
29 | specific time frame.
30 |
31 | * Client authorization is verified with PolicyKit
32 |
33 | RealtimeKit can also be used to hand outh high priority
34 | scheduling (i.e. negative nice level) to user processes.
35 |
36 | In addition to this a-priori policy enforcement, RealtimeKit
37 | also provides a-posteriori policy enforcement, i.e. it
38 | includes a canary-based watchdog that automatically demotes
39 | all real-time threads to SCHED_OTHER should the system
40 | overload despite the logic pointed out above. For more
41 | information regarding canary-based RT watchdogs, see the
42 | Acknowledgments section below.
43 |
44 | In its duty to manage real-time scheduling *securely*
45 | RealtimeKit runs as unpriviliged user, and uses capabalities,
46 | resource limits and chroot() to minimize its security impact.
47 |
48 | RealtimeKit probably has little use in embedded or server use
49 | cases, use RLIMIT_RTPRIO there instead.
50 |
51 | WHY:
52 | If processes that have real-time scheduling privileges enter a
53 | busy loop they can freeze the entire the system. To make sure
54 | such run-away processes cannot do this RLIMIT_RTTIME has been
55 | introduced. Being a per-process limit it is however easily
56 | cirumvented by combining a fork bomb with a busy loop.
57 |
58 | RealtimeKit hands out RT scheduling to specific threads that
59 | ask for it -- but only to those and due to SCHED_RESET_ON_FORK
60 | it can be sure that this won't 'leak'.
61 |
62 | In contrast to RLIMIT_RTPRIO the RealtimeKit logic makes sure
63 | that only a certain number of threads can be made realtime,
64 | per user, per process and per time interval.
65 |
66 |
67 | CLIENTS:
68 | To be able to make use of realtime scheduling clients may
69 | request so with a small D-Bus interface that is accessible on
70 | the interface org.freedesktop.RealtimeKit1 as object
71 | /org/freedesktop/RealtimeKit1 on the service
72 | org.freedesktop.RealtimeKit1:
73 |
74 | void MakeThreadRealtime(u64 thread_id, u32 priority);
75 |
76 | void MakeThreadHighPriority(u64 thread_id, s32 priority);
77 |
78 | The thread IDs need to be passed as kernel tids as returned by
79 | gettid(), not a pthread_t! (Please
80 | note that gettid() is not available in glibc, you need to
81 | implement that manually using syscall(). Consult the reference
82 | client implementation for details.)
83 |
84 | It is possible to promote thread in process to realtime/high
85 | priority from another process, that will make the DBUS call,
86 | using:
87 |
88 | void MakeThreadRealtimeWithPID(u64 process, u64 thread_id, u32 priority);
89 |
90 | void MakeThreadHighPriorityWithPID(u64 process, u64 thread_id, s32 priority);
91 |
92 | where process is the PID of the process that has thread thread_id.
93 |
94 | A BSD-licensed reference implementation of the client is
95 | available in rtkit.[ch] as part of the package. You may copy
96 | this into your sources if you wish. However given how simple
97 | the D-Bus interface is you might choose to implement your own
98 | client implementation.
99 |
100 | It is advisable to try acquiring realtime scheduling with
101 | sched_setsheduler() first, so that systems where RLIMIT_RTPRIO
102 | is set can be supported.
103 |
104 | Here's an example using the reference implementation. Replace
105 | this:
106 |
107 |
108 | struct sched_param p;
109 | memset(&p, 0, sizeof(p));
110 | p.sched_priority = 3;
111 | sched_setscheduler(0, SCHED_RR|SCHED_RESET_ON_FORK, &p);
112 |
113 |
114 | by this:
115 |
116 |
117 | struct sched_param p;
118 | memset(&p, 0, sizeof(p));
119 | p.sched_priority = 3;
120 | if (sched_setscheduler(0, SCHED_RR|SCHED_RESET_ON_FORK, &p) < 0
121 | && errno == EPERM)
122 | rtkit_make_realtime(system_bus, 0, p.sched_priority);
123 |
124 |
125 | But of course add more appropriate error checking! Also,
126 | falling back to plain SCHED_RR when SCHED_RESET_ON_FORK causes
127 | EINVAL migt be advisable).
128 |
129 | DAEMON:
130 |
131 | The daemon is automatically started on first use via D-Bus
132 | system bus activation.
133 |
134 | Currently the daemon does not read on any configuration file,
135 | however it can be configured with command line parameters. You
136 | can edit
137 |
138 | /usr/share/dbus-1/system-services/org.freedesktop.RealtimeKit1.service
139 |
140 | to set those.
141 |
142 | Run
143 |
144 | /usr/libexec/rtkit-daemon --help
145 |
146 | to get a quick overview on the supported parameters and their
147 | defaults. Many of them should be obvious in their meaning. For
148 | the remaining ones see below:
149 |
150 | --max-realtime-priority= may be used to specify the maximum
151 | realtime priority a client can acquire through
152 | RealtimeKit. Please note that this value must be smaller than
153 | the value passed to --our-realtime-priority=.
154 |
155 | --our-realtime-priority= may be used to specify the realtime
156 | priority of the daemon itself. Please note that this priority
157 | is only used for a very short time while processing a client
158 | request. Normally the daemon will not be running with a
159 | realtime scheduling policy. The real-time priorities handed
160 | out to the user must be lower than this value. (see above).
161 |
162 | --min-nice-level= may be used to specify the minimum nice
163 | level a client can acquire through RealtimeKit.
164 |
165 | --our-nice-level= may be used to specify the nice level the
166 | the daemon itself uses most of the time (except when
167 | processing requests, see above). It is probably a good idea to
168 | set this to a small positive value, to make sure that if the
169 | system is overloaded already handing out further RT scheduling
170 | will be delayed a bit.
171 |
172 | --rttime-usec-max= may be used to control which RLIMIT_RTTIME
173 | value clients need to have chosen at minimum before they may
174 | acquire RT scheduling through RealtimeKit.
175 |
176 | --users-max= specifies how many users may acquire RT
177 | scheduling at the same time for one or multiple of their
178 | processes.
179 |
180 | --processes-per-user-max= specifies how many processes per
181 | user may acquire RT scheduling at the same time.
182 |
183 | --threads-per-user-max= specifies how many threads per user
184 | may acquire RT scheduling at the same time. Of course this
185 | value should be set higher than --process-per-user-max=.
186 |
187 | --actions-burst-sec= may be used to influence the rate
188 | limiting logic in RealtimeKit. The daemon will only pass
189 | realtime scheduling privileges to a maximum number of threads
190 | within this timeframe (see below).
191 |
192 | --actions-per-burst-max= may be used to influence the rate
193 | limiting logic in RealtimeKit. The daemon will only pass
194 | realtime scheduling privileges to this number of threads
195 | within the time frame configured via
196 | --actions-burst-sec=. When this limit is reached clients need
197 | to wait until that time passes before requesting RT scheduling
198 | again.
199 |
200 | --canary-cheep-msec= may be used to control how often the
201 | canary thread shall cheep.
202 |
203 | --canary-watchdog-msec= may be used to control how quickly the
204 | watchdog thread expects to receive a cheep from the canary
205 | thread. This value must be chosen larger than
206 | --canary-cheep-msec=. If the former is set 10s and the latter
207 | to 7s, then the canary thread can trigger and deliver the
208 | cheep with a maximum latency of 3s.
209 |
210 | ACKNOWLEDGMENTS:
211 | The canary watchdog logic is inspired by previous work of
212 | Vernon Mauery, Florian Schmidt, Kjetil Matheussen:
213 |
214 | http://rt.wiki.kernel.org/index.php/RT_Watchdog
215 |
216 | LICENSE:
217 | GPLv3+ for the daemon
218 | BSD for the client reference implementation
219 |
220 | AUTHOR:
221 | Lennart Poettering
222 |
223 | REQUIREMENTS:
224 | Linux kernel >= 2.6.31
225 | D-Bus
226 | PolicyKit >= 0.92
227 |
228 | OPTIONAL DEPENDENCIES:
229 | libsystemd - to let rtkit talk to systemd using the sd-daemon API
230 |
--------------------------------------------------------------------------------
/rtkit.c:
--------------------------------------------------------------------------------
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/
2 |
3 | /***
4 | Copyright 2009 Lennart Poettering
5 | Copyright 2010 David Henningsson
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
29 |
30 | #include "rtkit.h"
31 |
32 | #ifdef __linux__
33 |
34 | #ifndef _GNU_SOURCE
35 | #define _GNU_SOURCE
36 | #endif
37 |
38 | #include
39 | #include
40 | #include
41 | #include
42 |
43 | static pid_t _gettid(void) {
44 | return (pid_t) syscall(SYS_gettid);
45 | }
46 |
47 | static int translate_error(const char *name) {
48 | if (strcmp(name, DBUS_ERROR_NO_MEMORY) == 0)
49 | return -ENOMEM;
50 | if (strcmp(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 ||
51 | strcmp(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0)
52 | return -ENOENT;
53 | if (strcmp(name, DBUS_ERROR_ACCESS_DENIED) == 0 ||
54 | strcmp(name, DBUS_ERROR_AUTH_FAILED) == 0)
55 | return -EACCES;
56 |
57 | return -EIO;
58 | }
59 |
60 | static long long rtkit_get_int_property(DBusConnection *connection, const char* propname, long long* propval) {
61 | DBusMessage *m = NULL, *r = NULL;
62 | DBusMessageIter iter, subiter;
63 | dbus_int64_t i64;
64 | dbus_int32_t i32;
65 | DBusError error;
66 | int current_type;
67 | long long ret;
68 | const char * interfacestr = "org.freedesktop.RealtimeKit1";
69 |
70 | dbus_error_init(&error);
71 |
72 | if (!(m = dbus_message_new_method_call(
73 | RTKIT_SERVICE_NAME,
74 | RTKIT_OBJECT_PATH,
75 | "org.freedesktop.DBus.Properties",
76 | "Get"))) {
77 | ret = -ENOMEM;
78 | goto finish;
79 | }
80 |
81 | if (!dbus_message_append_args(
82 | m,
83 | DBUS_TYPE_STRING, &interfacestr,
84 | DBUS_TYPE_STRING, &propname,
85 | DBUS_TYPE_INVALID)) {
86 | ret = -ENOMEM;
87 | goto finish;
88 | }
89 |
90 | if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
91 | ret = translate_error(error.name);
92 | goto finish;
93 | }
94 |
95 | if (dbus_set_error_from_message(&error, r)) {
96 | ret = translate_error(error.name);
97 | goto finish;
98 | }
99 |
100 | ret = -EBADMSG;
101 | dbus_message_iter_init(r, &iter);
102 | while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) {
103 |
104 | if (current_type == DBUS_TYPE_VARIANT) {
105 | dbus_message_iter_recurse(&iter, &subiter);
106 |
107 | while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) {
108 |
109 | if (current_type == DBUS_TYPE_INT32) {
110 | dbus_message_iter_get_basic(&subiter, &i32);
111 | *propval = i32;
112 | ret = 0;
113 | }
114 |
115 | if (current_type == DBUS_TYPE_INT64) {
116 | dbus_message_iter_get_basic(&subiter, &i64);
117 | *propval = i64;
118 | ret = 0;
119 | }
120 |
121 | dbus_message_iter_next (&subiter);
122 | }
123 | }
124 | dbus_message_iter_next (&iter);
125 | }
126 |
127 | finish:
128 |
129 | if (m)
130 | dbus_message_unref(m);
131 |
132 | if (r)
133 | dbus_message_unref(r);
134 |
135 | dbus_error_free(&error);
136 |
137 | return ret;
138 | }
139 |
140 | int rtkit_get_max_realtime_priority(DBusConnection *connection) {
141 | long long retval;
142 | int err;
143 |
144 | err = rtkit_get_int_property(connection, "MaxRealtimePriority", &retval);
145 | return err < 0 ? err : retval;
146 | }
147 |
148 | int rtkit_get_min_nice_level(DBusConnection *connection, int* min_nice_level) {
149 | long long retval;
150 | int err;
151 |
152 | err = rtkit_get_int_property(connection, "MinNiceLevel", &retval);
153 | if (err >= 0)
154 | *min_nice_level = retval;
155 | return err;
156 | }
157 |
158 | long long rtkit_get_rttime_usec_max(DBusConnection *connection) {
159 | long long retval;
160 | int err;
161 |
162 | err = rtkit_get_int_property(connection, "RTTimeUSecMax", &retval);
163 | return err < 0 ? err : retval;
164 | }
165 |
166 | int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) {
167 | DBusMessage *m = NULL, *r = NULL;
168 | dbus_uint64_t u64;
169 | dbus_uint32_t u32;
170 | DBusError error;
171 | int ret;
172 |
173 | dbus_error_init(&error);
174 |
175 | if (thread == 0)
176 | thread = _gettid();
177 |
178 | if (!(m = dbus_message_new_method_call(
179 | RTKIT_SERVICE_NAME,
180 | RTKIT_OBJECT_PATH,
181 | "org.freedesktop.RealtimeKit1",
182 | "MakeThreadRealtime"))) {
183 | ret = -ENOMEM;
184 | goto finish;
185 | }
186 |
187 | u64 = (dbus_uint64_t) thread;
188 | u32 = (dbus_uint32_t) priority;
189 |
190 | if (!dbus_message_append_args(
191 | m,
192 | DBUS_TYPE_UINT64, &u64,
193 | DBUS_TYPE_UINT32, &u32,
194 | DBUS_TYPE_INVALID)) {
195 | ret = -ENOMEM;
196 | goto finish;
197 | }
198 |
199 | if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
200 | ret = translate_error(error.name);
201 | goto finish;
202 | }
203 |
204 |
205 | if (dbus_set_error_from_message(&error, r)) {
206 | ret = translate_error(error.name);
207 | goto finish;
208 | }
209 |
210 | ret = 0;
211 |
212 | finish:
213 |
214 | if (m)
215 | dbus_message_unref(m);
216 |
217 | if (r)
218 | dbus_message_unref(r);
219 |
220 | dbus_error_free(&error);
221 |
222 | return ret;
223 | }
224 |
225 | int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) {
226 | DBusMessage *m = NULL, *r = NULL;
227 | dbus_uint64_t u64;
228 | dbus_int32_t s32;
229 | DBusError error;
230 | int ret;
231 |
232 | dbus_error_init(&error);
233 |
234 | if (thread == 0)
235 | thread = _gettid();
236 |
237 | if (!(m = dbus_message_new_method_call(
238 | RTKIT_SERVICE_NAME,
239 | RTKIT_OBJECT_PATH,
240 | "org.freedesktop.RealtimeKit1",
241 | "MakeThreadHighPriority"))) {
242 | ret = -ENOMEM;
243 | goto finish;
244 | }
245 |
246 | u64 = (dbus_uint64_t) thread;
247 | s32 = (dbus_int32_t) nice_level;
248 |
249 | if (!dbus_message_append_args(
250 | m,
251 | DBUS_TYPE_UINT64, &u64,
252 | DBUS_TYPE_INT32, &s32,
253 | DBUS_TYPE_INVALID)) {
254 | ret = -ENOMEM;
255 | goto finish;
256 | }
257 |
258 |
259 |
260 | if (!(r = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
261 | ret = translate_error(error.name);
262 | goto finish;
263 | }
264 |
265 |
266 | if (dbus_set_error_from_message(&error, r)) {
267 | ret = translate_error(error.name);
268 | goto finish;
269 | }
270 |
271 | ret = 0;
272 |
273 | finish:
274 |
275 | if (m)
276 | dbus_message_unref(m);
277 |
278 | if (r)
279 | dbus_message_unref(r);
280 |
281 | dbus_error_free(&error);
282 |
283 | return ret;
284 | }
285 |
286 | #else
287 |
288 | int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) {
289 | return -ENOTSUP;
290 | }
291 |
292 | int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) {
293 | return -ENOTSUP;
294 | }
295 |
296 | int rtkit_get_max_realtime_priority(DBusConnection *connection) {
297 | return -ENOTSUP;
298 | }
299 |
300 | int rtkit_get_min_nice_level(DBusConnection *connection, int* min_nice_level) {
301 | return -ENOTSUP;
302 | }
303 |
304 | long long rtkit_get_rttime_usec_max(DBusConnection *connection) {
305 | return -ENOTSUP;
306 | }
307 |
308 | #endif
309 |
--------------------------------------------------------------------------------
/m4/attributes.m4:
--------------------------------------------------------------------------------
1 | dnl Macros to check the presence of generic (non-typed) symbols.
2 | dnl Copyright (c) 2006-2008 Diego Pettenò
3 | dnl Copyright (c) 2006-2008 xine project
4 | dnl Copyright (c) 2012 Lucas De Marchi
5 | dnl
6 | dnl This program is free software; you can redistribute it and/or modify
7 | dnl it under the terms of the GNU General Public License as published by
8 | dnl the Free Software Foundation; either version 2, or (at your option)
9 | dnl any later version.
10 | dnl
11 | dnl This program is distributed in the hope that it will be useful,
12 | dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | dnl GNU General Public License for more details.
15 | dnl
16 | dnl You should have received a copy of the GNU General Public License
17 | dnl along with this program; if not, write to the Free Software
18 | dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 | dnl 02110-1301, USA.
20 | dnl
21 | dnl As a special exception, the copyright owners of the
22 | dnl macro gives unlimited permission to copy, distribute and modify the
23 | dnl configure scripts that are the output of Autoconf when processing the
24 | dnl Macro. You need not follow the terms of the GNU General Public
25 | dnl License when using or distributing such scripts, even though portions
26 | dnl of the text of the Macro appear in them. The GNU General Public
27 | dnl License (GPL) does govern all other use of the material that
28 | dnl constitutes the Autoconf Macro.
29 | dnl
30 | dnl This special exception to the GPL applies to versions of the
31 | dnl Autoconf Macro released by this project. When you make and
32 | dnl distribute a modified version of the Autoconf Macro, you may extend
33 | dnl this special exception to the GPL to apply to your modified version as
34 | dnl well.
35 |
36 | dnl Check if FLAG in ENV-VAR is supported by compiler and append it
37 | dnl to WHERE-TO-APPEND variable
38 | dnl CC_CHECK_FLAG_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG])
39 |
40 | AC_DEFUN([CC_CHECK_FLAG_APPEND], [
41 | AC_CACHE_CHECK([if $CC supports flag $3 in envvar $2],
42 | AS_TR_SH([cc_cv_$2_$3]),
43 | [eval "AS_TR_SH([cc_save_$2])='${$2}'"
44 | eval "AS_TR_SH([$2])='$3'"
45 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([int a = 0; int main(void) { return a; } ])],
46 | [eval "AS_TR_SH([cc_cv_$2_$3])='yes'"],
47 | [eval "AS_TR_SH([cc_cv_$2_$3])='no'"])
48 | eval "AS_TR_SH([$2])='$cc_save_$2'"])
49 |
50 | AS_IF([eval test x$]AS_TR_SH([cc_cv_$2_$3])[ = xyes],
51 | [eval "$1='${$1} $3'"])
52 | ])
53 |
54 | dnl CC_CHECK_FLAGS_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG1 FLAG2])
55 | AC_DEFUN([CC_CHECK_FLAGS_APPEND], [
56 | for flag in $3; do
57 | CC_CHECK_FLAG_APPEND($1, $2, $flag)
58 | done
59 | ])
60 |
61 | dnl Check if the flag is supported by linker (cacheable)
62 | dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
63 |
64 | AC_DEFUN([CC_CHECK_LDFLAGS], [
65 | AC_CACHE_CHECK([if $CC supports $1 flag],
66 | AS_TR_SH([cc_cv_ldflags_$1]),
67 | [ac_save_LDFLAGS="$LDFLAGS"
68 | LDFLAGS="$LDFLAGS $1"
69 | AC_LINK_IFELSE([int main() { return 1; }],
70 | [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"],
71 | [eval "AS_TR_SH([cc_cv_ldflags_$1])="])
72 | LDFLAGS="$ac_save_LDFLAGS"
73 | ])
74 |
75 | AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes],
76 | [$2], [$3])
77 | ])
78 |
79 | dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for
80 | dnl the current linker to avoid undefined references in a shared object.
81 | AC_DEFUN([CC_NOUNDEFINED], [
82 | dnl We check $host for which systems to enable this for.
83 | AC_REQUIRE([AC_CANONICAL_HOST])
84 |
85 | case $host in
86 | dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads
87 | dnl are requested, as different implementations are present; to avoid problems
88 | dnl use -Wl,-z,defs only for those platform not behaving this way.
89 | *-freebsd* | *-openbsd*) ;;
90 | *)
91 | dnl First of all check for the --no-undefined variant of GNU ld. This allows
92 | dnl for a much more readable commandline, so that people can understand what
93 | dnl it does without going to look for what the heck -z defs does.
94 | for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do
95 | CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"])
96 | break
97 | done
98 | ;;
99 | esac
100 |
101 | AC_SUBST([LDFLAGS_NOUNDEFINED])
102 | ])
103 |
104 | dnl Check for a -Werror flag or equivalent. -Werror is the GCC
105 | dnl and ICC flag that tells the compiler to treat all the warnings
106 | dnl as fatal. We usually need this option to make sure that some
107 | dnl constructs (like attributes) are not simply ignored.
108 | dnl
109 | dnl Other compilers don't support -Werror per se, but they support
110 | dnl an equivalent flag:
111 | dnl - Sun Studio compiler supports -errwarn=%all
112 | AC_DEFUN([CC_CHECK_WERROR], [
113 | AC_CACHE_CHECK(
114 | [for $CC way to treat warnings as errors],
115 | [cc_cv_werror],
116 | [CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror],
117 | [CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])])
118 | ])
119 | ])
120 |
121 | AC_DEFUN([CC_CHECK_ATTRIBUTE], [
122 | AC_REQUIRE([CC_CHECK_WERROR])
123 | AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))],
124 | AS_TR_SH([cc_cv_attribute_$1]),
125 | [ac_save_CFLAGS="$CFLAGS"
126 | CFLAGS="$CFLAGS $cc_cv_werror"
127 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])],
128 | [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"],
129 | [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"])
130 | CFLAGS="$ac_save_CFLAGS"
131 | ])
132 |
133 | AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes],
134 | [AC_DEFINE(
135 | AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1,
136 | [Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))]
137 | )
138 | $4],
139 | [$5])
140 | ])
141 |
142 | AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [
143 | CC_CHECK_ATTRIBUTE(
144 | [constructor],,
145 | [void __attribute__((constructor)) ctor() { int a; }],
146 | [$1], [$2])
147 | ])
148 |
149 | AC_DEFUN([CC_ATTRIBUTE_FORMAT], [
150 | CC_CHECK_ATTRIBUTE(
151 | [format], [format(printf, n, n)],
152 | [void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }],
153 | [$1], [$2])
154 | ])
155 |
156 | AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [
157 | CC_CHECK_ATTRIBUTE(
158 | [format_arg], [format_arg(printf)],
159 | [char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }],
160 | [$1], [$2])
161 | ])
162 |
163 | AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [
164 | CC_CHECK_ATTRIBUTE(
165 | [visibility_$1], [visibility("$1")],
166 | [void __attribute__((visibility("$1"))) $1_function() { }],
167 | [$2], [$3])
168 | ])
169 |
170 | AC_DEFUN([CC_ATTRIBUTE_NONNULL], [
171 | CC_CHECK_ATTRIBUTE(
172 | [nonnull], [nonnull()],
173 | [void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }],
174 | [$1], [$2])
175 | ])
176 |
177 | AC_DEFUN([CC_ATTRIBUTE_UNUSED], [
178 | CC_CHECK_ATTRIBUTE(
179 | [unused], ,
180 | [void some_function(void *foo, __attribute__((unused)) void *bar);],
181 | [$1], [$2])
182 | ])
183 |
184 | AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [
185 | CC_CHECK_ATTRIBUTE(
186 | [sentinel], ,
187 | [void some_function(void *foo, ...) __attribute__((sentinel));],
188 | [$1], [$2])
189 | ])
190 |
191 | AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [
192 | CC_CHECK_ATTRIBUTE(
193 | [deprecated], ,
194 | [void some_function(void *foo, ...) __attribute__((deprecated));],
195 | [$1], [$2])
196 | ])
197 |
198 | AC_DEFUN([CC_ATTRIBUTE_ALIAS], [
199 | CC_CHECK_ATTRIBUTE(
200 | [alias], [weak, alias],
201 | [void other_function(void *foo) { }
202 | void some_function(void *foo) __attribute__((weak, alias("other_function")));],
203 | [$1], [$2])
204 | ])
205 |
206 | AC_DEFUN([CC_ATTRIBUTE_MALLOC], [
207 | CC_CHECK_ATTRIBUTE(
208 | [malloc], ,
209 | [void * __attribute__((malloc)) my_alloc(int n);],
210 | [$1], [$2])
211 | ])
212 |
213 | AC_DEFUN([CC_ATTRIBUTE_PACKED], [
214 | CC_CHECK_ATTRIBUTE(
215 | [packed], ,
216 | [struct astructure { char a; int b; long c; void *d; } __attribute__((packed));],
217 | [$1], [$2])
218 | ])
219 |
220 | AC_DEFUN([CC_ATTRIBUTE_CONST], [
221 | CC_CHECK_ATTRIBUTE(
222 | [const], ,
223 | [int __attribute__((const)) twopow(int n) { return 1 << n; } ],
224 | [$1], [$2])
225 | ])
226 |
227 | AC_DEFUN([CC_FLAG_VISIBILITY], [
228 | AC_REQUIRE([CC_CHECK_WERROR])
229 | AC_CACHE_CHECK([if $CC supports -fvisibility=hidden],
230 | [cc_cv_flag_visibility],
231 | [cc_flag_visibility_save_CFLAGS="$CFLAGS"
232 | CFLAGS="$CFLAGS $cc_cv_werror"
233 | CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden],
234 | cc_cv_flag_visibility='yes',
235 | cc_cv_flag_visibility='no')
236 | CFLAGS="$cc_flag_visibility_save_CFLAGS"])
237 |
238 | AS_IF([test "x$cc_cv_flag_visibility" = "xyes"],
239 | [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1,
240 | [Define this if the compiler supports the -fvisibility flag])
241 | $1],
242 | [$2])
243 | ])
244 |
245 | AC_DEFUN([CC_FUNC_EXPECT], [
246 | AC_REQUIRE([CC_CHECK_WERROR])
247 | AC_CACHE_CHECK([if compiler has __builtin_expect function],
248 | [cc_cv_func_expect],
249 | [ac_save_CFLAGS="$CFLAGS"
250 | CFLAGS="$CFLAGS $cc_cv_werror"
251 | AC_COMPILE_IFELSE([AC_LANG_SOURCE(
252 | [int some_function() {
253 | int a = 3;
254 | return (int)__builtin_expect(a, 3);
255 | }])],
256 | [cc_cv_func_expect=yes],
257 | [cc_cv_func_expect=no])
258 | CFLAGS="$ac_save_CFLAGS"
259 | ])
260 |
261 | AS_IF([test "x$cc_cv_func_expect" = "xyes"],
262 | [AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1,
263 | [Define this if the compiler supports __builtin_expect() function])
264 | $1],
265 | [$2])
266 | ])
267 |
268 | AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [
269 | AC_REQUIRE([CC_CHECK_WERROR])
270 | AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported],
271 | [cc_cv_attribute_aligned],
272 | [ac_save_CFLAGS="$CFLAGS"
273 | CFLAGS="$CFLAGS $cc_cv_werror"
274 | for cc_attribute_align_try in 64 32 16 8 4 2; do
275 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([
276 | int main() {
277 | static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0;
278 | return c;
279 | }])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
280 | done
281 | CFLAGS="$ac_save_CFLAGS"
282 | ])
283 |
284 | if test "x$cc_cv_attribute_aligned" != "x"; then
285 | AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned],
286 | [Define the highest alignment supported])
287 | fi
288 | ])
289 |
--------------------------------------------------------------------------------
/m4/acx_pthread.m4:
--------------------------------------------------------------------------------
1 | # ===========================================================================
2 | # http://www.nongnu.org/autoconf-archive/acx_pthread.html
3 | # ===========================================================================
4 | #
5 | # SYNOPSIS
6 | #
7 | # ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
8 | #
9 | # DESCRIPTION
10 | #
11 | # This macro figures out how to build C programs using POSIX threads. It
12 | # sets the PTHREAD_LIBS output variable to the threads library and linker
13 | # flags, and the PTHREAD_CFLAGS output variable to any special C compiler
14 | # flags that are needed. (The user can also force certain compiler
15 | # flags/libs to be tested by setting these environment variables.)
16 | #
17 | # Also sets PTHREAD_CC to any special C compiler that is needed for
18 | # multi-threaded programs (defaults to the value of CC otherwise). (This
19 | # is necessary on AIX to use the special cc_r compiler alias.)
20 | #
21 | # NOTE: You are assumed to not only compile your program with these flags,
22 | # but also link it with them as well. e.g. you should link with
23 | # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
24 | #
25 | # If you are only building threads programs, you may wish to use these
26 | # variables in your default LIBS, CFLAGS, and CC:
27 | #
28 | # LIBS="$PTHREAD_LIBS $LIBS"
29 | # CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
30 | # CC="$PTHREAD_CC"
31 | #
32 | # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
33 | # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
34 | # (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
35 | #
36 | # ACTION-IF-FOUND is a list of shell commands to run if a threads library
37 | # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
38 | # is not found. If ACTION-IF-FOUND is not specified, the default action
39 | # will define HAVE_PTHREAD.
40 | #
41 | # Please let the authors know if this macro fails on any platform, or if
42 | # you have any other suggestions or comments. This macro was based on work
43 | # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
44 | # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
45 | # Alejandro Forero Cuervo to the autoconf macro repository. We are also
46 | # grateful for the helpful feedback of numerous users.
47 | #
48 | # LICENSE
49 | #
50 | # Copyright (c) 2008 Steven G. Johnson
51 | #
52 | # This program is free software: you can redistribute it and/or modify it
53 | # under the terms of the GNU General Public License as published by the
54 | # Free Software Foundation, either version 3 of the License, or (at your
55 | # option) any later version.
56 | #
57 | # This program is distributed in the hope that it will be useful, but
58 | # WITHOUT ANY WARRANTY; without even the implied warranty of
59 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
60 | # Public License for more details.
61 | #
62 | # You should have received a copy of the GNU General Public License along
63 | # with this program. If not, see .
64 | #
65 | # As a special exception, the respective Autoconf Macro's copyright owner
66 | # gives unlimited permission to copy, distribute and modify the configure
67 | # scripts that are the output of Autoconf when processing the Macro. You
68 | # need not follow the terms of the GNU General Public License when using
69 | # or distributing such scripts, even though portions of the text of the
70 | # Macro appear in them. The GNU General Public License (GPL) does govern
71 | # all other use of the material that constitutes the Autoconf Macro.
72 | #
73 | # This special exception to the GPL applies to versions of the Autoconf
74 | # Macro released by the Autoconf Archive. When you make and distribute a
75 | # modified version of the Autoconf Macro, you may extend this special
76 | # exception to the GPL to apply to your modified version as well.
77 |
78 | AC_DEFUN([ACX_PTHREAD], [
79 | AC_REQUIRE([AC_CANONICAL_HOST])
80 | AC_LANG_SAVE
81 | AC_LANG_C
82 | acx_pthread_ok=no
83 |
84 | # We used to check for pthread.h first, but this fails if pthread.h
85 | # requires special compiler flags (e.g. on True64 or Sequent).
86 | # It gets checked for in the link test anyway.
87 |
88 | # First of all, check if the user has set any of the PTHREAD_LIBS,
89 | # etcetera environment variables, and if threads linking works using
90 | # them:
91 | if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
92 | save_CFLAGS="$CFLAGS"
93 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
94 | save_LIBS="$LIBS"
95 | LIBS="$PTHREAD_LIBS $LIBS"
96 | AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
97 | AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
98 | AC_MSG_RESULT($acx_pthread_ok)
99 | if test x"$acx_pthread_ok" = xno; then
100 | PTHREAD_LIBS=""
101 | PTHREAD_CFLAGS=""
102 | fi
103 | LIBS="$save_LIBS"
104 | CFLAGS="$save_CFLAGS"
105 | fi
106 |
107 | # We must check for the threads library under a number of different
108 | # names; the ordering is very important because some systems
109 | # (e.g. DEC) have both -lpthread and -lpthreads, where one of the
110 | # libraries is broken (non-POSIX).
111 |
112 | # Create a list of thread flags to try. Items starting with a "-" are
113 | # C compiler flags, and other items are library names, except for "none"
114 | # which indicates that we try without any flags at all, and "pthread-config"
115 | # which is a program returning the flags for the Pth emulation library.
116 |
117 | acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
118 |
119 | # The ordering *is* (sometimes) important. Some notes on the
120 | # individual items follow:
121 |
122 | # pthreads: AIX (must check this before -lpthread)
123 | # none: in case threads are in libc; should be tried before -Kthread and
124 | # other compiler flags to prevent continual compiler warnings
125 | # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
126 | # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
127 | # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
128 | # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
129 | # -pthreads: Solaris/gcc
130 | # -mthreads: Mingw32/gcc, Lynx/gcc
131 | # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
132 | # doesn't hurt to check since this sometimes defines pthreads too;
133 | # also defines -D_REENTRANT)
134 | # ... -mt is also the pthreads flag for HP/aCC
135 | # pthread: Linux, etcetera
136 | # --thread-safe: KAI C++
137 | # pthread-config: use pthread-config program (for GNU Pth library)
138 |
139 | case "${host_cpu}-${host_os}" in
140 | *solaris*)
141 |
142 | # On Solaris (at least, for some versions), libc contains stubbed
143 | # (non-functional) versions of the pthreads routines, so link-based
144 | # tests will erroneously succeed. (We need to link with -pthreads/-mt/
145 | # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
146 | # a function called by this macro, so we could check for that, but
147 | # who knows whether they'll stub that too in a future libc.) So,
148 | # we'll just look for -pthreads and -lpthread first:
149 |
150 | acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
151 | ;;
152 | esac
153 |
154 | if test x"$acx_pthread_ok" = xno; then
155 | for flag in $acx_pthread_flags; do
156 |
157 | case $flag in
158 | none)
159 | AC_MSG_CHECKING([whether pthreads work without any flags])
160 | ;;
161 |
162 | -*)
163 | AC_MSG_CHECKING([whether pthreads work with $flag])
164 | PTHREAD_CFLAGS="$flag"
165 | ;;
166 |
167 | pthread-config)
168 | AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
169 | if test x"$acx_pthread_config" = xno; then continue; fi
170 | PTHREAD_CFLAGS="`pthread-config --cflags`"
171 | PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
172 | ;;
173 |
174 | *)
175 | AC_MSG_CHECKING([for the pthreads library -l$flag])
176 | PTHREAD_LIBS="-l$flag"
177 | ;;
178 | esac
179 |
180 | save_LIBS="$LIBS"
181 | save_CFLAGS="$CFLAGS"
182 | LIBS="$PTHREAD_LIBS $LIBS"
183 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
184 |
185 | # Check for various functions. We must include pthread.h,
186 | # since some functions may be macros. (On the Sequent, we
187 | # need a special flag -Kthread to make this header compile.)
188 | # We check for pthread_join because it is in -lpthread on IRIX
189 | # while pthread_create is in libc. We check for pthread_attr_init
190 | # due to DEC craziness with -lpthreads. We check for
191 | # pthread_cleanup_push because it is one of the few pthread
192 | # functions on Solaris that doesn't have a non-functional libc stub.
193 | # We try pthread_create on general principles.
194 | AC_TRY_LINK([#include ],
195 | [pthread_t th; pthread_join(th, 0);
196 | pthread_attr_init(0); pthread_cleanup_push(0, 0);
197 | pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
198 | [acx_pthread_ok=yes])
199 |
200 | LIBS="$save_LIBS"
201 | CFLAGS="$save_CFLAGS"
202 |
203 | AC_MSG_RESULT($acx_pthread_ok)
204 | if test "x$acx_pthread_ok" = xyes; then
205 | break;
206 | fi
207 |
208 | PTHREAD_LIBS=""
209 | PTHREAD_CFLAGS=""
210 | done
211 | fi
212 |
213 | # Various other checks:
214 | if test "x$acx_pthread_ok" = xyes; then
215 | save_LIBS="$LIBS"
216 | LIBS="$PTHREAD_LIBS $LIBS"
217 | save_CFLAGS="$CFLAGS"
218 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
219 |
220 | # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
221 | AC_MSG_CHECKING([for joinable pthread attribute])
222 | attr_name=unknown
223 | for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
224 | AC_TRY_LINK([#include ], [int attr=$attr; return attr;],
225 | [attr_name=$attr; break])
226 | done
227 | AC_MSG_RESULT($attr_name)
228 | if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
229 | AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
230 | [Define to necessary symbol if this constant
231 | uses a non-standard name on your system.])
232 | fi
233 |
234 | AC_MSG_CHECKING([if more special flags are required for pthreads])
235 | flag=no
236 | case "${host_cpu}-${host_os}" in
237 | *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
238 | *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
239 | esac
240 | AC_MSG_RESULT(${flag})
241 | if test "x$flag" != xno; then
242 | PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
243 | fi
244 |
245 | LIBS="$save_LIBS"
246 | CFLAGS="$save_CFLAGS"
247 |
248 | # More AIX lossage: must compile with xlc_r or cc_r
249 | if test x"$GCC" != xyes; then
250 | AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
251 | else
252 | PTHREAD_CC=$CC
253 | fi
254 | else
255 | PTHREAD_CC="$CC"
256 | fi
257 |
258 | AC_SUBST(PTHREAD_LIBS)
259 | AC_SUBST(PTHREAD_CFLAGS)
260 | AC_SUBST(PTHREAD_CC)
261 |
262 | # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
263 | if test x"$acx_pthread_ok" = xyes; then
264 | ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
265 | :
266 | else
267 | acx_pthread_ok=no
268 | $2
269 | fi
270 | AC_LANG_RESTORE
271 | ])dnl ACX_PTHREAD
272 |
--------------------------------------------------------------------------------
/GPL:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/rtkit-daemon.c:
--------------------------------------------------------------------------------
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/
2 |
3 | /***
4 | This file is part of RealtimeKit.
5 |
6 | Copyright 2009 Lennart Poettering
7 | Copyright 2010 Maarten Lankhorst
8 |
9 | RealtimeKit is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | RealtimeKit is distributed in the hope that it will be useful, but
15 | WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 | General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with RealtimeKit. If not, see .
21 | ***/
22 |
23 | #ifdef HAVE_CONFIG_H
24 | #include
25 | #endif
26 |
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | #include
45 | #include
46 | #include
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 |
54 | #ifdef HAVE_LIBSYSTEMD
55 | #include
56 | #endif
57 |
58 | #include "rtkit.h"
59 |
60 | #ifndef __linux__
61 | #error "This stuff only works on Linux!"
62 | #endif
63 |
64 | #ifndef SCHED_RESET_ON_FORK
65 | /* "Your libc lacks the definition of SCHED_RESET_ON_FORK. We'll now define it ourselves, however make sure your kernel is new enough! */
66 | #define SCHED_RESET_ON_FORK 0x40000000
67 | #endif
68 |
69 | #ifndef RLIMIT_RTTIME
70 | #define RLIMIT_RTTIME 15
71 | #endif
72 |
73 | /* The introspection XML from org.freedesktop.RealtimeKit1.xml */
74 | static const char introspect_xml[] = {
75 | #include "xml-introspection.h"
76 | ,0x00};
77 |
78 | /* Similar to assert(), but has side effects, and hence shall never be optimized away, regardless of NDEBUG */
79 | #define assert_se(expr) \
80 | do { \
81 | if (__builtin_expect(!(expr), 0)) { \
82 | fprintf(stderr, "Assertion %s failed at %s:%u, function %s(). Aborting.\n", #expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
83 | abort(); \
84 | } \
85 | } while(0)
86 |
87 | #define ELEMENTSOF(x) (sizeof(x)/sizeof(x[0]))
88 |
89 | #define TIMESPEC_MSEC(ts) (((int64_t) (ts).tv_sec * 1000LL) + ((int64_t) (ts).tv_nsec / 1000000LL))
90 |
91 | /* If we actually execute a request we temporarily upgrade our realtime priority to this level */
92 | static unsigned our_realtime_priority = 21;
93 |
94 | /* Normally we run at this nice level */
95 | static int our_nice_level = 1;
96 |
97 | /* The maximum realtime priority to hand out */
98 | static unsigned max_realtime_priority = 20;
99 |
100 | /* The minimum nice level to hand out */
101 | static int min_nice_level = -15;
102 |
103 | /* Username we shall run under */
104 | static const char *username = "rtkit";
105 |
106 | /* Enforce that clients have RLIMIT_RTTIME set to a value <= this */
107 | static unsigned long long rttime_usec_max = 200000ULL; /* 200 ms */
108 |
109 | /* How many users do we supervise at max? */
110 | static unsigned users_max = 2048;
111 |
112 | /* How many processes of a single user do we supervise at max? */
113 | static unsigned processes_per_user_max = 15;
114 |
115 | /* How many threads of a single user do we supervise at max? */
116 | static unsigned threads_per_user_max = 25;
117 |
118 | /* Refuse further requests if one user issues more than ACTIONS_PER_BURST_MAX in this time */
119 | static unsigned actions_burst_sec = 20;
120 |
121 | /* Refuse further requests if one user issues more than this many in ACTIONS_BURST_SEC time */
122 | static unsigned actions_per_burst_max = 25;
123 |
124 | /* Drop privileges */
125 | static bool do_drop_privileges = TRUE;
126 |
127 | /* Change root directory to /proc */
128 | static bool do_chroot = TRUE;
129 |
130 | /* Limit resources */
131 | static bool do_limit_resources = TRUE;
132 |
133 | /* Run a canary watchdog */
134 | static bool do_canary = TRUE;
135 |
136 | /* Canary cheep interval */
137 | static unsigned canary_cheep_msec = 5000; /* 5s */
138 |
139 | /* Canary watchdog interval */
140 | static unsigned canary_watchdog_msec = 10000; /* 10s */
141 |
142 | /* Watchdog realtime priority */
143 | static unsigned canary_watchdog_realtime_priority = 99;
144 |
145 | /* How long after the canary died shall we refuse further RT requests? */
146 | static unsigned canary_refusal_sec = 5*60;
147 |
148 | /* Demote root processes? */
149 | static bool canary_demote_root = FALSE;
150 |
151 | /* Demote unknown processes? */
152 | static bool canary_demote_unknown = FALSE;
153 |
154 | /* Log to stderr? */
155 | static bool log_stderr = FALSE;
156 |
157 | /* Scheduling policy to use */
158 | static int sched_policy = SCHED_RR;
159 |
160 | struct thread {
161 | /* We use the thread id plus its starttime as a unique identifier for threads */
162 | pid_t pid;
163 | unsigned long long starttime;
164 |
165 | struct thread *next;
166 | };
167 |
168 | struct process {
169 | /* We use the process id plus its starttime as a unique identifier for processes */
170 | pid_t pid;
171 | unsigned long long starttime;
172 |
173 | struct thread *threads;
174 | struct process *next;
175 | };
176 |
177 | struct rtkit_user {
178 | uid_t uid;
179 |
180 | time_t timestamp;
181 | unsigned n_actions;
182 |
183 | struct process *processes;
184 | unsigned n_processes;
185 | unsigned n_threads;
186 |
187 | struct rtkit_user *next;
188 | };
189 |
190 | static struct rtkit_user *users = NULL;
191 | static unsigned n_users = 0;
192 | static unsigned n_total_processes = 0;
193 | static unsigned n_total_threads = 0;
194 | static const char *proc = NULL;
195 | static int quit_fd = -1, canary_fd = -1;
196 | static pthread_t canary_thread_id = 0, watchdog_thread_id = 0;
197 | static volatile uint32_t refuse_until = 0;
198 |
199 | static const char *get_proc_path(void) {
200 | /* Useful for chroot environments */
201 |
202 | if (proc)
203 | return proc;
204 |
205 | return "/proc";
206 | }
207 |
208 | static char* get_user_name(uid_t uid, char *user, size_t len) {
209 | struct passwd *pw;
210 |
211 | if ((pw = getpwuid(uid))) {
212 | strncpy(user, pw->pw_name, len-1);
213 | user[len-1] = 0;
214 | return user;
215 | }
216 |
217 | snprintf(user, len-1, "%llu", (unsigned long long) uid);
218 | user[len-1] = 0;
219 | return user;
220 | }
221 |
222 | static int read_starttime(pid_t pid, pid_t tid, unsigned long long *st) {
223 | char fn[128], line[256], *p;
224 | int r;
225 | FILE *f;
226 |
227 | if (tid != 0)
228 | assert_se(snprintf(fn, sizeof(fn)-1, "%s/%llu/task/%llu/stat", get_proc_path(), (unsigned long long) pid, (unsigned long long) tid) < (int) (sizeof(fn)-1));
229 | else
230 | assert_se(snprintf(fn, sizeof(fn)-1, "%s/%llu/stat", get_proc_path(), (unsigned long long) pid) < (int) (sizeof(fn)-1));
231 | fn[sizeof(fn)-1] = 0;
232 |
233 | if (!(f = fopen(fn, "r")))
234 | return -errno;
235 |
236 | if (!(fgets(line, sizeof(line), f))) {
237 | r = -errno;
238 | fclose(f);
239 | return r;
240 | }
241 |
242 | fclose(f);
243 |
244 | /* Let's skip the pid and comm fields. The latter is enclosed
245 | * in () but does not escape any () in its value, so let's
246 | * skip over it manually */
247 |
248 | if (!(p = strrchr(line, ')')))
249 | return -EIO;
250 |
251 | p++;
252 |
253 | if (sscanf(p, " "
254 | "%*c " /* state */
255 | "%*d " /* ppid */
256 | "%*d " /* pgrp */
257 | "%*d " /* session */
258 | "%*d " /* tty_nr */
259 | "%*d " /* tpgid */
260 | "%*u " /* flags */
261 | "%*u " /* minflt */
262 | "%*u " /* cminflt */
263 | "%*u " /* majflt */
264 | "%*u " /* cmajflt */
265 | "%*u " /* utime */
266 | "%*u " /* stime */
267 | "%*d " /* cutime */
268 | "%*d " /* cstime */
269 | "%*d " /* priority */
270 | "%*d " /* nice */
271 | "%*d " /* num_threads */
272 | "%*d " /* itrealvalue */
273 | "%llu " /* starttime */,
274 | st) != 1)
275 | return -EIO;
276 |
277 | return 0;
278 | }
279 |
280 | static void free_thread(struct thread *t) {
281 | free(t);
282 | }
283 |
284 | static void free_process(struct process *p) {
285 | struct thread *t;
286 |
287 | while ((t = p->threads)) {
288 | p->threads = t->next;
289 | free_thread(t);
290 | }
291 |
292 | free(p);
293 | }
294 |
295 | static void free_user(struct rtkit_user *u) {
296 | struct process *p;
297 |
298 | while ((p = u->processes)) {
299 | u->processes = p->next;
300 | free_process(p);
301 | }
302 |
303 | free(u);
304 | }
305 |
306 | static bool user_in_burst(struct rtkit_user *u) {
307 | time_t now = time(NULL);
308 |
309 | return now < u->timestamp + (time_t) actions_burst_sec;
310 | }
311 |
312 | static bool verify_burst(struct rtkit_user *u) {
313 |
314 | if (!user_in_burst(u)) {
315 | /* Restart burst phase */
316 | time(&u->timestamp);
317 | u->n_actions = 0;
318 | return true;
319 | }
320 |
321 | if (u->n_actions >= actions_per_burst_max) {
322 | char user[64];
323 | syslog(LOG_WARNING, "Warning: Reached burst limit for user '%s', denying request.\n", get_user_name(u->uid, user, sizeof(user)));
324 | return false;
325 | }
326 |
327 | u->n_actions++;
328 | return true;
329 | }
330 |
331 | static int find_user(struct rtkit_user **_u, uid_t uid) {
332 | struct rtkit_user *u;
333 |
334 | for (u = users; u; u = u->next)
335 | if (u->uid == uid) {
336 | *_u = u;
337 | return 0;
338 | }
339 |
340 | if (n_users >= users_max) {
341 | syslog(LOG_WARNING, "Warning: Reached maximum concurrent user limit, denying request.\n");
342 | return -EBUSY;
343 | }
344 |
345 | if (!(u = malloc(sizeof(struct rtkit_user))))
346 | return -ENOMEM;
347 |
348 | u->uid = uid;
349 | u->timestamp = time(NULL);
350 | u->n_actions = 0;
351 | u->processes = NULL;
352 | u->n_processes = u->n_threads = 0;
353 | u->next = users;
354 | users = u;
355 | n_users++;
356 |
357 | *_u = u;
358 | return 0;
359 | }
360 |
361 | static int find_process(struct process** _p, struct rtkit_user *u, pid_t pid, unsigned long long starttime) {
362 | struct process *p;
363 |
364 | for (p = u->processes; p; p = p->next)
365 | if (p->pid == pid && p->starttime == starttime) {
366 | *_p = p;
367 | return 0;
368 | }
369 |
370 | if (u->n_processes >= processes_per_user_max) {
371 | char user[64];
372 | syslog(LOG_WARNING, "Warning: Reached maximum concurrent process limit for user '%s', denying request.\n", get_user_name(u->uid, user, sizeof(user)));
373 | return -EBUSY;
374 | }
375 |
376 | if (!(p = malloc(sizeof(struct process))))
377 | return -ENOMEM;
378 |
379 | p->pid = pid;
380 | p->starttime = starttime;
381 | p->threads = NULL;
382 | p->next = u->processes;
383 | u->processes = p;
384 | u->n_processes++;
385 | n_total_processes++;
386 |
387 | *_p = p;
388 | return 0;
389 | }
390 |
391 | static int find_thread(struct thread** _t, struct rtkit_user *u, struct process *p, pid_t pid, unsigned long long starttime) {
392 | struct thread *t;
393 |
394 | for (t = p->threads; t; t = t->next)
395 | if (t->pid == pid && t->starttime == starttime) {
396 | *_t = t;
397 | return 0;
398 | }
399 |
400 | if (u->n_threads >= threads_per_user_max) {
401 | char user[64];
402 | syslog(LOG_WARNING, "Warning: Reached maximum concurrent threads limit for user '%s', denying request.\n", get_user_name(u->uid, user, sizeof(user)));
403 | return -EBUSY;
404 | }
405 |
406 | if (!(t = malloc(sizeof(struct thread))))
407 | return -ENOMEM;
408 |
409 | t->pid = pid;
410 | t->starttime = starttime;
411 | t->next = p->threads;
412 | p->threads = t;
413 | u->n_threads++;
414 | n_total_threads++;
415 |
416 | *_t = t;
417 | return 0;
418 | }
419 |
420 | static bool thread_relevant(struct process *p, struct thread *t) {
421 | unsigned long long st;
422 | int r;
423 |
424 | /* This checks if a thread still matters to us, i.e. if its
425 | * PID still refers to the same thread and if it is still high
426 | * priority or real time */
427 |
428 | if ((r = read_starttime(p->pid, t->pid, &st)) < 0) {
429 |
430 | /* Did the thread die? */
431 | if (r == -ENOENT)
432 | return FALSE;
433 |
434 | syslog(LOG_WARNING, "Warning: failed to read start time: %s\n", strerror(-r));
435 | return FALSE;
436 | }
437 |
438 | /* Did the thread get replaced by another thread? */
439 | if (st != t->starttime)
440 | return FALSE;
441 |
442 | if ((r = sched_getscheduler(t->pid)) < 0) {
443 |
444 | /* Maybe it died right now? */
445 | if (errno == ESRCH)
446 | return FALSE;
447 |
448 | syslog(LOG_WARNING, "Warning: failed to read scheduler policy: %s\n", strerror(errno));
449 | return FALSE;
450 | }
451 |
452 | /* Is this a realtime thread? */
453 | r &= ~SCHED_RESET_ON_FORK;
454 | if (r == SCHED_RR || r == SCHED_FIFO)
455 | return TRUE;
456 |
457 | errno = 0;
458 | r = getpriority(PRIO_PROCESS, t->pid);
459 | if (errno != 0) {
460 |
461 | /* Maybe it died right now? */
462 | if (errno == ESRCH)
463 | return FALSE;
464 |
465 | syslog(LOG_WARNING, "Warning: failed to read nice level: %s\n", strerror(errno));
466 | return FALSE;
467 | }
468 |
469 | /* Is this a high priority thread? */
470 | if (r < 0)
471 | return TRUE;
472 |
473 | return FALSE;
474 | }
475 |
476 | static void thread_gc(struct rtkit_user *u, struct process *p) {
477 | struct thread *t, *n, *l;
478 |
479 | /* Cleanup dead theads of a specific user we don't need to keep track about anymore */
480 |
481 | for (l = NULL, t = p->threads; t; t = n) {
482 | n = t->next;
483 |
484 | if (!thread_relevant(p, t)) {
485 | free_thread(t);
486 | if (l)
487 | l->next = n;
488 | else
489 | p->threads = n;
490 |
491 | assert(u->n_threads >= 1);
492 | u->n_threads--;
493 |
494 | assert(n_total_threads >= 1);
495 | n_total_threads--;
496 | } else
497 | l = t;
498 | }
499 |
500 | assert(!p->threads || u->n_threads);
501 | }
502 |
503 | static void process_gc(struct rtkit_user *u) {
504 | struct process *p, *n, *l;
505 |
506 | /* Cleanup dead processes of a specific user we don't need to keep track about anymore */
507 |
508 | for (l = NULL, p = u->processes; p; p = n) {
509 | n = p->next;
510 | thread_gc(u, p);
511 |
512 | if (!p->threads) {
513 | free_process(p);
514 | if (l)
515 | l->next = n;
516 | else
517 | u->processes = n;
518 |
519 | assert(u->n_processes >= 1);
520 | u->n_processes--;
521 |
522 | assert(n_total_processes >= 1);
523 | n_total_processes--;
524 | } else
525 | l = p;
526 | }
527 |
528 | assert(!u->processes == !u->n_processes);
529 | }
530 |
531 | static void user_gc(void) {
532 | struct rtkit_user *u, *n, *l;
533 |
534 | /* Cleanup all users we don't need to keep track about anymore */
535 |
536 | for (l = NULL, u = users; u; u = n) {
537 | n = u->next;
538 | process_gc(u);
539 |
540 | if (!u->processes && !user_in_burst(u)) {
541 | free_user(u);
542 | if (l)
543 | l->next = n;
544 | else
545 | users = n;
546 |
547 | assert(n_users >= 1);
548 | n_users--;
549 | } else
550 | l = u;
551 | }
552 |
553 | assert(!users == !n_users);
554 | }
555 |
556 | static bool startswith(const char *s, const char *prefix) {
557 | return strncmp(s, prefix, strlen(prefix)) == 0;
558 | }
559 |
560 | static int self_set_realtime(unsigned priority) {
561 | struct sched_param param;
562 | int r;
563 |
564 | memset(¶m, 0, sizeof(param));
565 | param.sched_priority = priority;
566 |
567 | if (sched_setscheduler(0, sched_policy|SCHED_RESET_ON_FORK, ¶m) < 0) {
568 | r = -errno;
569 | syslog(LOG_ERR, "Failed to make ourselves RT: %s\n", strerror(errno));
570 | goto finish;
571 | }
572 |
573 | r = 0;
574 |
575 | finish:
576 | return r;
577 | }
578 |
579 | static void self_drop_realtime(int nice_level) {
580 | struct sched_param param;
581 |
582 | memset(¶m, 0, sizeof(param));
583 |
584 | if (sched_setscheduler(0, SCHED_OTHER, ¶m) < 0)
585 | syslog(LOG_WARNING, "Warning: Failed to reset scheduling to SCHED_OTHER: %s\n", strerror(errno));
586 |
587 | if (setpriority(PRIO_PROCESS, 0, nice_level) < 0)
588 | syslog(LOG_WARNING, "Warning: Failed to reset nice level to %u: %s\n", our_nice_level, strerror(errno));
589 | }
590 |
591 | /* Verifies that RLIMIT_RTTIME is set for the specified process */
592 | static int verify_process_rttime(struct process *p) {
593 | char fn[128];
594 | FILE *f;
595 | int r;
596 | bool good = false;
597 |
598 | assert_se(snprintf(fn, sizeof(fn)-1, "%s/%llu/limits", get_proc_path(), (unsigned long long) p->pid) < (int) (sizeof(fn)-1));
599 | fn[sizeof(fn)-1] = 0;
600 |
601 | if (!(f = fopen(fn, "r"))) {
602 | r = -errno;
603 | syslog(LOG_ERR, "Failed to open '%s': %s\n", fn, strerror(errno));
604 | return r;
605 | }
606 |
607 | for (;;) {
608 | char line[128];
609 | char soft[32], hard[32];
610 | unsigned long long rttime;
611 | char *e = NULL;
612 |
613 | if (!fgets(line, sizeof(line), f))
614 | break;
615 |
616 | if (!startswith(line, "Max realtime timeout"))
617 | continue;
618 |
619 | if (sscanf(line + 20, "%s %s", soft, hard) != 2) {
620 | syslog(LOG_WARNING, "Warning: parse failure in %s.\n", fn);
621 | break;
622 | }
623 |
624 | errno = 0;
625 | rttime = strtoll(hard, &e, 10);
626 |
627 | if (errno != 0 || !e || *e != 0)
628 | break;
629 |
630 | if (rttime <= rttime_usec_max)
631 | good = true;
632 |
633 | break;
634 | }
635 |
636 | fclose(f);
637 |
638 | return good ? 0 : -EPERM;
639 | }
640 |
641 | static int verify_process_user(struct rtkit_user *u, struct process *p) {
642 | char fn[128];
643 | int r;
644 | struct stat st;
645 |
646 | assert_se(snprintf(fn, sizeof(fn)-1, "%s/%llu", get_proc_path(), (unsigned long long) p->pid) < (int) (sizeof(fn)-1));
647 | fn[sizeof(fn)-1] = 0;
648 |
649 | memset(&st, 0, sizeof(st));
650 | if (stat(fn, &st) < 0) {
651 | r = -errno;
652 |
653 | if (r != -ENOENT)
654 | syslog(LOG_WARNING, "Warning: Failed to stat() file '%s': %s\n", fn, strerror(-r));
655 |
656 | return r;
657 | }
658 |
659 | return st.st_uid == u->uid ? 0 : -EPERM;
660 | }
661 |
662 | static int verify_process_starttime(struct process *p) {
663 | unsigned long long st;
664 | int r;
665 |
666 | if ((r = read_starttime(p->pid, 0, &st)) < 0) {
667 |
668 | if (r != -ENOENT)
669 | syslog(LOG_WARNING, "Warning: Failed to read start time of process %llu: %s\n", (unsigned long long) p->pid, strerror(-r));
670 |
671 | return r;
672 | }
673 |
674 | return st == p->starttime ? 0 : -EPERM;
675 | }
676 |
677 | static int verify_thread_starttime(struct process *p, struct thread *t) {
678 | unsigned long long st;
679 | int r;
680 |
681 | if ((r = read_starttime(p->pid, t->pid, &st)) < 0) {
682 |
683 | if (r != -ENOENT)
684 | syslog(LOG_WARNING, "Warning: Failed to read start time of thread %llu: %s\n", (unsigned long long) t->pid, strerror(-r));
685 |
686 | return r;
687 | }
688 |
689 | return st == t->starttime ? 0 : -EPERM;
690 | }
691 |
692 | static int thread_reset(pid_t tid) {
693 | struct sched_param param;
694 | int r = 0;
695 |
696 | memset(¶m, 0, sizeof(param));
697 | param.sched_priority = 0;
698 |
699 | if (sched_setscheduler(tid, SCHED_OTHER, ¶m) < 0) {
700 | if (errno != ESRCH)
701 | syslog(LOG_WARNING, "Warning: Failed to reset scheduling to SCHED_OTHER for thread %llu: %s\n", (unsigned long long) tid, strerror(errno));
702 | r = -1;
703 | }
704 |
705 | if (setpriority(PRIO_PROCESS, tid, 0) < 0) {
706 | if (errno != ESRCH)
707 | syslog(LOG_WARNING, "Warning: Failed to reset nice level to 0 for thread %llu: %s\n", (unsigned long long) tid, strerror(errno));
708 | r = -1;
709 | }
710 |
711 | return r;
712 | }
713 |
714 | static int process_set_realtime(struct rtkit_user *u, struct process *p, struct thread *t, unsigned priority) {
715 | int r;
716 | struct sched_param param;
717 | char user[64];
718 |
719 | if ((int) priority < sched_get_priority_min(sched_policy) ||
720 | (int) priority > sched_get_priority_max(sched_policy))
721 | return -EINVAL;
722 |
723 | /* We always want to be able to get a higher RT priority than
724 | * the client */
725 | if (priority >= our_realtime_priority ||
726 | priority > max_realtime_priority)
727 | return -EPERM;
728 |
729 | /* Make sure users don't flood us with requests */
730 | if (!verify_burst(u))
731 | return -EBUSY;
732 |
733 | /* Temporarily become a realtime process. We do this to make
734 | * sure that our verification code is not preempted by an evil
735 | * client's code which might have gotten RT through
736 | * us. */
737 | if ((r = self_set_realtime(our_realtime_priority)) < 0)
738 | return r;
739 |
740 | /* Let's make sure that everything is alright before we make
741 | * the process realtime */
742 | if ((r = verify_process_user(u, p)) < 0 ||
743 | (r = verify_process_starttime(p)) < 0 ||
744 | (r = verify_process_rttime(p)) < 0 ||
745 | (r = verify_thread_starttime(p, t)) < 0)
746 | goto finish;
747 |
748 | /* Ok, everything seems to be in order, now, let's do it */
749 | memset(¶m, 0, sizeof(param));
750 | param.sched_priority = (int) priority;
751 | if (sched_setscheduler(t->pid, sched_policy|SCHED_RESET_ON_FORK, ¶m) < 0) {
752 | r = -errno;
753 | syslog(LOG_ERR, "Failed to make thread %llu RT: %s\n", (unsigned long long) t->pid, strerror(errno));
754 | goto finish;
755 | }
756 |
757 | /* We do some sanity checks afterwards, to verify that the
758 | * caller didn't play games with us and replaced the process
759 | * behind the PID */
760 | if ((r = verify_thread_starttime(p, t)) < 0 ||
761 | (r = verify_process_rttime(p)) < 0 ||
762 | (r = verify_process_starttime(p)) < 0 ||
763 | (r = verify_process_user(u, p)) < 0) {
764 |
765 | thread_reset(t->pid);
766 | goto finish;
767 | }
768 |
769 | syslog(LOG_INFO, "Successfully made thread %llu of process %llu owned by '%s' RT at priority %u.\n",
770 | (unsigned long long) t->pid,
771 | (unsigned long long) p->pid,
772 | get_user_name(u->uid, user, sizeof(user)),
773 | priority);
774 |
775 | r = 0;
776 |
777 | finish:
778 | self_drop_realtime(our_nice_level);
779 |
780 | return r;
781 | }
782 |
783 | static int process_set_high_priority(struct rtkit_user *u, struct process *p, struct thread *t, int priority) {
784 | int r;
785 | struct sched_param param;
786 | char user[64];
787 |
788 | if (priority < PRIO_MIN || priority >= PRIO_MAX)
789 | return -EINVAL;
790 |
791 | if (priority < min_nice_level)
792 | return -EPERM;
793 |
794 | /* Make sure users don't flood us with requests */
795 | if (!verify_burst(u))
796 | return -EBUSY;
797 |
798 | /* Temporarily become a realtime process */
799 | if ((r = self_set_realtime(our_realtime_priority)) < 0)
800 | return r;
801 |
802 | /* Let's make sure that everything is alright before we make
803 | * the process high priority */
804 | if ((r = verify_process_user(u, p)) < 0 ||
805 | (r = verify_process_starttime(p)) < 0 ||
806 | (r = verify_thread_starttime(p, t)) < 0)
807 | goto finish;
808 |
809 | /* Ok, everything seems to be in order, now, let's do it */
810 | memset(¶m, 0, sizeof(param));
811 | param.sched_priority = 0;
812 | if (sched_setscheduler(t->pid, SCHED_OTHER|SCHED_RESET_ON_FORK, ¶m) < 0) {
813 | r = -errno;
814 | syslog(LOG_ERR, "Failed to make process %llu SCHED_NORMAL: %s\n", (unsigned long long) t->pid, strerror(errno));
815 | goto finish;
816 | }
817 |
818 | if (setpriority(PRIO_PROCESS, t->pid, priority) < 0) {
819 | r = -errno;
820 | syslog(LOG_ERR, "Failed to set nice level of process %llu to %i: %s\n", (unsigned long long) t->pid, priority, strerror(errno));
821 | goto finish;
822 | }
823 |
824 | if ((r = verify_thread_starttime(p, t)) < 0 ||
825 | (r = verify_process_starttime(p)) < 0 ||
826 | (r = verify_process_user(u, p)) < 0) {
827 |
828 | thread_reset(t->pid);
829 | goto finish;
830 | }
831 |
832 | syslog(LOG_INFO, "Successfully made thread %llu of process %llu owned by '%s' high priority at nice level %i.\n",
833 | (unsigned long long) t->pid,
834 | (unsigned long long) p->pid,
835 | get_user_name(u->uid, user, sizeof(user)),
836 | priority);
837 |
838 | r = 0;
839 |
840 | finish:
841 | self_drop_realtime(our_nice_level);
842 |
843 | return r;
844 | }
845 |
846 | static void reset_known(void) {
847 | struct rtkit_user *u;
848 | struct process *p;
849 | struct thread *t;
850 | unsigned n_demoted = 0;
851 |
852 | syslog(LOG_INFO, "Demoting known real-time threads.\n");
853 |
854 | for (u = users; u; u = u->next)
855 | for (p = u->processes; p; p = p->next)
856 | for (t = p->threads; t; t = t->next)
857 | if (verify_process_user(u, p) >= 0 &&
858 | verify_process_starttime(p) >= 0 &&
859 | verify_thread_starttime(p, t) >= 0)
860 | if (thread_reset(t->pid) >= 0) {
861 | syslog(LOG_NOTICE, "Successfully demoted thread %llu of process %llu.\n",
862 | (unsigned long long) t->pid,
863 | (unsigned long long) p->pid);
864 | n_demoted++;
865 | }
866 |
867 | syslog(LOG_NOTICE, "Demoted %u threads.\n", n_demoted);
868 | }
869 |
870 | static int reset_all(void) {
871 | DIR *pd;
872 | int r;
873 | unsigned n_demoted = 0;
874 |
875 | /* Goes through /proc and demotes *all* threads to
876 | * SCHED_OTHER */
877 |
878 | syslog(LOG_INFO, "Demoting known and unknown real-time threads.\n");
879 |
880 | if (!(pd = opendir(get_proc_path()))) {
881 | r = -errno;
882 | syslog(LOG_ERR, "opendir(%s) failed: %s\n", get_proc_path(), strerror(errno));
883 | return r;
884 | }
885 |
886 | for (;;) {
887 | const struct dirent *pde;
888 | char *e = NULL;
889 | unsigned long long pid;
890 | char fn[128];
891 | DIR *td;
892 | struct stat st;
893 |
894 | if (!(pde = readdir(pd)))
895 | break;
896 |
897 | if (!(pde->d_type & DT_DIR))
898 | continue;
899 |
900 | errno = 0;
901 | pid = strtoull(pde->d_name, &e, 10);
902 | if (errno != 0 || !e || *e != 0)
903 | continue;
904 |
905 | if ((pid_t) pid == getpid() ||
906 | pid == 1)
907 | continue;
908 |
909 | assert_se(snprintf(fn, sizeof(fn)-1, "%s/%llu", get_proc_path(), pid) < (int) (sizeof(fn)-1));
910 | fn[sizeof(fn)-1] = 0;
911 |
912 | if (stat(fn, &st) < 0) {
913 | if (errno != ENOENT)
914 | syslog(LOG_WARNING, "Warning: stat(%s) failed: %s\n", fn, strerror(errno));
915 | continue;
916 | }
917 |
918 | if (!S_ISDIR(st.st_mode))
919 | continue;
920 |
921 | if (!canary_demote_root && st.st_uid == 0)
922 | continue;
923 |
924 | assert_se(snprintf(fn, sizeof(fn)-1, "%s/%llu/task", get_proc_path(), pid) < (int) (sizeof(fn)-1));
925 | fn[sizeof(fn)-1] = 0;
926 |
927 | if (!(td = opendir(fn)))
928 | continue;
929 |
930 | for (;;) {
931 | const struct dirent *tde;
932 | unsigned long long tid;
933 |
934 | if (!(tde = readdir(td)))
935 | break;
936 |
937 | if (!(tde->d_type & DT_DIR))
938 | continue;
939 |
940 | e = NULL;
941 | errno = 0;
942 | tid = strtoull(tde->d_name, &e, 10);
943 | if (errno != 0 || !e || *e != 0)
944 | continue;
945 |
946 | if ((r = sched_getscheduler(tid)) < 0) {
947 | if (errno != ESRCH)
948 | syslog(LOG_WARNING, "Warning: sched_getscheduler() failed: %s\n", strerror(errno));
949 | continue;
950 | }
951 |
952 | if (r == SCHED_FIFO || r == SCHED_RR ||
953 | r == (SCHED_FIFO|SCHED_RESET_ON_FORK) || r == (SCHED_RR|SCHED_RESET_ON_FORK))
954 | if (thread_reset((pid_t) tid) >= 0) {
955 | syslog(LOG_NOTICE, "Successfully demoted thread %llu of process %llu.\n",
956 | (unsigned long long) tid,
957 | (unsigned long long) pid);
958 | n_demoted++;
959 | }
960 | }
961 |
962 | closedir(td);
963 | }
964 |
965 | closedir(pd);
966 |
967 | syslog(LOG_NOTICE, "Demoted %u threads.\n", n_demoted);
968 |
969 | return 0;
970 | }
971 |
972 | /* This mimics dbus_bus_get_unix_user() */
973 | static unsigned long get_unix_process_id(
974 | DBusConnection *connection,
975 | const char *name,
976 | DBusError *error) {
977 |
978 | DBusMessage *m, *r;
979 | uint32_t pid = (uint32_t) -1;
980 |
981 | assert_se(m = dbus_message_new_method_call(
982 | DBUS_SERVICE_DBUS,
983 | DBUS_PATH_DBUS,
984 | DBUS_INTERFACE_DBUS,
985 | "GetConnectionUnixProcessID"));
986 |
987 | assert_se(dbus_message_append_args(
988 | m,
989 | DBUS_TYPE_STRING, &name,
990 | DBUS_TYPE_INVALID));
991 |
992 | r = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
993 | dbus_message_unref (m);
994 |
995 | if (!r)
996 | goto finish;
997 |
998 | if (dbus_set_error_from_message(error, r))
999 | goto finish;
1000 |
1001 | if (!dbus_message_get_args(
1002 | r, error,
1003 | DBUS_TYPE_UINT32, &pid,
1004 | DBUS_TYPE_INVALID)) {
1005 | pid = (uint32_t) -1;
1006 | goto finish;
1007 | }
1008 |
1009 | finish:
1010 |
1011 | if (r)
1012 | dbus_message_unref(r);
1013 |
1014 | return (unsigned long) pid;
1015 | }
1016 |
1017 | static int lookup_client(
1018 | struct rtkit_user **_u,
1019 | struct process **_p,
1020 | struct thread **_t,
1021 | DBusConnection *c,
1022 | DBusMessage *m,
1023 | pid_t pid,
1024 | pid_t tid) {
1025 |
1026 | DBusError error;
1027 | int r;
1028 | unsigned long uid;
1029 | unsigned long long starttime;
1030 | struct rtkit_user *u;
1031 | struct process *p;
1032 | struct thread *t;
1033 |
1034 | dbus_error_init(&error);
1035 |
1036 | /* Determine caller credentials */
1037 | if ((uid = dbus_bus_get_unix_user(c, dbus_message_get_sender(m), &error)) == (unsigned long) -1) {
1038 | syslog(LOG_ERR, "dbus_message_get_unix_user() failed: %s\n", error.message);
1039 | r = -EIO;
1040 | goto fail;
1041 | }
1042 |
1043 | if (pid == (pid_t) -1 &&
1044 | (pid = get_unix_process_id(c, dbus_message_get_sender(m), &error)) == (pid_t) -1) {
1045 | syslog(LOG_ERR, "get_unix_process_id() failed: %s\n", error.message);
1046 | r = -EIO;
1047 | goto fail;
1048 | }
1049 |
1050 | /* Find or create user structure */
1051 | if ((r = find_user(&u, (uid_t) uid) < 0))
1052 | goto fail;
1053 |
1054 | /* Find or create process structure */
1055 | if ((r = read_starttime((pid_t) pid, 0, &starttime)) < 0)
1056 | goto fail;
1057 |
1058 | if ((r = find_process(&p, u, (pid_t) pid, starttime)) < 0)
1059 | goto fail;
1060 |
1061 | /* Find or create thread structure */
1062 | if (tid == 0)
1063 | tid = p->pid;
1064 |
1065 | if ((r = read_starttime(p->pid, tid, &starttime)) < 0)
1066 | goto fail;
1067 |
1068 | if ((r = find_thread(&t, u, p, (pid_t) tid, starttime)) < 0)
1069 | goto fail;
1070 |
1071 | *_u = u;
1072 | *_p = p;
1073 | *_t = t;
1074 |
1075 | return 0;
1076 |
1077 | fail:
1078 | dbus_error_free(&error);
1079 |
1080 | return r;
1081 | }
1082 |
1083 | static const char *translate_error_forward(int error) {
1084 | switch (error) {
1085 | case -EPERM:
1086 | case -EACCES:
1087 | case -EBUSY:
1088 | return DBUS_ERROR_ACCESS_DENIED;
1089 |
1090 | case -ENOMEM:
1091 | return DBUS_ERROR_NO_MEMORY;
1092 |
1093 | default:
1094 | return DBUS_ERROR_FAILED;
1095 | }
1096 | }
1097 |
1098 | static int translate_error_backwards(const char *name) {
1099 | if (strcmp(name, DBUS_ERROR_NO_MEMORY) == 0)
1100 | return -ENOMEM;
1101 | if (strcmp(name, DBUS_ERROR_SERVICE_UNKNOWN) == 0 ||
1102 | strcmp(name, DBUS_ERROR_NAME_HAS_NO_OWNER) == 0)
1103 | return -ENOENT;
1104 | if (strcmp(name, DBUS_ERROR_ACCESS_DENIED) == 0 ||
1105 | strcmp(name, DBUS_ERROR_AUTH_FAILED) == 0)
1106 | return -EACCES;
1107 |
1108 | return -EIO;
1109 | }
1110 |
1111 | static int verify_polkit(DBusConnection *c, struct rtkit_user *u, struct process *p, const char *action) {
1112 | DBusError error;
1113 | DBusMessage *m = NULL, *r = NULL;
1114 | const char *unix_process = "unix-process";
1115 | const char *pid = "pid";
1116 | const char *uid = "uid";
1117 | const char *start_time = "start-time";
1118 | const char *cancel_id = "";
1119 | uint32_t flags = 0;
1120 | uint32_t pid_u32 = p->pid;
1121 | uint32_t uid_u32 = (uint32_t)u->uid;
1122 | DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant;
1123 | uint64_t start_time_u64 = p->starttime;
1124 | int ret;
1125 | dbus_bool_t authorized = FALSE;
1126 |
1127 | dbus_error_init(&error);
1128 |
1129 | assert_se(m = dbus_message_new_method_call(
1130 | "org.freedesktop.PolicyKit1",
1131 | "/org/freedesktop/PolicyKit1/Authority",
1132 | "org.freedesktop.PolicyKit1.Authority",
1133 | "CheckAuthorization"));
1134 |
1135 | dbus_message_iter_init_append(m, &iter_msg);
1136 | assert_se(dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct));
1137 | assert_se(dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process));
1138 | assert_se(dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array));
1139 |
1140 | assert_se(dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict));
1141 | assert_se(dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid));
1142 | assert_se(dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant));
1143 | assert_se(dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32));
1144 | assert_se(dbus_message_iter_close_container(&iter_dict, &iter_variant));
1145 | assert_se(dbus_message_iter_close_container(&iter_array, &iter_dict));
1146 |
1147 | assert_se(dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict));
1148 | assert_se(dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &start_time));
1149 | assert_se(dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant));
1150 | assert_se(dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &start_time_u64));
1151 | assert_se(dbus_message_iter_close_container(&iter_dict, &iter_variant));
1152 | assert_se(dbus_message_iter_close_container(&iter_array, &iter_dict));
1153 |
1154 | assert_se(dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict));
1155 | assert_se(dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &uid));
1156 | assert_se(dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant));
1157 | assert_se(dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &uid_u32));
1158 | assert_se(dbus_message_iter_close_container(&iter_dict, &iter_variant));
1159 | assert_se(dbus_message_iter_close_container(&iter_array, &iter_dict));
1160 |
1161 | assert_se(dbus_message_iter_close_container(&iter_struct, &iter_array));
1162 | assert_se(dbus_message_iter_close_container(&iter_msg, &iter_struct));
1163 |
1164 | assert_se(dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action));
1165 |
1166 | assert_se(dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array));
1167 | assert_se(dbus_message_iter_close_container(&iter_msg, &iter_array));
1168 |
1169 | assert_se(dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags));
1170 | assert_se(dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id));
1171 |
1172 | if (!(r = dbus_connection_send_with_reply_and_block(c, m, -1, &error))) {
1173 | ret = translate_error_backwards(error.name);
1174 | goto finish;
1175 | }
1176 |
1177 | if (dbus_set_error_from_message(&error, r)) {
1178 | ret = translate_error_backwards(error.name);
1179 | goto finish;
1180 | }
1181 |
1182 | if (!dbus_message_iter_init(r, &iter_msg) ||
1183 | dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) {
1184 | ret = -EIO;
1185 | goto finish;
1186 | }
1187 |
1188 | dbus_message_iter_recurse(&iter_msg, &iter_struct);
1189 |
1190 | if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) {
1191 | ret = -EIO;
1192 | goto finish;
1193 | }
1194 |
1195 | dbus_message_iter_get_basic(&iter_struct, &authorized);
1196 |
1197 | ret = authorized ? 0 : -EPERM;
1198 |
1199 | finish:
1200 |
1201 | if (m)
1202 | dbus_message_unref(m);
1203 |
1204 | if (r)
1205 | dbus_message_unref(r);
1206 |
1207 | if (error.message)
1208 | syslog(LOG_WARNING, "Warning: PolicyKit call failed: %s\n", error.message);
1209 |
1210 | dbus_error_free(&error);
1211 |
1212 | return ret;
1213 | }
1214 |
1215 | static int verify_canary_refusal(void) {
1216 | struct timespec now;
1217 |
1218 | assert_se(clock_gettime(CLOCK_MONOTONIC, &now) == 0);
1219 |
1220 | if (now.tv_sec < (time_t) refuse_until) {
1221 | syslog(LOG_WARNING, "Recovering from system lockup, not allowing further RT threads.\n");
1222 | return -EPERM;
1223 | }
1224 |
1225 | return 0;
1226 | }
1227 |
1228 | static void add_variant(
1229 | DBusMessage *m,
1230 | int type,
1231 | const void *data) {
1232 |
1233 | DBusMessageIter iter, sub;
1234 | char t[2];
1235 |
1236 | t[0] = (char) type;
1237 | t[1] = 0;
1238 |
1239 | dbus_message_iter_init_append(m, &iter);
1240 |
1241 | assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, t, &sub));
1242 | assert_se(dbus_message_iter_append_basic(&sub, type, data));
1243 | assert_se(dbus_message_iter_close_container(&iter, &sub));
1244 | }
1245 |
1246 | static int handle_dbus_prop_get(const char* property, DBusMessage *r) {
1247 | if (strcmp(property, "RTTimeUSecMax") == 0)
1248 | add_variant(r, DBUS_TYPE_INT64, &rttime_usec_max);
1249 | else if (strcmp(property, "MaxRealtimePriority") == 0)
1250 | add_variant(r, DBUS_TYPE_INT32, &max_realtime_priority);
1251 | else if (strcmp(property, "MinNiceLevel") == 0)
1252 | add_variant(r, DBUS_TYPE_INT32, &min_nice_level);
1253 | else
1254 | return -1;
1255 |
1256 | return 0;
1257 | }
1258 |
1259 | static DBusHandlerResult dbus_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
1260 | DBusError error;
1261 | DBusMessage *r = NULL;
1262 | int is2 = 0;
1263 |
1264 | dbus_error_init(&error);
1265 |
1266 | /* We garbage collect on every user call */
1267 | user_gc();
1268 |
1269 | if (dbus_message_is_method_call(m, "org.freedesktop.RealtimeKit1", "MakeThreadRealtime") ||
1270 | (is2 = dbus_message_is_method_call(m, "org.freedesktop.RealtimeKit1", "MakeThreadRealtimeWithPID"))) {
1271 | uint64_t thread, process = (uint64_t) -1;
1272 | uint32_t priority;
1273 | struct rtkit_user *u;
1274 | struct process *p;
1275 | struct thread *t;
1276 | int ret;
1277 |
1278 | if ((ret = verify_canary_refusal()) < 0) {
1279 | assert_se(r = dbus_message_new_error_printf(m, translate_error_forward(ret), "%s", strerror(-ret)));
1280 | goto finish;
1281 | }
1282 |
1283 | if (is2)
1284 | ret = dbus_message_get_args(m, &error,
1285 | DBUS_TYPE_UINT64, &process,
1286 | DBUS_TYPE_UINT64, &thread,
1287 | DBUS_TYPE_UINT32, &priority,
1288 | DBUS_TYPE_INVALID);
1289 | else
1290 | ret = dbus_message_get_args(m, &error,
1291 | DBUS_TYPE_UINT64, &thread,
1292 | DBUS_TYPE_UINT32, &priority,
1293 | DBUS_TYPE_INVALID);
1294 |
1295 | if (!ret) {
1296 | syslog(LOG_DEBUG, "Failed to parse MakeThreadRealtime() method call: %s\n", error.message);
1297 | assert_se(r = dbus_message_new_error(m, error.name, error.message));
1298 |
1299 | goto finish;
1300 | }
1301 |
1302 | if ((ret = lookup_client(&u, &p, &t, c, m, (pid_t)process, (pid_t) thread)) < 0) {
1303 | syslog(LOG_DEBUG, "Failed to look up client: %s\n", strerror(-ret));
1304 | assert_se(r = dbus_message_new_error_printf(m, translate_error_forward(ret), "%s", strerror(-ret)));
1305 | goto finish;
1306 | }
1307 |
1308 | if ((ret = verify_polkit(c, u, p, "org.freedesktop.RealtimeKit1.acquire-real-time")) < 0) {
1309 | assert_se(r = dbus_message_new_error_printf(m, translate_error_forward(ret), "%s", strerror(-ret)));
1310 | goto finish;
1311 | }
1312 |
1313 | if ((ret = process_set_realtime(u, p, t, priority))) {
1314 | assert_se(r = dbus_message_new_error_printf(m, translate_error_forward(ret), "%s", strerror(-ret)));
1315 | goto finish;
1316 | }
1317 |
1318 | assert_se(r = dbus_message_new_method_return(m));
1319 |
1320 | } else if (dbus_message_is_method_call(m, "org.freedesktop.RealtimeKit1", "MakeThreadHighPriority")
1321 | || (is2 = dbus_message_is_method_call(m, "org.freedesktop.RealtimeKit1", "MakeThreadHighPriorityWithPID"))) {
1322 |
1323 | uint64_t thread, process = (uint64_t) -1;
1324 | int32_t priority;
1325 | struct rtkit_user *u;
1326 | struct process *p;
1327 | struct thread *t;
1328 | int ret;
1329 |
1330 | if ((ret = verify_canary_refusal()) < 0) {
1331 | assert_se(r = dbus_message_new_error_printf(m, translate_error_forward(ret), "%s", strerror(-ret)));
1332 | goto finish;
1333 | }
1334 |
1335 | if (is2)
1336 | ret = dbus_message_get_args(m, &error,
1337 | DBUS_TYPE_UINT64, &process,
1338 | DBUS_TYPE_UINT64, &thread,
1339 | DBUS_TYPE_INT32, &priority,
1340 | DBUS_TYPE_INVALID);
1341 | else
1342 | ret = dbus_message_get_args(m, &error,
1343 | DBUS_TYPE_UINT64, &thread,
1344 | DBUS_TYPE_INT32, &priority,
1345 | DBUS_TYPE_INVALID);
1346 |
1347 | if (!ret) {
1348 | syslog(LOG_DEBUG, "Failed to parse MakeThreadHighPriority() method call: %s\n", error.message);
1349 | assert_se(r = dbus_message_new_error(m, error.name, error.message));
1350 |
1351 | goto finish;
1352 | }
1353 |
1354 | if ((ret = lookup_client(&u, &p, &t, c, m, (pid_t)process, (pid_t) thread)) < 0) {
1355 | syslog(LOG_DEBUG, "Failed to look up client: %s\n", strerror(-ret));
1356 | assert_se(r = dbus_message_new_error_printf(m, translate_error_forward(ret), "%s", strerror(-ret)));
1357 | goto finish;
1358 | }
1359 |
1360 | if ((ret = verify_polkit(c, u, p, "org.freedesktop.RealtimeKit1.acquire-high-priority")) < 0) {
1361 | assert_se(r = dbus_message_new_error_printf(m, translate_error_forward(ret), "%s", strerror(-ret)));
1362 | goto finish;
1363 | }
1364 |
1365 | if ((ret = process_set_high_priority(u, p, t, priority))) {
1366 | assert_se(r = dbus_message_new_error_printf(m, translate_error_forward(ret), "%s", strerror(-ret)));
1367 | goto finish;
1368 | }
1369 |
1370 | assert_se(r = dbus_message_new_method_return(m));
1371 |
1372 | } else if (dbus_message_is_method_call(m, "org.freedesktop.RealtimeKit1", "ResetAll")) {
1373 |
1374 | reset_all();
1375 | user_gc();
1376 | assert_se(r = dbus_message_new_method_return(m));
1377 |
1378 | } else if (dbus_message_is_method_call(m, "org.freedesktop.RealtimeKit1", "ResetKnown")) {
1379 |
1380 | reset_known();
1381 | user_gc();
1382 | assert_se(r = dbus_message_new_method_return(m));
1383 |
1384 | } else if (dbus_message_is_method_call(m, "org.freedesktop.RealtimeKit1", "Exit")) {
1385 |
1386 | assert_se(r = dbus_message_new_method_return(m));
1387 | assert_se(dbus_connection_send(c, r, NULL));
1388 | dbus_message_unref(r);
1389 | r = NULL;
1390 |
1391 | dbus_connection_close(c);
1392 |
1393 | } else if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get")) {
1394 | const char *interface, *property;
1395 |
1396 | if (!dbus_message_get_args(m, &error,
1397 | DBUS_TYPE_STRING, &interface,
1398 | DBUS_TYPE_STRING, &property,
1399 | DBUS_TYPE_INVALID)) {
1400 |
1401 | syslog(LOG_DEBUG, "Failed to parse property get call: %s\n", error.message);
1402 | assert_se(r = dbus_message_new_error(m, error.name, error.message));
1403 | goto finish;
1404 | }
1405 |
1406 | if (strcmp(interface, "org.freedesktop.RealtimeKit1") == 0) {
1407 | assert_se(r = dbus_message_new_method_return(m));
1408 |
1409 | if (handle_dbus_prop_get(property, r) < 0) {
1410 | dbus_message_unref(r);
1411 | assert_se(r = dbus_message_new_error_printf(
1412 | m,
1413 | DBUS_ERROR_UNKNOWN_PROPERTY,
1414 | "Unknown property %s",
1415 | property));
1416 | }
1417 | } else
1418 | assert_se(r = dbus_message_new_error_printf(
1419 | m,
1420 | DBUS_ERROR_UNKNOWN_PROPERTY,
1421 | "Unknown interface %s",
1422 | interface));
1423 |
1424 | } else if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1425 | const char *xml = introspect_xml;
1426 |
1427 | assert_se(r = dbus_message_new_method_return(m));
1428 | assert_se(dbus_message_append_args(
1429 | r,
1430 | DBUS_TYPE_STRING, &xml,
1431 | DBUS_TYPE_INVALID));
1432 | } else
1433 | return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1434 |
1435 | syslog(LOG_DEBUG, "Supervising %u threads of %u processes of %u users.\n",
1436 | n_total_threads,
1437 | n_total_processes,
1438 | n_users);
1439 |
1440 | #ifdef HAVE_LIBSYSTEMD
1441 | sd_notifyf(0,
1442 | "STATUS=Supervising %u threads of %u processes of %u users.",
1443 | n_total_threads,
1444 | n_total_processes,
1445 | n_users);
1446 | #endif
1447 |
1448 | finish:
1449 | if (r) {
1450 | assert_se(dbus_connection_send(c, r, NULL));
1451 | dbus_message_unref(r);
1452 | }
1453 |
1454 | dbus_error_free(&error);
1455 |
1456 | return DBUS_HANDLER_RESULT_HANDLED;
1457 | }
1458 |
1459 | static int setup_dbus(DBusConnection **c) {
1460 | static const DBusObjectPathVTable vtable = {
1461 | .message_function = dbus_handler,
1462 | };
1463 |
1464 | DBusError error;
1465 |
1466 | dbus_error_init(&error);
1467 |
1468 | if (!(*c = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) {
1469 | syslog(LOG_ERR, "Failed to connect to system bus: %s\n", error.message);
1470 | goto fail;
1471 | }
1472 |
1473 | if (dbus_bus_request_name(*c, RTKIT_SERVICE_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
1474 | syslog(LOG_ERR, "Failed to register name on bus: %s\n", error.message);
1475 | goto fail;
1476 | }
1477 |
1478 | assert_se(dbus_connection_register_object_path(*c, RTKIT_OBJECT_PATH, &vtable, NULL));
1479 |
1480 | return 0;
1481 |
1482 | fail:
1483 | dbus_error_free(&error);
1484 | return -EIO;
1485 | }
1486 |
1487 |
1488 | static void block_all_signals(void) {
1489 | sigset_t set;
1490 |
1491 | assert_se(sigfillset(&set) == 0);
1492 | assert_se(pthread_sigmask(SIG_BLOCK, &set, NULL) == 0);
1493 | }
1494 |
1495 | static void* canary_thread(void *data) {
1496 | struct timespec last_cheep, now;
1497 | struct pollfd pollfd;
1498 |
1499 | assert(canary_fd >= 0);
1500 | assert(quit_fd >= 0);
1501 |
1502 | /* Make sure we are not disturbed by any signal */
1503 | block_all_signals();
1504 | self_drop_realtime(0);
1505 |
1506 | memset(&pollfd, 0, sizeof(pollfd));
1507 | pollfd.fd = quit_fd;
1508 | pollfd.events = POLLIN;
1509 |
1510 | assert_se(clock_gettime(CLOCK_MONOTONIC, &now) == 0);
1511 | last_cheep = now;
1512 |
1513 | syslog(LOG_DEBUG, "Canary thread running.\n");
1514 |
1515 | for (;;) {
1516 | int r;
1517 | int64_t msec;
1518 |
1519 | msec = TIMESPEC_MSEC(last_cheep) + canary_cheep_msec - TIMESPEC_MSEC(now);
1520 |
1521 | if (msec < 0)
1522 | msec = 0;
1523 |
1524 | r = poll(&pollfd, 1, (int) msec);
1525 |
1526 | assert_se(clock_gettime(CLOCK_MONOTONIC, &now) == 0);
1527 |
1528 | if (r < 0) {
1529 | if (errno == EINTR || errno == EAGAIN)
1530 | continue;
1531 |
1532 | syslog(LOG_ERR, "poll() failed: %s\n", strerror(errno));
1533 | break;
1534 | }
1535 |
1536 | if (pollfd.revents) {
1537 | syslog(LOG_DEBUG, "Exiting canary thread.\n");
1538 | break;
1539 | }
1540 |
1541 | if (TIMESPEC_MSEC(last_cheep) + canary_cheep_msec <= TIMESPEC_MSEC(now)) {
1542 | eventfd_t value = 1;
1543 |
1544 | if (eventfd_write(canary_fd, value) < 0) {
1545 | syslog(LOG_ERR, "eventfd_write() failed: %s\n", strerror(errno));
1546 | break;
1547 | }
1548 |
1549 | last_cheep = now;
1550 | continue;
1551 | }
1552 | }
1553 |
1554 | return NULL;
1555 | }
1556 |
1557 | static void* watchdog_thread(void *data) {
1558 | enum {
1559 | POLLFD_CANARY,
1560 | POLLFD_QUIT,
1561 | _POLLFD_MAX
1562 | };
1563 | struct timespec last_cheep, now;
1564 | struct pollfd pollfd[_POLLFD_MAX];
1565 |
1566 | assert(canary_fd >= 0);
1567 | assert(quit_fd >= 0);
1568 |
1569 | /* Make sure we are not disturbed by any signal */
1570 | block_all_signals();
1571 | self_set_realtime(canary_watchdog_realtime_priority);
1572 |
1573 | memset(pollfd, 0, sizeof(pollfd));
1574 | pollfd[POLLFD_CANARY].fd = canary_fd;
1575 | pollfd[POLLFD_CANARY].events = POLLIN;
1576 | pollfd[POLLFD_QUIT].fd = quit_fd;
1577 | pollfd[POLLFD_QUIT].events = POLLIN;
1578 |
1579 | assert_se(clock_gettime(CLOCK_MONOTONIC, &now) == 0);
1580 | last_cheep = now;
1581 |
1582 | syslog(LOG_DEBUG, "Watchdog thread running.\n");
1583 |
1584 | for (;;) {
1585 | int r;
1586 | int64_t msec;
1587 |
1588 | msec = TIMESPEC_MSEC(last_cheep) + canary_watchdog_msec - TIMESPEC_MSEC(now);
1589 |
1590 | if (msec < 0)
1591 | msec = 0;
1592 |
1593 | r = poll(pollfd, _POLLFD_MAX, (int) msec);
1594 |
1595 | assert_se(clock_gettime(CLOCK_MONOTONIC, &now) == 0);
1596 |
1597 | if (r < 0) {
1598 | if (errno == EINTR || errno == EAGAIN)
1599 | continue;
1600 |
1601 | syslog(LOG_ERR, "poll() failed: %s\n", strerror(errno));
1602 | break;
1603 | }
1604 |
1605 | if (pollfd[POLLFD_QUIT].revents) {
1606 | syslog(LOG_DEBUG, "Exiting watchdog thread.\n");
1607 | break;
1608 | }
1609 |
1610 | if (pollfd[POLLFD_CANARY].revents) {
1611 | eventfd_t value;
1612 |
1613 | if (eventfd_read(canary_fd, &value) < 0) {
1614 | syslog(LOG_ERR, "eventfd_read() failed: %s\n", strerror(errno));
1615 | break;
1616 | }
1617 |
1618 | last_cheep = now;
1619 | continue;
1620 | }
1621 |
1622 | if (TIMESPEC_MSEC(last_cheep) + canary_watchdog_msec <= TIMESPEC_MSEC(now)) {
1623 | last_cheep = now;
1624 | syslog(LOG_WARNING, "The canary thread is apparently starving. Taking action.\n");
1625 | refuse_until = (uint32_t) now.tv_sec + canary_refusal_sec;
1626 | __sync_synchronize();
1627 |
1628 | if (canary_demote_unknown)
1629 | reset_all();
1630 | else
1631 | reset_known();
1632 | continue;
1633 | }
1634 | }
1635 |
1636 | return NULL;
1637 | }
1638 |
1639 | static void stop_canary(void) {
1640 | int r;
1641 |
1642 | if (quit_fd >= 0) {
1643 | eventfd_t value = 1;
1644 | if (eventfd_write(quit_fd, value) < 0)
1645 | syslog(LOG_WARNING, "Warning: eventfd_write() failed: %s\n", strerror(errno));
1646 | }
1647 |
1648 | if (canary_thread_id != 0) {
1649 | if ((r = pthread_join(canary_thread_id, NULL)) != 0)
1650 | syslog(LOG_WARNING, "Warning: pthread_join() failed: %s\n", strerror(r));
1651 | canary_thread_id = 0;
1652 | }
1653 |
1654 | if (watchdog_thread_id != 0) {
1655 | if ((r = pthread_join(watchdog_thread_id, NULL)) != 0)
1656 | syslog(LOG_WARNING, "Warning: pthread_join() failed: %s\n", strerror(r));
1657 | watchdog_thread_id = 0;
1658 | }
1659 |
1660 | if (canary_fd >= 0) {
1661 | close(canary_fd);
1662 | canary_fd = -1;
1663 | }
1664 |
1665 | if (quit_fd >= 0) {
1666 | close(quit_fd);
1667 | quit_fd = -1;
1668 | }
1669 | }
1670 |
1671 | static int start_canary(void) {
1672 | int r;
1673 |
1674 | if (!do_canary)
1675 | return 0;
1676 |
1677 | if ((canary_fd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC)) < 0 ||
1678 | (quit_fd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC)) < 0) {
1679 | r = -errno;
1680 | syslog(LOG_ERR, "eventfd() failed: %s\n", strerror(errno));
1681 | goto fail;
1682 | }
1683 |
1684 | if ((r = -pthread_create(&canary_thread_id, NULL, canary_thread, NULL)) < 0 ||
1685 | (r = -pthread_create(&watchdog_thread_id, NULL, watchdog_thread, NULL)) < 0) {
1686 | syslog(LOG_ERR, "pthread_create failed: %s\n", strerror(-r));
1687 | goto fail;
1688 | }
1689 |
1690 | return 0;
1691 |
1692 | fail:
1693 | stop_canary();
1694 | return r;
1695 | }
1696 |
1697 | static int drop_privileges(void) {
1698 | struct passwd *pw = NULL;
1699 | int r;
1700 |
1701 | if (do_drop_privileges) {
1702 |
1703 | /* First, get user data */
1704 | if (!(pw = getpwnam(username))) {
1705 | syslog(LOG_ERR, "Failed to find user '%s'.\n", username);
1706 | return -ENOENT;
1707 | }
1708 | }
1709 |
1710 | if (do_chroot) {
1711 |
1712 | /* Second, chroot() */
1713 | if (chroot("/proc") < 0 ||
1714 | chdir("/") < 0) {
1715 | r = -errno;
1716 | syslog(LOG_ERR, "Failed to chroot() to /proc: %s\n", strerror(errno));
1717 | return r;
1718 | }
1719 | proc = "/";
1720 |
1721 | syslog(LOG_DEBUG, "Successfully called chroot.\n");
1722 | }
1723 |
1724 | if (do_drop_privileges) {
1725 | static const cap_value_t cap_values[] = {
1726 | CAP_SYS_NICE, /* Needed for obvious reasons */
1727 | CAP_DAC_READ_SEARCH /* Needed so that we can verify resource limits */
1728 | };
1729 |
1730 | cap_value_t c, m;
1731 | cap_t caps;
1732 |
1733 | m = CAP_LAST_CAP;
1734 | /* In case the number of caps in the kernel is increased, drop them too */
1735 | if (m < 63)
1736 | m = 63;
1737 |
1738 | /* Third, reduce bounding set */
1739 | for (c = 0; c <= m; c++) {
1740 | unsigned u;
1741 | bool keep = false;
1742 |
1743 | for (u = 0; u < ELEMENTSOF(cap_values); u++)
1744 | if (cap_values[u] == c) {
1745 | keep = true;
1746 | break;
1747 | }
1748 |
1749 | if (!keep)
1750 | assert_se(prctl(PR_CAPBSET_DROP, c) == 0 || errno == EINVAL || errno == EPERM);
1751 | }
1752 |
1753 | /* Fourth, say that we want to keep caps */
1754 | if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
1755 | r = -errno;
1756 | syslog(LOG_ERR, "PR_SET_KEEPCAPS failed: %s\n", strerror(errno));
1757 | return r;
1758 | }
1759 |
1760 | /* Fifth, drop privs */
1761 | if (setgroups(0, NULL) < 0 ||
1762 | setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0 ||
1763 | setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0) {
1764 | r = -errno;
1765 | syslog(LOG_ERR, "Failed to become %s: %s\n", username, strerror(errno));
1766 | return r;
1767 | }
1768 |
1769 | /* Sixth, reset caps flag */
1770 | if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
1771 | r = -errno;
1772 | syslog(LOG_ERR, "PR_SET_KEEPCAPS failed: %s\n", strerror(errno));
1773 | return r;
1774 | }
1775 |
1776 | /* Seventh, reduce caps */
1777 | assert_se(caps = cap_init());
1778 | assert_se(cap_clear(caps) == 0);
1779 | assert_se(cap_set_flag(caps, CAP_EFFECTIVE, ELEMENTSOF(cap_values), cap_values, CAP_SET) == 0);
1780 | assert_se(cap_set_flag(caps, CAP_PERMITTED, ELEMENTSOF(cap_values), cap_values, CAP_SET) == 0);
1781 |
1782 | if (cap_set_proc(caps) < 0) {
1783 | r = -errno;
1784 | syslog(LOG_ERR, "cap_set_proc() failed: %s\n", strerror(errno));
1785 | return r;
1786 | }
1787 |
1788 | assert_se(cap_free(caps) == 0);
1789 |
1790 | /* Eigth, update environment */
1791 | setenv("USER", username, 1);
1792 | setenv("USERNAME", username, 1);
1793 | setenv("LOGNAME", username, 1);
1794 | setenv("HOME", get_proc_path(), 1);
1795 |
1796 | syslog(LOG_DEBUG, "Successfully dropped privileges.\n");
1797 | }
1798 |
1799 | return 0;
1800 | }
1801 |
1802 | static int set_resource_limits(void) {
1803 |
1804 | static struct {
1805 | int id;
1806 | const char *name;
1807 | rlim_t value;
1808 | } table[] = {
1809 | { .id = RLIMIT_FSIZE, .name = "RLIMIT_FSIZE", .value = 0 },
1810 | { .id = RLIMIT_MEMLOCK, .name = "RLIMIT_MEMLOCK", .value = 0 },
1811 | { .id = RLIMIT_MSGQUEUE, .name = "RLIMIT_MSGQUEUE", .value = 0 },
1812 | { .id = RLIMIT_NICE, .name = "RLIMIT_NICE", .value = 20 },
1813 | { .id = RLIMIT_NOFILE, .name = "RLIMIT_NOFILE", .value = 50 },
1814 | { .id = RLIMIT_NPROC, .name = "RLIMIT_NPROC", .value = 3 },
1815 | { .id = RLIMIT_RTPRIO, .name = "RLIMIT_RTPRIO", .value = 0 }, /* Since we have CAP_SYS_NICE we don't need this */
1816 | { .id = RLIMIT_RTTIME, .name = "RLIMIT_RTTIME", .value = 0 }
1817 | };
1818 |
1819 | unsigned u;
1820 | int r;
1821 |
1822 | assert(table[7].id == RLIMIT_RTTIME);
1823 | table[7].value = rttime_usec_max; /* Do as I say AND do as I do */
1824 |
1825 | if (!do_limit_resources)
1826 | return 0;
1827 |
1828 | for (u = 0; u < ELEMENTSOF(table); u++) {
1829 | struct rlimit rlim;
1830 |
1831 | if (getrlimit(table[u].id, &rlim) < 0) {
1832 | r = -errno;
1833 | syslog(LOG_ERR, "Failed to get %s: %s\n", table[u].name, strerror(errno));
1834 | return r;
1835 | }
1836 |
1837 | if (rlim.rlim_max < table[u].value)
1838 | continue;
1839 |
1840 | rlim.rlim_cur = rlim.rlim_max = table[u].value;
1841 |
1842 | if (setrlimit(table[u].id, &rlim) < 0) {
1843 | r = -errno;
1844 | syslog(LOG_ERR, "Failed to set %s: %s\n", table[u].name, strerror(errno));
1845 | return r;
1846 | }
1847 | }
1848 |
1849 | syslog(LOG_DEBUG, "Successfully limited resources.\n");
1850 |
1851 | return 0;
1852 | }
1853 |
1854 | enum {
1855 | ARG_HELP = 256,
1856 | ARG_VERSION,
1857 | ARG_SCHEDULING_POLICY,
1858 | ARG_OUR_REALTIME_PRIORITY,
1859 | ARG_OUR_NICE_LEVEL,
1860 | ARG_MAX_REALTIME_PRIORITY,
1861 | ARG_MIN_NICE_LEVEL,
1862 | ARG_USER_NAME,
1863 | ARG_RTTIME_USEC_MAX,
1864 | ARG_USERS_MAX,
1865 | ARG_PROCESSES_PER_USER_MAX,
1866 | ARG_THREADS_PER_USER_MAX,
1867 | ARG_ACTIONS_BURST_SEC,
1868 | ARG_ACTIONS_PER_BURST_MAX,
1869 | ARG_NO_DROP_PRIVILEGES,
1870 | ARG_NO_CHROOT,
1871 | ARG_NO_LIMIT_RESOURCES,
1872 | ARG_NO_CANARY,
1873 | ARG_CANARY_CHEEP_MSEC,
1874 | ARG_CANARY_WATCHDOG_MSEC,
1875 | ARG_CANARY_DEMOTE_ROOT,
1876 | ARG_CANARY_DEMOTE_UNKNOWN,
1877 | ARG_CANARY_REFUSE_SEC,
1878 | ARG_STDERR,
1879 | ARG_INTROSPECT
1880 | };
1881 |
1882 | /* Table for getopt_long() */
1883 | static const struct option long_options[] = {
1884 | { "help", no_argument, 0, ARG_HELP },
1885 | { "version", no_argument, 0, ARG_VERSION },
1886 | { "scheduling-policy", required_argument, 0, ARG_SCHEDULING_POLICY },
1887 | { "our-realtime-priority", required_argument, 0, ARG_OUR_REALTIME_PRIORITY },
1888 | { "our-nice-level", required_argument, 0, ARG_OUR_NICE_LEVEL },
1889 | { "max-realtime-priority", required_argument, 0, ARG_MAX_REALTIME_PRIORITY },
1890 | { "min-nice-level", required_argument, 0, ARG_MIN_NICE_LEVEL },
1891 | { "user-name", required_argument, 0, ARG_USER_NAME },
1892 | { "rttime-usec-max", required_argument, 0, ARG_RTTIME_USEC_MAX },
1893 | { "users-max", required_argument, 0, ARG_USERS_MAX },
1894 | { "processes-per-user-max", required_argument, 0, ARG_PROCESSES_PER_USER_MAX },
1895 | { "threads-per-user-max", required_argument, 0, ARG_THREADS_PER_USER_MAX },
1896 | { "actions-burst-sec", required_argument, 0, ARG_ACTIONS_BURST_SEC },
1897 | { "actions-per-burst-max", required_argument, 0, ARG_ACTIONS_PER_BURST_MAX },
1898 | { "no-drop-privileges", no_argument, 0, ARG_NO_DROP_PRIVILEGES },
1899 | { "no-chroot", no_argument, 0, ARG_NO_CHROOT },
1900 | { "no-limit-resources", no_argument, 0, ARG_NO_LIMIT_RESOURCES },
1901 | { "no-canary", no_argument, 0, ARG_NO_CANARY },
1902 | { "canary-cheep-msec", required_argument, 0, ARG_CANARY_CHEEP_MSEC },
1903 | { "canary-watchdog-msec", required_argument, 0, ARG_CANARY_WATCHDOG_MSEC },
1904 | { "canary-demote-root", no_argument, 0, ARG_CANARY_DEMOTE_ROOT },
1905 | { "canary-demote-unknown", no_argument, 0, ARG_CANARY_DEMOTE_UNKNOWN },
1906 | { "canary-refuse-sec", required_argument, 0, ARG_CANARY_REFUSE_SEC },
1907 | { "stderr", no_argument, 0, ARG_STDERR },
1908 | { "introspect", no_argument, 0, ARG_INTROSPECT },
1909 | { NULL, 0, 0, 0}
1910 | };
1911 |
1912 | static char* get_file_name(const char *p) {
1913 | char *e;
1914 |
1915 | if ((e = strrchr(p, '/')))
1916 | return e + 1;
1917 | else
1918 | return (char*) p;
1919 | }
1920 |
1921 | static void show_help(const char *exe) {
1922 |
1923 | static const char * const sp_names[] = {
1924 | [SCHED_OTHER] = "OTHER",
1925 | [SCHED_BATCH] = "BATCH",
1926 | [SCHED_FIFO] = "FIFO",
1927 | [SCHED_RR] = "RR"
1928 | };
1929 |
1930 | printf("%s [options]\n\n"
1931 | "COMMANDS:\n"
1932 | " -h, --help Show this help\n"
1933 | " --version Show version\n\n"
1934 | "OPTIONS:\n"
1935 | " --stderr Log to STDERR in addition to syslog\n"
1936 | " --user-name=USER Run daemon as user (%s)\n\n"
1937 | " --scheduling-policy=(RR|FIFO) Choose scheduling policy (%s)\n"
1938 | " --our-realtime-priority=[%i..%i] Realtime priority for the daemon (%u)\n"
1939 | " --our-nice-level=[%i..%i] Nice level for the daemon (%i)\n"
1940 | " --max-realtime-priority=[%i..%i] Max realtime priority for clients (%u)\n"
1941 | " --min-nice-level=[%i..%i] Min nice level for clients (%i)\n\n"
1942 | " --rttime-usec-max=USEC Require clients to have set RLIMIT_RTTIME\n"
1943 | " not greater than this (%llu)\n\n"
1944 | " --users-max=INT How many users this daemon will serve at\n"
1945 | " max at the same time (%u)\n"
1946 | " --processes-per-user-max=INT How many processes this daemon will serve\n"
1947 | " at max per user at the same time (%u)\n"
1948 | " --threads-per-user-max=INT How many threads this daemon will serve\n"
1949 | " at max per user at the same time (%u)\n\n"
1950 | " --actions-burst-sec=SEC Enforce requests limits in this time (%u)\n"
1951 | " --actions-per-burst-max=INT Allow this many requests per burst (%u)\n\n"
1952 | " --canary-cheep-msec=MSEC Canary cheep interval (%u)\n"
1953 | " --canary-watchdog-msec=MSEC Watchdog action delay (%u)\n"
1954 | " --canary-demote-unknown When the canary dies demote unknown\n"
1955 | " processes too?\n"
1956 | " --canary-demote-root When the canary dies demote root\n"
1957 | " processes too?\n"
1958 | " --canary-refuse-sec=SEC How long to refuse further requests\n"
1959 | " after the canary died (%u)\n\n"
1960 | " --no-canary Don't run a canary-based RT watchdog\n\n"
1961 | " --no-drop-privileges Don't drop privileges\n"
1962 | " --no-chroot Don't chroot\n"
1963 | " --no-limit-resources Don't limit daemon's resources\n",
1964 | exe,
1965 | username,
1966 | sp_names[sched_policy],
1967 | sched_get_priority_min(sched_policy), sched_get_priority_max(sched_policy), our_realtime_priority,
1968 | PRIO_MIN, PRIO_MAX-1, our_nice_level,
1969 | sched_get_priority_min(sched_policy), sched_get_priority_max(sched_policy), max_realtime_priority,
1970 | PRIO_MIN, PRIO_MAX-1, min_nice_level,
1971 | rttime_usec_max,
1972 | users_max,
1973 | processes_per_user_max,
1974 | threads_per_user_max,
1975 | actions_burst_sec,
1976 | actions_per_burst_max,
1977 | canary_cheep_msec,
1978 | canary_watchdog_msec,
1979 | canary_refusal_sec);
1980 | }
1981 |
1982 | static int parse_command_line(int argc, char *argv[], int *ret) {
1983 |
1984 | int c;
1985 |
1986 | while ((c = getopt_long(argc, argv, "h", long_options, NULL)) >= 0) {
1987 |
1988 | switch (c) {
1989 | case 'h':
1990 | case ARG_HELP:
1991 | show_help(get_file_name(argv[0]));
1992 | *ret = 0;
1993 | return 0;
1994 |
1995 | case ARG_VERSION:
1996 | printf("%s " PACKAGE_VERSION "\n", get_file_name(argv[0]));
1997 | *ret = 0;
1998 | return 0;
1999 |
2000 | case ARG_USER_NAME:
2001 | username = optarg;
2002 | break;
2003 |
2004 | case ARG_SCHEDULING_POLICY: {
2005 | if (strcasecmp(optarg, "rr") == 0)
2006 | sched_policy = SCHED_RR;
2007 | else if (strcasecmp(optarg, "fifo") == 0)
2008 | sched_policy = SCHED_FIFO;
2009 | else {
2010 | fprintf(stderr, "--scheduling-policy parameter invalid.\n");
2011 | return -1;
2012 | }
2013 |
2014 | break;
2015 | }
2016 |
2017 | case ARG_OUR_REALTIME_PRIORITY: {
2018 | char *e = NULL;
2019 | unsigned long u;
2020 |
2021 | errno = 0;
2022 | u = strtoul(optarg, &e, 0);
2023 | if (errno != 0 || !e || *e || u < (unsigned) sched_get_priority_min(sched_policy) || u > (unsigned) sched_get_priority_max(sched_policy)) {
2024 | fprintf(stderr, "--our-realtime-priority parameter invalid.\n");
2025 | return -1;
2026 | }
2027 | our_realtime_priority = u;
2028 | break;
2029 | }
2030 |
2031 | case ARG_OUR_NICE_LEVEL: {
2032 | char *e = NULL;
2033 | long i;
2034 |
2035 | errno = 0;
2036 | i = strtol(optarg, &e, 0);
2037 | if (errno != 0 || !e || *e || i < PRIO_MIN || i > PRIO_MAX*2) {
2038 | fprintf(stderr, "--our-nice-level parameter invalid.\n");
2039 | return -1;
2040 | }
2041 | our_nice_level = i;
2042 | break;
2043 | }
2044 |
2045 | case ARG_MAX_REALTIME_PRIORITY: {
2046 | char *e = NULL;
2047 | unsigned long u;
2048 |
2049 | errno = 0;
2050 | u = strtoul(optarg, &e, 0);
2051 | if (errno != 0 || !e || *e || u < (unsigned) sched_get_priority_min(sched_policy) || u > (unsigned) sched_get_priority_max(sched_policy)) {
2052 | fprintf(stderr, "--max-realtime-priority parameter invalid.\n");
2053 | return -1;
2054 | }
2055 | max_realtime_priority = u;
2056 | break;
2057 | }
2058 |
2059 | case ARG_MIN_NICE_LEVEL: {
2060 | char *e = NULL;
2061 | long i;
2062 |
2063 | errno = 0;
2064 | i = strtol(optarg, &e, 0);
2065 | if (errno != 0 || !e || *e || i < PRIO_MIN || i >= PRIO_MAX) {
2066 | fprintf(stderr, "--min-nice-level parameter invalid.\n");
2067 | return -1;
2068 | }
2069 | min_nice_level = i;
2070 | break;
2071 | }
2072 |
2073 | case ARG_RTTIME_USEC_MAX: {
2074 | char *e = NULL;
2075 |
2076 | errno = 0;
2077 | rttime_usec_max = strtoull(optarg, &e, 0);
2078 | if (errno != 0 || !e || *e || rttime_usec_max <= 0) {
2079 | fprintf(stderr, "--rttime-usec-max parameter invalid.\n");
2080 | return -1;
2081 | }
2082 | break;
2083 | }
2084 |
2085 | case ARG_USERS_MAX: {
2086 | char *e = NULL;
2087 | unsigned long u;
2088 |
2089 | errno = 0;
2090 | u = strtoul(optarg, &e, 0);
2091 | if (errno != 0 || !e || *e || u <= 0) {
2092 | fprintf(stderr, "--users-max parameter invalid.\n");
2093 | return -1;
2094 | }
2095 | users_max = u;
2096 | break;
2097 | }
2098 |
2099 | case ARG_PROCESSES_PER_USER_MAX: {
2100 | char *e = NULL;
2101 | unsigned long u;
2102 |
2103 | errno = 0;
2104 | u = strtoul(optarg, &e, 0);
2105 | if (errno != 0 || !e || *e || u <= 0) {
2106 | fprintf(stderr, "--processes-per-user-max parameter invalid.\n");
2107 | return -1;
2108 | }
2109 | processes_per_user_max = u;
2110 | break;
2111 | }
2112 |
2113 | case ARG_THREADS_PER_USER_MAX: {
2114 | char *e = NULL;
2115 | unsigned long u;
2116 |
2117 | errno = 0;
2118 | u = strtoul(optarg, &e, 0);
2119 | if (errno != 0 || !e || *e || u <= 0) {
2120 | fprintf(stderr, "--threads-per-user-max parameter invalid.\n");
2121 | return -1;
2122 | }
2123 | threads_per_user_max = u;
2124 | break;
2125 | }
2126 |
2127 | case ARG_ACTIONS_BURST_SEC: {
2128 | char *e = NULL;
2129 | unsigned long u;
2130 |
2131 | errno = 0;
2132 | u = strtoul(optarg, &e, 0);
2133 | if (errno != 0 || !e || *e || u <= 0) {
2134 | fprintf(stderr, "--actions-burst-sec parameter invalid.\n");
2135 | return -1;
2136 | }
2137 | actions_burst_sec = u;
2138 | break;
2139 | }
2140 |
2141 | case ARG_ACTIONS_PER_BURST_MAX: {
2142 | char *e = NULL;
2143 | unsigned long u;
2144 |
2145 | errno = 0;
2146 | u = strtoul(optarg, &e, 0);
2147 | if (errno != 0 || !e || *e || u <= 0) {
2148 | fprintf(stderr, "--actions-per-burst-max parameter invalid.\n");
2149 | return -1;
2150 | }
2151 | actions_per_burst_max = u;
2152 | break;
2153 | }
2154 |
2155 | case ARG_NO_DROP_PRIVILEGES:
2156 | do_drop_privileges = FALSE;
2157 | break;
2158 |
2159 | case ARG_NO_CHROOT:
2160 | do_chroot = FALSE;
2161 | break;
2162 |
2163 | case ARG_NO_LIMIT_RESOURCES:
2164 | do_limit_resources = FALSE;
2165 | break;
2166 |
2167 | case ARG_NO_CANARY:
2168 | do_canary = FALSE;
2169 | break;
2170 |
2171 | case ARG_CANARY_WATCHDOG_MSEC: {
2172 | char *e = NULL;
2173 | unsigned long u;
2174 |
2175 | errno = 0;
2176 | u = strtoul(optarg, &e, 0);
2177 | if (errno != 0 || !e || *e || u <= 0) {
2178 | fprintf(stderr, "--canary-watchdog-msec parameter invalid.\n");
2179 | return -1;
2180 | }
2181 | canary_watchdog_msec = u;
2182 | break;
2183 | }
2184 |
2185 | case ARG_CANARY_CHEEP_MSEC: {
2186 | char *e = NULL;
2187 | unsigned long u;
2188 |
2189 | errno = 0;
2190 | u = strtoul(optarg, &e, 0);
2191 | if (errno != 0 || !e || *e || u <= 0) {
2192 | fprintf(stderr, "--canary-cheep-msec parameter invalid.\n");
2193 | return -1;
2194 | }
2195 | canary_cheep_msec = u;
2196 | break;
2197 | }
2198 |
2199 | case ARG_CANARY_DEMOTE_ROOT:
2200 | canary_demote_root = TRUE;
2201 | break;
2202 |
2203 | case ARG_CANARY_DEMOTE_UNKNOWN:
2204 | canary_demote_unknown = TRUE;
2205 | break;
2206 |
2207 | case ARG_CANARY_REFUSE_SEC: {
2208 | char *e = NULL;
2209 | unsigned long u;
2210 |
2211 | errno = 0;
2212 | u = strtoul(optarg, &e, 0);
2213 | if (errno != 0 || !e || *e) {
2214 | fprintf(stderr, "--canary-refuse-sec parameter invalid.\n");
2215 | return -1;
2216 | }
2217 | canary_refusal_sec = (uint32_t) u;
2218 | break;
2219 | }
2220 |
2221 | case ARG_STDERR:
2222 | log_stderr = TRUE;
2223 | break;
2224 |
2225 | case ARG_INTROSPECT:
2226 | fputs(introspect_xml, stdout);
2227 | *ret = 0;
2228 | return 0;
2229 |
2230 | case '?':
2231 | default:
2232 | fprintf(stderr, "Unknown command.\n");
2233 | return -1;
2234 | }
2235 | }
2236 |
2237 | if (optind < argc) {
2238 | fprintf(stderr, "Too many arguments.\n");
2239 | return -1;
2240 | }
2241 |
2242 | if (max_realtime_priority >= our_realtime_priority) {
2243 | fprintf(stderr, "The maximum realtime priority (%u) handed out to clients cannot be higher then our own (%u).\n",
2244 | max_realtime_priority,
2245 | our_realtime_priority);
2246 | return -1;
2247 | }
2248 |
2249 | if (canary_cheep_msec >= canary_watchdog_msec) {
2250 | fprintf(stderr, "The canary watchdog interval must be larger than the cheep interval.\n");
2251 | return -1;
2252 | }
2253 |
2254 | assert(our_realtime_priority >= (unsigned) sched_get_priority_min(sched_policy));
2255 | assert(our_realtime_priority <= (unsigned) sched_get_priority_max(sched_policy));
2256 |
2257 | assert(max_realtime_priority >= (unsigned) sched_get_priority_min(sched_policy));
2258 | assert(max_realtime_priority <= (unsigned) sched_get_priority_max(sched_policy));
2259 |
2260 | assert(canary_watchdog_realtime_priority >= (unsigned) sched_get_priority_min(sched_policy));
2261 | assert(canary_watchdog_realtime_priority <= (unsigned) sched_get_priority_max(sched_policy));
2262 |
2263 | assert(our_nice_level >= PRIO_MIN);
2264 | assert(our_nice_level < PRIO_MAX);
2265 |
2266 | assert(min_nice_level >= PRIO_MIN);
2267 | assert(min_nice_level < PRIO_MAX);
2268 |
2269 | return 1;
2270 | }
2271 |
2272 | int main(int argc, char *argv[]) {
2273 | DBusConnection *bus = NULL;
2274 | int ret = 1;
2275 | struct rtkit_user *u;
2276 | unsigned long slack_ns;
2277 |
2278 | if (parse_command_line(argc, argv, &ret) <= 0)
2279 | goto finish;
2280 |
2281 | if (getuid() != 0) {
2282 | fprintf(stderr, "Need to be run as root.\n");
2283 | goto finish;
2284 | }
2285 |
2286 | openlog(get_file_name(argv[0]),
2287 | LOG_NDELAY|LOG_PID|(log_stderr ? LOG_PERROR : 0),
2288 | LOG_DAEMON);
2289 |
2290 | /* Raise our timer slack, we don't really need to be woken up
2291 | * on time. */
2292 | slack_ns = (((unsigned long) canary_watchdog_msec - (unsigned long) canary_cheep_msec) / 2UL) * 1000000UL;
2293 | if (prctl(PR_SET_TIMERSLACK, slack_ns) < 0)
2294 | syslog(LOG_WARNING, "PRT_SET_TIMERSLACK failed: %s\n", strerror(errno));
2295 |
2296 | self_drop_realtime(our_nice_level);
2297 |
2298 | if (setup_dbus(&bus) < 0)
2299 | goto finish;
2300 |
2301 | if (drop_privileges() < 0)
2302 | goto finish;
2303 |
2304 | if (set_resource_limits() < 0)
2305 | goto finish;
2306 |
2307 | if (start_canary() < 0)
2308 | goto finish;
2309 |
2310 | umask(0777);
2311 |
2312 | syslog(LOG_DEBUG, "Running.\n");
2313 |
2314 | #ifdef HAVE_LIBSYSTEMD
2315 | sd_notify(0, "STATUS=Running.");
2316 | #endif
2317 |
2318 | dbus_connection_set_exit_on_disconnect(bus, FALSE);
2319 |
2320 | while (dbus_connection_read_write_dispatch(bus, -1))
2321 | ;
2322 |
2323 | ret = 0;
2324 |
2325 | syslog(LOG_DEBUG, "Exiting cleanly.\n");
2326 |
2327 | finish:
2328 |
2329 | if (bus) {
2330 | if (dbus_connection_get_is_connected(bus))
2331 | dbus_connection_close(bus);
2332 | dbus_connection_unref(bus);
2333 | }
2334 |
2335 | reset_known();
2336 |
2337 | while ((u = users)) {
2338 | users = u->next;
2339 | free_user(u);
2340 | }
2341 |
2342 | stop_canary();
2343 |
2344 | dbus_shutdown();
2345 |
2346 | return ret;
2347 | }
2348 |
--------------------------------------------------------------------------------