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