]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/jail.c
string: Split format function so that it takes ... and va_list
[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>
980b15af 22#include <linux/capability.h>
d853213d 23#include <linux/fcntl.h>
0bd84dc1 24#include <linux/sched.h>
d853213d 25#include <linux/wait.h>
0bd84dc1
MT
26#include <sched.h>
27#include <signal.h>
32d5f21d 28#include <stdlib.h>
0bd84dc1 29#include <syscall.h>
980b15af 30#include <sys/capability.h>
616f1fca 31#include <sys/epoll.h>
43dc0e16 32#include <sys/eventfd.h>
90d92b5c 33#include <sys/personality.h>
980b15af 34#include <sys/prctl.h>
cf440db8 35#include <sys/resource.h>
0bd84dc1
MT
36#include <sys/types.h>
37#include <sys/wait.h>
32d5f21d 38
739d5b57
MT
39// libseccomp
40#include <seccomp.h>
41
ae5201c5
MT
42// libuuid
43#include <uuid.h>
44
90d92b5c 45#include <pakfire/arch.h>
e3ddb498 46#include <pakfire/cgroup.h>
fd37ccaf 47#include <pakfire/jail.h>
4f59c39b
MT
48#include <pakfire/logging.h>
49#include <pakfire/mount.h>
fd37ccaf 50#include <pakfire/pakfire.h>
6ce56f90 51#include <pakfire/private.h>
4896e62c 52#include <pakfire/pwd.h>
d973a13d 53#include <pakfire/string.h>
32d5f21d
MT
54#include <pakfire/util.h>
55
616f1fca
MT
56#define BUFFER_SIZE 1024 * 64
57#define ENVIRON_SIZE 128
58#define EPOLL_MAX_EVENTS 2
cc6e2264 59#define MAX_MOUNTPOINTS 8
fd37ccaf 60
d5bc8fe0
MT
61// The default environment that will be set for every command
62static const struct environ {
63 const char* key;
64 const char* val;
65} ENV[] = {
66 { "LANG", "en_US.utf-8" },
67 { "TERM", "vt100" },
68 { NULL, NULL },
69};
70
cc6e2264
MT
71struct pakfire_jail_mountpoint {
72 char source[PATH_MAX];
73 char target[PATH_MAX];
74 int flags;
75};
76
fd37ccaf
MT
77struct pakfire_jail {
78 struct pakfire* pakfire;
79 int nrefs;
32d5f21d 80
ae5201c5
MT
81 // A unique ID for each jail
82 uuid_t uuid;
83 char __uuid[UUID_STR_LEN];
84
d639929b
MT
85 // Flags
86 int flags;
87
cf440db8
MT
88 // Resource Limits
89 int nice;
90
15503538
MT
91 // CGroup
92 struct pakfire_cgroup* cgroup;
93
32d5f21d
MT
94 // Environment
95 char* env[ENVIRON_SIZE];
616f1fca
MT
96
97 // Logging
98 pakfire_jail_log_callback log_callback;
99 void* log_data;
cc6e2264
MT
100
101 // Mountpoints
102 struct pakfire_jail_mountpoint mountpoints[MAX_MOUNTPOINTS];
103 unsigned int num_mountpoints;
616f1fca
MT
104};
105
106struct pakfire_log_buffer {
107 char data[BUFFER_SIZE];
108 size_t used;
109};
110
111struct pakfire_jail_exec {
112 // PID (of the child)
113 pid_t pid;
d853213d 114 int pidfd;
616f1fca 115
d853213d
MT
116 // Process status (from waitid)
117 siginfo_t status;
616f1fca 118
f7d240a7
MT
119 // FD to notify the client that the parent has finished initialization
120 int completed_fd;
121
616f1fca 122 // Log pipes
e33387d3 123 struct pakfire_jail_pipes {
616f1fca
MT
124 int stdout[2];
125 int stderr[2];
e33387d3
MT
126
127 // Logging
128 int log_INFO[2];
129 int log_ERROR[2];
130 int log_DEBUG[2];
616f1fca
MT
131 } pipes;
132
133 // Log buffers
e33387d3 134 struct pakfire_jail_buffers {
616f1fca
MT
135 struct pakfire_log_buffer stdout;
136 struct pakfire_log_buffer stderr;
e33387d3
MT
137
138 // Logging
139 struct pakfire_log_buffer log_INFO;
140 struct pakfire_log_buffer log_ERROR;
141 struct pakfire_log_buffer log_DEBUG;
616f1fca 142 } buffers;
aca565fc
MT
143
144 struct pakfire_cgroup* cgroup;
6b7cf275 145 struct pakfire_cgroup_stats cgroup_stats;
fd37ccaf
MT
146};
147
0bd84dc1
MT
148static int clone3(struct clone_args* args, size_t size) {
149 return syscall(__NR_clone3, args, size);
150}
151
d5bc8fe0
MT
152static void pakfire_jail_free(struct pakfire_jail* jail) {
153 DEBUG(jail->pakfire, "Freeing jail at %p\n", jail);
154
155 // Free environment
156 for (unsigned int i = 0; jail->env[i]; i++)
157 free(jail->env[i]);
158
d34b1e00
MT
159 if (jail->cgroup)
160 pakfire_cgroup_unref(jail->cgroup);
161
d5bc8fe0
MT
162 pakfire_unref(jail->pakfire);
163 free(jail);
164}
165
e33387d3
MT
166/*
167 Passes any log messages on to the default pakfire log callback
168*/
fed41508
MT
169static int pakfire_jail_default_log_callback(struct pakfire* pakfire, void* data,
170 int priority, const char* line, size_t length) {
171 switch (priority) {
172 case LOG_INFO:
173 INFO(pakfire, "%s", line);
174 break;
175
176 case LOG_ERR:
177 ERROR(pakfire, "%s", line);
178 break;
e33387d3
MT
179
180#ifdef ENABLE_DEBUG
181 case LOG_DEBUG:
182 DEBUG(pakfire, "%s", line);
183 break;
184#endif
fed41508
MT
185 }
186
187 return 0;
188}
189
00ba1d9a
MT
190static int pakfire_jail_setup_interactive_env(struct pakfire_jail* jail) {
191 // Set PS1
192 int r = pakfire_jail_set_env(jail, "PS1", "pakfire-jail \\w> ");
193 if (r)
194 return r;
195
196 // Copy TERM
197 char* TERM = secure_getenv("TERM");
198 if (TERM) {
199 r = pakfire_jail_set_env(jail, "TERM", TERM);
200 if (r)
201 return r;
202 }
203
204 // Copy LANG
205 char* LANG = secure_getenv("LANG");
206 if (LANG) {
207 r = pakfire_jail_set_env(jail, "LANG", LANG);
208 if (r)
209 return r;
210 }
211
212 return 0;
213}
214
6ce56f90
MT
215PAKFIRE_EXPORT int pakfire_jail_create(struct pakfire_jail** jail,
216 struct pakfire* pakfire, int flags) {
d5bc8fe0
MT
217 int r;
218
219 // Allocate a new jail
fd37ccaf
MT
220 struct pakfire_jail* j = calloc(1, sizeof(*j));
221 if (!j)
222 return 1;
223
224 // Reference Pakfire
225 j->pakfire = pakfire_ref(pakfire);
226
227 // Initialize reference counter
228 j->nrefs = 1;
229
d639929b
MT
230 // Store flags
231 j->flags = flags;
232
ae5201c5
MT
233 // Generate a random UUID
234 uuid_generate_random(j->uuid);
235
84bd7655
MT
236 DEBUG(j->pakfire, "Allocated new jail at %p\n", j);
237
fed41508
MT
238 // Set default log callback
239 r = pakfire_jail_set_log_callback(j, pakfire_jail_default_log_callback, NULL);
240 if (r)
241 goto ERROR;
242
d5bc8fe0
MT
243 // Set default environment
244 for (const struct environ* e = ENV; e->key; e++) {
245 r = pakfire_jail_set_env(j, e->key, e->val);
246 if (r)
247 goto ERROR;
248 }
249
fd37ccaf
MT
250 // Done
251 *jail = j;
252 return 0;
84bd7655 253
d5bc8fe0
MT
254ERROR:
255 pakfire_jail_free(j);
32d5f21d 256
d5bc8fe0 257 return r;
fd37ccaf
MT
258}
259
6ce56f90 260PAKFIRE_EXPORT struct pakfire_jail* pakfire_jail_ref(struct pakfire_jail* jail) {
fd37ccaf
MT
261 ++jail->nrefs;
262
263 return jail;
264}
265
6ce56f90 266PAKFIRE_EXPORT struct pakfire_jail* pakfire_jail_unref(struct pakfire_jail* jail) {
fd37ccaf
MT
267 if (--jail->nrefs > 0)
268 return jail;
269
270 pakfire_jail_free(jail);
271 return NULL;
272}
32d5f21d 273
ae5201c5 274static const char* pakfire_jail_uuid(struct pakfire_jail* jail) {
8b2cc8ae 275 if (!*jail->__uuid)
ae5201c5
MT
276 uuid_unparse_lower(jail->uuid, jail->__uuid);
277
278 return jail->__uuid;
279}
280
cf440db8
MT
281// Resource Limits
282
283PAKFIRE_EXPORT int pakfire_jail_nice(struct pakfire_jail* jail, int nice) {
284 // Check if nice level is in range
285 if (nice < -19 || nice > 20) {
286 errno = EINVAL;
287 return 1;
288 }
289
290 // Store nice level
291 jail->nice = nice;
292
293 return 0;
294}
295
15503538
MT
296int pakfire_jail_set_cgroup(struct pakfire_jail* jail, struct pakfire_cgroup* cgroup) {
297 // Free any previous cgroup
298 if (jail->cgroup) {
299 pakfire_cgroup_unref(jail->cgroup);
300 jail->cgroup = NULL;
301 }
302
303 // Set any new cgroup
304 if (cgroup) {
305 DEBUG(jail->pakfire, "Setting cgroup %p\n", cgroup);
306
307 jail->cgroup = pakfire_cgroup_ref(cgroup);
308 }
309
310 // Done
311 return 0;
312}
313
32d5f21d
MT
314// Environment
315
316// Returns the length of the environment
317static unsigned int pakfire_jail_env_length(struct pakfire_jail* jail) {
318 unsigned int i = 0;
319
320 // Count everything in the environment
321 for (char** e = jail->env; *e; e++)
322 i++;
323
324 return i;
325}
326
327// Finds an existing environment variable and returns its index or -1 if not found
328static int pakfire_jail_find_env(struct pakfire_jail* jail, const char* key) {
329 if (!key) {
330 errno = EINVAL;
331 return -1;
332 }
333
334 char buffer[strlen(key) + 2];
335 pakfire_string_format(buffer, "%s=", key);
336
337 for (unsigned int i = 0; jail->env[i]; i++) {
338 if (pakfire_string_startswith(jail->env[i], buffer))
339 return i;
340 }
341
342 // Nothing found
343 return -1;
344}
345
346// Returns the value of an environment variable or NULL
6ce56f90
MT
347PAKFIRE_EXPORT const char* pakfire_jail_get_env(struct pakfire_jail* jail,
348 const char* key) {
32d5f21d
MT
349 int i = pakfire_jail_find_env(jail, key);
350 if (i < 0)
351 return NULL;
352
353 return jail->env[i] + strlen(key) + 1;
354}
355
356// Sets an environment variable
6ce56f90
MT
357PAKFIRE_EXPORT int pakfire_jail_set_env(struct pakfire_jail* jail,
358 const char* key, const char* value) {
32d5f21d
MT
359 // Find the index where to write this value to
360 int i = pakfire_jail_find_env(jail, key);
361 if (i < 0)
362 i = pakfire_jail_env_length(jail);
363
364 // Return -ENOSPC when the environment is full
365 if (i >= ENVIRON_SIZE) {
366 errno = ENOSPC;
367 return -1;
368 }
369
370 // Free any previous value
371 if (jail->env[i])
372 free(jail->env[i]);
373
374 // Format and set environment variable
375 asprintf(&jail->env[i], "%s=%s", key, value);
376
377 DEBUG(jail->pakfire, "Set environment variable: %s\n", jail->env[i]);
378
379 return 0;
380}
9f50bf71 381
939025e7 382// Imports an environment
6ce56f90 383PAKFIRE_EXPORT int pakfire_jail_import_env(struct pakfire_jail* jail, const char* env[]) {
939025e7
MT
384 if (!env)
385 return 0;
386
387 char* key;
388 char* val;
389 int r;
390
391 // Copy environment variables
392 for (unsigned int i = 0; env[i]; i++) {
393 r = pakfire_string_partition(env[i], "=", &key, &val);
394 if (r)
395 continue;
396
397 // Set value
398 r = pakfire_jail_set_env(jail, key, val);
399
400 if (key)
401 free(key);
402 if (val)
403 free(val);
404
405 // Break on error
406 if (r)
407 return r;
408 }
409
410 return 0;
411}
412
616f1fca
MT
413// Logging
414
6ce56f90 415PAKFIRE_EXPORT int pakfire_jail_set_log_callback(struct pakfire_jail* jail,
dc3e6dd4
MT
416 pakfire_jail_log_callback callback, void* data) {
417 jail->log_callback = callback;
418 jail->log_data = data;
419
420 return 0;
421}
422
e33387d3
MT
423/*
424 This function replaces any logging in the child process.
425
426 All log messages will be sent to the parent process through their respective pipes.
427*/
428static void pakfire_jail_log(void* data, int priority, const char* file,
429 int line, const char* fn, const char* format, va_list args) {
430 struct pakfire_jail_pipes* pipes = (struct pakfire_jail_pipes*)data;
431 int fd;
432
433 switch (priority) {
434 case LOG_INFO:
435 fd = pipes->log_INFO[1];
436 break;
437
438 case LOG_ERR:
439 fd = pipes->log_ERROR[1];
440 break;
441
442#ifdef ENABLE_DEBUG
443 case LOG_DEBUG:
444 fd = pipes->log_DEBUG[1];
445 break;
446#endif /* ENABLE_DEBUG */
447
448 // Ignore any messages of an unknown priority
449 default:
450 return;
451 }
452
453 // Send the log message
454 if (fd)
455 vdprintf(fd, format, args);
456}
457
616f1fca
MT
458static int pakfire_jail_log_buffer_is_full(const struct pakfire_log_buffer* buffer) {
459 return (sizeof(buffer->data) == buffer->used);
460}
461
462/*
463 This function reads as much data as it can from the file descriptor.
464 If it finds a whole line in it, it will send it to the logger and repeat the process.
465 If not newline character is found, it will try to read more data until it finds one.
466*/
467static int pakfire_jail_handle_log(struct pakfire_jail* jail,
e33387d3
MT
468 struct pakfire_jail_exec* ctx, int priority, int fd,
469 struct pakfire_log_buffer* buffer, pakfire_jail_log_callback callback, void* data) {
616f1fca
MT
470 char line[BUFFER_SIZE + 1];
471
472 // Fill up buffer from fd
473 if (buffer->used < sizeof(buffer->data)) {
474 ssize_t bytes_read = read(fd, buffer->data + buffer->used,
475 sizeof(buffer->data) - buffer->used);
476
477 // Handle errors
478 if (bytes_read < 0) {
479 ERROR(jail->pakfire, "Could not read from fd %d: %m\n", fd);
480 return -1;
481 }
482
483 // Update buffer size
484 buffer->used += bytes_read;
485 }
486
487 // See if we have any lines that we can write
488 while (buffer->used) {
489 // Search for the end of the first line
490 char* eol = memchr(buffer->data, '\n', buffer->used);
491
492 // No newline found
493 if (!eol) {
494 // If the buffer is full, we send the content to the logger and try again
495 // This should not happen in practise
496 if (pakfire_jail_log_buffer_is_full(buffer)) {
497 DEBUG(jail->pakfire, "Logging buffer is full. Sending all content\n");
498
499 eol = buffer->data + sizeof(buffer->data) - 1;
500
501 // Otherwise we might have only read parts of the output
502 } else
503 break;
504 }
505
506 // Find the length of the string
507 size_t length = eol - buffer->data + 1;
508
509 // Copy the line into the buffer
510 memcpy(line, buffer->data, length);
511
512 // Terminate the string
513 line[length] = '\0';
514
515 // Log the line
e33387d3
MT
516 if (callback) {
517 int r = callback(jail->pakfire, data, priority, line, length);
616f1fca
MT
518 if (r) {
519 ERROR(jail->pakfire, "The logging callback returned an error: %d\n", r);
520 return r;
521 }
522 }
523
524 // Remove line from buffer
525 memmove(buffer->data, buffer->data + length, buffer->used - length);
526 buffer->used -= length;
527 }
528
529 return 0;
530}
531
195fe455
MT
532static int pakfire_jail_setup_pipe(struct pakfire_jail* jail, int (*fds)[2], const int flags) {
533 int r = pipe2(*fds, flags);
534 if (r < 0) {
535 ERROR(jail->pakfire, "Could not setup pipe: %m\n");
536 return 1;
537 }
538
539 return 0;
540}
541
542static void pakfire_jail_close_pipe(struct pakfire_jail* jail, int fds[2]) {
543 for (unsigned int i = 0; i < 2; i++)
544 if (fds[i])
545 close(fds[i]);
546}
547
e33387d3
MT
548/*
549 This is a convenience function to fetch the reading end of a pipe and
550 closes the write end.
551*/
552static int pakfire_jail_get_pipe(struct pakfire_jail* jail, int (*fds)[2]) {
553 // Give the variables easier names to avoid confusion
554 int* fd_read = &(*fds)[0];
555 int* fd_write = &(*fds)[1];
556
557 // Close the write end of the pipe
558 if (*fd_write) {
559 close(*fd_write);
560 *fd_write = 0;
561 }
562
563 // Return the read end
564 return *fd_read;
565}
566
d853213d 567static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
616f1fca
MT
568 int epollfd = -1;
569 struct epoll_event ev;
570 struct epoll_event events[EPOLL_MAX_EVENTS];
571 int r = 0;
572
573 // Fetch file descriptors from context
e33387d3
MT
574 const int stdout = pakfire_jail_get_pipe(jail, &ctx->pipes.stdout);
575 const int stderr = pakfire_jail_get_pipe(jail, &ctx->pipes.stderr);
d853213d 576 const int pidfd = ctx->pidfd;
616f1fca 577
e33387d3
MT
578 // Logging
579 const int log_INFO = pakfire_jail_get_pipe(jail, &ctx->pipes.log_INFO);
580 const int log_ERROR = pakfire_jail_get_pipe(jail, &ctx->pipes.log_ERROR);
581 const int log_DEBUG = pakfire_jail_get_pipe(jail, &ctx->pipes.log_DEBUG);
7ebfb7cb
MT
582
583 // Make a list of all file descriptors we are interested in
d853213d 584 int fds[] = {
e33387d3 585 stdout, stderr, pidfd, log_INFO, log_ERROR, log_DEBUG,
616f1fca
MT
586 };
587
588 // Setup epoll
589 epollfd = epoll_create1(0);
590 if (epollfd < 0) {
591 ERROR(jail->pakfire, "Could not initialize epoll(): %m\n");
592 r = 1;
d853213d 593 goto ERROR;
616f1fca
MT
594 }
595
e068b964 596 ev.events = EPOLLIN|EPOLLHUP;
616f1fca
MT
597
598 // Turn file descriptors into non-blocking mode and add them to epoll()
3aad8c0d 599 for (unsigned int i = 0; i < sizeof(fds) / sizeof(*fds); i++) {
616f1fca
MT
600 int fd = fds[i];
601
d853213d
MT
602 // Skip fds which were not initialized
603 if (fd <= 0)
604 continue;
605
616f1fca
MT
606 ev.data.fd = fd;
607
608 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
609 ERROR(jail->pakfire, "Could not add file descriptor %d to epoll(): %m\n", fd);
610 r = 1;
d853213d 611 goto ERROR;
616f1fca
MT
612 }
613 }
614
615 int ended = 0;
616
617 // Loop for as long as the process is alive
618 while (!ended) {
616f1fca
MT
619 int num = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, -1);
620 if (num < 1) {
621 // Ignore if epoll_wait() has been interrupted
622 if (errno == EINTR)
623 continue;
624
625 ERROR(jail->pakfire, "epoll_wait() failed: %m\n");
626 r = 1;
627
d853213d 628 goto ERROR;
616f1fca
MT
629 }
630
616f1fca 631 for (int i = 0; i < num; i++) {
e068b964 632 int e = events[i].events;
616f1fca
MT
633 int fd = events[i].data.fd;
634
e33387d3
MT
635 struct pakfire_log_buffer* buffer = NULL;
636 pakfire_jail_log_callback callback = NULL;
637 void* data = NULL;
638 int priority;
639
e068b964
MT
640 // Check if there is any data to be read
641 if (e & EPOLLIN) {
642 // Handle any changes to the PIDFD
643 if (fd == pidfd) {
644 // Call waidid() and store the result
645 r = waitid(P_PIDFD, ctx->pidfd, &ctx->status, WEXITED);
646 if (r) {
647 ERROR(jail->pakfire, "waitid() failed: %m\n");
648 goto ERROR;
649 }
d853213d 650
e068b964
MT
651 // Mark that we have ended so that we will process the remaining
652 // events from epoll() now, but won't restart the outer loop.
653 ended = 1;
654 continue;
d853213d 655
e068b964
MT
656 // Handle logging messages
657 } else if (fd == log_INFO) {
658 buffer = &ctx->buffers.log_INFO;
659 priority = LOG_INFO;
e33387d3 660
e068b964 661 callback = pakfire_jail_default_log_callback;
e33387d3 662
e068b964
MT
663 } else if (fd == log_ERROR) {
664 buffer = &ctx->buffers.log_ERROR;
665 priority = LOG_ERR;
e33387d3 666
e068b964 667 callback = pakfire_jail_default_log_callback;
e33387d3 668
e068b964
MT
669 } else if (fd == log_DEBUG) {
670 buffer = &ctx->buffers.log_DEBUG;
671 priority = LOG_DEBUG;
e33387d3 672
e068b964 673 callback = pakfire_jail_default_log_callback;
e33387d3 674
e068b964
MT
675 // Handle anything from the log pipes
676 } else if (fd == stdout) {
677 buffer = &ctx->buffers.stdout;
678 priority = LOG_INFO;
616f1fca 679
e068b964
MT
680 callback = jail->log_callback;
681 data = jail->log_data;
e33387d3 682
e068b964
MT
683 } else if (fd == stderr) {
684 buffer = &ctx->buffers.stderr;
685 priority = LOG_ERR;
616f1fca 686
e068b964
MT
687 callback = jail->log_callback;
688 data = jail->log_data;
e33387d3 689
e068b964
MT
690 } else {
691 DEBUG(jail->pakfire, "Received invalid file descriptor %d\n", fd);
692 continue;
693 }
694
695 // Handle log event
696 r = pakfire_jail_handle_log(jail, ctx, priority, fd, buffer, callback, data);
697 if (r)
698 goto ERROR;
616f1fca
MT
699 }
700
e068b964
MT
701 // Check if any file descriptors have been closed
702 if (e & EPOLLHUP) {
703 // Remove the file descriptor
704 r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
705 if (r) {
706 ERROR(jail->pakfire, "Could not remove closed file-descriptor %d: %m\n", fd);
707 goto ERROR;
708 }
709 }
616f1fca
MT
710 }
711 }
712
d853213d 713ERROR:
616f1fca
MT
714 if (epollfd > 0)
715 close(epollfd);
716
717 return r;
718}
719
0de6bb30
MT
720static int pakfire_jail_capture_stdout(struct pakfire* pakfire, void* data, int priority,
721 const char* line, size_t length) {
12b9b39f
MT
722 char** output = (char**)data;
723 int r;
0de6bb30 724
12b9b39f 725 // Append everything from stdout to a buffer
0de6bb30 726 if (priority == LOG_INFO) {
12b9b39f
MT
727 r = asprintf(output, "%s%s", (output && *output) ? *output : "", line);
728 if (r < 0)
0de6bb30 729 return 1;
0de6bb30
MT
730 return 0;
731 }
732
733 // Send everything else to the default logger
734 return pakfire_jail_default_log_callback(pakfire, NULL, priority, line, length);
735}
736
980b15af
MT
737// Capabilities
738
739static int pakfire_jail_drop_capabilities(struct pakfire_jail* jail) {
740 const int capabilities[] = {
741 // Deny access to the kernel's audit system
742 CAP_AUDIT_CONTROL,
743 CAP_AUDIT_READ,
744 CAP_AUDIT_WRITE,
745
746 // Deny suspending block devices
747 CAP_BLOCK_SUSPEND,
748
749 // Deny any stuff with BPF
750 CAP_BPF,
751
752 // Deny checkpoint restore
753 CAP_CHECKPOINT_RESTORE,
754
755 // Deny opening files by inode number (open_by_handle_at)
756 CAP_DAC_READ_SEARCH,
757
758 // Deny setting SUID bits
759 CAP_FSETID,
760
761 // Deny locking more memory
762 CAP_IPC_LOCK,
763
764 // Deny modifying any Apparmor/SELinux/SMACK configuration
765 CAP_MAC_ADMIN,
766 CAP_MAC_OVERRIDE,
767
768 // Deny creating any special devices
769 CAP_MKNOD,
770
771 // Deny setting any capabilities
772 CAP_SETFCAP,
773
774 // Deny reading from syslog
775 CAP_SYSLOG,
776
777 // Deny any admin actions (mount, sethostname, ...)
778 CAP_SYS_ADMIN,
779
780 // Deny rebooting the system
781 CAP_SYS_BOOT,
782
783 // Deny loading kernel modules
784 CAP_SYS_MODULE,
785
786 // Deny setting nice level
787 CAP_SYS_NICE,
788
789 // Deny access to /proc/kcore, /dev/mem, /dev/kmem
790 CAP_SYS_RAWIO,
791
792 // Deny circumventing any resource limits
793 CAP_SYS_RESOURCE,
794
795 // Deny setting the system time
796 CAP_SYS_TIME,
797
798 // Deny playing with suspend
799 CAP_WAKE_ALARM,
800
801 0,
802 };
803
804 DEBUG(jail->pakfire, "Dropping capabilities...\n");
805
806 size_t num_caps = 0;
807 int r;
808
809 // Drop any capabilities
810 for (const int* cap = capabilities; *cap; cap++) {
811 r = prctl(PR_CAPBSET_DROP, *cap, 0, 0, 0);
812 if (r) {
813 ERROR(jail->pakfire, "Could not drop capability %d: %m\n", *cap);
814 return r;
815 }
816
817 num_caps++;
818 }
819
820 // Fetch any capabilities
821 cap_t caps = cap_get_proc();
822 if (!caps) {
823 ERROR(jail->pakfire, "Could not read capabilities: %m\n");
824 return 1;
825 }
826
827 /*
828 Set inheritable capabilities
829
830 This ensures that no processes will be able to gain any of the listed
831 capabilities again.
832 */
833 r = cap_set_flag(caps, CAP_INHERITABLE, num_caps, capabilities, CAP_CLEAR);
834 if (r) {
835 ERROR(jail->pakfire, "cap_set_flag() failed: %m\n");
836 goto ERROR;
837 }
838
839 // Restore capabilities
840 r = cap_set_proc(caps);
841 if (r) {
842 ERROR(jail->pakfire, "Could not restore capabilities: %m\n");
843 goto ERROR;
844 }
845
846ERROR:
847 if (caps)
848 cap_free(caps);
849
850 return r;
851}
852
739d5b57
MT
853// Syscall Filter
854
855static int pakfire_jail_limit_syscalls(struct pakfire_jail* jail) {
856 const int syscalls[] = {
857 // The kernel's keyring isn't namespaced
858 SCMP_SYS(keyctl),
859 SCMP_SYS(add_key),
860 SCMP_SYS(request_key),
861
862 // Disable userfaultfd
863 SCMP_SYS(userfaultfd),
864
865 // Disable perf which could leak a lot of information about the host
866 SCMP_SYS(perf_event_open),
867
868 0,
869 };
870 int r = 1;
871
872 DEBUG(jail->pakfire, "Applying syscall filter...\n");
873
874 // Setup a syscall filter which allows everything by default
875 scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
876 if (!ctx) {
877 ERROR(jail->pakfire, "Could not setup seccomp filter: %m\n");
878 goto ERROR;
879 }
880
881 // All all syscalls
882 for (const int* syscall = syscalls; *syscall; syscall++) {
883 r = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), *syscall, 0);
884 if (r) {
885 ERROR(jail->pakfire, "Could not configure syscall %d: %m\n", *syscall);
886 goto ERROR;
887 }
888 }
889
890 // Load syscall filter into the kernel
891 r = seccomp_load(ctx);
892 if (r) {
893 ERROR(jail->pakfire, "Could not load syscall filter into the kernel: %m\n");
894 goto ERROR;
895 }
896
897ERROR:
898 if (ctx)
899 seccomp_release(ctx);
900
901 return r;
902}
903
cc6e2264
MT
904// Mountpoints
905
061223f7 906PAKFIRE_EXPORT int pakfire_jail_bind(struct pakfire_jail* jail,
cc6e2264
MT
907 const char* source, const char* target, int flags) {
908 struct pakfire_jail_mountpoint* mp = NULL;
909 int r;
910
911 // Check if there is any space left
912 if (jail->num_mountpoints >= MAX_MOUNTPOINTS) {
913 errno = ENOSPC;
914 return 1;
915 }
916
917 // Check for valid inputs
918 if (!source || !target) {
919 errno = EINVAL;
920 return 1;
921 }
922
923 // Select the next free slot
924 mp = &jail->mountpoints[jail->num_mountpoints];
925
926 // Copy source
927 r = pakfire_string_set(mp->source, source);
a60955af 928 if (r) {
cc6e2264 929 ERROR(jail->pakfire, "Could not copy source: %m\n");
a60955af 930 return r;
cc6e2264
MT
931 }
932
933 // Copy target
934 r = pakfire_string_set(mp->target, target);
a60955af 935 if (r) {
cc6e2264 936 ERROR(jail->pakfire, "Could not copy target: %m\n");
a60955af 937 return r;
cc6e2264
MT
938 }
939
940 // Copy flags
941 mp->flags = flags;
942
943 // Increment counter
944 jail->num_mountpoints++;
945
946 return 0;
947}
948
949/*
950 Mounts everything that we require in the new namespace
951*/
952static int pakfire_jail_mount(struct pakfire_jail* jail) {
953 struct pakfire_jail_mountpoint* mp = NULL;
954 int r;
955
956 // Mount all default stuff
957 r = pakfire_mount_all(jail->pakfire);
958 if (r)
959 return r;
960
961 // Mount all custom stuff
962 for (unsigned int i = 0; i < jail->num_mountpoints; i++) {
963 // Fetch mountpoint
964 mp = &jail->mountpoints[i];
965
966 // Mount it
967 r = pakfire_bind(jail->pakfire, mp->source, mp->target, mp->flags);
968 if (r)
969 return r;
970 }
971
972 // Log all mountpoints
973 pakfire_mount_list(jail->pakfire);
974
975 return 0;
976}
977
679ee2fa
MT
978// UID/GID Mapping
979
980static int pakfire_jail_write_uidgid_mapping(struct pakfire_jail* jail,
a1ff2863 981 const char* path, const struct pakfire_subid* subid) {
1d4b54ef
MT
982 return pakfire_file_write(jail->pakfire, path, 0, 0, 0,
983 "%d %u %lu\n", 0, subid->id, subid->length);
679ee2fa
MT
984}
985
986static int pakfire_jail_setup_uid_mapping(struct pakfire_jail* jail, pid_t pid) {
987 char path[PATH_MAX];
988 int r;
989
4896e62c
MT
990 // Skip mapping anything when running on /
991 if (pakfire_on_root(jail->pakfire))
992 return 0;
0f7f068b 993
4896e62c 994 // Fetch SUBUID
a1ff2863 995 const struct pakfire_subid* subuid = pakfire_subuid(jail->pakfire);
4896e62c
MT
996 if (!subuid)
997 return 1;
679ee2fa
MT
998
999 // Make path
1000 r = pakfire_string_format(path, "/proc/%d/uid_map", pid);
a60955af
MT
1001 if (r)
1002 return r;
679ee2fa 1003
4896e62c 1004 DEBUG(jail->pakfire, "Mapping UID range (%u - %lu)\n",
a1ff2863 1005 subuid->id, subuid->id + subuid->length);
679ee2fa 1006
a1ff2863 1007 return pakfire_jail_write_uidgid_mapping(jail, path, subuid);
679ee2fa
MT
1008}
1009
1010static int pakfire_jail_setup_gid_mapping(struct pakfire_jail* jail, pid_t pid) {
1011 char path[PATH_MAX];
1012 int r;
1013
4896e62c
MT
1014 // Skip mapping anything when running on /
1015 if (pakfire_on_root(jail->pakfire))
1016 return 0;
0f7f068b 1017
4896e62c 1018 // Fetch SUBGID
a1ff2863 1019 const struct pakfire_subid* subgid = pakfire_subgid(jail->pakfire);
4896e62c
MT
1020 if (!subgid)
1021 return 1;
679ee2fa
MT
1022
1023 // Make path
1024 r = pakfire_string_format(path, "/proc/%d/gid_map", pid);
a60955af
MT
1025 if (r)
1026 return r;
679ee2fa 1027
4896e62c 1028 DEBUG(jail->pakfire, "Mapping GID range (%u - %lu)\n",
a1ff2863 1029 subgid->id, subgid->id + subgid->length);
679ee2fa 1030
a1ff2863 1031 return pakfire_jail_write_uidgid_mapping(jail, path, subgid);
679ee2fa
MT
1032}
1033
78d7488a
MT
1034static int pakfire_jail_setgroups(struct pakfire_jail* jail, pid_t pid) {
1035 char path[PATH_MAX];
1036 int r = 1;
1037
1038 // Make path
1039 r = pakfire_string_format(path, "/proc/%d/setgroups", pid);
a60955af
MT
1040 if (r)
1041 return r;
78d7488a
MT
1042
1043 // Open file for writing
1044 FILE* f = fopen(path, "w");
1045 if (!f) {
1046 ERROR(jail->pakfire, "Could not open %s for writing: %m\n", path);
1047 goto ERROR;
1048 }
1049
1050 // Write content
1051 int bytes_written = fprintf(f, "deny\n");
1052 if (bytes_written <= 0) {
1053 ERROR(jail->pakfire, "Could not write to %s: %m\n", path);
1054 goto ERROR;
1055 }
1056
1057 r = fclose(f);
1058 f = NULL;
1059 if (r) {
1060 ERROR(jail->pakfire, "Could not close %s: %m\n", path);
1061 goto ERROR;
1062 }
1063
1064ERROR:
1065 if (f)
1066 fclose(f);
1067
1068 return r;
1069}
1070
43dc0e16 1071static int pakfire_jail_send_signal(struct pakfire_jail* jail, int fd) {
743f449e
MT
1072 const uint64_t val = 1;
1073 int r = 0;
43dc0e16
MT
1074
1075 DEBUG(jail->pakfire, "Sending signal...\n");
1076
743f449e
MT
1077 // Write to the file descriptor
1078 ssize_t bytes_written = write(fd, &val, sizeof(val));
1079 if (bytes_written < 0 || (size_t)bytes_written < sizeof(val)) {
1080 ERROR(jail->pakfire, "Could not send signal: %m\n");
1081 r = 1;
1082 }
1083
1084 // Close the file descriptor
43dc0e16
MT
1085 close(fd);
1086
743f449e 1087 return r;
43dc0e16
MT
1088}
1089
1090static int pakfire_jail_wait_for_signal(struct pakfire_jail* jail, int fd) {
743f449e
MT
1091 uint64_t val = 0;
1092 int r = 0;
43dc0e16
MT
1093
1094 DEBUG(jail->pakfire, "Waiting for signal...\n");
1095
743f449e
MT
1096 ssize_t bytes_read = read(fd, &val, sizeof(val));
1097 if (bytes_read < 0 || (size_t)bytes_read < sizeof(val)) {
1098 ERROR(jail->pakfire, "Error waiting for signal: %m\n");
1099 r = 1;
1100 }
1101
1102 // Close the file descriptor
43dc0e16
MT
1103 close(fd);
1104
743f449e 1105 return r;
43dc0e16
MT
1106}
1107
679ee2fa
MT
1108/*
1109 Performs the initialisation that needs to happen in the parent part
1110*/
f7d240a7 1111static int pakfire_jail_parent(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
679ee2fa
MT
1112 int r;
1113
d3d3d7b7
MT
1114 // Write "deny" to /proc/PID/setgroups
1115 r = pakfire_jail_setgroups(jail, ctx->pid);
679ee2fa
MT
1116 if (r)
1117 return r;
1118
d3d3d7b7
MT
1119 // Setup UID mapping
1120 r = pakfire_jail_setup_uid_mapping(jail, ctx->pid);
78d7488a
MT
1121 if (r)
1122 return r;
1123
679ee2fa 1124 // Setup GID mapping
616f1fca 1125 r = pakfire_jail_setup_gid_mapping(jail, ctx->pid);
679ee2fa
MT
1126 if (r)
1127 return r;
1128
43dc0e16
MT
1129 // Parent has finished initialisation
1130 DEBUG(jail->pakfire, "Parent has finished initialization\n");
1131
1132 // Send signal to client
f7d240a7 1133 r = pakfire_jail_send_signal(jail, ctx->completed_fd);
43dc0e16
MT
1134 if (r)
1135 return r;
1136
679ee2fa
MT
1137 return 0;
1138}
1139
616f1fca 1140static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
f7d240a7 1141 const char* argv[]) {
43dc0e16
MT
1142 int r;
1143
e33387d3
MT
1144 // Redirect any logging to our log pipe
1145 pakfire_set_log_callback(jail->pakfire, pakfire_jail_log, &ctx->pipes);
0bd84dc1 1146
2a7b5e00
MT
1147 // Die with parent
1148 r = prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
1149 if (r) {
1150 ERROR(jail->pakfire, "Could not configure to die with parent: %m\n");
1151 return 126;
1152 }
1153
cf440db8
MT
1154 // Fetch my own PID
1155 pid_t pid = getpid();
1156
1157 DEBUG(jail->pakfire, "Launched child process in jail with PID %d\n", pid);
0bd84dc1 1158
b324c1f7
MT
1159 // Log argv
1160 for (unsigned int i = 0; argv[i]; i++)
1161 DEBUG(jail->pakfire, " argv[%d] = %s\n", i, argv[i]);
1162
43dc0e16 1163 // Wait for the parent to finish initialization
f7d240a7 1164 r = pakfire_jail_wait_for_signal(jail, ctx->completed_fd);
43dc0e16
MT
1165 if (r)
1166 return r;
1167
4f59c39b
MT
1168 // Perform further initialization
1169
1170 // Fetch UID/GID
1171 uid_t uid = getuid();
1172 gid_t gid = getgid();
1173
1174 // Fetch EUID/EGID
1175 uid_t euid = geteuid();
1176 gid_t egid = getegid();
1177
1178 DEBUG(jail->pakfire, " UID: %d (effective %d)\n", uid, euid);
1179 DEBUG(jail->pakfire, " GID: %d (effective %d)\n", gid, egid);
1180
1181 // Check if we are (effectively running as root)
4f719e21 1182 if (uid || gid || euid || egid) {
4f59c39b
MT
1183 ERROR(jail->pakfire, "Child process is not running as root\n");
1184 return 126;
1185 }
1186
1187 const char* root = pakfire_get_path(jail->pakfire);
1188 const char* arch = pakfire_get_arch(jail->pakfire);
1189
1190 // Change root (unless root is /)
1191 if (!pakfire_on_root(jail->pakfire)) {
1192 // Mount everything
cc6e2264 1193 r = pakfire_jail_mount(jail);
4f59c39b
MT
1194 if (r)
1195 return r;
1196
4f59c39b
MT
1197 // Call chroot()
1198 r = chroot(root);
1199 if (r) {
1200 ERROR(jail->pakfire, "chroot() to %s failed: %m\n", root);
1201 return 1;
1202 }
1203
1204 // Change directory to /
1205 r = chdir("/");
1206 if (r) {
1207 ERROR(jail->pakfire, "chdir() after chroot() failed: %m\n");
1208 return 1;
1209 }
1210 }
1211
90d92b5c
MT
1212 // Set personality
1213 unsigned long persona = pakfire_arch_personality(arch);
1214 if (persona) {
1215 r = personality(persona);
1216 if (r < 0) {
1217 ERROR(jail->pakfire, "Could not set personality (%x)\n", (unsigned int)persona);
1218 return 1;
1219 }
1220 }
1221
cf440db8
MT
1222 // Set nice level
1223 if (jail->nice) {
1224 DEBUG(jail->pakfire, "Setting nice level to %d\n", jail->nice);
1225
1226 r = setpriority(PRIO_PROCESS, pid, jail->nice);
1227 if (r) {
1228 ERROR(jail->pakfire, "Could not set nice level: %m\n");
1229 return 1;
1230 }
1231 }
1232
e33387d3
MT
1233 // Close other end of log pipes
1234 close(ctx->pipes.log_INFO[0]);
1235 close(ctx->pipes.log_ERROR[0]);
1236#ifdef ENABLE_DEBUG
1237 close(ctx->pipes.log_DEBUG[0]);
1238#endif /* ENABLE_DEBUG */
1239
7ebfb7cb
MT
1240 // Connect standard output and error
1241 if (ctx->pipes.stdout[1] && ctx->pipes.stderr[1]) {
1242 r = dup2(ctx->pipes.stdout[1], STDOUT_FILENO);
1243 if (r < 0) {
1244 ERROR(jail->pakfire, "Could not connect fd %d to stdout: %m\n",
1245 ctx->pipes.stdout[1]);
1246
1247 return 1;
1248 }
1249
1250 r = dup2(ctx->pipes.stderr[1], STDERR_FILENO);
1251 if (r < 0) {
1252 ERROR(jail->pakfire, "Could not connect fd %d to stderr: %m\n",
1253 ctx->pipes.stderr[1]);
1254
1255 return 1;
1256 }
1257
195fe455
MT
1258 // Close the pipe (as we have moved the original file descriptors)
1259 pakfire_jail_close_pipe(jail, ctx->pipes.stdout);
1260 pakfire_jail_close_pipe(jail, ctx->pipes.stderr);
7ebfb7cb
MT
1261 }
1262
007bc66c
MT
1263 // Reset open file limit (http://0pointer.net/blog/file-descriptor-limits.html)
1264 r = pakfire_rlimit_reset_nofile(jail->pakfire);
1265 if (r)
1266 return r;
1267
980b15af
MT
1268 // Drop capabilities
1269 r = pakfire_jail_drop_capabilities(jail);
1270 if (r)
1271 return r;
1272
739d5b57
MT
1273 // Filter syscalls
1274 r = pakfire_jail_limit_syscalls(jail);
1275 if (r)
1276 return r;
1277
b3498aeb
MT
1278 // exec() command
1279 r = execvpe(argv[0], (char**)argv, jail->env);
1280 if (r < 0)
1281 ERROR(jail->pakfire, "Could not execve(): %m\n");
1282
1283 // Translate errno into regular exit code
1284 switch (errno) {
1285 case ENOENT:
1286 r = 127;
1287 break;
1288
1289 default:
1290 r = 1;
1291 }
1292
1293 // We should not get here
1294 return r;
0bd84dc1
MT
1295}
1296
9f50bf71 1297// Run a command in the jail
db4f234f
MT
1298static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[],
1299 const int interactive) {
4f59c39b 1300 int exit = -1;
0bd84dc1
MT
1301 int r;
1302
b3498aeb
MT
1303 // Check if argv is valid
1304 if (!argv || !argv[0]) {
1305 errno = EINVAL;
1306 return -1;
1307 }
1308
616f1fca
MT
1309 // Initialize context for this call
1310 struct pakfire_jail_exec ctx = {
1311 .pipes = {
e33387d3
MT
1312 .stdout = { 0, 0 },
1313 .stderr = { 0, 0 },
616f1fca 1314 },
616f1fca
MT
1315 };
1316
0bd84dc1
MT
1317 DEBUG(jail->pakfire, "Executing jail...\n");
1318
43dc0e16
MT
1319 /*
1320 Setup a file descriptor which can be used to notify the client that the parent
1321 has completed configuration.
1322 */
f7d240a7
MT
1323 ctx.completed_fd = eventfd(0, EFD_CLOEXEC);
1324 if (ctx.completed_fd < 0) {
43dc0e16
MT
1325 ERROR(jail->pakfire, "eventfd() failed: %m\n");
1326 return -1;
1327 }
1328
616f1fca 1329 // Create pipes to communicate with child process if we are not running interactively
58963c75 1330 if (!interactive) {
616f1fca 1331 // stdout
e33387d3
MT
1332 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdout, 0);
1333 if (r)
616f1fca 1334 goto ERROR;
616f1fca
MT
1335
1336 // stderr
e33387d3
MT
1337 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stderr, 0);
1338 if (r)
616f1fca 1339 goto ERROR;
616f1fca
MT
1340 }
1341
e33387d3
MT
1342 // Setup pipes for logging
1343 // INFO
1344 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_INFO, O_CLOEXEC);
1345 if (r)
1346 goto ERROR;
1347
1348 // ERROR
1349 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_ERROR, O_CLOEXEC);
1350 if (r)
1351 goto ERROR;
1352
1353#ifdef ENABLE_DEBUG
1354 // DEBUG
1355 r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_DEBUG, O_CLOEXEC);
1356 if (r)
1357 goto ERROR;
1358#endif /* ENABLE_DEBUG */
1359
0bd84dc1
MT
1360 // Configure child process
1361 struct clone_args args = {
1362 .flags =
1363 CLONE_NEWCGROUP |
1364 CLONE_NEWIPC |
1365 CLONE_NEWNS |
1366 CLONE_NEWPID |
1367 CLONE_NEWUSER |
d853213d 1368 CLONE_NEWUTS |
02fd4f8b 1369 CLONE_PIDFD,
0bd84dc1 1370 .exit_signal = SIGCHLD,
d853213d 1371 .pidfd = (long long unsigned int)&ctx.pidfd,
0bd84dc1
MT
1372 };
1373
aca565fc 1374 // Launch the process in a cgroup that is a leaf of the configured cgroup
02fd4f8b
MT
1375 if (jail->cgroup) {
1376 args.flags |= CLONE_INTO_CGROUP;
1377
ae5201c5
MT
1378 // Fetch our UUID
1379 const char* uuid = pakfire_jail_uuid(jail);
aca565fc
MT
1380
1381 // Create a temporary cgroup
ae5201c5 1382 r = pakfire_cgroup_child(&ctx.cgroup, jail->cgroup, uuid, 0);
aca565fc
MT
1383 if (r) {
1384 ERROR(jail->pakfire, "Could not create cgroup for jail: %m\n");
1385 goto ERROR;
1386 }
1387
02fd4f8b 1388 // Clone into this cgroup
aca565fc 1389 args.cgroup = pakfire_cgroup_fd(ctx.cgroup);
02fd4f8b
MT
1390 }
1391
0bd84dc1 1392 // Fork this process
616f1fca
MT
1393 ctx.pid = clone3(&args, sizeof(args));
1394 if (ctx.pid < 0) {
0bd84dc1
MT
1395 ERROR(jail->pakfire, "Could not clone: %m\n");
1396 return -1;
1397
1398 // Child process
616f1fca 1399 } else if (ctx.pid == 0) {
f7d240a7 1400 r = pakfire_jail_child(jail, &ctx, argv);
0bd84dc1
MT
1401 _exit(r);
1402 }
1403
679ee2fa 1404 // Parent process
f7d240a7 1405 r = pakfire_jail_parent(jail, &ctx);
679ee2fa
MT
1406 if (r)
1407 goto ERROR;
1408
616f1fca 1409 DEBUG(jail->pakfire, "Waiting for PID %d to finish its work\n", ctx.pid);
0bd84dc1 1410
616f1fca 1411 // Read output of the child process
d853213d
MT
1412 r = pakfire_jail_wait(jail, &ctx);
1413 if (r)
1414 goto ERROR;
0bd84dc1 1415
d853213d
MT
1416 // Handle exit status
1417 switch (ctx.status.si_code) {
1418 case CLD_EXITED:
1419 DEBUG(jail->pakfire, "The child process exited with code %d\n",
1420 ctx.status.si_status);
616f1fca 1421
d853213d
MT
1422 // Pass exit code
1423 exit = ctx.status.si_status;
1424 break;
0bd84dc1 1425
d853213d
MT
1426 case CLD_KILLED:
1427 case CLD_DUMPED:
1428 ERROR(jail->pakfire, "The child process was killed\n");
1429 break;
0bd84dc1 1430
d853213d
MT
1431 // Log anything else
1432 default:
1433 ERROR(jail->pakfire, "Unknown child exit code: %d\n", ctx.status.si_code);
1434 break;
0bd84dc1
MT
1435 }
1436
679ee2fa 1437ERROR:
aca565fc
MT
1438 // Destroy the temporary cgroup (if any)
1439 if (ctx.cgroup) {
6b7cf275
MT
1440 // Read cgroup stats
1441 r = pakfire_cgroup_stat(ctx.cgroup, &ctx.cgroup_stats);
1442 if (r) {
1443 ERROR(jail->pakfire, "Could not read cgroup stats: %m\n");
1444 } else {
1445 pakfire_cgroup_stat_dump(ctx.cgroup, &ctx.cgroup_stats);
1446 }
1447
aca565fc
MT
1448 pakfire_cgroup_destroy(ctx.cgroup);
1449 pakfire_cgroup_unref(ctx.cgroup);
1450 }
1451
616f1fca 1452 // Close any file descriptors
e33387d3
MT
1453 pakfire_jail_close_pipe(jail, ctx.pipes.stdout);
1454 pakfire_jail_close_pipe(jail, ctx.pipes.stderr);
d853213d
MT
1455 if (ctx.pidfd)
1456 close(ctx.pidfd);
e33387d3
MT
1457 pakfire_jail_close_pipe(jail, ctx.pipes.log_INFO);
1458 pakfire_jail_close_pipe(jail, ctx.pipes.log_ERROR);
1459 pakfire_jail_close_pipe(jail, ctx.pipes.log_DEBUG);
616f1fca 1460
4f59c39b 1461 return exit;
9f50bf71 1462}
a45ed6b0 1463
6ce56f90 1464PAKFIRE_EXPORT int pakfire_jail_exec(struct pakfire_jail* jail,
12b9b39f 1465 const char* argv[], char** output) {
0de6bb30
MT
1466 int r;
1467
1468 // Store logging callback
1469 pakfire_jail_log_callback log_callback = jail->log_callback;
1470 void* log_data = jail->log_data;
1471
1472 // Capture output if requested by user
1473 if (output)
1474 pakfire_jail_set_log_callback(jail, pakfire_jail_capture_stdout, output);
1475
1476 // Run exec()
db4f234f 1477 r = __pakfire_jail_exec(jail, argv, 0);
0de6bb30
MT
1478
1479 // Restore log callback
1480 pakfire_jail_set_log_callback(jail, log_callback, log_data);
1481
1482 return r;
1483}
1484
db4f234f
MT
1485static int pakfire_jail_exec_interactive(
1486 struct pakfire_jail* jail, const char* argv[]) {
1487 int r;
1488
1489 // Setup interactive stuff
1490 r = pakfire_jail_setup_interactive_env(jail);
1491 if (r)
1492 return r;
1493
1494 return __pakfire_jail_exec(jail, argv, 1);
1495}
1496
6ce56f90 1497PAKFIRE_EXPORT int pakfire_jail_exec_script(struct pakfire_jail* jail,
12b9b39f 1498 const char* script, const size_t size, const char* args[], char** output) {
a45ed6b0
MT
1499 char path[PATH_MAX];
1500 const char** argv = NULL;
1501 int r;
1502
1503 const char* root = pakfire_get_path(jail->pakfire);
1504
1505 // Write the scriptlet to disk
1506 r = pakfire_path_join(path, root, "pakfire-script.XXXXXX");
56796f84 1507 if (r)
a45ed6b0
MT
1508 goto ERROR;
1509
1510 // Open a temporary file
1511 int fd = mkstemp(path);
1512 if (fd < 0) {
1513 ERROR(jail->pakfire, "Could not open a temporary file: %m\n");
1514 r = 1;
1515 goto ERROR;
1516 }
1517
1518 DEBUG(jail->pakfire, "Writing script to %s:\n%.*s\n", path, (int)size, script);
1519
1520 // Write data
1521 ssize_t bytes_written = write(fd, script, size);
1522 if (bytes_written < (ssize_t)size) {
1523 ERROR(jail->pakfire, "Could not write script to file %s: %m\n", path);
1524 r = 1;
1525 goto ERROR;
1526 }
1527
1528 // Make the script executable
1529 r = fchmod(fd, S_IRUSR|S_IWUSR|S_IXUSR);
1530 if (r) {
1531 ERROR(jail->pakfire, "Could not set executable permissions on %s: %m\n", path);
1532 goto ERROR;
1533 }
1534
1535 // Close file
1536 r = close(fd);
1537 if (r) {
1538 ERROR(jail->pakfire, "Could not close script file %s: %m\n", path);
1539 r = 1;
1540 goto ERROR;
1541 }
1542
1543 // Count how many arguments were passed
1544 unsigned int argc = 1;
1545 if (args) {
1546 for (const char** arg = args; *arg; arg++)
1547 argc++;
1548 }
1549
1550 argv = calloc(argc + 1, sizeof(*argv));
1551 if (!argv) {
1552 ERROR(jail->pakfire, "Could not allocate argv: %m\n");
1553 goto ERROR;
1554 }
1555
1556 // Set command
1557 argv[0] = (root) ? pakfire_path_relpath(root, path) : path;
1558
1559 // Copy args
1560 for (unsigned int i = 1; i < argc; i++)
1561 argv[i] = args[i-1];
1562
1563 // Run the script
0de6bb30 1564 r = pakfire_jail_exec(jail, argv, output);
a45ed6b0
MT
1565
1566ERROR:
1567 if (argv)
1568 free(argv);
1569
1570 // Remove script from disk
1571 if (*path)
1572 unlink(path);
1573
1574 return r;
1575}
82df3c77
MT
1576
1577/*
1578 A convenience function that creates a new jail, runs the given command and destroys
1579 the jail again.
1580*/
12b9b39f 1581int pakfire_jail_run(struct pakfire* pakfire, const char* argv[], int flags, char** output) {
82df3c77
MT
1582 struct pakfire_jail* jail = NULL;
1583 int r;
1584
1585 // Create a new jail
1586 r = pakfire_jail_create(&jail, pakfire, flags);
1587 if (r)
1588 goto ERROR;
1589
1590 // Execute the command
0de6bb30 1591 r = pakfire_jail_exec(jail, argv, output);
82df3c77
MT
1592
1593ERROR:
1594 if (jail)
1595 pakfire_jail_unref(jail);
1596
1597 return r;
1598}
4f688bd8
MT
1599
1600int pakfire_jail_run_script(struct pakfire* pakfire,
12b9b39f 1601 const char* script, const size_t length, const char* argv[], int flags, char** output) {
4f688bd8
MT
1602 struct pakfire_jail* jail = NULL;
1603 int r;
1604
1605 // Create a new jail
1606 r = pakfire_jail_create(&jail, pakfire, flags);
1607 if (r)
1608 goto ERROR;
1609
1610 // Execute the command
0de6bb30 1611 r = pakfire_jail_exec_script(jail, script, length, argv, output);
4f688bd8
MT
1612
1613ERROR:
1614 if (jail)
1615 pakfire_jail_unref(jail);
1616
1617 return r;
1618}
e43489f7 1619
5f6e42a2 1620int pakfire_jail_shell(struct pakfire_jail* jail) {
e43489f7
MT
1621 const char* argv[] = {
1622 "/bin/bash", "--login", NULL,
1623 };
1624
1625 // Execute /bin/bash
db4f234f 1626 return pakfire_jail_exec_interactive(jail, argv);
e43489f7
MT
1627}
1628
1629int pakfire_jail_ldconfig(struct pakfire* pakfire) {
1630 char path[PATH_MAX];
1631
1632 const char* ldconfig = "/sbin/ldconfig";
1633
1634 // Check if ldconfig exists before calling it to avoid overhead
1635 int r = pakfire_make_path(pakfire, path, ldconfig);
1636 if (r < 0)
1637 return 1;
1638
1639 // Check if ldconfig is executable
1640 r = access(path, X_OK);
1641 if (r) {
1642 DEBUG(pakfire, "%s is not executable. Skipping...\n", ldconfig);
1643 return 0;
1644 }
1645
1646 const char* argv[] = {
1647 ldconfig, NULL,
1648 };
1649
1650 // Run ldconfig
1651 return pakfire_jail_run(pakfire, argv, 0, NULL);
1652}