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