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