Skip to content

Commit 20b0692

Browse files
committed
lib: support the new pidfd based APIs
Pidfds are process file descriptors representing processes. Since Linux 5.2 the corresponding pid in the callers pid namespace can be obtained via the fdinfo in procfs; this by reading the "Pid:" field in '/proc/self/fdinfo/<pidfd>'. In Linux 5.3 the pidfd_open system call was added, allowing us to easily open a pidfd for a given pid. Additionally select/poll/epoll can be used to monitor the status of the process, which can be used to watch for process termination. Add a new set of APIs, analogous to the existing *byPID APIs, that work with pidfds instead of pids and convert the client library to default to using those, if supported by the system. The two main advantages are: - pidfds drastically simplify the container use case, because there is no need to translate the pids between different namespaces anymore - it prepares the use of pidfds in the daemon, which in the future could be used to monitor process terminations and thus removes the need of the reaper background thread.
1 parent 0027dcf commit 20b0692

2 files changed

Lines changed: 118 additions & 24 deletions

File tree

lib/client_impl.c

Lines changed: 117 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,14 @@ POSSIBILITY OF SUCH DAMAGE.
3131

3232
#define _GNU_SOURCE
3333

34+
#include <common-helpers.h>
35+
#include <common-pidfds.h>
36+
3437
#include <dbus/dbus.h>
38+
#include <errno.h>
3539
#include <stdio.h>
40+
#include <stdlib.h>
41+
#include <string.h>
3642
#include <sys/stat.h>
3743
#include <sys/types.h>
3844
#include <unistd.h>
@@ -54,6 +60,7 @@ POSSIBILITY OF SUCH DAMAGE.
5460
#define _cleanup_bus_ _cleanup_(hop_off_the_bus)
5561
#define _cleanup_msg_ _cleanup_(cleanup_msg)
5662
#define _cleanup_dpc_ _cleanup_(cleanup_pending_call)
63+
#define _cleanup_fds_ _cleanup_(cleanup_fd_array)
5764

5865
#ifdef NDEBUG
5966
#define DEBUG(...)
@@ -73,6 +80,35 @@ static int log_error(const char *fmt, ...) __attribute__((format(printf, 1, 2)))
7380
// Storage for error strings
7481
static char error_string[512] = { 0 };
7582

83+
// memory helpers
84+
static void cleanup_fd_array(int **fdlist)
85+
{
86+
if (fdlist == NULL || *fdlist == NULL)
87+
return;
88+
89+
int errsave = errno;
90+
for (int *fd = *fdlist; *fd != -1; fd++) {
91+
TRACE("GM Closing fd %d\n", *fd);
92+
(void)close(*fd);
93+
}
94+
95+
errno = errsave;
96+
free(*fdlist);
97+
}
98+
99+
// Allocate a -1 termianted array of ints
100+
static inline int *alloc_fd_array(int n)
101+
{
102+
int *fds;
103+
104+
size_t count = (size_t)n + 1; /* -1, terminated */
105+
fds = (int *)malloc(sizeof(int) * count);
106+
for (size_t i = 0; i < count; i++)
107+
fds[i] = -1;
108+
109+
return fds;
110+
}
111+
76112
// Helper to check if we are running inside a flatpak
77113
static int in_flatpak(void)
78114
{
@@ -136,32 +172,62 @@ static DBusConnection *hop_on_the_bus(void)
136172
/* cleanup functions */
137173
static void cleanup_msg(DBusMessage **msg)
138174
{
139-
if (msg == NULL)
175+
if (msg == NULL || *msg == NULL)
140176
return;
141177

142178
dbus_message_unref(*msg);
143179
}
144180

145181
static void cleanup_pending_call(DBusPendingCall **call)
146182
{
147-
if (call == NULL)
183+
if (call == NULL || *call == NULL)
148184
return;
149185

150186
dbus_pending_call_unref(*call);
151187
}
152188

153189
/* internal API */
154-
static int make_request(DBusConnection *bus,
155-
int native, const char *method,
156-
pid_t *pids, int npids,
157-
DBusError *error)
190+
static int make_request(DBusConnection *bus, int native, int use_pidfds, const char *method,
191+
pid_t *pids, int npids, DBusError *error)
158192
{
159193
_cleanup_msg_ DBusMessage *msg = NULL;
160194
_cleanup_dpc_ DBusPendingCall *call = NULL;
195+
_cleanup_fds_ int *fds = NULL;
196+
char action[256] = {
197+
0,
198+
};
161199
DBusError err;
162200
DBusMessageIter iter;
163201
int res = -1;
164202

203+
TRACE("GM: Incoming request: %s, npids: %d, native: %d pifds: %d\n",
204+
method,
205+
npids,
206+
native,
207+
use_pidfds);
208+
209+
if (use_pidfds) {
210+
fds = alloc_fd_array(npids);
211+
212+
res = open_pidfds(pids, fds, npids);
213+
if (res != npids) {
214+
dbus_set_error(error, DBUS_ERROR_FAILED, "Could not open pidfd for %d", (int)pids[res]);
215+
return -1;
216+
}
217+
218+
if (strstr(method, "ByPID"))
219+
snprintf(action, sizeof(action), "%sFd", method);
220+
else
221+
snprintf(action, sizeof(action), "%sByPIDFd", method);
222+
method = action;
223+
}
224+
225+
TRACE("GM: Making request: %s, npids: %d, native: %d pifds: %d\n",
226+
method,
227+
npids,
228+
native,
229+
use_pidfds);
230+
165231
// If we are inside a flatpak we need to talk to the portal instead
166232
const char *dest = native ? DAEMON_DBUS_NAME : PORTAL_DBUS_NAME;
167233
const char *path = native ? DAEMON_DBUS_PATH : PORTAL_DBUS_PATH;
@@ -170,15 +236,24 @@ static int make_request(DBusConnection *bus,
170236
msg = dbus_message_new_method_call(dest, path, iface, method);
171237

172238
if (!msg) {
173-
dbus_set_error_const(error, DBUS_ERROR_FAILED,
174-
"Could not create dbus message");
239+
dbus_set_error_const(error, DBUS_ERROR_FAILED, "Could not create dbus message");
175240
return -1;
176241
}
177242

178243
dbus_message_iter_init_append(msg, &iter);
244+
179245
for (int i = 0; i < npids; i++) {
180-
dbus_int32_t p = (dbus_int32_t)pids[i];
181-
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &p);
246+
dbus_int32_t p;
247+
int type;
248+
249+
if (use_pidfds) {
250+
type = DBUS_TYPE_UNIX_FD;
251+
p = (dbus_int32_t)fds[i];
252+
} else {
253+
type = DBUS_TYPE_INT32;
254+
p = (dbus_int32_t)pids[i];
255+
}
256+
dbus_message_iter_append_basic(&iter, type, &p);
182257
}
183258

184259
dbus_connection_send_with_reply(bus, msg, &call, -1);
@@ -190,21 +265,22 @@ static int make_request(DBusConnection *bus,
190265
msg = dbus_pending_call_steal_reply(call);
191266

192267
if (msg == NULL) {
193-
dbus_set_error_const(error, DBUS_ERROR_FAILED,
194-
"Did not receive a reply");
268+
dbus_set_error_const(error, DBUS_ERROR_FAILED, "Did not receive a reply");
195269
return -1;
196270
}
197271

198272
dbus_error_init(&err);
199-
273+
res = -1;
200274
if (dbus_set_error_from_message(&err, msg)) {
201-
dbus_set_error(error, err.name,
202-
"Could not call method '%s' on '%s': %s",
203-
method, dest, err.message);
275+
dbus_set_error(error,
276+
err.name,
277+
"Could not call method '%s' on '%s': %s",
278+
method,
279+
dest,
280+
err.message);
204281
} else if (!dbus_message_iter_init(msg, &iter) ||
205-
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
206-
dbus_set_error(error, DBUS_ERROR_INVALID_SIGNATURE,
207-
"Failed to parse response");
282+
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
283+
dbus_set_error(error, DBUS_ERROR_INVALID_SIGNATURE, "Failed to parse response");
208284
} else {
209285
dbus_message_iter_get_basic(&iter, &res);
210286
}
@@ -219,14 +295,20 @@ static int make_request(DBusConnection *bus,
219295
static int gamemode_request(const char *method, pid_t for_pid)
220296
{
221297
_cleanup_bus_ DBusConnection *bus = NULL;
298+
static int use_pidfs = 1;
222299
DBusError err;
223-
pid_t pids[2] = {0, for_pid};
300+
pid_t pids[2];
224301
int npids;
225302
int native;
226303
int res = -1;
227304

228305
native = !in_flatpak();
306+
307+
/* we setup the array such that pids[1] will always be a
308+
* valid pid, either for_pid as given, or same as pid[0],
309+
* because that simplifies the logic in case of use_pidfs */
229310
pids[0] = getpid();
311+
pids[1] = for_pid != 0 ? for_pid : pids[0];
230312

231313
TRACE("GM: [%d] request '%s' received (for pid: %d) [portal: %s]\n",
232314
(int)pids[0],
@@ -239,12 +321,23 @@ static int gamemode_request(const char *method, pid_t for_pid)
239321
if (bus == NULL)
240322
return -1;
241323

242-
npids = for_pid > 0 ? 2 : 1;
243-
244324
dbus_error_init(&err);
245-
res = make_request(bus, native, method, pids, npids, &err);
325+
retry:
326+
if (for_pid != 0 || use_pidfs)
327+
npids = 2;
328+
else
329+
npids = 1;
330+
331+
res = make_request(bus, native, use_pidfs, method, pids, npids, &err);
332+
333+
if (res == -1 && use_pidfs && dbus_error_is_set(&err)) {
334+
TRACE("GM: Request with pidfds failed (%s). Retrying.\n", err.message);
335+
use_pidfs = 0;
336+
dbus_error_free(&err);
337+
goto retry;
338+
}
246339

247-
if (res == -1)
340+
if (res == -1 && dbus_error_is_set(&err))
248341
log_error("D-Bus error: %s", err.message);
249342

250343
TRACE("GM: [%d] request '%s' done: %d\n", (int)pids[0], method, res);

lib/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ gamemode = shared_library(
1212
'client_impl.c',
1313
],
1414
dependencies: [
15+
link_lib_common,
1516
dep_dbus,
1617
],
1718
install: true,

0 commit comments

Comments
 (0)