]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/jail.c
jail: Remove callbacks from command calls
[pakfire.git] / src / libpakfire / jail.c
CommitLineData
fd37ccaf
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2022 Pakfire development team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
32d5f21d 21#include <errno.h>
bcf09bf5 22#include <fcntl.h>
980b15af 23#include <linux/capability.h>
0bd84dc1 24#include <linux/sched.h>
58ee649f 25#include <sys/wait.h>
4f23b498 26#include <linux/wait.h>
0bd84dc1
MT
27#include <sched.h>
28#include <signal.h>
32d5f21d 29#include <stdlib.h>
0bd84dc1 30#include <syscall.h>
980b15af 31#include <sys/capability.h>
616f1fca 32#include <sys/epoll.h>
43dc0e16 33#include <sys/eventfd.h>
7bdf1d8e 34#include <sys/mount.h>
90d92b5c 35#include <sys/personality.h>
980b15af 36#include <sys/prctl.h>
cf440db8 37#include <sys/resource.h>
335b8a44 38#include <sys/timerfd.h>
0bd84dc1
MT
39#include <sys/types.h>
40#include <sys/wait.h>
c40722e3 41#include <termios.h>
32d5f21d 42
fec79a33
MT
43// libnl3
44#include <net/if.h>
45#include <netlink/route/link.h>
46
739d5b57
MT
47// libseccomp
48#include <seccomp.h>
49
ae5201c5
MT
50// libuuid
51#include <uuid.h>
52
90d92b5c 53#include <pakfire/arch.h>
e3ddb498 54#include <pakfire/cgroup.h>
fd37ccaf 55#include <pakfire/jail.h>
4f59c39b
MT
56#include <pakfire/logging.h>
57#include <pakfire/mount.h>
fd37ccaf 58#include <pakfire/pakfire.h>
729827f7 59#include <pakfire/path.h>
6ce56f90 60#include <pakfire/private.h>
4896e62c 61#include <pakfire/pwd.h>
d973a13d 62#include <pakfire/string.h>
32d5f21d
MT
63#include <pakfire/util.h>
64
616f1fca
MT
65#define BUFFER_SIZE 1024 * 64
66#define ENVIRON_SIZE 128
67#define EPOLL_MAX_EVENTS 2
cc6e2264 68#define MAX_MOUNTPOINTS 8
fd37ccaf 69
d5bc8fe0
MT
70// The default environment that will be set for every command
71static const struct environ {
72 const char* key;
73 const char* val;
74} ENV[] = {
fbe95cdf 75 { "HOME", "/root" },
55f54757 76 { "LANG", "C.utf-8" },
a7ad6d4f 77 { "PATH", "/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin", },
d5bc8fe0 78 { "TERM", "vt100" },
3bf01105
MT
79
80 // Tell everything that it is running inside a Pakfire container
81 { "container", "pakfire" },
d5bc8fe0
MT
82 { NULL, NULL },
83};
84
dcdda292
MT
85struct pakfire_log_buffer {
86 char data[BUFFER_SIZE];
87 size_t used;
1a7eb888 88 int priority;
dcdda292
MT
89};
90
6ce285a1 91enum pakfire_jail_pty_io {
dcdda292
MT
92 PAKFIRE_JAIL_PTY_READY_TO_READ = (1 << 0),
93 PAKFIRE_JAIL_PTY_READY_TO_WRITE = (1 << 1),
94};
95
96struct pakfire_jail_pty_stdio {
97 // File Descriptor
98 int fd;
99
100 // Buffer
101 struct pakfire_log_buffer buffer;
102
103 // Terminal Attributes
104 struct termios attrs;
105
106 // File Descriptor Flags
6ce285a1 107 int flags;
dcdda292
MT
108
109 // IO Flags
6ce285a1 110 enum pakfire_jail_pty_io io;
dcdda292
MT
111};
112
cc6e2264
MT
113struct pakfire_jail_mountpoint {
114 char source[PATH_MAX];
115 char target[PATH_MAX];
116 int flags;
117};
118
fd37ccaf 119struct pakfire_jail {
a13df023 120 struct pakfire_ctx* ctx;
fd37ccaf
MT
121 struct pakfire* pakfire;
122 int nrefs;
32d5f21d 123
ae5201c5
MT
124 // A unique ID for each jail
125 uuid_t uuid;
126 char __uuid[UUID_STR_LEN];
127
cf440db8
MT
128 // Resource Limits
129 int nice;
130
335b8a44
MT
131 // Timeout
132 struct itimerspec timeout;
133
15503538
MT
134 // CGroup
135 struct pakfire_cgroup* cgroup;
136
32d5f21d
MT
137 // Environment
138 char* env[ENVIRON_SIZE];
616f1fca 139
d8b94dd5
MT
140 struct pakfire_jail_callbacks {
141 // Standard Input
142 struct pakfire_jail_callbacks_stdin {
143 pakfire_jail_stdin_callback callback;
144 void* data;
145 } stdin;
146
147 // Standard Output
148 struct pakfire_jail_callbacks_stdout {
149 pakfire_jail_stdout_callback callback;
150 void* data;
151 } stdout;
152 } callbacks;
153
cc6e2264
MT
154 // Mountpoints
155 struct pakfire_jail_mountpoint mountpoints[MAX_MOUNTPOINTS];
156 unsigned int num_mountpoints;
616f1fca
MT
157};
158
616f1fca 159struct pakfire_jail_exec {
7bdf1d8e
MT
160 int flags;
161
f949feac
MT
162 // PID (of the child)
163 pid_t pid;
164 int pidfd;
616f1fca 165
8f49d254
MT
166 // Socket to pass FDs
167 int socket[2];
168
f949feac
MT
169 // Process status (from waitid)
170 siginfo_t status;
616f1fca 171
f7d240a7
MT
172 // FD to notify the client that the parent has finished initialization
173 int completed_fd;
174
616f1fca 175 // Log pipes
e33387d3 176 struct pakfire_jail_pipes {
e33387d3
MT
177 // Logging
178 int log_INFO[2];
179 int log_ERROR[2];
6ac51607 180#ifdef ENABLE_DEBUG
e33387d3 181 int log_DEBUG[2];
6ac51607 182#endif /* ENABLE_DEBUG */
616f1fca
MT
183 } pipes;
184
185 // Log buffers
e33387d3 186 struct pakfire_jail_buffers {
e33387d3
MT
187 // Logging
188 struct pakfire_log_buffer log_INFO;
189 struct pakfire_log_buffer log_ERROR;
6ac51607 190#ifdef ENABLE_DEBUG
e33387d3 191 struct pakfire_log_buffer log_DEBUG;
6ac51607 192#endif /* ENABLE_DEBUG */
616f1fca 193 } buffers;
aca565fc
MT
194
195 struct pakfire_cgroup* cgroup;
6b7cf275 196 struct pakfire_cgroup_stats cgroup_stats;
374e6268 197
c40722e3
MT
198 // PTY
199 struct pakfire_jail_pty {
200 // The path to the console
201 char console[PATH_MAX];
202
dcdda292 203 // The master device
c40722e3
MT
204 struct pakfire_jail_pty_master {
205 int fd;
6ce285a1 206 enum pakfire_jail_pty_io io;
dcdda292 207 } master;
c40722e3 208
dcdda292
MT
209 // Standard Input/Output
210 struct pakfire_jail_pty_stdio stdin;
c40722e3
MT
211 struct pakfire_jail_pty_stdio stdout;
212 } pty;
fd37ccaf
MT
213};
214
0bd84dc1
MT
215static int clone3(struct clone_args* args, size_t size) {
216 return syscall(__NR_clone3, args, size);
217}
218
335b8a44
MT
219static int pidfd_send_signal(int pidfd, int sig, siginfo_t* info, unsigned int flags) {
220 return syscall(SYS_pidfd_send_signal, pidfd, sig, info, flags);
221}
222
14df7388
MT
223static int pivot_root(const char* new_root, const char* old_root) {
224 return syscall(SYS_pivot_root, new_root, old_root);
225}
226
7bdf1d8e
MT
227static int pakfire_jail_exec_has_flag(
228 const struct pakfire_jail_exec* ctx, const enum pakfire_jail_exec_flags flag) {
229 return ctx->flags & flag;
230}
231
d5bc8fe0
MT
232static void pakfire_jail_free(struct pakfire_jail* jail) {
233 DEBUG(jail->pakfire, "Freeing jail at %p\n", jail);
234
235 // Free environment
236 for (unsigned int i = 0; jail->env[i]; i++)
237 free(jail->env[i]);
238
d34b1e00
MT
239 if (jail->cgroup)
240 pakfire_cgroup_unref(jail->cgroup);
a13df023
MT
241 if (jail->pakfire)
242 pakfire_unref(jail->pakfire);
243 if (jail->ctx)
244 pakfire_ctx_unref(jail->ctx);
d5bc8fe0
MT
245 free(jail);
246}
247
3bf01105
MT
248static const char* pakfire_jail_uuid(struct pakfire_jail* jail) {
249 if (!*jail->__uuid)
250 uuid_unparse_lower(jail->uuid, jail->__uuid);
251
252 return jail->__uuid;
253}
254
00ba1d9a
MT
255static int pakfire_jail_setup_interactive_env(struct pakfire_jail* jail) {
256 // Set PS1
257 int r = pakfire_jail_set_env(jail, "PS1", "pakfire-jail \\w> ");
258 if (r)
259 return r;
260
261 // Copy TERM
262 char* TERM = secure_getenv("TERM");
263 if (TERM) {
264 r = pakfire_jail_set_env(jail, "TERM", TERM);
265 if (r)
266 return r;
267 }
268
269 // Copy LANG
270 char* LANG = secure_getenv("LANG");
271 if (LANG) {
272 r = pakfire_jail_set_env(jail, "LANG", LANG);
273 if (r)
274 return r;
275 }
276
277 return 0;
278}
279
9fa1afb6 280PAKFIRE_EXPORT int pakfire_jail_create(struct pakfire_jail** jail, struct pakfire* pakfire) {
d5bc8fe0
MT
281 int r;
282
652f2a99 283 const char* arch = pakfire_get_effective_arch(pakfire);
aac86bd3 284
d5bc8fe0 285 // Allocate a new jail
fd37ccaf
MT
286 struct pakfire_jail* j = calloc(1, sizeof(*j));
287 if (!j)
288 return 1;
289
a13df023
MT
290 // Reference context
291 j->ctx = pakfire_ctx(pakfire);
292
fd37ccaf
MT
293 // Reference Pakfire
294 j->pakfire = pakfire_ref(pakfire);
295
296 // Initialize reference counter
297 j->nrefs = 1;
298
ae5201c5
MT
299 // Generate a random UUID
300 uuid_generate_random(j->uuid);
301
84bd7655
MT
302 DEBUG(j->pakfire, "Allocated new jail at %p\n", j);
303
d5bc8fe0
MT
304 // Set default environment
305 for (const struct environ* e = ENV; e->key; e++) {
306 r = pakfire_jail_set_env(j, e->key, e->val);
307 if (r)
308 goto ERROR;
309 }
310
aac86bd3 311 // Enable all CPU features that CPU has to offer
1f4e66a4 312 if (!pakfire_arch_is_supported_by_host(arch)) {
aac86bd3
MT
313 r = pakfire_jail_set_env(j, "QEMU_CPU", "max");
314 if (r)
315 goto ERROR;
316 }
317
3bf01105
MT
318 // Set container UUID
319 r = pakfire_jail_set_env(j, "container_uuid", pakfire_jail_uuid(j));
320 if (r)
321 goto ERROR;
322
367e708d
MT
323 // Disable systemctl to talk to systemd
324 if (!pakfire_on_root(j->pakfire)) {
325 r = pakfire_jail_set_env(j, "SYSTEMD_OFFLINE", "1");
326 if (r)
327 goto ERROR;
328 }
329
fd37ccaf
MT
330 // Done
331 *jail = j;
332 return 0;
84bd7655 333
d5bc8fe0
MT
334ERROR:
335 pakfire_jail_free(j);
32d5f21d 336
d5bc8fe0 337 return r;
fd37ccaf
MT
338}
339
6ce56f90 340PAKFIRE_EXPORT struct pakfire_jail* pakfire_jail_ref(struct pakfire_jail* jail) {
fd37ccaf
MT
341 ++jail->nrefs;
342
343 return jail;
344}
345
6ce56f90 346PAKFIRE_EXPORT struct pakfire_jail* pakfire_jail_unref(struct pakfire_jail* jail) {
fd37ccaf
MT
347 if (--jail->nrefs > 0)
348 return jail;
349
350 pakfire_jail_free(jail);
351 return NULL;
352}
32d5f21d 353
cf440db8
MT
354// Resource Limits
355
356PAKFIRE_EXPORT int pakfire_jail_nice(struct pakfire_jail* jail, int nice) {
357 // Check if nice level is in range
358 if (nice < -19 || nice > 20) {
359 errno = EINVAL;
360 return 1;
361 }
362
363 // Store nice level
364 jail->nice = nice;
365
366 return 0;
367}
368
15503538
MT
369int pakfire_jail_set_cgroup(struct pakfire_jail* jail, struct pakfire_cgroup* cgroup) {
370 // Free any previous cgroup
371 if (jail->cgroup) {
372 pakfire_cgroup_unref(jail->cgroup);
373 jail->cgroup = NULL;
374 }
375
376 // Set any new cgroup
377 if (cgroup) {
378 DEBUG(jail->pakfire, "Setting cgroup %p\n", cgroup);
379
380 jail->cgroup = pakfire_cgroup_ref(cgroup);
381 }
382
383 // Done
384 return 0;
385}
386
32d5f21d
MT
387// Environment
388
389// Returns the length of the environment
390static unsigned int pakfire_jail_env_length(struct pakfire_jail* jail) {
391 unsigned int i = 0;
392
393 // Count everything in the environment
394 for (char** e = jail->env; *e; e++)
395 i++;
396
397 return i;
398}
399
400// Finds an existing environment variable and returns its index or -1 if not found
401static int pakfire_jail_find_env(struct pakfire_jail* jail, const char* key) {
402 if (!key) {
403 errno = EINVAL;
404 return -1;
405 }
406
b88ae806 407 const size_t length = strlen(key);
32d5f21d
MT
408
409 for (unsigned int i = 0; jail->env[i]; i++) {
b88ae806
MT
410 if ((pakfire_string_startswith(jail->env[i], key)
411 && *(jail->env[i] + length) == '=')) {
32d5f21d 412 return i;
b88ae806 413 }
32d5f21d
MT
414 }
415
416 // Nothing found
417 return -1;
418}
419
420// Returns the value of an environment variable or NULL
6ce56f90
MT
421PAKFIRE_EXPORT const char* pakfire_jail_get_env(struct pakfire_jail* jail,
422 const char* key) {
32d5f21d
MT
423 int i = pakfire_jail_find_env(jail, key);
424 if (i < 0)
425 return NULL;
426
427 return jail->env[i] + strlen(key) + 1;
428}
429
430// Sets an environment variable
6ce56f90
MT
431PAKFIRE_EXPORT int pakfire_jail_set_env(struct pakfire_jail* jail,
432 const char* key, const char* value) {
32d5f21d
MT
433 // Find the index where to write this value to
434 int i = pakfire_jail_find_env(jail, key);
435 if (i < 0)
436 i = pakfire_jail_env_length(jail);
437
438 // Return -ENOSPC when the environment is full
439 if (i >= ENVIRON_SIZE) {
440 errno = ENOSPC;
441 return -1;
442 }
443
444 // Free any previous value
445 if (jail->env[i])
446 free(jail->env[i]);
447
448 // Format and set environment variable
449 asprintf(&jail->env[i], "%s=%s", key, value);
450
451 DEBUG(jail->pakfire, "Set environment variable: %s\n", jail->env[i]);
452
453 return 0;
454}
9f50bf71 455
939025e7 456// Imports an environment
6ce56f90 457PAKFIRE_EXPORT int pakfire_jail_import_env(struct pakfire_jail* jail, const char* env[]) {
939025e7
MT
458 if (!env)
459 return 0;
460
461 char* key;
462 char* val;
463 int r;
464
465 // Copy environment variables
466 for (unsigned int i = 0; env[i]; i++) {
467 r = pakfire_string_partition(env[i], "=", &key, &val);
468 if (r)
469 continue;
470
471 // Set value
472 r = pakfire_jail_set_env(jail, key, val);
473
474 if (key)
475 free(key);
476 if (val)
477 free(val);
478
479 // Break on error
480 if (r)
481 return r;
482 }
483
484 return 0;
485}
486
335b8a44
MT
487// Timeout
488
489PAKFIRE_EXPORT int pakfire_jail_set_timeout(
490 struct pakfire_jail* jail, unsigned int timeout) {
491 // Store value
492 jail->timeout.it_value.tv_sec = timeout;
493
494 if (timeout > 0)
a8a41064 495 DEBUG(jail->pakfire, "Timeout set to %u second(s)\n", timeout);
335b8a44
MT
496 else
497 DEBUG(jail->pakfire, "Timeout disabled\n");
498
499 return 0;
500}
501
502static int pakfire_jail_create_timer(struct pakfire_jail* jail) {
503 int r;
504
505 // Nothing to do if no timeout has been set
506 if (!jail->timeout.it_value.tv_sec)
507 return -1;
508
509 // Create a new timer
510 const int fd = timerfd_create(CLOCK_MONOTONIC, 0);
511 if (fd < 0) {
512 ERROR(jail->pakfire, "Could not create timer: %m\n");
513 goto ERROR;
514 }
515
516 // Arm timer
517 r = timerfd_settime(fd, 0, &jail->timeout, NULL);
518 if (r) {
519 ERROR(jail->pakfire, "Could not arm timer: %m\n");
520 goto ERROR;
521 }
522
523 return fd;
524
525ERROR:
dd2d7dbb 526 if (fd >= 0)
335b8a44
MT
527 close(fd);
528
529 return -1;
530}
531
d8b94dd5
MT
532/*
533 Standard Input
534*/
535PAKFIRE_EXPORT void pakfire_jail_set_stdin_callback(struct pakfire_jail* jail,
536 pakfire_jail_stdin_callback callback, void* data) {
537 jail->callbacks.stdin.callback = callback;
538 jail->callbacks.stdin.data = data;
539}
540
541/*
542 Standard Output
543*/
544PAKFIRE_EXPORT void pakfire_jail_set_stdout_callback(struct pakfire_jail* jail,
545 pakfire_jail_stdout_callback callback, void* data) {
546 jail->callbacks.stdout.callback = callback;
547 jail->callbacks.stdout.data = data;
548}
549
e33387d3
MT
550/*
551 This function replaces any logging in the child process.
552
553 All log messages will be sent to the parent process through their respective pipes.
554*/
19e50d86 555static void pakfire_jail_log_redirect(void* data, int priority, const char* file,
e33387d3
MT
556 int line, const char* fn, const char* format, va_list args) {
557 struct pakfire_jail_pipes* pipes = (struct pakfire_jail_pipes*)data;
558 int fd;
559
560 switch (priority) {
561 case LOG_INFO:
562 fd = pipes->log_INFO[1];
563 break;
564
565 case LOG_ERR:
566 fd = pipes->log_ERROR[1];
567 break;
568
569#ifdef ENABLE_DEBUG
570 case LOG_DEBUG:
571 fd = pipes->log_DEBUG[1];
572 break;
573#endif /* ENABLE_DEBUG */
574
575 // Ignore any messages of an unknown priority
576 default:
577 return;
578 }
579
f52ed269
MT
580 // End if we do not have a file descriptor to write to
581 if (fd < 0)
582 return;
583
584 // Optionally log the function name
585 if (fn)
586 dprintf(fd, "%s: ", fn);
587
e33387d3 588 // Send the log message
f52ed269 589 vdprintf(fd, format, args);
e33387d3
MT
590}
591
bb1a7acd
MT
592static int pakfire_jail_fill_buffer(struct pakfire_jail* jail, int fd, struct pakfire_log_buffer* buffer) {
593 int r;
594
595 // Skip this if there is not space left in the buffer
596 if (buffer->used >= sizeof(buffer->data))
597 return 0;
598
599 // Fill the buffer
600 r = read(fd, buffer->data + buffer->used, sizeof(buffer->data) - buffer->used);
601
602 // Handle errors
603 if (r < 0) {
604 switch (errno) {
605 case EAGAIN:
606 case EIO:
607 break;
608
609 default:
610 return -errno;
611 }
612
613 // EOF
614 } else if (r == 0) {
615 // XXX What to do here?
616
617 // Successful read
618 } else {
619 buffer->used += r;
620 }
621
622 return 0;
623}
624
685389c5 625static int pakfire_jail_drain_buffer_with_callback(struct pakfire_jail* jail,
1a7eb888 626 struct pakfire_log_buffer* buffer, pakfire_jail_stdout_callback callback, void* data) {
685389c5
MT
627 const char* eol = NULL;
628 int r;
629
630 while (buffer->used) {
631 // Search for the end of the first line
632 eol = memchr(buffer->data, '\n', buffer->used);
633
634 // No newline found
635 if (!eol) {
636 // If the buffer is full, we send the entire content to make space.
3fef3887 637 if (buffer->used >= sizeof(buffer->data)) {
685389c5
MT
638 CTX_DEBUG(jail->ctx, "Buffer is full. Sending all content\n");
639
640 eol = buffer->data + buffer->used - 1;
641
642 // Otherwise we might have only read parts of the output...
643 } else {
644 break;
645 }
646 }
647
648 // Find the length of the string
649 const size_t length = eol - buffer->data + 1;
650
651 // Call the callback
04d3c185 652 r = callback(jail->ctx, jail, data, buffer->data, length);
685389c5 653 if (r) {
1a7eb888 654 CTX_ERROR(jail->ctx, "The standard output callback returned an error: %d\n", r);
685389c5
MT
655 return r;
656 }
657
658 // Remove line from buffer
659 memmove(buffer->data, buffer->data + length, buffer->used - length);
660 buffer->used -= length;
661 }
662
663 return 0;
664}
665
bb1a7acd
MT
666static int pakfire_jail_drain_buffer(struct pakfire_jail* jail, int fd, struct pakfire_log_buffer* buffer) {
667 int r;
668
669 // Nothing to do if the buffer is empty
670 if (!buffer->used)
671 return 0;
672
d45cb127
MT
673 // Do not try to write to an invalid file descriptor
674 if (fd < 0)
675 return 0;
676
bb1a7acd
MT
677 // Drain the buffer
678 r = write(fd, buffer->data, buffer->used);
679
680 // Handle errors
681 if (r < 0) {
682 switch (errno) {
683 case EAGAIN:
684 case EIO:
685 break;
686
687 default:
688 return -errno;
689 }
690
691 // Successful write
692 } else {
693 memmove(buffer->data, buffer->data + r, buffer->used - r);
694
695 buffer->used -= r;
696 }
697
698 return 0;
699}
700
1a7eb888
MT
701/*
702 Passes any log messages on to the default pakfire log callback
703*/
04d3c185 704static int pakfire_jail_log(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
1a7eb888
MT
705 void* data, const char* line, size_t length) {
706 int* priority = data;
707
708 if (pakfire_ctx_get_log_level(jail->ctx) >= *priority)
709 pakfire_ctx_log(jail->ctx, *priority, NULL, 0, NULL, "%.*s", (int)length, line);
710
711 return 0;
712}
713
616f1fca
MT
714/*
715 This function reads as much data as it can from the file descriptor.
716 If it finds a whole line in it, it will send it to the logger and repeat the process.
717 If not newline character is found, it will try to read more data until it finds one.
718*/
685389c5 719static int pakfire_jail_handle_log(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
1a7eb888 720 int fd, struct pakfire_log_buffer* buffer, pakfire_jail_stdout_callback callback, void* data) {
bb1a7acd 721 int r;
616f1fca
MT
722
723 // Fill up buffer from fd
bb1a7acd
MT
724 r = pakfire_jail_fill_buffer(jail, fd, buffer);
725 if (r)
726 return r;
616f1fca 727
685389c5 728 // Drain the buffer
1a7eb888 729 r = pakfire_jail_drain_buffer_with_callback(jail, buffer, callback, data);
685389c5
MT
730 if (r)
731 return r;
616f1fca
MT
732
733 return 0;
734}
735
06b864ae
MT
736static int pakfire_jail_stream_stdin(struct pakfire_jail* jail,
737 struct pakfire_jail_exec* ctx, const int fd) {
eefb6204 738 const char eof = 0x04;
06b864ae
MT
739 int r;
740
f5a70a96 741 // Skip if the writing pipe has already been closed
eefb6204 742 if (fd < 0)
f5a70a96
MT
743 return 0;
744
eefb6204 745 CTX_DEBUG(jail->ctx, "Streaming standard input...\n");
06b864ae
MT
746
747 // Calling the callback
04d3c185 748 r = jail->callbacks.stdin.callback(jail->ctx, jail, jail->callbacks.stdin.data, fd);
06b864ae 749
eefb6204
MT
750 switch (r) {
751 case EOF:
752 // The callback signaled that it has written everything
753 CTX_DEBUG(jail->ctx, "Closing standard input pipe\n");
06b864ae 754
eefb6204
MT
755 // Send EOF (Ctrl-D)
756 r = write(fd, &eof, sizeof(eof));
757 if (r < 0) {
758 CTX_ERROR(jail->ctx, "Could not write EOF: %s\n", strerror(errno));
759 return -errno;
760 }
06b864ae 761
eefb6204 762 return 0;
f5a70a96 763
eefb6204
MT
764 case 0:
765 CTX_DEBUG(jail->ctx, "Standard input callback finished\n");
766 return 0;
f5a70a96 767
eefb6204
MT
768 default:
769 CTX_ERROR(jail->ctx, "Standard input callback failed: %s\n", strerror(-r));
770 return r;
06b864ae 771 }
06b864ae
MT
772}
773
8f49d254
MT
774static int pakfire_jail_recv_fd(struct pakfire_jail* jail, int socket, int* fd) {
775 const size_t payload_length = sizeof(fd);
776 char buffer[CMSG_SPACE(payload_length)];
777 int r;
778
779 struct msghdr msg = {
780 .msg_control = buffer,
781 .msg_controllen = sizeof(buffer),
782 };
783
784 // Receive the message
785 r = recvmsg(socket, &msg, 0);
786 if (r) {
787 CTX_ERROR(jail->ctx, "Could not receive file descriptor: %s\n", strerror(errno));
788 return -errno;
789 }
790
791 // Fetch the payload
792 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
793 if (!cmsg)
794 return -EBADMSG;
795
796 *fd = *((int*)CMSG_DATA(cmsg));
797
798 CTX_DEBUG(jail->ctx, "Received fd %d from socket %d\n", *fd, socket);
799
800 return 0;
801}
802
803static int pakfire_jail_send_fd(struct pakfire_jail* jail, int socket, int fd) {
804 const size_t payload_length = sizeof(fd);
805 char buffer[CMSG_SPACE(payload_length)];
806 int r;
807
808 CTX_DEBUG(jail->ctx, "Sending fd %d to socket %d\n", fd, socket);
809
810 // Header
811 struct msghdr msg = {
812 .msg_control = buffer,
813 .msg_controllen = sizeof(buffer),
814 };
815
816 // Payload
817 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
818 cmsg->cmsg_level = SOL_SOCKET;
819 cmsg->cmsg_type = SCM_RIGHTS;
820 cmsg->cmsg_len = CMSG_LEN(payload_length);
821
822 // Set payload
823 *((int*)CMSG_DATA(cmsg)) = fd;
824
825 // Send the message
826 r = sendmsg(socket, &msg, 0);
827 if (r) {
828 CTX_ERROR(jail->ctx, "Could not send file descriptor: %s\n", strerror(errno));
829 return -errno;
830 }
831
832 return 0;
833}
834
195fe455
MT
835static int pakfire_jail_setup_pipe(struct pakfire_jail* jail, int (*fds)[2], const int flags) {
836 int r = pipe2(*fds, flags);
837 if (r < 0) {
838 ERROR(jail->pakfire, "Could not setup pipe: %m\n");
839 return 1;
840 }
841
842 return 0;
843}
844
845static void pakfire_jail_close_pipe(struct pakfire_jail* jail, int fds[2]) {
846 for (unsigned int i = 0; i < 2; i++)
dd2d7dbb 847 if (fds[i] >= 0)
195fe455
MT
848 close(fds[i]);
849}
850
e33387d3
MT
851/*
852 This is a convenience function to fetch the reading end of a pipe and
853 closes the write end.
854*/
06b864ae 855static int pakfire_jail_get_pipe_to_read(struct pakfire_jail* jail, int (*fds)[2]) {
e33387d3
MT
856 // Give the variables easier names to avoid confusion
857 int* fd_read = &(*fds)[0];
858 int* fd_write = &(*fds)[1];
859
860 // Close the write end of the pipe
4c3bab92 861 if (*fd_write >= 0) {
e33387d3 862 close(*fd_write);
d2eaf8dc 863 *fd_write = -1;
e33387d3
MT
864 }
865
866 // Return the read end
4c3bab92
MT
867 if (*fd_read >= 0)
868 return *fd_read;
869
870 return -1;
e33387d3
MT
871}
872
06b864ae
MT
873static int pakfire_jail_get_pipe_to_write(struct pakfire_jail* jail, int (*fds)[2]) {
874 // Give the variables easier names to avoid confusion
875 int* fd_read = &(*fds)[0];
876 int* fd_write = &(*fds)[1];
877
878 // Close the read end of the pipe
4c3bab92 879 if (*fd_read >= 0) {
06b864ae 880 close(*fd_read);
d2eaf8dc 881 *fd_read = -1;
06b864ae
MT
882 }
883
884 // Return the write end
4c3bab92
MT
885 if (*fd_write >= 0)
886 return *fd_write;
887
888 return -1;
06b864ae
MT
889}
890
e4fded3a
MT
891static int pakfire_jail_epoll_add_fd(struct pakfire_jail* jail, int epollfd, int fd, int events) {
892 struct epoll_event event = {
893 .events = events|EPOLLHUP,
894 .data = {
895 .fd = fd,
896 },
897 };
898 int r;
899
900 // Read flags
901 int flags = fcntl(fd, F_GETFL, 0);
902
903 // Set modified flags
904 r = fcntl(fd, F_SETFL, flags|O_NONBLOCK);
905 if (r < 0) {
906 CTX_ERROR(jail->ctx, "Could not set file descriptor %d into non-blocking mode: %s\n",
907 fd, strerror(errno));
908 return -errno;
909 }
910
911 // Add the file descriptor to the loop
912 r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
913 if (r < 0) {
914 ERROR(jail->pakfire, "Could not add file descriptor %d to epoll(): %s\n",
915 fd, strerror(errno));
916 return -errno;
917 }
918
919 return 0;
920}
921
690ff87c
MT
922// PTY Forwarding
923
924static int pakfire_jail_enable_raw_mode(struct pakfire_jail* jail,
925 struct pakfire_jail_pty_stdio* stdio) {
926 struct termios raw_attrs;
927 int r;
928
d45cb127
MT
929 // Skip if we don't know the file descriptor
930 if (stdio->fd < 0)
931 return 0;
932
a6d1853b
MT
933 // Skip everything if fd is not a TTY
934 if (!isatty(stdio->fd))
935 return 0;
936
a12a3659 937 // Store flags
6ce285a1
MT
938 stdio->flags = fcntl(stdio->fd, F_GETFL);
939 if (stdio->flags < 0) {
a12a3659
MT
940 CTX_ERROR(jail->ctx, "Could not fetch flags from fd %d: %s\n",
941 stdio->fd, strerror(errno));
942 return -errno;
943 }
944
690ff87c
MT
945 // Fetch all attributes
946 r = tcgetattr(stdio->fd, &stdio->attrs);
947 if (r) {
948 CTX_ERROR(jail->ctx, "Could not fetch terminal attributes from fd %d: %s\n",
949 stdio->fd, strerror(errno));
950 return -errno;
951 }
952
953 // Copy all attributes
954 raw_attrs = stdio->attrs;
955
956 // Make it RAW
957 cfmakeraw(&raw_attrs);
958
dd699e59
MT
959 switch (stdio->fd) {
960 case STDIN_FILENO:
961 raw_attrs.c_oflag = stdio->attrs.c_oflag;
962 break;
963
964 case STDOUT_FILENO:
965 raw_attrs.c_iflag = stdio->attrs.c_iflag;
966 raw_attrs.c_lflag = stdio->attrs.c_lflag;
967 break;
968 }
969
690ff87c
MT
970 // Restore the attributes
971 r = tcsetattr(stdio->fd, TCSANOW, &raw_attrs);
972 if (r) {
973 CTX_ERROR(jail->ctx, "Could not restore terminal attributes for fd %d: %s\n",
974 stdio->fd, strerror(errno));
975 return -errno;
976 }
977
978 return 0;
979}
980
a12a3659 981static int pakfire_jail_restore_attrs(struct pakfire_jail* jail,
690ff87c
MT
982 const struct pakfire_jail_pty_stdio* stdio) {
983 int r;
984
d45cb127
MT
985 // Skip if we don't know the file descriptor
986 if (stdio->fd < 0)
987 return 0;
988
a6d1853b
MT
989 // Skip everything if fd is not a TTY
990 if (!isatty(stdio->fd))
991 return 0;
992
a12a3659 993 // Restore the flags
6ce285a1 994 r = fcntl(stdio->fd, F_SETFL, stdio->flags);
a12a3659
MT
995 if (r < 0) {
996 CTX_ERROR(jail->ctx, "Could not set flags for file descriptor %d: %s\n",
997 stdio->fd, strerror(errno));
998 return -errno;
999 }
1000
690ff87c
MT
1001 // Restore the attributes
1002 r = tcsetattr(stdio->fd, TCSANOW, &stdio->attrs);
1003 if (r) {
a12a3659 1004 CTX_ERROR(jail->ctx, "Could not restore terminal attributes for %d, ignoring: %s\n",
690ff87c 1005 stdio->fd, strerror(errno));
a12a3659 1006 return -errno;
690ff87c 1007 }
a12a3659
MT
1008
1009 return 0;
690ff87c
MT
1010}
1011
92f2120e
MT
1012static int pakfire_jail_setup_pty_forwarding(struct pakfire_jail* jail,
1013 struct pakfire_jail_exec* ctx, const int epollfd, const int fd) {
690ff87c
MT
1014 struct winsize size;
1015 int r;
1016
1017 CTX_DEBUG(jail->ctx, "Setting up PTY forwarding on fd %d\n", fd);
1018
92f2120e 1019 // Store the file descriptor
690ff87c 1020 ctx->pty.master.fd = fd;
92f2120e 1021
d45cb127
MT
1022 // Add the master to the event loop
1023 r = pakfire_jail_epoll_add_fd(jail, epollfd, ctx->pty.master.fd, EPOLLIN|EPOLLOUT|EPOLLET);
1024 if (r)
1025 return r;
690ff87c 1026
d45cb127
MT
1027 if (ctx->flags & PAKFIRE_JAIL_PTY_FORWARDING) {
1028 // Configure stdin/stdout
1029 ctx->pty.stdin.fd = STDIN_FILENO;
1030 ctx->pty.stdout.fd = STDOUT_FILENO;
690ff87c 1031
d45cb127
MT
1032 // Fetch dimensions
1033 if (isatty(ctx->pty.stdout.fd)) {
1034 r = ioctl(ctx->pty.stdout.fd, TIOCGWINSZ, &size);
1035 if (r) {
1036 CTX_ERROR(jail->ctx, "Failed to determine terminal dimensions: %s\n", strerror(errno));
1037 return -errno;
1038 }
690ff87c 1039
d45cb127
MT
1040 // Set dimensions
1041 r = ioctl(ctx->pty.master.fd, TIOCSWINSZ, &size);
1042 if (r) {
1043 CTX_ERROR(jail->ctx, "Failed setting dimensions: %s\n", strerror(errno));
1044 return -errno;
1045 }
1046 }
690ff87c 1047
d45cb127
MT
1048 // Enable RAW mode on standard input
1049 r = pakfire_jail_enable_raw_mode(jail, &ctx->pty.stdin);
1050 if (r)
1051 return r;
690ff87c 1052
d45cb127
MT
1053 // Enable RAW mode on standard output
1054 r = pakfire_jail_enable_raw_mode(jail, &ctx->pty.stdout);
1055 if (r)
1056 return r;
690ff87c 1057
d45cb127
MT
1058 // Add standard input to the event loop
1059 r = pakfire_jail_epoll_add_fd(jail, epollfd, ctx->pty.stdin.fd, EPOLLIN|EPOLLET);
1060 if (r)
1061 return r;
690ff87c 1062
d45cb127
MT
1063 // Add standard output to the event loop
1064 r = pakfire_jail_epoll_add_fd(jail, epollfd, ctx->pty.stdout.fd, EPOLLOUT|EPOLLET);
1065 if (r)
1066 return r;
1067 }
92f2120e
MT
1068
1069 return 0;
1070}
1071
04d3c185
MT
1072static int pakfire_jail_command_output(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
1073 void* data, const char* line, const size_t length) {
1074 CTX_INFO(ctx, "Command Output: %.*s", (int)length, line);
1a7eb888
MT
1075
1076 return 0;
1077}
1078
c15801c7
MT
1079static int pakfire_jail_forward_pty(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
1080 int r;
1081
6ce285a1 1082 while (ctx->pty.master.io || ctx->pty.stdin.io || ctx->pty.stdout.io) {
6c7ddcda 1083 // Read from standard input
6ce285a1 1084 if (ctx->pty.stdin.io & PAKFIRE_JAIL_PTY_READY_TO_READ) {
6c7ddcda
MT
1085 r = pakfire_jail_fill_buffer(jail, ctx->pty.stdin.fd, &ctx->pty.stdin.buffer);
1086 if (r) {
1087 CTX_ERROR(jail->ctx, "Failed reading from standard input: %s\n", strerror(-r));
1088 return r;
1089 }
c15801c7 1090
6c7ddcda 1091 // We are done reading for now
6ce285a1 1092 ctx->pty.stdin.io &= ~PAKFIRE_JAIL_PTY_READY_TO_READ;
c15801c7 1093
6c7ddcda
MT
1094 // But we may have data to write
1095 if (ctx->pty.stdin.buffer.used)
6ce285a1 1096 ctx->pty.master.io |= PAKFIRE_JAIL_PTY_READY_TO_WRITE;
c15801c7
MT
1097 }
1098
6c7ddcda 1099 // Write to the master
6ce285a1 1100 if (ctx->pty.master.io & PAKFIRE_JAIL_PTY_READY_TO_WRITE) {
1a7eb888 1101 if (jail->callbacks.stdin.callback) {
eefb6204
MT
1102 r = pakfire_jail_stream_stdin(jail, ctx, ctx->pty.master.fd);
1103 if (r)
1104 return r;
1105
1106 } else {
1107 r = pakfire_jail_drain_buffer(jail, ctx->pty.master.fd, &ctx->pty.stdin.buffer);
1108 if (r) {
1109 CTX_ERROR(jail->ctx, "Failed writing to the PTY: %s\n", strerror(-r));
1110 return r;
1111 }
6c7ddcda 1112 }
c15801c7 1113
6c7ddcda 1114 // We are done writing for now
6ce285a1 1115 ctx->pty.master.io &= ~PAKFIRE_JAIL_PTY_READY_TO_WRITE;
c15801c7
MT
1116 }
1117
6c7ddcda 1118 // Read from the master
6ce285a1 1119 if (ctx->pty.master.io & PAKFIRE_JAIL_PTY_READY_TO_READ) {
6c7ddcda 1120 r = pakfire_jail_fill_buffer(jail, ctx->pty.master.fd, &ctx->pty.stdout.buffer);
112458fc 1121 if (r) {
6c7ddcda 1122 CTX_ERROR(jail->ctx, "Failed reading from the PTY: %s\n", strerror(-r));
112458fc
MT
1123 return r;
1124 }
1125
6c7ddcda 1126 // We are done reading for now
6ce285a1 1127 ctx->pty.master.io &= ~PAKFIRE_JAIL_PTY_READY_TO_READ;
6c7ddcda
MT
1128
1129 // But we may have data to write
1130 if (ctx->pty.stdout.buffer.used)
6ce285a1 1131 ctx->pty.stdout.io |= PAKFIRE_JAIL_PTY_READY_TO_WRITE;
c15801c7
MT
1132 }
1133
6c7ddcda 1134 // Write to standard output
c63d939b 1135 if (ctx->pty.stdout.io & PAKFIRE_JAIL_PTY_READY_TO_WRITE) {
6c7ddcda 1136 // If we have a callback, we will send any output to the callback
1a7eb888 1137 if (jail->callbacks.stdout.callback) {
6c7ddcda 1138 r = pakfire_jail_drain_buffer_with_callback(jail, &ctx->pty.stdout.buffer,
1a7eb888 1139 jail->callbacks.stdout.callback, jail->callbacks.stdout.data);
6c7ddcda
MT
1140 if (r)
1141 return r;
1142
1143 // If we have a file descriptor, we will forward any output
1144 } else if (ctx->pty.stdout.fd >= 0) {
1145 r = pakfire_jail_drain_buffer(jail, ctx->pty.stdout.fd, &ctx->pty.stdout.buffer);
1146 if (r) {
1147 CTX_ERROR(jail->ctx, "Failed writing to standard output: %s\n", strerror(-r));
1148 return r;
1149 }
1150
1151 // Otherwise send the output to the default logger
1152 } else {
1153 r = pakfire_jail_drain_buffer_with_callback(jail, &ctx->pty.stdout.buffer,
1a7eb888 1154 pakfire_jail_command_output, NULL);
6c7ddcda
MT
1155 if (r)
1156 return r;
1157 }
1158
1159 // We are done writing for now
6ce285a1 1160 ctx->pty.stdout.io &= ~PAKFIRE_JAIL_PTY_READY_TO_WRITE;
6c7ddcda 1161 }
c15801c7
MT
1162 }
1163
1164 return 0;
1165}
1166
d853213d 1167static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
616f1fca 1168 int epollfd = -1;
616f1fca 1169 struct epoll_event events[EPOLL_MAX_EVENTS];
335b8a44 1170 char garbage[8];
616f1fca
MT
1171 int r = 0;
1172
1173 // Fetch file descriptors from context
f949feac 1174 const int pidfd = ctx->pidfd;
616f1fca 1175
8f49d254
MT
1176 // Fetch the UNIX domain socket
1177 const int socket_recv = pakfire_jail_get_pipe_to_read(jail, &ctx->socket);
1178
335b8a44
MT
1179 // Timer
1180 const int timerfd = pakfire_jail_create_timer(jail);
1181
e33387d3 1182 // Logging
06b864ae
MT
1183 const int log_INFO = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_INFO);
1184 const int log_ERROR = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_ERROR);
6ac51607 1185#ifdef ENABLE_DEBUG
06b864ae 1186 const int log_DEBUG = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_DEBUG);
6ac51607 1187#endif /* ENABLE_DEBUG */
7ebfb7cb
MT
1188
1189 // Make a list of all file descriptors we are interested in
e4fded3a
MT
1190 const struct pakfire_wait_fds {
1191 const int fd;
1192 const int events;
1193 } fds[] = {
e4fded3a
MT
1194 // Timer
1195 { timerfd, EPOLLIN },
1196
1197 // Child Process
1198 { ctx->pidfd, EPOLLIN },
1199
1200 // Log Pipes
1201 { log_INFO, EPOLLIN },
1202 { log_ERROR, EPOLLIN },
6ac51607 1203#ifdef ENABLE_DEBUG
e4fded3a 1204 { log_DEBUG, EPOLLIN },
6ac51607 1205#endif /* ENABLE_DEBUG */
e4fded3a 1206
8f49d254
MT
1207 // UNIX Domain Socket
1208 { socket_recv, EPOLLIN },
1209
e4fded3a
MT
1210 // Sentinel
1211 { -1, 0 },
616f1fca
MT
1212 };
1213
1214 // Setup epoll
1215 epollfd = epoll_create1(0);
1216 if (epollfd < 0) {
1217 ERROR(jail->pakfire, "Could not initialize epoll(): %m\n");
1218 r = 1;
d853213d 1219 goto ERROR;
616f1fca
MT
1220 }
1221
616f1fca 1222 // Turn file descriptors into non-blocking mode and add them to epoll()
e4fded3a 1223 for (const struct pakfire_wait_fds* fd = fds; fd->events; fd++) {
d853213d 1224 // Skip fds which were not initialized
e4fded3a 1225 if (fd->fd < 0)
d853213d
MT
1226 continue;
1227
e4fded3a
MT
1228 // Add the FD to the event loop
1229 r = pakfire_jail_epoll_add_fd(jail, epollfd, fd->fd, fd->events);
1230 if (r)
f949feac 1231 goto ERROR;
616f1fca
MT
1232 }
1233
1234 int ended = 0;
1235
1236 // Loop for as long as the process is alive
1237 while (!ended) {
616f1fca
MT
1238 int num = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, -1);
1239 if (num < 1) {
1240 // Ignore if epoll_wait() has been interrupted
1241 if (errno == EINTR)
1242 continue;
1243
1244 ERROR(jail->pakfire, "epoll_wait() failed: %m\n");
1245 r = 1;
1246
d853213d 1247 goto ERROR;
616f1fca
MT
1248 }
1249
616f1fca 1250 for (int i = 0; i < num; i++) {
e068b964 1251 int e = events[i].events;
616f1fca
MT
1252 int fd = events[i].data.fd;
1253
c15801c7
MT
1254 // Handle PTY forwarding events
1255 if (ctx->pty.master.fd == fd) {
1256 if (e & (EPOLLIN|EPOLLHUP))
6ce285a1 1257 ctx->pty.master.io |= PAKFIRE_JAIL_PTY_READY_TO_READ;
c15801c7
MT
1258
1259 if (e & (EPOLLOUT|EPOLLHUP))
6ce285a1 1260 ctx->pty.master.io |= PAKFIRE_JAIL_PTY_READY_TO_WRITE;
c15801c7
MT
1261
1262 // Perform the work
1263 r = pakfire_jail_forward_pty(jail, ctx);
1264 if (r) {
1265 CTX_ERROR(jail->ctx, "Failed forwarding the PTY: %s\n", strerror(-r));
1266 goto ERROR;
1267 }
1268
1269 // Handle standard input
1270 } else if (ctx->pty.stdin.fd == fd) {
1271 if (e & (EPOLLIN|EPOLLHUP))
6ce285a1 1272 ctx->pty.stdin.io |= PAKFIRE_JAIL_PTY_READY_TO_READ;
c15801c7
MT
1273
1274 // Perform the work
1275 r = pakfire_jail_forward_pty(jail, ctx);
1276 if (r) {
1277 CTX_ERROR(jail->ctx, "Failed forwarding the PTY: %s\n", strerror(-r));
1278 goto ERROR;
1279 }
1280
1281 // Handle standard output
1282 } else if (ctx->pty.stdout.fd == fd) {
1283 if (e & (EPOLLOUT|EPOLLHUP))
6ce285a1 1284 ctx->pty.stdout.io |= PAKFIRE_JAIL_PTY_READY_TO_WRITE;
c15801c7
MT
1285
1286 // Perform the work
1287 r = pakfire_jail_forward_pty(jail, ctx);
1288 if (r) {
1289 CTX_ERROR(jail->ctx, "Failed forwarding the PTY: %s\n", strerror(-r));
1290 goto ERROR;
1291 }
c15801c7 1292
a2d03d52
MT
1293 // Handle any changes to the PIDFD
1294 } else if (pidfd == fd) {
1295 if (e & EPOLLIN) {
f949feac
MT
1296 // Call waidid() and store the result
1297 r = waitid(P_PIDFD, ctx->pidfd, &ctx->status, WEXITED);
e068b964 1298 if (r) {
f949feac 1299 ERROR(jail->pakfire, "waitid() failed: %m\n");
e068b964
MT
1300 goto ERROR;
1301 }
d853213d 1302
e068b964
MT
1303 // Mark that we have ended so that we will process the remaining
1304 // events from epoll() now, but won't restart the outer loop.
1305 ended = 1;
a2d03d52 1306 }
d853213d 1307
a2d03d52
MT
1308 // Handle timer events
1309 } else if (timerfd == fd) {
1310 if (e & EPOLLIN) {
335b8a44
MT
1311 DEBUG(jail->pakfire, "Timer event received\n");
1312
1313 // Disarm the timer
1314 r = read(timerfd, garbage, sizeof(garbage));
1315 if (r < 1) {
1316 ERROR(jail->pakfire, "Could not disarm timer: %m\n");
1317 r = 1;
1318 goto ERROR;
1319 }
1320
1321 // Terminate the process if it hasn't already ended
1322 if (!ended) {
1323 DEBUG(jail->pakfire, "Terminating process...\n");
1324
1325 // Send SIGTERM to the process
f949feac 1326 r = pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
335b8a44
MT
1327 if (r) {
1328 ERROR(jail->pakfire, "Could not kill process: %m\n");
1329 goto ERROR;
1330 }
1331 }
a2d03d52 1332 }
335b8a44 1333
a2d03d52
MT
1334 // Handle socket messages
1335 } else if (socket_recv == fd) {
1336 if (e & EPOLLIN) {
8f49d254
MT
1337 // Receive the passed FD
1338 r = pakfire_jail_recv_fd(jail, socket_recv, &fd);
1339 if (r)
1340 goto ERROR;
1341
92f2120e 1342 // Setup PTY forwarding
c40722e3 1343 if (ctx->pty.master.fd < 0) {
92f2120e
MT
1344 r = pakfire_jail_setup_pty_forwarding(jail, ctx, epollfd, fd);
1345 if (r) {
1346 CTX_ERROR(jail->ctx, "Failed setting up PTY forwarding: %s\n", strerror(-r));
1347 goto ERROR;
1348 }
1349 }
a2d03d52 1350 }
8f49d254 1351
a2d03d52
MT
1352 // Handle log INFO messages
1353 } else if (log_INFO == fd) {
1354 if (e & EPOLLIN) {
1a7eb888
MT
1355 r = pakfire_jail_handle_log(jail, ctx, fd, &ctx->buffers.log_INFO,
1356 pakfire_jail_log, &ctx->buffers.log_INFO.priority);
a2d03d52
MT
1357 if (r)
1358 goto ERROR;
1359 }
8f49d254 1360
a2d03d52
MT
1361 // Handle log ERROR messages
1362 } else if (log_ERROR == fd) {
1363 if (e & EPOLLIN) {
1a7eb888
MT
1364 r = pakfire_jail_handle_log(jail, ctx, fd, &ctx->buffers.log_ERROR,
1365 pakfire_jail_log, &ctx->buffers.log_ERROR.priority);
a2d03d52
MT
1366 if (r)
1367 goto ERROR;
e068b964
MT
1368 }
1369
a2d03d52
MT
1370#ifdef ENABLE_DEBUG
1371 // Handle log DEBUG messages
1372 } else if (log_DEBUG == fd) {
1373 if (e & EPOLLIN) {
1a7eb888
MT
1374 r = pakfire_jail_handle_log(jail, ctx, fd, &ctx->buffers.log_DEBUG,
1375 pakfire_jail_log, &ctx->buffers.log_DEBUG.priority);
a2d03d52
MT
1376 if (r)
1377 goto ERROR;
1378 }
1379#endif /* ENABLE_DEBUG */
1380
1381 // Log a message for anything else
1382 } else {
1383 DEBUG(jail->pakfire, "Received invalid file descriptor %d\n", fd);
1384 continue;
616f1fca
MT
1385 }
1386
e068b964
MT
1387 // Check if any file descriptors have been closed
1388 if (e & EPOLLHUP) {
1389 // Remove the file descriptor
1390 r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
1391 if (r) {
1392 ERROR(jail->pakfire, "Could not remove closed file-descriptor %d: %m\n", fd);
1393 goto ERROR;
1394 }
1395 }
616f1fca
MT
1396 }
1397 }
1398
d853213d 1399ERROR:
dd2d7dbb 1400 if (epollfd >= 0)
616f1fca 1401 close(epollfd);
dd2d7dbb 1402 if (timerfd >= 0)
335b8a44 1403 close(timerfd);
616f1fca 1404
690ff87c 1405 // Restore any changed terminal attributes
7ac38d66
MT
1406 if (ctx->pty.stdin.fd >= 0)
1407 pakfire_jail_restore_attrs(jail, &ctx->pty.stdin);
1408 if (ctx->pty.stdout.fd >= 0)
1409 pakfire_jail_restore_attrs(jail, &ctx->pty.stdout);
690ff87c 1410
616f1fca
MT
1411 return r;
1412}
1413
04d3c185
MT
1414int pakfire_jail_capture_stdout(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
1415 void* data, const char* line, size_t length) {
12b9b39f
MT
1416 char** output = (char**)data;
1417 int r;
0de6bb30 1418
2015cb92 1419 // Append everything from stdout to a buffer
1a7eb888
MT
1420 r = asprintf(output, "%s%.*s", (output && *output) ? *output : "", (int)length, line);
1421 if (r < 0)
1422 return -errno;
0de6bb30 1423
1a7eb888 1424 return 0;
0de6bb30
MT
1425}
1426
980b15af
MT
1427// Capabilities
1428
e6791c52
MT
1429// Logs all capabilities of the current process
1430static int pakfire_jail_show_capabilities(struct pakfire_jail* jail) {
1431 cap_t caps = NULL;
1432 char* name = NULL;
1433 cap_flag_value_t value_e;
1434 cap_flag_value_t value_i;
1435 cap_flag_value_t value_p;
1436 int r;
980b15af 1437
e6791c52
MT
1438 // Fetch PID
1439 pid_t pid = getpid();
980b15af 1440
e6791c52
MT
1441 // Fetch all capabilities
1442 caps = cap_get_proc();
1443 if (!caps) {
1444 ERROR(jail->pakfire, "Could not fetch capabilities: %m\n");
1445 r = 1;
1446 goto ERROR;
1447 }
980b15af 1448
e6791c52 1449 DEBUG(jail->pakfire, "Capabilities of PID %d:\n", pid);
980b15af 1450
e6791c52
MT
1451 // Iterate over all capabilities
1452 for (unsigned int cap = 0; cap_valid(cap); cap++) {
1453 name = cap_to_name(cap);
980b15af 1454
e6791c52
MT
1455 // Fetch effective value
1456 r = cap_get_flag(caps, cap, CAP_EFFECTIVE, &value_e);
1457 if (r)
1458 goto ERROR;
980b15af 1459
e6791c52
MT
1460 // Fetch inheritable value
1461 r = cap_get_flag(caps, cap, CAP_INHERITABLE, &value_i);
1462 if (r)
1463 goto ERROR;
980b15af 1464
e6791c52
MT
1465 // Fetch permitted value
1466 r = cap_get_flag(caps, cap, CAP_PERMITTED, &value_p);
1467 if (r)
1468 goto ERROR;
980b15af 1469
e6791c52
MT
1470 DEBUG(jail->pakfire,
1471 " %-24s : %c%c%c\n",
1472 name,
1473 (value_e == CAP_SET) ? 'e' : '-',
1474 (value_i == CAP_SET) ? 'i' : '-',
1475 (value_p == CAP_SET) ? 'p' : '-'
1476 );
980b15af 1477
e6791c52
MT
1478 // Free name
1479 cap_free(name);
1480 name = NULL;
1481 }
980b15af 1482
e6791c52
MT
1483 // Success
1484 r = 0;
980b15af 1485
e6791c52
MT
1486ERROR:
1487 if (name)
1488 cap_free(name);
1489 if (caps)
1490 cap_free(caps);
980b15af 1491
e6791c52
MT
1492 return r;
1493}
980b15af 1494
e6791c52
MT
1495static int pakfire_jail_set_capabilities(struct pakfire_jail* jail) {
1496 cap_t caps = NULL;
1497 char* name = NULL;
1498 int r;
980b15af 1499
e6791c52
MT
1500 // Fetch capabilities
1501 caps = cap_get_proc();
1502 if (!caps) {
1503 ERROR(jail->pakfire, "Could not read capabilities: %m\n");
1504 r = 1;
1505 goto ERROR;
1506 }
980b15af 1507
e6791c52
MT
1508 // Walk through all capabilities
1509 for (cap_value_t cap = 0; cap_valid(cap); cap++) {
1510 cap_value_t _caps[] = { cap };
980b15af 1511
e6791c52
MT
1512 // Fetch the name of the capability
1513 name = cap_to_name(cap);
980b15af 1514
e6791c52
MT
1515 r = cap_set_flag(caps, CAP_EFFECTIVE, 1, _caps, CAP_SET);
1516 if (r) {
1517 ERROR(jail->pakfire, "Could not set %s: %m\n", name);
1518 goto ERROR;
1519 }
980b15af 1520
e6791c52 1521 r = cap_set_flag(caps, CAP_INHERITABLE, 1, _caps, CAP_SET);
980b15af 1522 if (r) {
e6791c52
MT
1523 ERROR(jail->pakfire, "Could not set %s: %m\n", name);
1524 goto ERROR;
980b15af
MT
1525 }
1526
e6791c52
MT
1527 r = cap_set_flag(caps, CAP_PERMITTED, 1, _caps, CAP_SET);
1528 if (r) {
1529 ERROR(jail->pakfire, "Could not set %s: %m\n", name);
1530 goto ERROR;
1531 }
980b15af 1532
e6791c52
MT
1533 // Free name
1534 cap_free(name);
1535 name = NULL;
980b15af
MT
1536 }
1537
e6791c52
MT
1538 // Restore all capabilities
1539 r = cap_set_proc(caps);
980b15af 1540 if (r) {
e6791c52 1541 ERROR(jail->pakfire, "Restoring capabilities failed: %m\n");
980b15af
MT
1542 goto ERROR;
1543 }
1544
e6791c52
MT
1545 // Add all capabilities to the ambient set
1546 for (unsigned int cap = 0; cap_valid(cap); cap++) {
1547 name = cap_to_name(cap);
1548
1549 // Raise the capability
1550 r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0);
1551 if (r) {
1552 ERROR(jail->pakfire, "Could not set ambient capability %s: %m\n", name);
1553 goto ERROR;
1554 }
1555
1556 // Free name
1557 cap_free(name);
1558 name = NULL;
980b15af
MT
1559 }
1560
e6791c52
MT
1561 // Success
1562 r = 0;
1563
980b15af 1564ERROR:
e6791c52
MT
1565 if (name)
1566 cap_free(name);
980b15af
MT
1567 if (caps)
1568 cap_free(caps);
1569
1570 return r;
1571}
1572
739d5b57
MT
1573// Syscall Filter
1574
1575static int pakfire_jail_limit_syscalls(struct pakfire_jail* jail) {
1576 const int syscalls[] = {
1577 // The kernel's keyring isn't namespaced
1578 SCMP_SYS(keyctl),
1579 SCMP_SYS(add_key),
1580 SCMP_SYS(request_key),
1581
1582 // Disable userfaultfd
1583 SCMP_SYS(userfaultfd),
1584
1585 // Disable perf which could leak a lot of information about the host
1586 SCMP_SYS(perf_event_open),
1587
1588 0,
1589 };
1590 int r = 1;
1591
1592 DEBUG(jail->pakfire, "Applying syscall filter...\n");
1593
1594 // Setup a syscall filter which allows everything by default
1595 scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
1596 if (!ctx) {
1597 ERROR(jail->pakfire, "Could not setup seccomp filter: %m\n");
1598 goto ERROR;
1599 }
1600
1601 // All all syscalls
1602 for (const int* syscall = syscalls; *syscall; syscall++) {
1603 r = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), *syscall, 0);
1604 if (r) {
1605 ERROR(jail->pakfire, "Could not configure syscall %d: %m\n", *syscall);
1606 goto ERROR;
1607 }
1608 }
1609
1610 // Load syscall filter into the kernel
1611 r = seccomp_load(ctx);
1612 if (r) {
1613 ERROR(jail->pakfire, "Could not load syscall filter into the kernel: %m\n");
1614 goto ERROR;
1615 }
1616
1617ERROR:
1618 if (ctx)
1619 seccomp_release(ctx);
1620
1621 return r;
1622}
1623
cc6e2264
MT
1624// Mountpoints
1625
061223f7 1626PAKFIRE_EXPORT int pakfire_jail_bind(struct pakfire_jail* jail,
cc6e2264
MT
1627 const char* source, const char* target, int flags) {
1628 struct pakfire_jail_mountpoint* mp = NULL;
1629 int r;
1630
1631 // Check if there is any space left
1632 if (jail->num_mountpoints >= MAX_MOUNTPOINTS) {
1633 errno = ENOSPC;
1634 return 1;
1635 }
1636
1637 // Check for valid inputs
1638 if (!source || !target) {
1639 errno = EINVAL;
1640 return 1;
1641 }
1642
1643 // Select the next free slot
1644 mp = &jail->mountpoints[jail->num_mountpoints];
1645
1646 // Copy source
1647 r = pakfire_string_set(mp->source, source);
a60955af 1648 if (r) {
cc6e2264 1649 ERROR(jail->pakfire, "Could not copy source: %m\n");
a60955af 1650 return r;
cc6e2264
MT
1651 }
1652
1653 // Copy target
1654 r = pakfire_string_set(mp->target, target);
a60955af 1655 if (r) {
cc6e2264 1656 ERROR(jail->pakfire, "Could not copy target: %m\n");
a60955af 1657 return r;
cc6e2264
MT
1658 }
1659
1660 // Copy flags
1661 mp->flags = flags;
1662
1663 // Increment counter
1664 jail->num_mountpoints++;
1665
1666 return 0;
1667}
1668
7bdf1d8e
MT
1669static int pakfire_jail_mount_networking(struct pakfire_jail* jail) {
1670 int r;
1671
1672 const char* paths[] = {
1673 "/etc/hosts",
1674 "/etc/resolv.conf",
1675 NULL,
1676 };
1677
1678 // Bind-mount all paths read-only
1679 for (const char** path = paths; *path; path++) {
1680 r = pakfire_bind(jail->pakfire, *path, NULL, MS_RDONLY);
693a979a
MT
1681 if (r) {
1682 switch (errno) {
1683 // Ignore if we don't have permission
1684 case EPERM:
1685 continue;
1686
1687 default:
1688 break;
1689 }
7bdf1d8e 1690 return r;
693a979a 1691 }
7bdf1d8e
MT
1692 }
1693
1694 return 0;
1695}
1696
cc6e2264
MT
1697/*
1698 Mounts everything that we require in the new namespace
1699*/
7bdf1d8e 1700static int pakfire_jail_mount(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
cc6e2264 1701 struct pakfire_jail_mountpoint* mp = NULL;
282b732a 1702 int flags = 0;
cc6e2264
MT
1703 int r;
1704
282b732a
MT
1705 // Enable loop devices
1706 if (pakfire_jail_exec_has_flag(ctx, PAKFIRE_JAIL_HAS_LOOP_DEVICES))
1707 flags |= PAKFIRE_MOUNT_LOOP_DEVICES;
1708
cc6e2264 1709 // Mount all default stuff
b2ccb363
MT
1710 r = pakfire_mount_all(jail->pakfire, PAKFIRE_MNTNS_OUTER, flags);
1711 if (r)
1712 return r;
1713
1714 // Populate /dev
1715 r = pakfire_populate_dev(jail->pakfire, flags);
1716 if (r)
1717 return r;
1718
1719 // Mount the interpreter (if needed)
1720 r = pakfire_mount_interpreter(jail->pakfire);
cc6e2264
MT
1721 if (r)
1722 return r;
1723
7bdf1d8e
MT
1724 // Mount networking stuff
1725 if (pakfire_jail_exec_has_flag(ctx, PAKFIRE_JAIL_HAS_NETWORKING)) {
1726 r = pakfire_jail_mount_networking(jail);
1727 if (r)
1728 return r;
1729 }
1730
cc6e2264
MT
1731 // Mount all custom stuff
1732 for (unsigned int i = 0; i < jail->num_mountpoints; i++) {
1733 // Fetch mountpoint
1734 mp = &jail->mountpoints[i];
1735
1736 // Mount it
1737 r = pakfire_bind(jail->pakfire, mp->source, mp->target, mp->flags);
1738 if (r)
1739 return r;
1740 }
1741
cc6e2264
MT
1742 return 0;
1743}
1744
fec79a33
MT
1745// Networking
1746
1747static int pakfire_jail_setup_loopback(struct pakfire_jail* jail) {
1748 struct nl_sock* nl = NULL;
1749 struct nl_cache* cache = NULL;
1750 struct rtnl_link* link = NULL;
1751 struct rtnl_link* change = NULL;
1752 int r;
1753
1754 DEBUG(jail->pakfire, "Setting up loopback...\n");
1755
1756 // Allocate a netlink socket
1757 nl = nl_socket_alloc();
1758 if (!nl) {
1759 ERROR(jail->pakfire, "Could not allocate a netlink socket: %m\n");
1760 r = 1;
1761 goto ERROR;
1762 }
1763
1764 // Connect the socket
1765 r = nl_connect(nl, NETLINK_ROUTE);
1766 if (r) {
1767 ERROR(jail->pakfire, "Could not connect netlink socket: %s\n", nl_geterror(r));
1768 goto ERROR;
1769 }
1770
1771 // Allocate the netlink cache
1772 r = rtnl_link_alloc_cache(nl, AF_UNSPEC, &cache);
1773 if (r < 0) {
1774 ERROR(jail->pakfire, "Unable to allocate netlink cache: %s\n", nl_geterror(r));
1775 goto ERROR;
1776 }
1777
1778 // Fetch loopback interface
1779 link = rtnl_link_get_by_name(cache, "lo");
1780 if (!link) {
1781 ERROR(jail->pakfire, "Could not find lo interface. Ignoring.\n");
1782 r = 0;
1783 goto ERROR;
1784 }
1785
1786 // Allocate a new link
1787 change = rtnl_link_alloc();
1788 if (!change) {
1789 ERROR(jail->pakfire, "Could not allocate change link\n");
1790 r = 1;
1791 goto ERROR;
1792 }
1793
1794 // Set the link to UP
1795 rtnl_link_set_flags(change, IFF_UP);
1796
1797 // Apply any changes
1798 r = rtnl_link_change(nl, link, change, 0);
1799 if (r) {
1800 ERROR(jail->pakfire, "Unable to activate loopback: %s\n", nl_geterror(r));
1801 goto ERROR;
1802 }
1803
1804 // Success
1805 r = 0;
1806
1807ERROR:
1808 if (nl)
1809 nl_socket_free(nl);
1810
1811 return r;
1812}
1813
679ee2fa
MT
1814// UID/GID Mapping
1815
679ee2fa
MT
1816static int pakfire_jail_setup_uid_mapping(struct pakfire_jail* jail, pid_t pid) {
1817 char path[PATH_MAX];
1818 int r;
1819
4896e62c
MT
1820 // Skip mapping anything when running on /
1821 if (pakfire_on_root(jail->pakfire))
1822 return 0;
0f7f068b 1823
abe4ee37
MT
1824 // Make path
1825 r = pakfire_string_format(path, "/proc/%d/uid_map", pid);
1826 if (r)
1827 return r;
1828
1829 // Fetch UID
1830 const uid_t uid = pakfire_uid(jail->pakfire);
1831
4896e62c 1832 // Fetch SUBUID
a1ff2863 1833 const struct pakfire_subid* subuid = pakfire_subuid(jail->pakfire);
4896e62c
MT
1834 if (!subuid)
1835 return 1;
679ee2fa 1836
abe4ee37 1837 /* When running as root, we will map the entire range.
679ee2fa 1838
abe4ee37
MT
1839 When running as a non-privileged user, we will map the root user inside the jail
1840 to the user's UID outside of the jail, and we will map the rest starting from one.
1841 */
679ee2fa 1842
abe4ee37
MT
1843 // Running as root
1844 if (uid == 0) {
1845 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
1846 "0 %lu %lu\n", subuid->id, subuid->length);
1847 } else {
1848 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
b64888fa 1849 "0 %lu 1\n1 %lu %lu\n", uid, subuid->id, subuid->length);
abe4ee37
MT
1850 }
1851
1852 if (r) {
1853 ERROR(jail->pakfire, "Could not map UIDs: %m\n");
1854 return r;
1855 }
1856
1857 return r;
679ee2fa
MT
1858}
1859
1860static int pakfire_jail_setup_gid_mapping(struct pakfire_jail* jail, pid_t pid) {
1861 char path[PATH_MAX];
1862 int r;
1863
4896e62c
MT
1864 // Skip mapping anything when running on /
1865 if (pakfire_on_root(jail->pakfire))
1866 return 0;
0f7f068b 1867
abe4ee37
MT
1868 // Fetch GID
1869 const gid_t gid = pakfire_gid(jail->pakfire);
1870
4896e62c 1871 // Fetch SUBGID
a1ff2863 1872 const struct pakfire_subid* subgid = pakfire_subgid(jail->pakfire);
4896e62c
MT
1873 if (!subgid)
1874 return 1;
679ee2fa
MT
1875
1876 // Make path
1877 r = pakfire_string_format(path, "/proc/%d/gid_map", pid);
a60955af
MT
1878 if (r)
1879 return r;
679ee2fa 1880
abe4ee37
MT
1881 // Running as root
1882 if (gid == 0) {
1883 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
1884 "0 %lu %lu\n", subgid->id, subgid->length);
1885 } else {
1886 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
b9a1d857 1887 "0 %lu 1\n1 %lu %lu\n", gid, subgid->id, subgid->length);
abe4ee37 1888 }
679ee2fa 1889
abe4ee37
MT
1890 if (r) {
1891 ERROR(jail->pakfire, "Could not map GIDs: %m\n");
1892 return r;
1893 }
1894
1895 return r;
679ee2fa
MT
1896}
1897
78d7488a
MT
1898static int pakfire_jail_setgroups(struct pakfire_jail* jail, pid_t pid) {
1899 char path[PATH_MAX];
fe30bb0a 1900 int r;
78d7488a
MT
1901
1902 // Make path
1903 r = pakfire_string_format(path, "/proc/%d/setgroups", pid);
a60955af
MT
1904 if (r)
1905 return r;
78d7488a 1906
fe30bb0a 1907 r = pakfire_file_write(jail->pakfire, path, 0, 0, 0, "deny\n");
78d7488a 1908 if (r) {
fe30bb0a
MT
1909 CTX_ERROR(jail->ctx, "Could not set setgroups to deny: %s\n", strerror(errno));
1910 r = -errno;
78d7488a
MT
1911 }
1912
78d7488a
MT
1913 return r;
1914}
1915
43dc0e16 1916static int pakfire_jail_send_signal(struct pakfire_jail* jail, int fd) {
743f449e
MT
1917 const uint64_t val = 1;
1918 int r = 0;
43dc0e16
MT
1919
1920 DEBUG(jail->pakfire, "Sending signal...\n");
1921
743f449e 1922 // Write to the file descriptor
a87c52e2
MT
1923 r = eventfd_write(fd, val);
1924 if (r < 0) {
1925 ERROR(jail->pakfire, "Could not send signal: %s\n", strerror(errno));
1926 r = -errno;
743f449e
MT
1927 }
1928
1929 // Close the file descriptor
43dc0e16
MT
1930 close(fd);
1931
743f449e 1932 return r;
43dc0e16
MT
1933}
1934
1935static int pakfire_jail_wait_for_signal(struct pakfire_jail* jail, int fd) {
743f449e
MT
1936 uint64_t val = 0;
1937 int r = 0;
43dc0e16
MT
1938
1939 DEBUG(jail->pakfire, "Waiting for signal...\n");
1940
a87c52e2
MT
1941 r = eventfd_read(fd, &val);
1942 if (r < 0) {
1943 ERROR(jail->pakfire, "Error waiting for signal: %s\n", strerror(errno));
1944 r = -errno;
743f449e
MT
1945 }
1946
1947 // Close the file descriptor
43dc0e16
MT
1948 close(fd);
1949
743f449e 1950 return r;
43dc0e16
MT
1951}
1952
f949feac
MT
1953/*
1954 Performs the initialisation that needs to happen in the parent part
1955*/
1956static int pakfire_jail_parent(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
1957 int r;
1958
1959 // Setup UID mapping
1960 r = pakfire_jail_setup_uid_mapping(jail, ctx->pid);
1961 if (r)
1962 return r;
1963
1964 // Write "deny" to /proc/PID/setgroups
1965 r = pakfire_jail_setgroups(jail, ctx->pid);
1966 if (r)
1967 return r;
1968
1969 // Setup GID mapping
1970 r = pakfire_jail_setup_gid_mapping(jail, ctx->pid);
1971 if (r)
1972 return r;
1973
1974 // Parent has finished initialisation
1975 DEBUG(jail->pakfire, "Parent has finished initialization\n");
1976
1977 // Send signal to client
1978 r = pakfire_jail_send_signal(jail, ctx->completed_fd);
1979 if (r)
1980 return r;
1981
1982 return 0;
1983}
1984
14df7388
MT
1985static int pakfire_jail_switch_root(struct pakfire_jail* jail, const char* root) {
1986 int r;
1987
1988 // Change to the new root
1989 r = chdir(root);
1990 if (r) {
1991 ERROR(jail->pakfire, "chdir(%s) failed: %m\n", root);
1992 return r;
1993 }
1994
1995 // Switch Root!
1996 r = pivot_root(".", ".");
1997 if (r) {
1998 ERROR(jail->pakfire, "Failed changing into the new root directory %s: %m\n", root);
1999 return r;
2000 }
2001
2002 // Umount the old root
2003 r = umount2(".", MNT_DETACH);
2004 if (r) {
2005 ERROR(jail->pakfire, "Could not umount the old root filesystem: %m\n");
2006 return r;
2007 }
2008
2009 return 0;
2010}
2011
374e6268
MT
2012static int pakfire_jail_open_pty(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
2013 int r;
2014
2015 // Allocate a new PTY
c40722e3
MT
2016 ctx->pty.master.fd = posix_openpt(O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
2017 if (ctx->pty.master.fd < 0)
374e6268
MT
2018 return -errno;
2019
2020 // Fetch the path
c40722e3 2021 r = ptsname_r(ctx->pty.master.fd, ctx->pty.console, sizeof(ctx->pty.console));
374e6268
MT
2022 if (r)
2023 return -r;
2024
c40722e3 2025 CTX_DEBUG(jail->ctx, "Allocated console at %s (%d)\n", ctx->pty.console, ctx->pty.master.fd);
374e6268 2026
c15801c7
MT
2027 // Unlock the master device
2028 r = unlockpt(ctx->pty.master.fd);
2029 if (r) {
2030 CTX_ERROR(jail->ctx, "Could not unlock the PTY: %s\n", strerror(errno));
2031 return -errno;
2032 }
2033
374e6268 2034 // Create a symlink
c15801c7 2035 r = pakfire_symlink(jail->ctx, ctx->pty.console, "/dev/console");
374e6268
MT
2036 if (r)
2037 return r;
2038
2039 return r;
2040}
374e6268 2041
c15801c7
MT
2042static int pakfire_jail_setup_terminal(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
2043 int fd;
2044 int r;
2045
2046 // Open a new terminal
2047 fd = open("/dev/console", O_RDWR|O_NOCTTY);
2048 if (fd < 0) {
2049 CTX_ERROR(jail->ctx, "Failed to open a new terminal: %s\n", strerror(errno));
2050 return -errno;
2051 }
2052
2053 CTX_DEBUG(jail->ctx, "Opened a new terminal %d\n", fd);
2054
2055 // Connect the new terminal to standard input
2056 r = dup2(fd, STDIN_FILENO);
2057 if (r < 0) {
2058 CTX_ERROR(jail->ctx, "Failed to open standard input: %s\n", strerror(errno));
2059 return -errno;
2060 }
2061
2062 // Connect the new terminal to standard output
2063 r = dup2(fd, STDOUT_FILENO);
2064 if (r < 0) {
2065 CTX_ERROR(jail->ctx, "Failed to open standard output: %s\n", strerror(errno));
2066 return -errno;
2067 }
2068
2069 // Connect the new terminal to standard error
2070 r = dup2(fd, STDERR_FILENO);
2071 if (r < 0) {
2072 CTX_ERROR(jail->ctx, "Failed to open standard error: %s\n", strerror(errno));
2073 return -errno;
2074 }
2075
2076 return 0;
2077}
2078
f949feac
MT
2079static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
2080 const char* argv[]) {
43dc0e16
MT
2081 int r;
2082
f949feac
MT
2083 // Redirect any logging to our log pipe
2084 pakfire_ctx_set_log_callback(jail->ctx, pakfire_jail_log_redirect, &ctx->pipes);
0bd84dc1 2085
f949feac
MT
2086 // Fetch my own PID
2087 pid_t pid = getpid();
9b171c6a 2088
f949feac 2089 DEBUG(jail->pakfire, "Launched child process in jail with PID %d\n", pid);
9b171c6a 2090
f949feac
MT
2091 // Wait for the parent to finish initialization
2092 r = pakfire_jail_wait_for_signal(jail, ctx->completed_fd);
43dc0e16
MT
2093 if (r)
2094 return r;
2095
f949feac
MT
2096 // Die with parent
2097 r = prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
2098 if (r) {
2099 ERROR(jail->pakfire, "Could not configure to die with parent: %m\n");
2100 return 126;
2101 }
9b171c6a 2102
90503c53
MT
2103 // Make this process dumpable
2104 r = prctl (PR_SET_DUMPABLE, 1, 0, 0, 0);
2105 if (r) {
f949feac 2106 ERROR(jail->pakfire, "Could not make the process dumpable: %m\n");
90503c53
MT
2107 return 126;
2108 }
2109
2110 // Don't drop any capabilities on setuid()
2111 r = prctl(PR_SET_KEEPCAPS, 1);
2112 if (r) {
f949feac 2113 ERROR(jail->pakfire, "Could not set PR_SET_KEEPCAPS: %m\n");
90503c53
MT
2114 return 126;
2115 }
4f59c39b
MT
2116
2117 // Fetch UID/GID
2118 uid_t uid = getuid();
2119 gid_t gid = getgid();
2120
2121 // Fetch EUID/EGID
2122 uid_t euid = geteuid();
2123 gid_t egid = getegid();
2124
a8a41064
MT
2125 DEBUG(jail->pakfire, " UID: %u (effective %u)\n", uid, euid);
2126 DEBUG(jail->pakfire, " GID: %u (effective %u)\n", gid, egid);
4f59c39b 2127
7f572522
MT
2128 // Log all mountpoints
2129 pakfire_mount_list(jail->ctx);
2130
9b171c6a
MT
2131 // Fail if we are not PID 1
2132 if (pid != 1) {
2133 CTX_ERROR(jail->ctx, "Child process is not PID 1\n");
3a9f6931 2134 return 126;
9b171c6a
MT
2135 }
2136
2137 // Fail if we are not running as root
4f719e21 2138 if (uid || gid || euid || egid) {
4f59c39b 2139 ERROR(jail->pakfire, "Child process is not running as root\n");
3a9f6931 2140 return 126;
4f59c39b
MT
2141 }
2142
92f2120e
MT
2143 const int socket_send = pakfire_jail_get_pipe_to_write(jail, &ctx->socket);
2144
b2ccb363
MT
2145 // Mount all default stuff
2146 r = pakfire_mount_all(jail->pakfire, PAKFIRE_MNTNS_INNER, 0);
2147 if (r)
2148 return 126;
2149
f949feac 2150 const char* root = pakfire_get_path(jail->pakfire);
652f2a99 2151 const char* arch = pakfire_get_effective_arch(jail->pakfire);
4f59c39b 2152
f949feac
MT
2153 // Change mount propagation to slave to receive anything from the parent namespace
2154 r = pakfire_mount_change_propagation(jail->ctx, "/", MS_SLAVE);
2155 if (r)
2156 return r;
2157
2158 // Make root a mountpoint in the new mount namespace
2159 r = pakfire_mount_make_mounpoint(jail->pakfire, root);
2160 if (r)
2161 return r;
2162
2163 // Change mount propagation to private
2164 r = pakfire_mount_change_propagation(jail->ctx, root, MS_PRIVATE);
2165 if (r)
2166 return r;
2167
2168 // Change root (unless root is /)
2169 if (!pakfire_on_root(jail->pakfire)) {
2170 // Mount everything
2171 r = pakfire_jail_mount(jail, ctx);
2172 if (r)
2173 return r;
2174
2175 // chroot()
2176 r = pakfire_jail_switch_root(jail, root);
2177 if (r)
2178 return r;
2179 }
2180
90d92b5c
MT
2181 // Set personality
2182 unsigned long persona = pakfire_arch_personality(arch);
2183 if (persona) {
2184 r = personality(persona);
2185 if (r < 0) {
2186 ERROR(jail->pakfire, "Could not set personality (%x)\n", (unsigned int)persona);
f949feac 2187 return 1;
90d92b5c
MT
2188 }
2189 }
2190
fec79a33
MT
2191 // Setup networking
2192 if (!pakfire_jail_exec_has_flag(ctx, PAKFIRE_JAIL_HAS_NETWORKING)) {
2193 r = pakfire_jail_setup_loopback(jail);
2194 if (r)
2195 return 1;
2196 }
2197
cf440db8
MT
2198 // Set nice level
2199 if (jail->nice) {
2200 DEBUG(jail->pakfire, "Setting nice level to %d\n", jail->nice);
2201
2202 r = setpriority(PRIO_PROCESS, pid, jail->nice);
2203 if (r) {
2204 ERROR(jail->pakfire, "Could not set nice level: %m\n");
2205 return 1;
2206 }
2207 }
2208
92f2120e
MT
2209 // Create a new session
2210 r = setsid();
2211 if (r < 0) {
2212 CTX_ERROR(jail->ctx, "Could not create a new session: %s\n", strerror(errno));
2213 return r;
2214 }
92f2120e
MT
2215
2216 // Allocate a new PTY
2217 r = pakfire_jail_open_pty(jail, ctx);
2218 if (r) {
2219 CTX_ERROR(jail->ctx, "Could not allocate a new PTY: %s\n", strerror(-r));
2220 return r;
2221 }
2222
2223 // Send the PTY master to the parent process
c40722e3 2224 r = pakfire_jail_send_fd(jail, socket_send, ctx->pty.master.fd);
92f2120e
MT
2225 if (r) {
2226 CTX_ERROR(jail->ctx, "Failed sending the PTY master to the parent: %s\n", strerror(-r));
2227 return r;
2228 }
2229
c15801c7
MT
2230 // Setup the terminal
2231 r = pakfire_jail_setup_terminal(jail, ctx);
2232 if (r)
2233 return r;
2234
92f2120e 2235 // Close the master of the PTY
c40722e3
MT
2236 close(ctx->pty.master.fd);
2237 ctx->pty.master.fd = -1;
92f2120e
MT
2238
2239 // Close the socket
2240 close(socket_send);
2241
e33387d3
MT
2242 // Close other end of log pipes
2243 close(ctx->pipes.log_INFO[0]);
2244 close(ctx->pipes.log_ERROR[0]);
2245#ifdef ENABLE_DEBUG
2246 close(ctx->pipes.log_DEBUG[0]);
2247#endif /* ENABLE_DEBUG */
2248
007bc66c
MT
2249 // Reset open file limit (http://0pointer.net/blog/file-descriptor-limits.html)
2250 r = pakfire_rlimit_reset_nofile(jail->pakfire);
2251 if (r)
2252 return r;
2253
e6791c52
MT
2254 // Set capabilities
2255 r = pakfire_jail_set_capabilities(jail);
2256 if (r)
2257 return r;
2258
2259 // Show capabilities
2260 r = pakfire_jail_show_capabilities(jail);
980b15af
MT
2261 if (r)
2262 return r;
2263
739d5b57
MT
2264 // Filter syscalls
2265 r = pakfire_jail_limit_syscalls(jail);
2266 if (r)
2267 return r;
2268
f949feac
MT
2269 DEBUG(jail->pakfire, "Child process initialization done\n");
2270 DEBUG(jail->pakfire, "Launching command:\n");
2015cb92
MT
2271
2272 // Log argv
2273 for (unsigned int i = 0; argv[i]; i++)
f949feac 2274 DEBUG(jail->pakfire, " argv[%u] = %s\n", i, argv[i]);
2015cb92 2275
b3498aeb
MT
2276 // exec() command
2277 r = execvpe(argv[0], (char**)argv, jail->env);
9fa1afb6
MT
2278 if (r < 0) {
2279 // Translate errno into regular exit code
2280 switch (errno) {
2281 case ENOENT:
2282 // Ignore if the command doesn't exist
2283 if (ctx->flags & PAKFIRE_JAIL_NOENT_OK)
2284 r = 0;
2285 else
2286 r = 127;
b3498aeb 2287
9fa1afb6 2288 break;
b3498aeb 2289
9fa1afb6
MT
2290 default:
2291 r = 1;
2292 }
2293
f949feac 2294 ERROR(jail->pakfire, "Could not execve(%s): %m\n", argv[0]);
b3498aeb
MT
2295 }
2296
2297 // We should not get here
2298 return r;
0bd84dc1
MT
2299}
2300
9f50bf71 2301// Run a command in the jail
04d3c185 2302static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[], int flags) {
f949feac 2303 int exit = -1;
0bd84dc1
MT
2304 int r;
2305
b3498aeb
MT
2306 // Check if argv is valid
2307 if (!argv || !argv[0]) {
2308 errno = EINVAL;
2309 return -1;
2310 }
2311
616f1fca
MT
2312 // Initialize context for this call
2313 struct pakfire_jail_exec ctx = {
9fa1afb6 2314 .flags = flags,
7bdf1d8e 2315
8f49d254
MT
2316 .socket = { -1, -1 },
2317
616f1fca 2318 .pipes = {
4c3bab92
MT
2319 .log_INFO = { -1, -1 },
2320 .log_ERROR = { -1, -1 },
6ac51607 2321#ifdef ENABLE_DEBUG
4c3bab92 2322 .log_DEBUG = { -1, -1 },
6ac51607 2323#endif /* ENABLE_DEBUG */
2015cb92
MT
2324 },
2325
1a7eb888
MT
2326 .buffers = {
2327 .log_INFO = {
2328 .priority = LOG_INFO,
2329 },
2330
2331 .log_ERROR = {
2332 .priority = LOG_ERR,
2333 },
2334
2335#ifdef ENABLE_DEBUG
2336 .log_DEBUG = {
2337 .priority = LOG_DEBUG,
2338 },
2339#endif /* ENABLE_DEBUG */
616f1fca 2340 },
d2eaf8dc 2341
f949feac 2342 .pidfd = -1,
92f2120e 2343
c40722e3
MT
2344 // PTY
2345 .pty = {
2346 .master = {
2347 .fd = -1,
2348 },
2349 .stdin = {
2350 .fd = -1,
2351 },
2352 .stdout = {
2353 .fd = -1,
2354 },
2355 },
616f1fca
MT
2356 };
2357
0bd84dc1
MT
2358 DEBUG(jail->pakfire, "Executing jail...\n");
2359
7bdf1d8e 2360 // Enable networking in interactive mode
1190bb5f 2361 if (ctx.flags & PAKFIRE_JAIL_PTY_FORWARDING)
7bdf1d8e
MT
2362 ctx.flags |= PAKFIRE_JAIL_HAS_NETWORKING;
2363
43dc0e16
MT
2364 /*
2365 Setup a file descriptor which can be used to notify the client that the parent
2366 has completed configuration.
2367 */
f7d240a7
MT
2368 ctx.completed_fd = eventfd(0, EFD_CLOEXEC);
2369 if (ctx.completed_fd < 0) {
43dc0e16
MT
2370 ERROR(jail->pakfire, "eventfd() failed: %m\n");
2371 return -1;
2372 }
2373
8f49d254
MT
2374 // Create a UNIX domain socket
2375 r = socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ctx.socket);
2376 if (r < 0) {
2377 CTX_ERROR(jail->ctx, "Could not create UNIX socket: %s\n", strerror(errno));
2378 r = -errno;
2379 goto ERROR;
2380 }
2381
e33387d3
MT
2382 // Setup pipes for logging
2383 // INFO
2384 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_INFO, O_CLOEXEC);
2385 if (r)
2386 goto ERROR;
2387
2388 // ERROR
2389 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_ERROR, O_CLOEXEC);
2390 if (r)
2391 goto ERROR;
2392
2393#ifdef ENABLE_DEBUG
2394 // DEBUG
2395 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_DEBUG, O_CLOEXEC);
2396 if (r)
2397 goto ERROR;
2398#endif /* ENABLE_DEBUG */
2399
f949feac
MT
2400 // Configure child process
2401 struct clone_args args = {
2402 .flags =
2403 CLONE_NEWCGROUP |
2404 CLONE_NEWIPC |
2405 CLONE_NEWNS |
2406 CLONE_NEWPID |
2407 CLONE_NEWTIME |
2408 CLONE_NEWUSER |
2409 CLONE_NEWUTS |
2410 CLONE_PIDFD,
2411 .exit_signal = SIGCHLD,
2412 .pidfd = (long long unsigned int)&ctx.pidfd,
2413 };
2414
aca565fc 2415 // Launch the process in a cgroup that is a leaf of the configured cgroup
02fd4f8b 2416 if (jail->cgroup) {
f949feac
MT
2417 args.flags |= CLONE_INTO_CGROUP;
2418
ae5201c5
MT
2419 // Fetch our UUID
2420 const char* uuid = pakfire_jail_uuid(jail);
aca565fc
MT
2421
2422 // Create a temporary cgroup
ae5201c5 2423 r = pakfire_cgroup_child(&ctx.cgroup, jail->cgroup, uuid, 0);
aca565fc
MT
2424 if (r) {
2425 ERROR(jail->pakfire, "Could not create cgroup for jail: %m\n");
2426 goto ERROR;
2427 }
7bdf1d8e 2428
f949feac
MT
2429 // Clone into this cgroup
2430 args.cgroup = pakfire_cgroup_fd(ctx.cgroup);
2431 }
9b171c6a 2432
f949feac
MT
2433 // Setup networking
2434 if (!pakfire_jail_exec_has_flag(&ctx, PAKFIRE_JAIL_HAS_NETWORKING)) {
2435 args.flags |= CLONE_NEWNET;
2436 }
9b171c6a 2437
f949feac
MT
2438 // Fork this process
2439 ctx.pid = clone3(&args, sizeof(args));
2440 if (ctx.pid < 0) {
2441 ERROR(jail->pakfire, "Could not clone: %m\n");
2442 return -1;
0bd84dc1
MT
2443
2444 // Child process
f949feac
MT
2445 } else if (ctx.pid == 0) {
2446 r = pakfire_jail_child(jail, &ctx, argv);
0bd84dc1
MT
2447 _exit(r);
2448 }
2449
679ee2fa 2450 // Parent process
f949feac
MT
2451 r = pakfire_jail_parent(jail, &ctx);
2452 if (r)
2453 goto ERROR;
2454
2455 DEBUG(jail->pakfire, "Waiting for PID %d to finish its work\n", ctx.pid);
2456
2457 // Read output of the child process
d853213d
MT
2458 r = pakfire_jail_wait(jail, &ctx);
2459 if (r)
2460 goto ERROR;
0bd84dc1 2461
f949feac
MT
2462 // Handle exit status
2463 switch (ctx.status.si_code) {
2464 case CLD_EXITED:
2465 DEBUG(jail->pakfire, "The child process exited with code %d\n",
2466 ctx.status.si_status);
2467
2468 // Pass exit code
2469 exit = ctx.status.si_status;
2470 break;
2471
2472 case CLD_KILLED:
2473 ERROR(jail->pakfire, "The child process was killed\n");
2474 exit = 139;
2475 break;
2476
2477 case CLD_DUMPED:
2478 ERROR(jail->pakfire, "The child process terminated abnormally\n");
2479 break;
2480
2481 // Log anything else
2482 default:
2483 ERROR(jail->pakfire, "Unknown child exit code: %d\n", ctx.status.si_code);
2484 break;
2485 }
2486
679ee2fa 2487ERROR:
aca565fc
MT
2488 // Destroy the temporary cgroup (if any)
2489 if (ctx.cgroup) {
6b7cf275 2490 // Read cgroup stats
ec64b312
MT
2491 pakfire_cgroup_stat(ctx.cgroup, &ctx.cgroup_stats);
2492 pakfire_cgroup_stat_dump(ctx.cgroup, &ctx.cgroup_stats);
aca565fc
MT
2493 pakfire_cgroup_destroy(ctx.cgroup);
2494 pakfire_cgroup_unref(ctx.cgroup);
2495 }
2496
616f1fca 2497 // Close any file descriptors
f949feac
MT
2498 if (ctx.pidfd >= 0)
2499 close(ctx.pidfd);
c40722e3
MT
2500 if (ctx.pty.master.fd >= 0)
2501 close(ctx.pty.master.fd);
e33387d3
MT
2502 pakfire_jail_close_pipe(jail, ctx.pipes.log_INFO);
2503 pakfire_jail_close_pipe(jail, ctx.pipes.log_ERROR);
6ac51607 2504#ifdef ENABLE_DEBUG
e33387d3 2505 pakfire_jail_close_pipe(jail, ctx.pipes.log_DEBUG);
6ac51607 2506#endif /* ENABLE_DEBUG */
8f49d254 2507 pakfire_jail_close_pipe(jail, ctx.socket);
616f1fca 2508
f949feac 2509 return exit;
9f50bf71 2510}
a45ed6b0 2511
ccdd2e95 2512PAKFIRE_EXPORT int pakfire_jail_exec(
2015cb92
MT
2513 struct pakfire_jail* jail,
2514 const char* argv[],
04d3c185
MT
2515 int flags) {
2516 return __pakfire_jail_exec(jail, argv, flags);
2015cb92
MT
2517}
2518
db4f234f 2519static int pakfire_jail_exec_interactive(
9fa1afb6 2520 struct pakfire_jail* jail, const char* argv[], int flags) {
db4f234f
MT
2521 int r;
2522
1190bb5f
MT
2523 flags |= PAKFIRE_JAIL_PTY_FORWARDING;
2524
db4f234f
MT
2525 // Setup interactive stuff
2526 r = pakfire_jail_setup_interactive_env(jail);
2527 if (r)
2528 return r;
2529
04d3c185 2530 return __pakfire_jail_exec(jail, argv, flags);
db4f234f
MT
2531}
2532
ccdd2e95
MT
2533int pakfire_jail_exec_script(struct pakfire_jail* jail,
2534 const char* script,
2535 const size_t size,
04d3c185 2536 const char* args[]) {
a45ed6b0
MT
2537 char path[PATH_MAX];
2538 const char** argv = NULL;
35291cb7 2539 FILE* f = NULL;
a45ed6b0
MT
2540 int r;
2541
2542 const char* root = pakfire_get_path(jail->pakfire);
2543
2544 // Write the scriptlet to disk
819232d6 2545 r = pakfire_path_append(path, root, PAKFIRE_TMP_DIR "/pakfire-script.XXXXXX");
56796f84 2546 if (r)
a45ed6b0
MT
2547 goto ERROR;
2548
35291cb7
MT
2549 // Create a temporary file
2550 f = pakfire_mktemp(path, 0700);
2551 if (!f) {
2552 ERROR(jail->pakfire, "Could not create temporary file: %m\n");
a45ed6b0
MT
2553 goto ERROR;
2554 }
2555
2556 DEBUG(jail->pakfire, "Writing script to %s:\n%.*s\n", path, (int)size, script);
2557
2558 // Write data
35291cb7
MT
2559 r = fprintf(f, "%s", script);
2560 if (r < 0) {
a45ed6b0 2561 ERROR(jail->pakfire, "Could not write script to file %s: %m\n", path);
a45ed6b0
MT
2562 goto ERROR;
2563 }
2564
2565 // Close file
35291cb7 2566 r = fclose(f);
a45ed6b0
MT
2567 if (r) {
2568 ERROR(jail->pakfire, "Could not close script file %s: %m\n", path);
a45ed6b0
MT
2569 goto ERROR;
2570 }
2571
35291cb7
MT
2572 f = NULL;
2573
a45ed6b0
MT
2574 // Count how many arguments were passed
2575 unsigned int argc = 1;
2576 if (args) {
2577 for (const char** arg = args; *arg; arg++)
2578 argc++;
2579 }
2580
2581 argv = calloc(argc + 1, sizeof(*argv));
2582 if (!argv) {
2583 ERROR(jail->pakfire, "Could not allocate argv: %m\n");
2584 goto ERROR;
2585 }
2586
2587 // Set command
2588 argv[0] = (root) ? pakfire_path_relpath(root, path) : path;
2589
2590 // Copy args
2591 for (unsigned int i = 1; i < argc; i++)
2592 argv[i] = args[i-1];
2593
2594 // Run the script
04d3c185 2595 r = pakfire_jail_exec(jail, argv, 0);
a45ed6b0
MT
2596
2597ERROR:
2598 if (argv)
2599 free(argv);
35291cb7
MT
2600 if (f)
2601 fclose(f);
a45ed6b0
MT
2602
2603 // Remove script from disk
2604 if (*path)
2605 unlink(path);
2606
2607 return r;
2608}
82df3c77
MT
2609
2610/*
2611 A convenience function that creates a new jail, runs the given command and destroys
2612 the jail again.
2613*/
12b9b39f 2614int pakfire_jail_run(struct pakfire* pakfire, const char* argv[], int flags, char** output) {
82df3c77
MT
2615 struct pakfire_jail* jail = NULL;
2616 int r;
2617
2618 // Create a new jail
9fa1afb6 2619 r = pakfire_jail_create(&jail, pakfire);
82df3c77
MT
2620 if (r)
2621 goto ERROR;
2622
1a7eb888
MT
2623 // Set the callback that captures the output
2624 pakfire_jail_set_stdout_callback(jail, pakfire_jail_capture_stdout, output);
2625
82df3c77 2626 // Execute the command
04d3c185 2627 r = pakfire_jail_exec(jail, argv, 0);
82df3c77
MT
2628
2629ERROR:
2630 if (jail)
2631 pakfire_jail_unref(jail);
2632
2633 return r;
2634}
4f688bd8
MT
2635
2636int pakfire_jail_run_script(struct pakfire* pakfire,
49a9babc 2637 const char* script, const size_t length, const char* argv[], int flags) {
4f688bd8
MT
2638 struct pakfire_jail* jail = NULL;
2639 int r;
2640
2641 // Create a new jail
9fa1afb6 2642 r = pakfire_jail_create(&jail, pakfire);
4f688bd8
MT
2643 if (r)
2644 goto ERROR;
2645
2646 // Execute the command
04d3c185 2647 r = pakfire_jail_exec_script(jail, script, length, argv);
4f688bd8
MT
2648
2649ERROR:
2650 if (jail)
2651 pakfire_jail_unref(jail);
2652
2653 return r;
2654}
e43489f7 2655
5f6e42a2 2656int pakfire_jail_shell(struct pakfire_jail* jail) {
42a41fdf
MT
2657 int r;
2658
e43489f7
MT
2659 const char* argv[] = {
2660 "/bin/bash", "--login", NULL,
2661 };
2662
2663 // Execute /bin/bash
42a41fdf
MT
2664 r = pakfire_jail_exec_interactive(jail, argv, 0);
2665
2666 // Raise any errors
2667 if (r < 0)
2668 return r;
2669
2670 // Ignore any return codes from the shell
2671 return 0;
e43489f7
MT
2672}
2673
f7ffbb93 2674static int pakfire_jail_run_if_possible(struct pakfire* pakfire, const char** argv) {
e43489f7 2675 char path[PATH_MAX];
f7ffbb93 2676 int r;
e43489f7 2677
f7ffbb93 2678 r = pakfire_path(pakfire, path, "%s", *argv);
77e26129
MT
2679 if (r)
2680 return r;
e43489f7 2681
f7ffbb93 2682 // Check if the file is executable
e43489f7
MT
2683 r = access(path, X_OK);
2684 if (r) {
f7ffbb93 2685 DEBUG(pakfire, "%s is not executable. Skipping...\n", *argv);
e43489f7
MT
2686 return 0;
2687 }
2688
f7ffbb93
MT
2689 return pakfire_jail_run(pakfire, argv, 0, NULL);
2690}
2691
2692int pakfire_jail_ldconfig(struct pakfire* pakfire) {
e43489f7 2693 const char* argv[] = {
f7ffbb93
MT
2694 "/sbin/ldconfig",
2695 NULL,
e43489f7
MT
2696 };
2697
f7ffbb93
MT
2698 return pakfire_jail_run_if_possible(pakfire, argv);
2699}
2700
2701int pakfire_jail_run_systemd_tmpfiles(struct pakfire* pakfire) {
2702 const char* argv[] = {
2703 "/usr/bin/systemd-tmpfiles",
2704 "--create",
2705 NULL,
2706 };
2707
2708 return pakfire_jail_run_if_possible(pakfire, argv);
e43489f7 2709}