]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/build.c
build: Make memory limit configurable
[people/stevee/pakfire.git] / src / libpakfire / build.c
CommitLineData
1a276007
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2021 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
21#include <errno.h>
22#include <stdlib.h>
22a0733e 23#include <sys/mount.h>
cee6363a 24#include <unistd.h>
57e2cf99 25#include <uuid/uuid.h>
1a276007
MT
26
27#include <pakfire/build.h>
f877fdfa 28#include <pakfire/cgroup.h>
2a623cf8 29#include <pakfire/dependencies.h>
1a276007 30#include <pakfire/dist.h>
ae7968a7 31#include <pakfire/file.h>
e545f6ec 32#include <pakfire/i18n.h>
8e99f22d 33#include <pakfire/jail.h>
1a276007 34#include <pakfire/logging.h>
163851bc 35#include <pakfire/mount.h>
4c07774f 36#include <pakfire/package.h>
48c6f2e7 37#include <pakfire/packager.h>
1a276007
MT
38#include <pakfire/parser.h>
39#include <pakfire/private.h>
4651122b 40#include <pakfire/repo.h>
99a56775 41#include <pakfire/request.h>
106d2edd 42#include <pakfire/scriptlet.h>
c0f2502a 43#include <pakfire/snapshot.h>
d973a13d 44#include <pakfire/string.h>
1a276007
MT
45#include <pakfire/util.h>
46
5a06668c
MT
47#define CCACHE_DIR "/var/cache/ccache"
48
882ae03b 49// We guarantee 2 GiB of memory to every build container
0a1284ff 50#define PAKFIRE_BUILD_MEMORY_GUARANTEED (size_t)2 * 1024 * 1024 * 1024
882ae03b 51
de4c8fe6
MT
52// We allow only up to 2048 processes/threads for every build container
53#define PAKFIRE_BUILD_PID_LIMIT (size_t)2048
54
abbad00b
MT
55struct pakfire_build {
56 struct pakfire* pakfire;
57 int nrefs;
58
59 // Flags
60 int flags;
61
62 // Build ID
63 uuid_t id;
f877fdfa
MT
64 char _id[UUID_STR_LEN];
65
a84624be
MT
66 char target[PATH_MAX];
67
f877fdfa
MT
68 // cgroup
69 struct pakfire_cgroup* cgroup;
753ddf74
MT
70
71 // Jail
72 struct pakfire_jail* jail;
e545f6ec
MT
73
74 // Local build repo
75 struct pakfire_repo* repo;
8dbb69f9 76
6fc3956f
MT
77 // Buildroot
78 char buildroot[PATH_MAX];
79
8dbb69f9
MT
80 // States
81 int init:1;
abbad00b
MT
82};
83
1a276007
MT
84#define TEMPLATE \
85 "#!/bin/bash --login\n" \
86 "\n" \
87 "set -e\n" \
88 "set -x\n" \
89 "\n" \
90 "%%{_%s}\n" \
91 "\n" \
92 "exit 0\n"
93
c0f2502a
MT
94static int pakfire_build_has_flag(struct pakfire_build* build, int flag) {
95 return build->flags & flag;
96}
97
5f6e42a2 98/*
7a347de7 99 This function enables the local repository so that it can be accessed by
5f6e42a2
MT
100 a pakfire instance running inside the chroot.
101*/
102static int pakfire_build_enable_repos(struct pakfire_build* build) {
103 char repopath[PATH_MAX];
5f6e42a2
MT
104 int r = 1;
105
5f6e42a2 106 // Fetch repository configuration
e545f6ec 107 char* config = pakfire_repo_get_config(build->repo);
5f6e42a2
MT
108 if (!config) {
109 ERROR(build->pakfire, "Could not generate repository configuration: %m\n");
110 goto ERROR;
111 }
112
e545f6ec 113 const char* path = pakfire_repo_get_path(build->repo);
5f6e42a2
MT
114
115 // Make sure the source directory exists
116 r = pakfire_mkdir(path, 0700);
117 if (r && errno != EEXIST) {
118 ERROR(build->pakfire, "Could not create repository directory %s: %m\n", path);
119 goto ERROR;
120 }
121
122 // Bind-mount the repository data read-only
7a347de7 123 r = pakfire_jail_bind(build->jail, path, path, MS_RDONLY);
5f6e42a2
MT
124 if (r) {
125 ERROR(build->pakfire, "Could not bind-mount the repository at %s: %m\n", path);
126 goto ERROR;
127 }
128
129 // Make path for configuration file
77e26129
MT
130 r = pakfire_path(build->pakfire, repopath,
131 "%s", PAKFIRE_CONFIG_DIR "/repos/" PAKFIRE_REPO_LOCAL ".repo");
132 if (r)
5f6e42a2
MT
133 goto ERROR;
134
c2803561
MT
135 // Write repository configuration
136 r = pakfire_file_write(build->pakfire, repopath, 0, 0, 0644, "%s", config);
137 if (r) {
5f6e42a2 138 ERROR(build->pakfire, "Could not write repository configuration: %m\n");
5f6e42a2
MT
139 goto ERROR;
140 }
141
5f6e42a2 142ERROR:
5f6e42a2
MT
143 if (config)
144 free(config);
5f6e42a2
MT
145
146 return r;
147}
148
149/*
150 Drops the user into a shell
151*/
152static int pakfire_build_shell(struct pakfire_build* build) {
153 int r;
154
155 // Export local repository if running in interactive mode
156 r = pakfire_build_enable_repos(build);
157 if (r)
158 return r;
159
160 // Run shell
161 return pakfire_jail_shell(build->jail);
162}
163
779e16de 164static int pakfire_build_run_script(struct pakfire_build* build, const char* filename,
12b9b39f 165 const char* args[], char** output) {
8d0f3a35
MT
166 char* script = NULL;
167 size_t size = 0;
168 char path[PATH_MAX];
169
779e16de 170 DEBUG(build->pakfire, "Running build script '%s'...\n", filename);
8d0f3a35
MT
171
172 // Make the source path
173 pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename);
174
175 // Open the source script
176 FILE* f = fopen(path, "r");
177 if (!f) {
779e16de 178 ERROR(build->pakfire, "Could not open %s: %m\n", path);
8d0f3a35
MT
179 return 1;
180 }
181
182 // Read the script into memory
183 int r = pakfire_read_file_into_buffer(f, &script, &size);
184 if (r) {
779e16de 185 ERROR(build->pakfire, "Could not read script into memory: %m\n");
8d0f3a35
MT
186 goto ERROR;
187 }
188
8e99f22d 189 // Execute the script
001bcbd7 190 r = pakfire_jail_exec_script(build->jail, script, size, args, output);
8d0f3a35 191 if (r) {
779e16de 192 ERROR(build->pakfire, "Script '%s' failed with status %d\n", filename, r);
8d0f3a35
MT
193 }
194
195ERROR:
196 if (script)
197 free(script);
198
199 return r;
200}
201
bd95a433 202static int pakfire_build_find_dependencies(struct pakfire_build* build,
1bbbfb9e 203 struct pakfire_package* pkg, struct pakfire_filelist* filelist, const char* buildroot) {
12b9b39f
MT
204 char* provides = NULL;
205 char* requires = NULL;
5b0b3dc2
MT
206 char path[PATH_MAX];
207
208 // Allocate path to write the filelist to
77e26129
MT
209 int r = pakfire_path(build->pakfire, path, "%s", "/var/tmp/.pakfire-filelist.XXXXXX");
210 if (r)
5b0b3dc2
MT
211 return 1;
212
213 // Create a temporary file
214 FILE* f = pakfire_mktemp(path);
215 if (!f)
216 goto ERROR;
217
218 // Write filelist to the temporary file
219 r = pakfire_filelist_export(filelist, f);
220 fclose(f);
221 if (r) {
bd95a433 222 ERROR(build->pakfire, "Could not export filelist: %m\n");
5b0b3dc2
MT
223 goto ERROR;
224 }
225
bd95a433 226 const char* root = pakfire_get_path(build->pakfire);
5b0b3dc2
MT
227
228 // Pass buildroot and the filelist as arguments
229 const char* args[] = {
230 buildroot, pakfire_path_relpath(root, path), NULL
231 };
232
233 // Find all provides
779e16de 234 r = pakfire_build_run_script(build, "find-provides", args, &provides);
5b0b3dc2 235 if (r) {
bd95a433 236 ERROR(build->pakfire, "find-provides returned with error %d\n", r);
5b0b3dc2
MT
237 goto ERROR;
238 }
239
240 // Find all requires
779e16de 241 r = pakfire_build_run_script(build, "find-requires", args, &requires);
5b0b3dc2 242 if (r) {
bd95a433 243 ERROR(build->pakfire, "find-requires returned with error %d\n", r);
5b0b3dc2
MT
244 goto ERROR;
245 }
246
247 // Add all provides to the package
248 if (provides) {
83d93f33
MT
249 r = pakfire_str2deps(build->pakfire, pkg,
250 pakfire_package_add_provides, provides);
251 if (r) {
252 ERROR(build->pakfire, "Could not add provides: %m\n");
253 goto ERROR;
5b0b3dc2
MT
254 }
255 }
256
257 // Add all requires to the package
258 if (requires) {
83d93f33
MT
259 r = pakfire_str2deps(build->pakfire, pkg,
260 pakfire_package_add_requires, requires);
261 if (r) {
262 ERROR(build->pakfire, "Could not add provides: %m\n");
263 goto ERROR;
5b0b3dc2
MT
264 }
265 }
266
267 // Success
268 r = 0;
269
270ERROR:
12b9b39f 271 if (provides)
5b0b3dc2 272 free(provides);
12b9b39f 273 if (requires)
5b0b3dc2 274 free(requires);
5b0b3dc2
MT
275 unlink(path);
276
277 return r;
278}
279
73543ae3
MT
280static int append_to_array(const char*** array, const char* s) {
281 unsigned int length = 0;
282
283 // Determine the length of the existing array
284 if (*array) {
285 for (const char** element = *array; *element; element++)
286 length++;
287 }
288
289 // Allocate space
290 *array = reallocarray(*array, length + 2, sizeof(**array));
291 if (!*array)
292 return 1;
293
294 // Append s and terminate the array
295 (*array)[length] = s;
296 (*array)[length + 1] = NULL;
297
298 return 0;
299}
300
adfa3573
MT
301static int pakfire_build_package_add_files(struct pakfire_build* build,
302 struct pakfire_parser* makefile, const char* buildroot, const char* namespace,
303 struct pakfire_package* pkg, struct pakfire_packager* packager) {
1bbbfb9e 304 struct pakfire_filelist* filelist = NULL;
73543ae3
MT
305 int r = 1;
306
307 char** files = NULL;
308 const char** includes = NULL;
309 const char** excludes = NULL;
310
311 // Fetch filelist from makefile
312 files = pakfire_parser_get_split(makefile, namespace, "files", '\n');
313
314 // No files to package?
315 if (!files)
316 return 0;
317
73543ae3
MT
318 // Split into includes and excludes
319 for (char** file = files; *file; file++) {
320 if (**file == '!')
321 r = append_to_array(&excludes, *file);
322 else
323 r = append_to_array(&includes, *file);
324 if (r)
325 goto ERROR;
326 }
327
328 // Allocate a new filelist
adfa3573 329 r = pakfire_filelist_create(&filelist, build->pakfire);
73543ae3
MT
330 if (r)
331 goto ERROR;
332
333 // Scan for files
77e26129 334 r = pakfire_filelist_scan(filelist, build->buildroot, includes, excludes);
73543ae3
MT
335 if (r)
336 goto ERROR;
337
ae7968a7 338 const size_t length = pakfire_filelist_size(filelist);
adfa3573 339 DEBUG(build->pakfire, "%zu file(s) found\n", length);
73543ae3 340
84556307
MT
341 // Nothing to do if the filelist is empty
342 if (!length)
343 goto ERROR;
344
5b0b3dc2 345 // Find dependencies
bd95a433 346 r = pakfire_build_find_dependencies(build, pkg, filelist, buildroot);
5b0b3dc2 347 if (r) {
adfa3573 348 ERROR(build->pakfire, "Finding dependencies failed: %m\n");
5b0b3dc2
MT
349 goto ERROR;
350 }
351
ae7968a7
MT
352 // Add all files to the package
353 for (unsigned int i = 0; i < length; i++) {
5803b5f6 354 struct pakfire_file* file = pakfire_filelist_get(filelist, i);
ae7968a7
MT
355
356 // Add the file to the package
357 r = pakfire_packager_add(
358 packager,
359 pakfire_file_get_abspath(file),
360 pakfire_file_get_path(file)
361 );
362 if (r) {
363 pakfire_file_unref(file);
364 goto ERROR;
365 }
366
3fca5032
MT
367 // Remove the file after it was packaged
368 r = pakfire_file_cleanup(file);
369 if (r) {
370 pakfire_file_unref(file);
371 goto ERROR;
372 }
373
ae7968a7
MT
374 pakfire_file_unref(file);
375 }
73543ae3
MT
376
377ERROR:
378 if (filelist)
379 pakfire_filelist_unref(filelist);
380 if (files) {
381 for (char** file = files; *file; file++)
382 free(*file);
383 free(files);
384 }
385 if (includes)
386 free(includes);
387 if (excludes)
388 free(excludes);
73543ae3
MT
389
390 return r;
391}
392
9df89a8f
MT
393static int pakfire_build_add_scriptlet_requires(struct pakfire_build* build,
394 struct pakfire_package* pkg, struct pakfire_scriptlet* scriptlet) {
12b9b39f 395 char* prerequires = NULL;
106d2edd
MT
396 char path[PATH_MAX];
397 size_t size;
398 int r;
399
9df89a8f 400 const char* root = pakfire_get_path(build->pakfire);
106d2edd
MT
401
402 // Make filename
77e26129
MT
403 r = pakfire_path(build->pakfire, path, "%s", "/var/tmp/.pakfire-scriptlet.XXXXXX");
404 if (r)
106d2edd
MT
405 return r;
406
407 // Fetch scriptlet payload
408 const char* data = pakfire_scriptlet_get_data(scriptlet, &size);
409
410 // Create a temporary file
411 FILE* f = pakfire_mktemp(path);
412 if (!f)
413 return 1;
414
415 // Write scriptlet
416 ssize_t bytes_written = fwrite(data, 1, size, f);
417 if (bytes_written < 0) {
9df89a8f 418 ERROR(build->pakfire, "Could not write to %s: %m\n", path);
106d2edd
MT
419 fclose(f);
420 goto ERROR;
421 }
422
423 // Close file
424 fclose(f);
425
426 // Build commandline
427 const char* args[] = {
428 pakfire_path_relpath(root, path),
429 NULL,
430 };
431
432 // Find all pre-requires
779e16de 433 r = pakfire_build_run_script(build, "find-prerequires", args, &prerequires);
106d2edd
MT
434 if (r)
435 goto ERROR;
436
437 // Add all pre-requires to the package
438 if (prerequires) {
83d93f33
MT
439 r = pakfire_str2deps(build->pakfire, pkg,
440 pakfire_package_add_prerequires, prerequires);
441 if (r) {
442 ERROR(build->pakfire, "Could not add pre-requires: %m\n");
443 goto ERROR;
106d2edd
MT
444 }
445 }
446
447ERROR:
448 if (r && *path)
449 unlink(path);
12b9b39f 450 if (prerequires)
106d2edd 451 free(prerequires);
106d2edd
MT
452 return r;
453}
454
9df89a8f
MT
455static int pakfire_build_package_add_scriptlet(struct pakfire_build* build,
456 struct pakfire_package* pkg, struct pakfire_packager* packager,
457 const char* type, const char* data) {
106d2edd
MT
458 struct pakfire_scriptlet* scriptlet = NULL;
459 char* shell = NULL;
460 int r;
461
462 // Wrap scriptlet into a shell script
463 r = asprintf(&shell, "#!/bin/sh\n\nset -e\n\n%s\n\nexit 0\n", data);
464 if (r < 0)
465 goto ERROR;
466
467 // Create a scriptlet
9df89a8f 468 r = pakfire_scriptlet_create(&scriptlet, build->pakfire, type, shell, 0);
106d2edd
MT
469 if (r)
470 goto ERROR;
471
472 // Add it to the package
473 r = pakfire_packager_add_scriptlet(packager, scriptlet);
474 if (r) {
9df89a8f 475 ERROR(build->pakfire, "Could not add scriptlet %s\n", type);
106d2edd
MT
476 goto ERROR;
477 }
478
479 // Add scriptlet requirements
9df89a8f 480 r = pakfire_build_add_scriptlet_requires(build, pkg, scriptlet);
106d2edd 481 if (r) {
9df89a8f 482 ERROR(build->pakfire, "Could not add scriptlet requirements: %m\n");
106d2edd
MT
483 goto ERROR;
484 }
485
486 // Success
487 r = 0;
488
489ERROR:
490 if (scriptlet)
491 pakfire_scriptlet_unref(scriptlet);
492 if (shell)
493 free(shell);
494
495 return r;
496}
497
9df89a8f
MT
498static int pakfire_build_package_add_scriptlets(struct pakfire_build* build,
499 struct pakfire_parser* makefile, const char* namespace,
500 struct pakfire_package* pkg, struct pakfire_packager* packager) {
106d2edd
MT
501 char name[NAME_MAX];
502 int r;
503
504 for (const char** type = pakfire_scriptlet_types; *type; type++) {
505 r = pakfire_string_format(name, "scriptlet:%s", *type);
a60955af 506 if (r)
106d2edd
MT
507 return r;
508
509 // Fetch the scriptlet
510 char* data = pakfire_parser_get(makefile, namespace, name);
511 if (!data)
512 continue;
513
514 // Add it to the package
9df89a8f 515 r = pakfire_build_package_add_scriptlet(build, pkg, packager, *type, data);
106d2edd
MT
516 if (r) {
517 free(data);
518 return r;
519 }
520
521 free(data);
522 }
523
524 return 0;
525}
526
0e177be7
MT
527static int pakfire_build_package(struct pakfire_build* build, struct pakfire_parser* makefile,
528 const char* buildroot, const char* namespace) {
31480bee 529 struct pakfire_package* pkg = NULL;
48c6f2e7 530 struct pakfire_packager* packager = NULL;
73543ae3 531
a50bde9c
MT
532 int r = 1;
533
534 // Expand the handle into the package name
4c07774f 535 char* name = pakfire_parser_expand(makefile, "packages", namespace);
a50bde9c 536 if (!name) {
0e177be7 537 ERROR(build->pakfire, "Could not get package name: %m\n");
a50bde9c
MT
538 goto ERROR;
539 }
540
0e177be7 541 INFO(build->pakfire, "Building package '%s'...\n", name);
a50bde9c 542
4c07774f 543 // Fetch build architecture
0e177be7 544 const char* arch = pakfire_get_arch(build->pakfire);
4c07774f
MT
545 if (!arch)
546 goto ERROR;
547
4c07774f 548 // Fetch package from makefile
6ebd55e2 549 r = pakfire_parser_create_package(makefile, &pkg, NULL, namespace, arch);
4c07774f 550 if (r) {
0e177be7 551 ERROR(build->pakfire, "Could not create package from makefile: %m\n");
4c07774f
MT
552 goto ERROR;
553 }
554
ca002cae
MT
555 // Set distribution
556 const char* distribution = pakfire_parser_get(makefile, NULL, "DISTRO_NAME");
557 if (distribution)
558 pakfire_package_set_distribution(pkg, distribution);
559
7996020a 560 // Set build ID
0e177be7 561 pakfire_package_set_build_id_from_uuid(pkg, &build->id);
7996020a 562
571539a7
MT
563 // Set source package
564 const char* source_name = pakfire_parser_get(makefile, NULL, "name");
565 if (source_name)
566 pakfire_package_set_source_name(pkg, source_name);
567
568 // Set source EVR
569 const char* source_evr = pakfire_parser_get(makefile, NULL, "evr");
570 if (source_evr)
571 pakfire_package_set_source_evr(pkg, source_evr);
572
573 // Set source arch
574 pakfire_package_set_source_arch(pkg, "src");
575
48c6f2e7 576 // Create a packager
0e177be7 577 r = pakfire_packager_create(&packager, build->pakfire, pkg);
48c6f2e7
MT
578 if (r)
579 goto ERROR;
580
73543ae3 581 // Add files
adfa3573 582 r = pakfire_build_package_add_files(build, makefile, buildroot, namespace,
5b0b3dc2 583 pkg, packager);
73543ae3
MT
584 if (r)
585 goto ERROR;
586
106d2edd 587 // Add scriptlets
9df89a8f 588 r = pakfire_build_package_add_scriptlets(build, makefile, namespace,
0e177be7 589 pkg, packager);
106d2edd
MT
590 if (r)
591 goto ERROR;
592
ae7968a7 593 // Write the finished package
0e177be7 594 r = pakfire_packager_finish_to_directory(packager, build->target, NULL);
ae7968a7 595 if (r) {
0e177be7 596 ERROR(build->pakfire, "pakfire_packager_finish() failed: %m\n");
ae7968a7
MT
597 goto ERROR;
598 }
599
4c07774f 600#ifdef ENABLE_DEBUG
23e22751 601 char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
4c07774f 602 if (dump) {
0e177be7 603 DEBUG(build->pakfire, "%s\n", dump);
4c07774f
MT
604 free(dump);
605 }
606#endif
a50bde9c
MT
607
608 // Success
609 r = 0;
610
611ERROR:
48c6f2e7
MT
612 if (packager)
613 pakfire_packager_unref(packager);
4c07774f
MT
614 if (pkg)
615 pakfire_package_unref(pkg);
a50bde9c
MT
616 if (name)
617 free(name);
618
619 return r;
620}
621
0e177be7 622static int pakfire_build_packages(struct pakfire_build* build,
03dc1d52 623 struct pakfire_parser* makefile) {
0e177be7 624 DEBUG(build->pakfire, "Creating packages...");
a50bde9c
MT
625 int r = 1;
626
03dc1d52
MT
627 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
628
a50bde9c
MT
629 // Fetch a list all all packages
630 char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*");
631 if (!packages) {
0e177be7 632 ERROR(build->pakfire, "Could not find any packages: %m\n");
a50bde9c
MT
633 goto ERROR;
634 }
635
636 unsigned int num_packages = 0;
637
638 // Count how many packages we have
639 for (char** package = packages; *package; package++)
640 num_packages++;
641
0e177be7 642 DEBUG(build->pakfire, "Found %d package(s)\n", num_packages);
a50bde9c
MT
643
644 // Build packages in reverse order
645 for (int i = num_packages - 1; i >= 0; i--) {
0e177be7 646 r = pakfire_build_package(build, makefile, buildroot, packages[i]);
a50bde9c
MT
647 if (r)
648 goto ERROR;
649 }
650
651 // Success
652 r = 0;
653
654ERROR:
655 if (packages)
656 free(packages);
657
658 return r;
659}
660
46748697
MT
661static int pakfire_build_stage(struct pakfire_build* build,
662 struct pakfire_parser* makefile, const char* stage) {
1a276007
MT
663 char template[1024];
664
665 // Prepare template for this stage
666 int r = pakfire_string_format(template, TEMPLATE, stage);
a60955af 667 if (r)
1a276007
MT
668 return r;
669
2ffd21f9
MT
670 // Fetch the environment
671 char** envp = pakfire_parser_make_environ(makefile);
672
1a276007
MT
673 // Create the build script
674 char* script = pakfire_parser_expand(makefile, "build", template);
675 if (!script) {
46748697
MT
676 ERROR(build->pakfire, "Could not generate the build script for stage '%s': %m\n",
677 stage);
1a276007
MT
678 goto ERROR;
679 }
680
46748697 681 INFO(build->pakfire, "Running build stage '%s'\n", stage);
1a276007 682
830f5d18 683 // Import environment
44d5ebfd
MT
684 // XXX is this a good idea?
685 r = pakfire_jail_import_env(build->jail, (const char**)envp);
830f5d18 686 if (r) {
46748697 687 ERROR(build->pakfire, "Could not import environment: %m\n");
830f5d18
MT
688 goto ERROR;
689 }
690
691 // Run the script
44d5ebfd 692 r = pakfire_jail_exec_script(build->jail, script, strlen(script), NULL, NULL);
1a276007 693 if (r) {
46748697 694 ERROR(build->pakfire, "Build stage '%s' failed with status %d\n", stage, r);
1a276007
MT
695 }
696
697ERROR:
2ffd21f9
MT
698 if (envp) {
699 for (char** e = envp; *e; e++)
700 free(*e);
701 free(envp);
702 }
1a276007
MT
703 if (script)
704 free(script);
705
706 return r;
707}
708
39f45b30
MT
709static const char* post_build_scripts[] = {
710 "remove-static-libs",
1f62ffae 711 "check-symlinks",
022b794e 712 "check-unsafe-files",
0b5f0bbc 713 "check-libraries",
16043831 714 "check-rpaths",
99aee237 715 "check-buildroot",
7e1fec6f 716 "check-include",
dd864160 717 "check-hardening",
ae703321 718 "check-interpreters",
2504194a 719 "check-fhs",
39f45b30 720 "compress-man-pages",
ffb65de6 721 "strip",
39f45b30
MT
722 NULL,
723};
724
03dc1d52
MT
725static int pakfire_build_run_post_build_scripts(struct pakfire_build* build) {
726 // Fetch buildroot
727 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
728
39f45b30
MT
729 // Set default arguments for build scripts
730 const char* args[] = {
731 buildroot, NULL
732 };
733
734 // Run them one by one
735 for (const char** script = post_build_scripts; *script; script++) {
779e16de 736 int r = pakfire_build_run_script(build, *script, args, NULL);
39f45b30
MT
737 if (r)
738 return r;
739 }
740
741 return 0;
742}
743
abbad00b 744static void pakfire_build_free(struct pakfire_build* build) {
e545f6ec
MT
745 if (build->repo)
746 pakfire_repo_unref(build->repo);
747
753ddf74
MT
748 if (build->jail)
749 pakfire_jail_unref(build->jail);
750
49fd9926
MT
751 if (build->cgroup) {
752 // Destroy the cgroup
753 pakfire_cgroup_destroy(build->cgroup);
754
755 // Free it
756 pakfire_cgroup_unref(build->cgroup);
757 }
758
abbad00b
MT
759 pakfire_unref(build->pakfire);
760 free(build);
761}
762
763static int pakfire_build_parse_id(struct pakfire_build* build, const char* id) {
764 int r;
765
766 // Try parsing the Build ID
767 if (id) {
768 r = uuid_parse(id, build->id);
769 if (r) {
770 ERROR(build->pakfire, "Could not parse build ID '%s'\n", id);
1df321ff 771 errno = EINVAL;
abbad00b
MT
772 return r;
773 }
774
775 // Otherwise initialize the Build ID with something random
776 } else {
777 uuid_generate_random(build->id);
778 }
779
f877fdfa
MT
780 // Store the ID as string, too
781 uuid_unparse_lower(build->id, build->_id);
782
783 return 0;
784}
785
a84624be
MT
786/*
787 Sets the default target
788*/
789static int pakfire_build_set_default_target(struct pakfire_build* build) {
790 // Look for the "local" repository
e545f6ec
MT
791 if (build->repo) {
792 const char* target = pakfire_repo_get_path(build->repo);
a84624be
MT
793 if (target)
794 pakfire_string_set(build->target, target);
a84624be
MT
795 }
796
797 // Default to the current working directory
798 if (!*build->target) {
799 char* cwd = getcwd(build->target, sizeof(build->target));
800 if (!cwd) {
801 ERROR(build->pakfire, "getcwd() failed: %m\n");
802 return 1;
803 }
804 }
805
806 return 0;
807}
808
f877fdfa
MT
809/*
810 Sets up a new cgroup for this build
811*/
812static int pakfire_build_setup_cgroup(struct pakfire_build* build) {
0a1284ff 813 struct pakfire_config* config = NULL;
f877fdfa
MT
814 char path[PATH_MAX];
815 int r;
816
817 // Compose path
818 r = pakfire_string_format(path, "pakfire/build-%s", build->_id);
a60955af 819 if (r) {
f877fdfa 820 ERROR(build->pakfire, "Could not compose path for cgroup: %m\n");
0a1284ff 821 goto ERROR;
f877fdfa
MT
822 }
823
824 // Create a new cgroup
825 r = pakfire_cgroup_open(&build->cgroup, build->pakfire, path,
826 PAKFIRE_CGROUP_ENABLE_ACCOUNTING);
827 if (r) {
828 ERROR(build->pakfire, "Could not create cgroup for build %s: %m\n", build->_id);
0a1284ff 829 goto ERROR;
f877fdfa
MT
830 }
831
0a1284ff
MT
832 // Fetch config
833 config = pakfire_get_config(build->pakfire);
834 if (!config)
835 goto ERROR;
836
882ae03b 837 // Guarantee some minimum memory
0a1284ff
MT
838 size_t memory_guaranteed = pakfire_config_get_bytes(config, "build",
839 "memory_guaranteed", PAKFIRE_BUILD_MEMORY_GUARANTEED);
840 if (memory_guaranteed) {
841 r = pakfire_cgroup_set_guaranteed_memory(build->cgroup, memory_guaranteed);
842 if (r)
843 goto ERROR;
844 }
882ae03b 845
5b877e84
MT
846 // Limit memory
847 size_t memory_limit = pakfire_config_get_bytes(config, "build", "memory_limit", 0);
848 if (memory_limit) {
849 r = pakfire_cgroup_set_memory_limit(build->cgroup, memory_limit);
850 if (r)
851 goto ERROR;
852 }
853
de4c8fe6 854 // Set PID limit
0a1284ff
MT
855 size_t pid_limit = pakfire_config_get_int(config, "build",
856 "pid_limit", PAKFIRE_BUILD_PID_LIMIT);
857 if (pid_limit) {
858 r = pakfire_cgroup_set_pid_limit(build->cgroup, pid_limit);
859 if (r)
860 goto ERROR;
861 }
de4c8fe6 862
0a1284ff
MT
863ERROR:
864 if (config)
865 pakfire_config_unref(config);
866
867 return r;
abbad00b
MT
868}
869
753ddf74
MT
870/*
871 Sets up a new jail for this build
872*/
873static int pakfire_build_setup_jail(struct pakfire_build* build) {
874 int r;
875
876 // Create a new jail
877 r = pakfire_jail_create(&build->jail, build->pakfire, 0);
878 if (r) {
879 ERROR(build->pakfire, "Could not create jail for build %s: %m\n", build->_id);
880 return r;
881 }
882
15503538
MT
883 // Connect the jail to our cgroup
884 r = pakfire_jail_set_cgroup(build->jail, build->cgroup);
885 if (r) {
886 ERROR(build->pakfire, "Could not set cgroup for jail: %m\n");
887 return r;
888 }
889
753ddf74
MT
890 // Done
891 return 0;
892}
893
5a06668c
MT
894/*
895 Sets up the ccache for this build
896*/
897static int pakfire_build_setup_ccache(struct pakfire_build* build) {
898 char path[PATH_MAX];
899 int r;
900
901 // Check if we want a ccache
902 if (pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_CCACHE)) {
903 DEBUG(build->pakfire, "ccache usage has been disabled for this build\n");
0570ccd2
MT
904
905 // Set CCACHE_DISABLE=1 so that if ccache is installed, it will disable itself
906 r = pakfire_jail_set_env(build->jail, "CCACHE_DISABLE", "1");
907 if (r) {
908 ERROR(build->pakfire, "Could not disable ccache: %m\n");
909 return r;
910 }
911
5a06668c
MT
912 return 0;
913 }
914
915 // Compose path
916 r = pakfire_make_cache_path(build->pakfire, path, "%s", "ccache");
917 if (r < 0) {
918 ERROR(build->pakfire, "Could not compose ccache path: %m\n");
919 return 1;
920 }
921
922 DEBUG(build->pakfire, "Mounting ccache from %s\n", path);
923
924 // Ensure path exists
925 r = pakfire_mkdir(path, 0755);
926 if (r && errno != EEXIST) {
927 ERROR(build->pakfire, "Could not create ccache directory %s: %m\n", path);
928 return r;
929 }
930
931 // Bind-mount the directory
932 r = pakfire_jail_bind(build->jail, path, CCACHE_DIR, MS_NOSUID|MS_NOEXEC|MS_NODEV);
933 if (r) {
934 ERROR(build->pakfire, "Could not mount ccache: %m\n");
935 return r;
936 }
937
938 return 0;
939}
940
e545f6ec
MT
941static int pakfire_build_setup_repos(struct pakfire_build* build) {
942 int r;
943
944 // Create a new repository
945 r = pakfire_repo_create(&build->repo, build->pakfire, PAKFIRE_REPO_LOCAL);
946 if (r) {
947 ERROR(build->pakfire, "Could not create repository %s: %m\n", PAKFIRE_REPO_LOCAL);
948 return 1;
949 }
950
951 // Set description
952 pakfire_repo_set_description(build->repo, _("Locally Built Packages"));
953
954 // Set path
955 pakfire_repo_set_baseurl(build->repo, PAKFIRE_REPO_LOCAL_PATH);
956
957 // Set priority
958 pakfire_repo_set_priority(build->repo, PAKFIRE_REPO_LOCAL_PRIORITY);
959
960 return 0;
961}
962
abbad00b
MT
963PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build,
964 struct pakfire* pakfire, const char* id, int flags) {
965 int r;
966
967 // Allocate build object
968 struct pakfire_build* b = calloc(1, sizeof(*b));
969 if (!b)
970 return 1;
971
972 // Reference pakfire
973 b->pakfire = pakfire_ref(pakfire);
974
975 // Initialize reference counter
976 b->nrefs = 1;
977
978 // Copy flags
979 b->flags = flags;
980
981 // Parse ID
982 r = pakfire_build_parse_id(b, id);
983 if (r)
984 goto ERROR;
985
e545f6ec
MT
986 // Setup repos
987 r = pakfire_build_setup_repos(b);
988 if (r)
989 goto ERROR;
990
a84624be
MT
991 // Set target
992 r = pakfire_build_set_default_target(b);
993 if (r)
994 goto ERROR;
995
f877fdfa
MT
996 // Create cgroup
997 r = pakfire_build_setup_cgroup(b);
998 if (r)
999 goto ERROR;
1000
753ddf74
MT
1001 // Create jail
1002 r = pakfire_build_setup_jail(b);
1003 if (r)
1004 goto ERROR;
1005
5a06668c
MT
1006 // Setup ccache
1007 r = pakfire_build_setup_ccache(b);
1008 if (r)
1009 goto ERROR;
1010
abbad00b
MT
1011 *build = b;
1012 return 0;
1013
1014ERROR:
1015 pakfire_build_free(b);
1016 return r;
1017}
1018
77a1964f 1019PAKFIRE_EXPORT struct pakfire_build* pakfire_build_ref(struct pakfire_build* build) {
abbad00b
MT
1020 ++build->nrefs;
1021
1022 return build;
1023}
1024
77a1964f 1025PAKFIRE_EXPORT struct pakfire_build* pakfire_build_unref(struct pakfire_build* build) {
abbad00b
MT
1026 if (--build->nrefs > 0)
1027 return build;
1028
1029 pakfire_build_free(build);
1030 return NULL;
1031}
1032
ea924657
MT
1033PAKFIRE_EXPORT int pakfire_build_set_target(
1034 struct pakfire_build* build, const char* target) {
a60955af 1035 return pakfire_string_set(build->target, target);
ea924657 1036}
57e2cf99 1037
c0f2502a
MT
1038static int pakfire_build_install_packages(struct pakfire_build* build,
1039 int* snapshot_needs_update) {
1040 char** packages = NULL;
1041 int r = 1;
1042
1043 // Fetch configuration
1044 struct pakfire_config* config = pakfire_get_config(build->pakfire);
1045 if (!config) {
1046 ERROR(build->pakfire, "Could not fetch configuration: %m\n");
1047 r = 1;
1048 goto ERROR;
1049 }
1050
1051 // Fetch build environment
1052 const char* requires = pakfire_config_get(config, "build", "requires", NULL);
1053 if (!requires) {
1054 ERROR(build->pakfire, "No build requirements have been defined\n");
1055 goto ERROR;
1056 }
1057
1058 // Split requirements into packages
d973a13d 1059 packages = pakfire_string_split(requires, ',');
c0f2502a
MT
1060 if (!packages)
1061 goto ERROR;
1062
1063 int changed = 0;
1064
1065 // Install everything
1066 r = pakfire_install(build->pakfire, 0, 0, (const char**)packages, NULL, 0,
1067 &changed, NULL, NULL);
1068 if (r) {
1069 ERROR(build->pakfire, "Could not install build dependencies: %m\n");
1070 goto ERROR;
1071 }
1072
1073 // Mark snapshot as changed if new packages were installed
1074 if (changed)
1075 *snapshot_needs_update = 1;
1076
1077 // Update everything
1078 r = pakfire_sync(build->pakfire, 0, 0, &changed, NULL, NULL);
1079 if (r) {
1080 ERROR(build->pakfire, "Could not update packages: %m\n");
1081 goto ERROR;
1082 }
1083
1084 // Has anything changed?
1085 if (changed)
1086 *snapshot_needs_update = 1;
1087
1088 // Success
1089 r = 0;
1090
1091ERROR:
1092 if (config)
1093 pakfire_config_unref(config);
1094
1095 if (packages) {
1096 for (char** package = packages; *package; package++)
1097 free(*package);
1098 free(packages);
1099 }
1100
1101 return r;
1102}
1103
8dbb69f9
MT
1104/*
1105 Initializes the build environment
1106*/
1107static int pakfire_build_init(struct pakfire_build* build) {
c0f2502a
MT
1108 char path[PATH_MAX];
1109 int r;
1110
8dbb69f9
MT
1111 // Don't do it again
1112 if (build->init) {
1113 DEBUG(build->pakfire, "Build environment has already been initialized\n");
1114 return 0;
1115 }
1116
e545f6ec
MT
1117 // Refresh the local repository
1118 if (build->repo) {
f3c4b36a 1119 r = pakfire_repo_refresh(build->repo, 0);
e545f6ec
MT
1120 if (r) {
1121 ERROR(build->pakfire, "Could not refresh the local repository: %m\n");
1122 return 1;
1123 }
1124 }
1125
1ea7b360
MT
1126 // Compose path for snapshot
1127 r = pakfire_make_cache_path(build->pakfire, path, "%s", "snapshot.tar.zst");
1128 if (r < 0) {
1129 ERROR(build->pakfire, "Could not compose snapshot path: %m\n");
1130 return 1;
1131 }
1132
ffc2630d
MT
1133 // Tells us whether we need to (re-)create the snapshot
1134 int snapshot_needs_update = 0;
c0f2502a 1135
1ea7b360 1136 // Extract snapshot
ffc2630d 1137 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT)) {
ffc2630d 1138 // Try restoring the snapshot
4667a2ca
MT
1139 r = pakfire_snapshot_restore(build->pakfire, path);
1140 if (r && errno != ENOENT)
1141 return r;
c0f2502a
MT
1142 }
1143
c0f2502a
MT
1144 // Install or update any build dependencies
1145 r = pakfire_build_install_packages(build, &snapshot_needs_update);
1146 if (r)
1147 return r;
1148
ffc2630d
MT
1149 // Update the snapshot if there were changes
1150 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT) && snapshot_needs_update) {
c0f2502a 1151 // Create a new snapshot
4667a2ca 1152 r = pakfire_snapshot_create(build->pakfire, path);
c0f2502a
MT
1153 if (r)
1154 return r;
1155 }
1156
8dbb69f9
MT
1157 // Mark as initialized
1158 build->init = 1;
1159
c0f2502a
MT
1160 return 0;
1161}
1162
e57188c0
MT
1163static int pakfire_build_read_makefile(struct pakfire_build* build,
1164 struct pakfire_parser** parser, struct pakfire_package* package) {
e57188c0
MT
1165 char path[PATH_MAX];
1166 int r;
1167
1168 struct pakfire_parser_error* error = NULL;
1169
1170 const char* nevra = pakfire_package_get_nevra(package);
1171 const char* name = pakfire_package_get_name(package);
1172
1173 // Compose path to makefile
77e26129 1174 r = pakfire_path(build->pakfire, path, "/usr/src/packages/%s/%s.nm", nevra, name);
e57188c0
MT
1175 if (r < 0)
1176 return 1;
1177
1178 // Read makefile
1179 r = pakfire_read_makefile(parser, build->pakfire, path, &error);
1180 if (r) {
1181 if (error) {
1182 ERROR(build->pakfire, "Could not parse makefile %s: %s\n", path,
1183 pakfire_parser_error_get_message(error));
1184 } else {
1185 ERROR(build->pakfire, "Could not parse makefile %s: %m\n", path);
1186 }
1187
1188 goto ERROR;
1189 }
1190
6fc3956f 1191 // Set BUILDROOT
5caf06e3 1192 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
6fc3956f
MT
1193 if (buildroot)
1194 pakfire_parser_set(*parser, NULL, "BUILDROOT", buildroot, 0);
1195
e57188c0
MT
1196ERROR:
1197 if (error)
1198 pakfire_parser_error_unref(error);
1199
1200 return r;
1201}
1202
03dc1d52
MT
1203static int pakfire_build_perform(struct pakfire_build* build,
1204 struct pakfire_parser* makefile) {
1205 int r;
1206
b8786cdb
MT
1207 // Prepare the build
1208 r = pakfire_build_stage(build, makefile, "prepare");
1209 if (r)
1210 goto ERROR;
03dc1d52 1211
b8786cdb
MT
1212 // Perform the build
1213 r = pakfire_build_stage(build, makefile, "build");
1214 if (r)
1215 goto ERROR;
1216
1217 // Test the build
1218 r = pakfire_build_stage(build, makefile, "test");
1219 if (r)
1220 goto ERROR;
1221
1222 // Install everything
1223 r = pakfire_build_stage(build, makefile, "install");
1224 if (r)
1225 goto ERROR;
03dc1d52
MT
1226
1227 // Run post build scripts
b8786cdb
MT
1228 r = pakfire_build_run_post_build_scripts(build);
1229 if (r)
1230 goto ERROR;
1231
1232 // Done!
1233 return 0;
1234
1235ERROR:
1236 // Drop to a shell for debugging
1237 if (pakfire_has_flag(build->pakfire, PAKFIRE_BUILD_INTERACTIVE))
1238 pakfire_build_shell(build);
1239
1240 return r;
03dc1d52
MT
1241}
1242
ea924657 1243PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* path) {
33a8ba3c
MT
1244 struct pakfire_archive* archive = NULL;
1245 struct pakfire_package* package = NULL;
e57188c0 1246 struct pakfire_parser* makefile = NULL;
6fc3956f 1247 char* buildroot = NULL;
ea924657 1248 int r;
01b29895 1249
6fc3956f 1250 // Set buildroot
77e26129
MT
1251 r = pakfire_path(build->pakfire, build->buildroot, "%s", "/var/tmp/.pakfire-buildroot.XXXXXX");
1252 if (r)
1253 goto ERROR;
6fc3956f 1254
33a8ba3c
MT
1255 // Open source archive
1256 r = pakfire_archive_open(&archive, build->pakfire, path);
1257 if (r) {
1258 ERROR(build->pakfire, "Could not open source archive %s: %m\n", path);
1259 goto ERROR;
1260 }
1261
1262 // Fetch package metadata
1263 r = pakfire_archive_make_package(archive, NULL, &package);
1264 if (r) {
1265 ERROR(build->pakfire, "Could not read package metadata: %m\n");
1266 goto ERROR;
1267 }
1268
33a8ba3c 1269 const char* nevra = pakfire_package_get_nevra(package);
33a8ba3c
MT
1270
1271 INFO(build->pakfire, "Building %s...\n", nevra);
01b29895 1272
8dbb69f9
MT
1273 // Initialize the build environment
1274 r = pakfire_build_init(build);
1275 if (r)
c0f2502a 1276 goto ERROR;
c0f2502a 1277
01b29895
MT
1278 const char* packages[] = {
1279 path, NULL
1280 };
1281
1282 // Install the package into the build environment
ea924657 1283 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, PAKFIRE_REQUEST_ESSENTIAL,
99a56775 1284 NULL, NULL, NULL);
01b29895 1285 if (r) {
ea924657 1286 ERROR(build->pakfire, "Could not install %s\n", path);
33a8ba3c 1287 goto ERROR;
01b29895
MT
1288 }
1289
6fc3956f
MT
1290 // Create BUILDROOT
1291 buildroot = pakfire_mkdtemp(build->buildroot);
1292 if (!buildroot) {
1293 ERROR(build->pakfire, "Could not create BUILDROOT: %m\n");
1294 goto ERROR;
1295 }
1296
e57188c0
MT
1297 // Open the makefile
1298 r = pakfire_build_read_makefile(build, &makefile, package);
1299 if (r)
01b29895
MT
1300 goto ERROR;
1301
03dc1d52
MT
1302 // Perform the actual build
1303 r = pakfire_build_perform(build, makefile);
1304 if (r)
1305 goto ERROR;
1306
1307 // Create the packages
1308 r = pakfire_build_packages(build, makefile);
01b29895 1309 if (r) {
03dc1d52 1310 ERROR(build->pakfire, "Could not create packages: %m\n");
01b29895
MT
1311 goto ERROR;
1312 }
1313
01b29895 1314ERROR:
e57188c0
MT
1315 if (makefile)
1316 pakfire_parser_unref(makefile);
33a8ba3c
MT
1317 if (archive)
1318 pakfire_archive_unref(archive);
1319 if (package)
1320 pakfire_package_unref(package);
1321
6fc3956f
MT
1322 // Cleanup buildroot
1323 if (buildroot)
1324 pakfire_rmtree(buildroot, 0);
1325
01b29895
MT
1326 return r;
1327}
1328
ea924657
MT
1329/*
1330 Compatibility function to keep the legacy API.
1331*/
1332PAKFIRE_EXPORT int pakfire_build(struct pakfire* pakfire, const char* path,
1333 const char* target, const char* id, int flags) {
1334 struct pakfire_build* build = NULL;
1335 int r;
1336
1337 // Check if path is set
1338 if (!path) {
1339 errno = EINVAL;
1340 return 1;
1341 }
1342
1343 // Create a new build environment
1344 r = pakfire_build_create(&build, pakfire, id, flags);
1345 if (r)
1346 goto ERROR;
1347
1348 // Set target
1349 if (target) {
1350 r = pakfire_build_set_target(build, target);
1351 if (r)
1352 goto ERROR;
1353 }
1354
1355 // Run build
1356 r = pakfire_build_exec(build, path);
1357
1358ERROR:
1359 if (build)
1360 pakfire_build_unref(build);
1361
1362 return r;
1363}
1364
5f6e42a2
MT
1365int pakfire_build_clean(struct pakfire* pakfire, int flags) {
1366 struct pakfire_repo* local = NULL;
1367 int r = 0;
22a0733e 1368
5f6e42a2
MT
1369 // Fetch local repository
1370 local = pakfire_get_repo(pakfire, PAKFIRE_REPO_LOCAL);
1371 if (!local) {
1372 ERROR(pakfire, "Could not find repository %s: %m\n", PAKFIRE_REPO_LOCAL);
22a0733e
MT
1373 goto ERROR;
1374 }
1375
5f6e42a2
MT
1376 // Destroy everything in it
1377 r = pakfire_repo_clean(local, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY);
1378 if (r)
22a0733e 1379 goto ERROR;
22a0733e
MT
1380
1381ERROR:
5f6e42a2
MT
1382 if (local)
1383 pakfire_repo_unref(local);
22a0733e
MT
1384
1385 return r;
1386}
1387
5f6e42a2
MT
1388/*
1389 This is a convenience function that sets up a build environment and
1390 then drops the user into an interactive shell.
1391*/
7f068a08 1392PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages) {
5f6e42a2 1393 struct pakfire_build* build = NULL;
48dc31f7 1394 int r;
1a276007 1395
5f6e42a2 1396 // Create a new build environment
db4f234f 1397 r = pakfire_build_create(&build, pakfire, NULL, PAKFIRE_BUILD_INTERACTIVE);
5f6e42a2
MT
1398 if (r) {
1399 ERROR(pakfire, "Could not create build: %m\n");
1400 goto ERROR;
7f068a08
MT
1401 }
1402
8dbb69f9
MT
1403 // Initialize the build environment
1404 r = pakfire_build_init(build);
1405 if (r)
1406 goto ERROR;
22a0733e 1407
5f6e42a2
MT
1408 // Install any additional packages
1409 if (packages) {
1410 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, 0, NULL, NULL, NULL);
1411 if (r)
1412 return r;
397371db
MT
1413 }
1414
5f6e42a2
MT
1415 // Run shell
1416 r = pakfire_build_shell(build);
397371db
MT
1417
1418ERROR:
5f6e42a2
MT
1419 if (build)
1420 pakfire_build_unref(build);
397371db
MT
1421
1422 return r;
1423}