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