]> git.ipfire.org Git - pakfire.git/blob - src/libpakfire/build.c
Revert "snapshots: Pass path instead of file descriptor"
[pakfire.git] / src / libpakfire / build.c
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>
23 #include <sys/mount.h>
24 #include <time.h>
25 #include <unistd.h>
26 #include <uuid/uuid.h>
27
28 #define PCRE2_CODE_UNIT_WIDTH 8
29 #include <pcre2.h>
30
31 #include <pakfire/build.h>
32 #include <pakfire/cgroup.h>
33 #include <pakfire/dependencies.h>
34 #include <pakfire/dist.h>
35 #include <pakfire/file.h>
36 #include <pakfire/i18n.h>
37 #include <pakfire/jail.h>
38 #include <pakfire/logging.h>
39 #include <pakfire/mount.h>
40 #include <pakfire/package.h>
41 #include <pakfire/packager.h>
42 #include <pakfire/parser.h>
43 #include <pakfire/private.h>
44 #include <pakfire/problem.h>
45 #include <pakfire/repo.h>
46 #include <pakfire/request.h>
47 #include <pakfire/scriptlet.h>
48 #include <pakfire/snapshot.h>
49 #include <pakfire/solution.h>
50 #include <pakfire/string.h>
51 #include <pakfire/util.h>
52
53 #define CCACHE_DIR "/var/cache/ccache"
54
55 // We guarantee 2 GiB of memory to every build container
56 #define PAKFIRE_BUILD_MEMORY_GUARANTEED (size_t)2 * 1024 * 1024 * 1024
57
58 // We allow only up to 2048 processes/threads for every build container
59 #define PAKFIRE_BUILD_PID_LIMIT (size_t)2048
60
61 struct pakfire_build {
62 struct pakfire* pakfire;
63 int nrefs;
64
65 // Flags
66 int flags;
67
68 // Build ID
69 uuid_t id;
70 char _id[UUID_STR_LEN];
71
72 char target[PATH_MAX];
73
74 // Times
75 time_t time_start;
76
77 // cgroup
78 struct pakfire_cgroup* cgroup;
79
80 // Jail
81 struct pakfire_jail* jail;
82
83 // The build repository
84 struct pakfire_repo* repo;
85
86 // A list of all built packages
87 struct pakfire_packagelist* packages;
88
89 // Buildroot
90 char buildroot[PATH_MAX];
91
92 // States
93 int init:1;
94 };
95
96 #define TEMPLATE \
97 "#!/bin/bash --login\n" \
98 "\n" \
99 "set -e\n" \
100 "set -x\n" \
101 "\n" \
102 "%%{_%s}\n" \
103 "\n" \
104 "exit 0\n"
105
106 static int pakfire_build_has_flag(struct pakfire_build* build, int flag) {
107 return build->flags & flag;
108 }
109
110 static time_t pakfire_build_duration(struct pakfire_build* build) {
111 // What time is it now?
112 time_t now = time(NULL);
113
114 // Return any errors
115 if (now < 0)
116 return now;
117
118 return now - build->time_start;
119 }
120
121 static int __pakfire_build_setup_repo(struct pakfire* pakfire,
122 struct pakfire_repo* repo, void* p) {
123 char path[PATH_MAX];
124 FILE* f = NULL;
125 int r;
126
127 struct pakfire_build* build = (struct pakfire_build*)p;
128
129 // Skip processing the installed repository
130 if (pakfire_repo_is_installed_repo(repo))
131 return 0;
132
133 // Skip processing any other internal repositories
134 if (pakfire_repo_is_internal(repo))
135 return 0;
136
137 const char* name = pakfire_repo_get_name(repo);
138
139 DEBUG(pakfire, "Exporting repository configuration for '%s'\n", name);
140
141 // Make path for configuration file
142 r = pakfire_path(build->pakfire, path, PAKFIRE_CONFIG_DIR "/repos/%s.repo", name);
143 if (r) {
144 ERROR(pakfire, "Could not make repository configuration path for %s: %m\n", name);
145 goto ERROR;
146 }
147
148 // Create the parent directory
149 r = pakfire_mkparentdir(path, 0755);
150 if (r)
151 goto ERROR;
152
153 // Open the repository configuration
154 f = fopen(path, "w");
155 if (!f) {
156 ERROR(pakfire, "Could not open %s for writing: %m\n", path);
157 goto ERROR;
158 }
159
160 // Write repository configuration
161 r = pakfire_repo_write_config(repo, f);
162 if (r) {
163 ERROR(pakfire, "Could not write repository configuration for %s: %m\n", name);
164 goto ERROR;
165 }
166
167 // Bind-mount any local repositories
168 if (pakfire_repo_is_local(repo)) {
169 const char* _path = pakfire_repo_get_path(repo);
170
171 // Bind-mount the repository data read-only
172 r = pakfire_jail_bind(build->jail, _path, _path, MS_RDONLY);
173 if (r) {
174 ERROR(pakfire, "Could not bind-mount the repository at %s: %m\n", _path);
175 goto ERROR;
176 }
177 }
178
179 ERROR:
180 if (f)
181 fclose(f);
182
183 return r;
184 }
185
186 /*
187 This function enables the local repository so that it can be accessed by
188 a pakfire instance running inside the chroot.
189 */
190 static int pakfire_build_enable_repos(struct pakfire_build* build) {
191 return pakfire_repo_walk(build->pakfire, __pakfire_build_setup_repo, build);
192 }
193
194 /*
195 Drops the user into a shell
196 */
197 static int pakfire_build_shell(struct pakfire_build* build) {
198 int r;
199
200 // Export local repository if running in interactive mode
201 r = pakfire_build_enable_repos(build);
202 if (r)
203 return r;
204
205 // Run shell
206 return pakfire_jail_shell(build->jail);
207 }
208
209 static int pakfire_build_read_script(struct pakfire_build* build,
210 const char* filename, char** buffer, size_t* length) {
211 char path[PATH_MAX];
212 FILE* f = NULL;
213 int r;
214
215 // Compose the source path
216 r = pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename);
217 if (r) {
218 ERROR(build->pakfire, "Could not compose path for script '%s': %m\n", filename);
219 goto ERROR;
220 }
221
222 DEBUG(build->pakfire, "Reading script from %s...\n", path);
223
224 // Open the file
225 f = fopen(path, "r");
226 if (!f) {
227 ERROR(build->pakfire, "Could not open script %s: %m\n", path);
228 goto ERROR;
229 }
230
231 // Read the file into a the buffer
232 r = pakfire_read_file_into_buffer(f, buffer, length);
233 if (r) {
234 ERROR(build->pakfire, "Could not read script: %m\n");
235 goto ERROR;
236 }
237
238 ERROR:
239 if (f)
240 fclose(f);
241
242 return r;
243 }
244
245 static int pakfire_build_run_script(
246 struct pakfire_build* build,
247 const char* filename,
248 const char* args[],
249 pakfire_jail_communicate_in communicate_in,
250 pakfire_jail_communicate_out communicate_out,
251 void* data) {
252 int r;
253
254 char* script = NULL;
255 size_t length = 0;
256
257 DEBUG(build->pakfire, "Running build script '%s'...\n", filename);
258
259 // Read the script
260 r = pakfire_build_read_script(build, filename, &script, &length);
261 if (r) {
262 ERROR(build->pakfire, "Could not read script %s: %m\n", filename);
263 return r;
264 }
265
266 // Execute the script
267 r = pakfire_jail_exec_script(build->jail, script, length, args,
268 communicate_in, communicate_out, data);
269 if (r) {
270 ERROR(build->pakfire, "Script '%s' failed with status %d\n", filename, r);
271 }
272
273 if (script)
274 free(script);
275
276 return r;
277 }
278
279 struct pakfire_find_deps_ctx {
280 struct pakfire_package* pkg;
281 int dep;
282 struct pakfire_scriptlet* scriptlet;
283 int class;
284 const pcre2_code* filter;
285
286 struct pakfire_filelist* filelist;
287 unsigned int i;
288 };
289
290 static int pakfire_build_make_filter(struct pakfire_build* build, pcre2_code** regex,
291 struct pakfire_parser* makefile, const char* namespace, const char* filter) {
292 char* pattern = NULL;
293 int r = 0;
294
295 // Fetch the pattern
296 pattern = pakfire_parser_get(makefile, namespace, filter);
297
298 // Nothing if to if there is no or an empty pattern
299 if (!pattern || !*pattern)
300 goto ERROR;
301
302 DEBUG(build->pakfire, "Found filter for %s: %s\n", filter, pattern);
303
304 // Compile the regular expression
305 r = pakfire_compile_regex(build->pakfire, regex, pattern);
306 if (r)
307 goto ERROR;
308
309 ERROR:
310 if (pattern)
311 free(pattern);
312
313 return r;
314 }
315
316 static int pakfire_build_send_filelist(struct pakfire* pakfire, void* data, int fd) {
317 struct pakfire_find_deps_ctx* ctx = (struct pakfire_find_deps_ctx*)data;
318 struct pakfire_file* file = NULL;
319 int r = 0;
320
321 const size_t length = pakfire_filelist_length(ctx->filelist);
322
323 // Check if we have reached the end of the filelist
324 if (ctx->i >= length)
325 return EOF;
326
327 // Fetch the next file
328 file = pakfire_filelist_get(ctx->filelist, ctx->i);
329 if (!file) {
330 DEBUG(pakfire, "Could not fetch file %d: %m\n", ctx->i);
331 r = 1;
332 goto ERROR;
333 }
334
335 // Fetch the path of the file
336 const char* path = pakfire_file_get_path(file);
337 if (!path) {
338 ERROR(pakfire, "Received a file with an empty path\n");
339 r = 1;
340 goto ERROR;
341 }
342
343 // Skip files that don't match what we are looking for
344 if (ctx->class && !pakfire_file_matches_class(file, ctx->class))
345 goto SKIP;
346
347 // Write path to stdin
348 r = dprintf(fd, "%s\n", path);
349 if (r < 0)
350 return r;
351
352 SKIP:
353 // Move on to the next file
354 ctx->i++;
355
356 // Success
357 r = 0;
358
359 ERROR:
360 if (file)
361 pakfire_file_unref(file);
362
363 return r;
364 }
365
366 static int pakfire_build_process_deps(struct pakfire* pakfire,
367 void* data, int priority, const char* buffer, const size_t length) {
368 const struct pakfire_find_deps_ctx* ctx = (struct pakfire_find_deps_ctx*)data;
369 char dep[PATH_MAX];
370 pcre2_match_data* match = NULL;
371 int r = 0;
372
373 // Nothing to do for an empty buffer
374 if (!buffer || !*buffer)
375 return 0;
376
377 switch (priority) {
378 // Add every dependency that we have received
379 case LOG_INFO:
380 // Copy the dependency to the stack (and remove the trailing newline)
381 r = pakfire_string_format(dep, "%.*s", (int)length - 1, buffer);
382 if (r)
383 return r;
384
385 DEBUG(pakfire, "Processing dependency: %s\n", dep);
386
387 // Filter out any dependencies that are provided by this package
388 if (ctx->dep == PAKFIRE_PKG_REQUIRES) {
389 // If this is a file, we check if it is on the filelist
390 if (pakfire_filelist_contains(ctx->filelist, dep))
391 goto SKIP;
392
393 // Otherwise check if this dependency is provided by this package
394 else if (pakfire_package_matches_dep(ctx->pkg, PAKFIRE_PKG_PROVIDES, dep))
395 goto SKIP;
396 }
397
398 // Check if this dependency should be filtered
399 if (ctx->filter) {
400 match = pcre2_match_data_create_from_pattern(ctx->filter, NULL);
401 if (!match) {
402 ERROR(pakfire, "Could not allocate PCRE match data: %m\n");
403 goto ERROR;
404 }
405
406 // Perform matching
407 r = pcre2_jit_match(ctx->filter, (PCRE2_SPTR)dep, length - 1,
408 0, 0, match, NULL);
409
410 // No match
411 if (r == PCRE2_ERROR_NOMATCH) {
412 // Fall through...
413
414 // Handle any errors
415 } else if (r < 0) {
416 char error[120];
417
418 // Fetch the error message
419 r = pcre2_get_error_message(r, (PCRE2_UCHAR*)error, sizeof(error));
420 if (r < 0) {
421 ERROR(pakfire, "Could not fetch PCRE error message: %m\n");
422 r = 1;
423 goto ERROR;
424 }
425
426 ERROR(pakfire, "Could not match the filter: %s\n", error);
427 r = 1;
428 goto ERROR;
429
430 // Match!
431 } else {
432 DEBUG(pakfire, "Skipping dependency that has been filtered: %s\n", dep);
433 r = 0;
434 goto ERROR;
435 }
436 }
437
438 // Add dependency
439 r = pakfire_package_add_dep(ctx->pkg, ctx->dep, buffer);
440 if (r) {
441 ERROR(pakfire, "Could not process dependency '%s': %m\n", buffer);
442 return r;
443 }
444 break;
445
446 // Send everything else to the default logger
447 default:
448 ERROR(pakfire, "%s\n", buffer);
449 break;
450 }
451
452 goto ERROR;
453
454 SKIP:
455 DEBUG(pakfire, "Skipping dependency that is provided by the package itself: %s\n", dep);
456
457 ERROR:
458 if (match)
459 pcre2_match_data_free(match);
460
461 return r;
462 }
463
464 /*
465 This function is a special way to run a script.
466
467 It will pipe the filelist into the standard input of the called script
468 and will read any dependencies from the standard output.
469 */
470 static int pakfire_build_find_deps(struct pakfire_build* build,
471 struct pakfire_package* pkg, int dep, const char* script,
472 struct pakfire_filelist* filelist, int class, const pcre2_code* filter) {
473 // Construct the context
474 struct pakfire_find_deps_ctx ctx = {
475 .pkg = pkg,
476 .dep = dep,
477 .class = class,
478 .filter = filter,
479
480 // Filelist
481 .filelist = filelist,
482 .i = 0,
483 };
484 int r;
485
486 // Skip calling the script if class doesn't match
487 if (class && !pakfire_filelist_matches_class(filelist, class)) {
488 DEBUG(build->pakfire, "Skipping calling %s as class does not match\n", script);
489 return 0;
490 }
491
492 // Pass the buildroot as first argument
493 const char* args[] = {
494 pakfire_relpath(build->pakfire, build->buildroot),
495 NULL,
496 };
497
498 // Run the script
499 r = pakfire_build_run_script(build, script, args,
500 pakfire_build_send_filelist, pakfire_build_process_deps, &ctx);
501 if (r)
502 ERROR(build->pakfire, "%s returned with error %d\n", script, r);
503
504 return r;
505 }
506
507 static int pakfire_build_find_dependencies(struct pakfire_build* build,
508 struct pakfire_parser* makefile, const char* namespace,
509 struct pakfire_package* pkg, struct pakfire_filelist* filelist) {
510 pcre2_code* filter_provides = NULL;
511 pcre2_code* filter_requires = NULL;
512 int r;
513
514 // Fetch the provides filter
515 r = pakfire_build_make_filter(build, &filter_provides,
516 makefile, namespace, "filter_provides");
517 if (r) {
518 ERROR(build->pakfire, "Provides filter is broken: %m\n");
519 goto ERROR;
520 }
521
522 // Fetch the requires filter
523 r = pakfire_build_make_filter(build, &filter_requires,
524 makefile, namespace, "filter_requires");
525 if (r) {
526 ERROR(build->pakfire, "Requires filter is broken: %m\n");
527 goto ERROR;
528 }
529
530 // Find all provides
531 r = pakfire_build_find_deps(build, pkg,
532 PAKFIRE_PKG_PROVIDES, "find-provides", filelist, 0, filter_provides);
533 if (r)
534 goto ERROR;
535
536 // Find all Perl provides
537 r = pakfire_build_find_deps(build, pkg,
538 PAKFIRE_PKG_PROVIDES, "perl.prov", filelist, PAKFIRE_FILE_PERL, filter_provides);
539 if (r)
540 goto ERROR;
541
542 // Find all requires
543 r = pakfire_build_find_deps(build, pkg,
544 PAKFIRE_PKG_REQUIRES, "find-requires", filelist, 0, filter_requires);
545 if (r)
546 goto ERROR;
547
548 // Find all Perl requires
549 r = pakfire_build_find_deps(build, pkg,
550 PAKFIRE_PKG_REQUIRES, "perl.req", filelist, PAKFIRE_FILE_PERL, filter_requires);
551 if (r)
552 goto ERROR;
553
554 ERROR:
555 if (filter_provides)
556 pcre2_code_free(filter_provides);
557 if (filter_requires)
558 pcre2_code_free(filter_requires);
559
560 return r;
561 }
562
563 static int __pakfire_build_package_mark_config_files(
564 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
565 char** configfiles = (char**)data;
566 int r;
567
568 // Skip anything that isn't a regular file
569 if (pakfire_file_get_type(file) == S_IFREG)
570 return 0;
571
572 // Fetch path
573 const char* path = pakfire_file_get_path(file);
574
575 // Check if any configfile entry matches against this
576 for (char** configfile = configfiles; *configfile; configfile++) {
577 if (pakfire_string_startswith(path, *configfile)) {
578 r = pakfire_file_set_flags(file, PAKFIRE_FILE_CONFIG);
579 if (r)
580 return r;
581
582 // Done
583 break;
584 }
585 }
586
587 return 0;
588 }
589
590 static int pakfire_build_package_mark_config_files(struct pakfire_build* build,
591 struct pakfire_filelist* filelist, struct pakfire_parser* makefile, const char* namespace) {
592 char** configfiles = NULL;
593 int r;
594
595 // Fetch configfiles from makefile
596 r = pakfire_parser_get_filelist(makefile, namespace, "configfiles", &configfiles, NULL);
597
598 // If configfiles is not set, there is nothing to do
599 if (!configfiles)
600 return 0;
601
602 r = pakfire_filelist_walk(filelist, __pakfire_build_package_mark_config_files, configfiles);
603
604 // Cleanup
605 if (configfiles) {
606 for (char** configfile = configfiles; *configfile; configfile++)
607 free(*configfile);
608 free(configfiles);
609 }
610
611 return r;
612 }
613
614 static int pakfire_build_package_add_files(struct pakfire_build* build,
615 struct pakfire_parser* makefile, const char* buildroot, const char* namespace,
616 struct pakfire_package* pkg, struct pakfire_packager* packager) {
617 struct pakfire_filelist* filelist = NULL;
618 char** includes = NULL;
619 char** excludes = NULL;
620 int r;
621
622 // Fetch filelist from makefile
623 r = pakfire_parser_get_filelist(makefile, namespace, "files", &includes, &excludes);
624
625 // No files to package?
626 if (!includes && !excludes)
627 return 0;
628
629 // Allocate a new filelist
630 r = pakfire_filelist_create(&filelist, build->pakfire);
631 if (r)
632 goto ERROR;
633
634 // Scan for files
635 r = pakfire_filelist_scan(filelist, build->buildroot,
636 (const char**)includes, (const char**)excludes);
637 if (r)
638 goto ERROR;
639
640 DEBUG(build->pakfire, "%zu file(s) found\n", pakfire_filelist_length(filelist));
641
642 // Nothing to do if the filelist is empty
643 if (pakfire_filelist_is_empty(filelist))
644 goto ERROR;
645
646 // Dump the filelist
647 pakfire_filelist_dump(filelist, PAKFIRE_FILE_DUMP_FULL);
648
649 // Find dependencies
650 r = pakfire_build_find_dependencies(build, makefile, namespace, pkg, filelist);
651 if (r) {
652 ERROR(build->pakfire, "Finding dependencies failed: %m\n");
653 goto ERROR;
654 }
655
656 // Mark configuration files
657 r = pakfire_build_package_mark_config_files(build, filelist, makefile, namespace);
658 if (r)
659 goto ERROR;
660
661 // Add all files to the package
662 r = pakfire_packager_add_files(packager, filelist);
663 if (r)
664 goto ERROR;
665
666 ERROR:
667 if (filelist)
668 pakfire_filelist_unref(filelist);
669 if (includes) {
670 for (char** include = includes; *include; include++)
671 free(*include);
672 free(includes);
673 }
674 if (excludes) {
675 for (char** exclude = excludes; *exclude; exclude++)
676 free(*exclude);
677 free(excludes);
678 }
679
680 return r;
681 }
682
683 static int pakfire_build_send_scriptlet(struct pakfire* pakfire, void* data, int fd) {
684 const struct pakfire_find_deps_ctx* ctx = (struct pakfire_find_deps_ctx*)data;
685 size_t length = 0;
686
687 // Fetch the scriptlet
688 const char* p = pakfire_scriptlet_get_data(ctx->scriptlet, &length);
689 if (!p) {
690 ERROR(pakfire, "Could not fetch scriptlet: %m\n");
691 return 1;
692 }
693
694 // Write it into the pipe
695 ssize_t bytes_written = write(fd, p, length);
696 if (bytes_written < 0) {
697 ERROR(pakfire, "Could not send scriptlet: %m\n");
698 return 1;
699 }
700
701 return EOF;
702 }
703
704 static int pakfire_build_add_scriptlet_requires(struct pakfire_build* build,
705 struct pakfire_package* pkg, struct pakfire_scriptlet* scriptlet) {
706 int r;
707
708 struct pakfire_find_deps_ctx ctx = {
709 .pkg = pkg,
710 .dep = PAKFIRE_PKG_PREREQUIRES,
711 .scriptlet = scriptlet,
712 };
713
714 // Find all pre-requires
715 r = pakfire_build_run_script(build, "find-prerequires", NULL,
716 pakfire_build_send_scriptlet, pakfire_build_process_deps, &ctx);
717 if (r)
718 goto ERROR;
719
720 ERROR:
721 return r;
722 }
723
724 static int pakfire_build_package_add_scriptlet(struct pakfire_build* build,
725 struct pakfire_package* pkg, struct pakfire_packager* packager,
726 const char* type, const char* data) {
727 struct pakfire_scriptlet* scriptlet = NULL;
728 char* shell = NULL;
729 int r;
730
731 // Wrap scriptlet into a shell script
732 r = asprintf(&shell, "#!/bin/sh\n\nset -e\n\n%s\n\nexit 0\n", data);
733 if (r < 0)
734 goto ERROR;
735
736 // Create a scriptlet
737 r = pakfire_scriptlet_create(&scriptlet, build->pakfire, type, shell, 0);
738 if (r)
739 goto ERROR;
740
741 // Add it to the package
742 r = pakfire_packager_add_scriptlet(packager, scriptlet);
743 if (r) {
744 ERROR(build->pakfire, "Could not add scriptlet %s\n", type);
745 goto ERROR;
746 }
747
748 // Add scriptlet requirements
749 r = pakfire_build_add_scriptlet_requires(build, pkg, scriptlet);
750 if (r) {
751 ERROR(build->pakfire, "Could not add scriptlet requirements: %m\n");
752 goto ERROR;
753 }
754
755 // Success
756 r = 0;
757
758 ERROR:
759 if (scriptlet)
760 pakfire_scriptlet_unref(scriptlet);
761 if (shell)
762 free(shell);
763
764 return r;
765 }
766
767 static int pakfire_build_package_add_scriptlets(struct pakfire_build* build,
768 struct pakfire_parser* makefile, const char* namespace,
769 struct pakfire_package* pkg, struct pakfire_packager* packager) {
770 char name[NAME_MAX];
771 int r;
772
773 for (const char** type = pakfire_scriptlet_types; *type; type++) {
774 r = pakfire_string_format(name, "scriptlet:%s", *type);
775 if (r)
776 return r;
777
778 // Fetch the scriptlet
779 char* data = pakfire_parser_get(makefile, namespace, name);
780 if (!data)
781 continue;
782
783 // Add it to the package
784 r = pakfire_build_package_add_scriptlet(build, pkg, packager, *type, data);
785 if (r) {
786 free(data);
787 return r;
788 }
789
790 free(data);
791 }
792
793 return 0;
794 }
795
796 static int pakfire_build_package(struct pakfire_build* build, struct pakfire_parser* makefile,
797 const char* buildroot, const char* namespace) {
798 struct pakfire_package* pkg = NULL;
799 struct pakfire_packager* packager = NULL;
800
801 int r = 1;
802
803 // Expand the handle into the package name
804 char* name = pakfire_parser_expand(makefile, "packages", namespace);
805 if (!name) {
806 ERROR(build->pakfire, "Could not get package name: %m\n");
807 goto ERROR;
808 }
809
810 INFO(build->pakfire, "Building package '%s'...\n", name);
811 DEBUG(build->pakfire, " buildroot = %s\n", buildroot);
812
813 // Fetch build architecture
814 const char* arch = pakfire_get_arch(build->pakfire);
815 if (!arch)
816 goto ERROR;
817
818 // Fetch package from makefile
819 r = pakfire_parser_create_package(makefile, &pkg, NULL, namespace, arch);
820 if (r) {
821 ERROR(build->pakfire, "Could not create package from makefile: %m\n");
822 goto ERROR;
823 }
824
825 // Set distribution
826 const char* distribution = pakfire_parser_get(makefile, NULL, "DISTRO_NAME");
827 if (distribution) {
828 r = pakfire_package_set_string(pkg, PAKFIRE_PKG_DISTRO, distribution);
829 if (r)
830 goto ERROR;
831 }
832
833 // Set build ID
834 pakfire_package_set_uuid(pkg, PAKFIRE_PKG_BUILD_ID, build->id);
835
836 // Set source package
837 const char* source_name = pakfire_parser_get(makefile, NULL, "name");
838 if (source_name) {
839 r = pakfire_package_set_string(pkg, PAKFIRE_PKG_SOURCE_NAME, source_name);
840 if (r)
841 goto ERROR;
842 }
843
844 // Set source EVR
845 const char* source_evr = pakfire_parser_get(makefile, NULL, "evr");
846 if (source_evr) {
847 r = pakfire_package_set_string(pkg, PAKFIRE_PKG_SOURCE_EVR, source_evr);
848 if (r)
849 goto ERROR;
850 }
851
852 // Set source arch
853 r = pakfire_package_set_string(pkg, PAKFIRE_PKG_SOURCE_ARCH, "src");
854 if (r)
855 goto ERROR;
856
857 // Create a packager
858 r = pakfire_packager_create(&packager, build->pakfire, pkg);
859 if (r)
860 goto ERROR;
861
862 // Add files
863 r = pakfire_build_package_add_files(build, makefile, buildroot, namespace,
864 pkg, packager);
865 if (r)
866 goto ERROR;
867
868 // Add scriptlets
869 r = pakfire_build_package_add_scriptlets(build, makefile, namespace,
870 pkg, packager);
871 if (r)
872 goto ERROR;
873
874 const char* path = pakfire_repo_get_path(build->repo);
875
876 // Write the finished package
877 r = pakfire_packager_finish_to_directory(packager, path, NULL);
878 if (r) {
879 ERROR(build->pakfire, "pakfire_packager_finish() failed: %m\n");
880 goto ERROR;
881 }
882
883 // Cleanup all packaged files
884 r = pakfire_packager_cleanup(packager);
885 if (r)
886 goto ERROR;
887
888 // Success
889 r = 0;
890
891 ERROR:
892 if (packager)
893 pakfire_packager_unref(packager);
894 if (pkg)
895 pakfire_package_unref(pkg);
896 if (name)
897 free(name);
898
899 return r;
900 }
901
902 static int pakfire_build_package_dump(struct pakfire* pakfire,
903 struct pakfire_package* pkg, void* p) {
904 char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
905 if (!dump)
906 return 1;
907
908 INFO(pakfire, "%s\n", dump);
909 free(dump);
910
911 return 0;
912 }
913
914 static int pakfire_build_packages(struct pakfire_build* build,
915 struct pakfire_parser* makefile) {
916 DEBUG(build->pakfire, "Creating packages...");
917 int r = 1;
918
919 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
920
921 // Fetch a list all all packages
922 char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*");
923 if (!packages) {
924 ERROR(build->pakfire, "Could not find any packages: %m\n");
925 goto ERROR;
926 }
927
928 unsigned int num_packages = 0;
929
930 // Count how many packages we have
931 for (char** package = packages; *package; package++)
932 num_packages++;
933
934 DEBUG(build->pakfire, "Found %d package(s)\n", num_packages);
935
936 // Build packages in reverse order
937 for (int i = num_packages - 1; i >= 0; i--) {
938 r = pakfire_build_package(build, makefile, buildroot, packages[i]);
939 if (r)
940 goto ERROR;
941 }
942
943 // Rescan the build repository to import all packages again
944 r = pakfire_repo_scan(build->repo, 0);
945 if (r)
946 goto ERROR;
947
948 // Create a new packagelist
949 r = pakfire_packagelist_create(&build->packages, build->pakfire);
950 if (r)
951 goto ERROR;
952
953 // Fetch all packages
954 r = pakfire_repo_to_packagelist(build->repo, build->packages);
955 if (r)
956 goto ERROR;
957
958 // Dump them all
959 r = pakfire_packagelist_walk(build->packages, pakfire_build_package_dump, NULL);
960 if (r)
961 goto ERROR;
962
963 // Success
964 r = 0;
965
966 ERROR:
967 if (packages)
968 free(packages);
969
970 return r;
971 }
972
973 static int pakfire_build_stage(struct pakfire_build* build,
974 struct pakfire_parser* makefile, const char* stage) {
975 char template[1024];
976
977 // Prepare template for this stage
978 int r = pakfire_string_format(template, TEMPLATE, stage);
979 if (r)
980 return r;
981
982 // Fetch the environment
983 char** envp = pakfire_parser_make_environ(makefile);
984
985 // Create the build script
986 char* script = pakfire_parser_expand(makefile, "build", template);
987 if (!script) {
988 ERROR(build->pakfire, "Could not generate the build script for stage '%s': %m\n",
989 stage);
990 goto ERROR;
991 }
992
993 INFO(build->pakfire, "Running build stage '%s'\n", stage);
994
995 // Import environment
996 // XXX is this a good idea?
997 r = pakfire_jail_import_env(build->jail, (const char**)envp);
998 if (r) {
999 ERROR(build->pakfire, "Could not import environment: %m\n");
1000 goto ERROR;
1001 }
1002
1003 // Run the script
1004 r = pakfire_jail_exec_script(build->jail, script, strlen(script), NULL, NULL, NULL, NULL);
1005 if (r) {
1006 ERROR(build->pakfire, "Build stage '%s' failed with status %d\n", stage, r);
1007 }
1008
1009 ERROR:
1010 if (envp) {
1011 for (char** e = envp; *e; e++)
1012 free(*e);
1013 free(envp);
1014 }
1015 if (script)
1016 free(script);
1017
1018 return r;
1019 }
1020
1021 enum {
1022 PAKFIRE_BUILD_CLEANUP_FILES = (1 << 0),
1023 PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY = (1 << 1),
1024 };
1025
1026 /*
1027 This helper function takes a callback which is expected to add any files
1028 to the given filelist which will optionally be all removed after.
1029 */
1030 static int pakfire_build_post_process_files(struct pakfire_build* build,
1031 struct pakfire_filelist* filelist, const char* description,
1032 int (*callback)(struct pakfire* pakfire, struct pakfire_file* file, void* data),
1033 int flags) {
1034 struct pakfire_filelist* removees = NULL;
1035 int r;
1036
1037 // Create a filelist with objects that need to be removed
1038 r = pakfire_filelist_create(&removees, build->pakfire);
1039 if (r)
1040 goto ERROR;
1041
1042 // Find all files that need to be removed
1043 r = pakfire_filelist_walk(filelist, callback, removees);
1044 if (r)
1045 goto ERROR;
1046
1047 if (!pakfire_filelist_is_empty(removees)) {
1048 if (description)
1049 INFO(build->pakfire, "%s\n", description);
1050
1051 // Show all files which will be removed
1052 pakfire_filelist_dump(removees, PAKFIRE_FILE_DUMP_FULL|PAKFIRE_FILE_DUMP_HARDENING);
1053
1054 // Remove all files on the removee list
1055 if (flags & PAKFIRE_BUILD_CLEANUP_FILES) {
1056 r = pakfire_filelist_cleanup(removees);
1057 if (r)
1058 goto ERROR;
1059
1060 // Remove all files from the filelist
1061 r = pakfire_filelist_remove_all(filelist, removees);
1062 if (r)
1063 goto ERROR;
1064 }
1065
1066 // Report an error if any files have been found
1067 if (flags & PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY)
1068 r = 1;
1069 }
1070
1071 ERROR:
1072 if (removees)
1073 pakfire_filelist_unref(removees);
1074
1075 return r;
1076 }
1077
1078 static int __pakfire_build_remove_static_libraries(
1079 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
1080 struct pakfire_filelist* removees = (struct pakfire_filelist*)data;
1081 char path[PATH_MAX];
1082 int r;
1083
1084 // Find all static libraries
1085 if (pakfire_file_matches_class(file, PAKFIRE_FILE_STATIC_LIBRARY)) {
1086 // Copy the filename
1087 r = pakfire_string_set(path, pakfire_file_get_abspath(file));
1088 if (r)
1089 return -1;
1090
1091 // Remove the extension
1092 r = pakfire_path_replace_extension(path, "so");
1093 if (r)
1094 return -1;
1095
1096 // Only delete if there is a shared object with the same name
1097 if (pakfire_path_exists(path))
1098 return pakfire_filelist_add(removees, file);
1099 }
1100
1101 return 0;
1102 }
1103
1104 static int pakfire_build_post_remove_static_libraries(
1105 struct pakfire_build* build, struct pakfire_filelist* filelist) {
1106 return pakfire_build_post_process_files(build, filelist,
1107 "Removing static libaries...",
1108 __pakfire_build_remove_static_libraries,
1109 PAKFIRE_BUILD_CLEANUP_FILES);
1110 }
1111
1112 static int __pakfire_build_post_check_stripped(
1113 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
1114 struct pakfire_filelist* filelist = (struct pakfire_filelist*)data;
1115 int r;
1116
1117 // Skip anything that isn't an ELF file
1118 if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF))
1119 return 0;
1120
1121 // Collect all stripped files
1122 if (pakfire_file_is_stripped(file)) {
1123 r = pakfire_filelist_add(filelist, file);
1124 if (r) {
1125 ERROR(pakfire, "Could not add file to filelist: %m\n");
1126 return r;
1127 }
1128 }
1129
1130 return 0;
1131 }
1132
1133 static int pakfire_build_post_check_stripped(
1134 struct pakfire_build* build, struct pakfire_filelist* filelist) {
1135 return pakfire_build_post_process_files(build, filelist,
1136 "Files lacking debugging information:",
1137 __pakfire_build_post_check_stripped,
1138 PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
1139 }
1140
1141 static int __pakfire_build_remove_libtool_archives(
1142 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
1143 struct pakfire_filelist* removees = (struct pakfire_filelist*)data;
1144
1145 // Find all libtool archive files
1146 if (pakfire_file_matches_class(file, PAKFIRE_FILE_LIBTOOL_ARCHIVE))
1147 return pakfire_filelist_add(removees, file);
1148
1149 return 0;
1150 }
1151
1152 static int pakfire_build_post_remove_libtool_archives(
1153 struct pakfire_build* build, struct pakfire_filelist* filelist) {
1154 return pakfire_build_post_process_files(build, filelist,
1155 "Removing libtool archives...",
1156 __pakfire_build_remove_libtool_archives,
1157 PAKFIRE_BUILD_CLEANUP_FILES);
1158 }
1159
1160 static int __pakfire_build_check_broken_symlinks(
1161 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
1162 struct pakfire_filelist* broken = (struct pakfire_filelist*)data;
1163 int r;
1164
1165 // Ignore anything that isn't a symlink
1166 switch (pakfire_file_get_type(file)) {
1167 case S_IFLNK:
1168 if (!pakfire_file_symlink_target_exists(file)) {
1169 r = pakfire_filelist_add(broken, file);
1170 if (r)
1171 return r;
1172 }
1173
1174 break;
1175
1176 // Ignore anything that isn't a symlink
1177 default:
1178 break;
1179 }
1180
1181 return 0;
1182 }
1183
1184 static int pakfire_build_post_check_broken_symlinks(
1185 struct pakfire_build* build, struct pakfire_filelist* filelist) {
1186 return pakfire_build_post_process_files(build, filelist,
1187 "Broken symlinks have been found:",
1188 __pakfire_build_check_broken_symlinks,
1189 PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
1190 }
1191
1192 /*
1193 Hardening
1194 */
1195
1196 static int __pakfire_build_post_check_hardening(
1197 struct pakfire* pakfire, struct pakfire_file* file, void* data) {
1198 struct pakfire_filelist* broken = (struct pakfire_filelist*)data;
1199 int issues = 0;
1200 int r;
1201
1202 // Skip anything that isn't an ELF file
1203 if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF))
1204 return 0;
1205
1206 // Check hardening
1207 r = pakfire_file_check_hardening(file, &issues);
1208 if (r) {
1209 ERROR(pakfire, "%s: Hardening Check failed: %m\n",
1210 pakfire_file_get_path(file));
1211 return r;
1212 }
1213
1214 // If any issues have been found, consider this file to be on the list
1215 if (issues) {
1216 r = pakfire_filelist_add(broken, file);
1217 if (r)
1218 return r;
1219 }
1220
1221 return 0;
1222 }
1223
1224 static int pakfire_build_post_check_hardening(
1225 struct pakfire_build* build, struct pakfire_filelist* filelist) {
1226 return pakfire_build_post_process_files(
1227 build,
1228 filelist,
1229 "Hardening Issues:",
1230 __pakfire_build_post_check_hardening,
1231 PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY);
1232 }
1233
1234 static int pakfire_build_run_post_build_checks(struct pakfire_build* build) {
1235 struct pakfire_filelist* filelist = NULL;
1236 int r;
1237
1238 // Create a filelist of all files in the build
1239 r = pakfire_filelist_create(&filelist, build->pakfire);
1240 if (r) {
1241 ERROR(build->pakfire, "Could not create filelist: %m\n");
1242 goto ERROR;
1243 }
1244
1245 // Scan for all files in BUILDROOT
1246 r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL);
1247 if (r)
1248 goto ERROR;
1249
1250 // If the filelist is empty, we can are done
1251 if (pakfire_filelist_is_empty(filelist)) {
1252 DEBUG(build->pakfire, "Empty BUILDROOT. Skipping post build checks...\n");
1253 r = 0;
1254 goto ERROR;
1255 }
1256
1257 // Check if binaries have been stripped
1258 r = pakfire_build_post_check_stripped(build, filelist);
1259 if (r)
1260 goto ERROR;
1261
1262 // Remove any static libraries
1263 r = pakfire_build_post_remove_static_libraries(build, filelist);
1264 if (r)
1265 goto ERROR;
1266
1267 // Remove any libtool archives
1268 r = pakfire_build_post_remove_libtool_archives(build, filelist);
1269 if (r)
1270 goto ERROR;
1271
1272 // Check for any broken symlinks
1273 r = pakfire_build_post_check_broken_symlinks(build, filelist);
1274 if (r)
1275 goto ERROR;
1276
1277 // Check hardening
1278 r = pakfire_build_post_check_hardening(build, filelist);
1279 if (r)
1280 goto ERROR;
1281
1282 ERROR:
1283 if (filelist)
1284 pakfire_filelist_unref(filelist);
1285
1286 return r;
1287 }
1288
1289 static const char* post_build_scripts[] = {
1290 "check-unsafe-files",
1291 "check-libraries",
1292 "check-rpaths",
1293 "check-buildroot",
1294 "check-include",
1295 "check-hardening",
1296 "check-interpreters",
1297 "check-fhs",
1298 "compress-man-pages",
1299 "strip",
1300 NULL,
1301 };
1302
1303 static int pakfire_build_run_post_build_scripts(struct pakfire_build* build) {
1304 // Fetch buildroot
1305 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
1306
1307 // Set default arguments for build scripts
1308 const char* args[] = {
1309 buildroot, NULL
1310 };
1311
1312 // Run them one by one
1313 for (const char** script = post_build_scripts; *script; script++) {
1314 int r = pakfire_build_run_script(build, *script, args, NULL, NULL, NULL);
1315 if (r)
1316 return r;
1317 }
1318
1319 return 0;
1320 }
1321
1322 static void pakfire_build_free(struct pakfire_build* build) {
1323 if (build->packages)
1324 pakfire_packagelist_unref(build->packages);
1325
1326 if (build->repo) {
1327 pakfire_repo_clean(build->repo, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY);
1328 pakfire_repo_unref(build->repo);
1329 }
1330
1331 if (build->jail)
1332 pakfire_jail_unref(build->jail);
1333
1334 if (build->cgroup) {
1335 // Destroy the cgroup
1336 pakfire_cgroup_destroy(build->cgroup);
1337
1338 // Free it
1339 pakfire_cgroup_unref(build->cgroup);
1340 }
1341
1342 pakfire_unref(build->pakfire);
1343 free(build);
1344 }
1345
1346 static int pakfire_build_parse_id(struct pakfire_build* build, const char* id) {
1347 int r;
1348
1349 // Try parsing the Build ID
1350 if (id) {
1351 r = uuid_parse(id, build->id);
1352 if (r) {
1353 ERROR(build->pakfire, "Could not parse build ID '%s'\n", id);
1354 errno = EINVAL;
1355 return r;
1356 }
1357
1358 // Otherwise initialize the Build ID with something random
1359 } else {
1360 uuid_generate_random(build->id);
1361 }
1362
1363 // Store the ID as string, too
1364 uuid_unparse_lower(build->id, build->_id);
1365
1366 return 0;
1367 }
1368
1369 /*
1370 Sets up a new cgroup for this build
1371 */
1372 static int pakfire_build_setup_cgroup(struct pakfire_build* build) {
1373 struct pakfire_config* config = NULL;
1374 char path[PATH_MAX];
1375 int r;
1376
1377 // Compose path
1378 r = pakfire_string_format(path, "pakfire/build-%s", build->_id);
1379 if (r) {
1380 ERROR(build->pakfire, "Could not compose path for cgroup: %m\n");
1381 goto ERROR;
1382 }
1383
1384 // Create a new cgroup
1385 r = pakfire_cgroup_open(&build->cgroup, build->pakfire, path,
1386 PAKFIRE_CGROUP_ENABLE_ACCOUNTING);
1387 if (r) {
1388 ERROR(build->pakfire, "Could not create cgroup for build %s: %m\n", build->_id);
1389 goto ERROR;
1390 }
1391
1392 // Fetch config
1393 config = pakfire_get_config(build->pakfire);
1394 if (!config)
1395 goto ERROR;
1396
1397 // Guarantee some minimum memory
1398 size_t memory_guaranteed = pakfire_config_get_bytes(config, "build",
1399 "memory_guaranteed", PAKFIRE_BUILD_MEMORY_GUARANTEED);
1400 if (memory_guaranteed) {
1401 r = pakfire_cgroup_set_guaranteed_memory(build->cgroup, memory_guaranteed);
1402 if (r)
1403 goto ERROR;
1404 }
1405
1406 // Limit memory
1407 size_t memory_limit = pakfire_config_get_bytes(config, "build", "memory_limit", 0);
1408 if (memory_limit) {
1409 r = pakfire_cgroup_set_memory_limit(build->cgroup, memory_limit);
1410 if (r)
1411 goto ERROR;
1412 }
1413
1414 // Set PID limit
1415 size_t pid_limit = pakfire_config_get_int(config, "build",
1416 "pid_limit", PAKFIRE_BUILD_PID_LIMIT);
1417 if (pid_limit) {
1418 r = pakfire_cgroup_set_pid_limit(build->cgroup, pid_limit);
1419 if (r)
1420 goto ERROR;
1421 }
1422
1423 ERROR:
1424 if (config)
1425 pakfire_config_unref(config);
1426
1427 return r;
1428 }
1429
1430 /*
1431 Sets up a new jail for this build
1432 */
1433 static int pakfire_build_setup_jail(struct pakfire_build* build) {
1434 int r;
1435
1436 // Create a new jail
1437 r = pakfire_jail_create(&build->jail, build->pakfire, 0);
1438 if (r) {
1439 ERROR(build->pakfire, "Could not create jail for build %s: %m\n", build->_id);
1440 return r;
1441 }
1442
1443 // Connect the jail to our cgroup
1444 r = pakfire_jail_set_cgroup(build->jail, build->cgroup);
1445 if (r) {
1446 ERROR(build->pakfire, "Could not set cgroup for jail: %m\n");
1447 return r;
1448 }
1449
1450 // Done
1451 return 0;
1452 }
1453
1454 /*
1455 Sets up the ccache for this build
1456 */
1457 static int pakfire_build_setup_ccache(struct pakfire_build* build) {
1458 char path[PATH_MAX];
1459 int r;
1460
1461 // Check if we want a ccache
1462 if (pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_CCACHE)) {
1463 DEBUG(build->pakfire, "ccache usage has been disabled for this build\n");
1464
1465 // Set CCACHE_DISABLE=1 so that if ccache is installed, it will disable itself
1466 r = pakfire_jail_set_env(build->jail, "CCACHE_DISABLE", "1");
1467 if (r) {
1468 ERROR(build->pakfire, "Could not disable ccache: %m\n");
1469 return r;
1470 }
1471
1472 return 0;
1473 }
1474
1475 // Compose path
1476 r = pakfire_cache_path(build->pakfire, path, "%s", "ccache");
1477 if (r) {
1478 ERROR(build->pakfire, "Could not compose ccache path: %m\n");
1479 return 1;
1480 }
1481
1482 DEBUG(build->pakfire, "Mounting ccache from %s\n", path);
1483
1484 // Ensure path exists
1485 r = pakfire_mkdir(path, 0755);
1486 if (r && errno != EEXIST) {
1487 ERROR(build->pakfire, "Could not create ccache directory %s: %m\n", path);
1488 return r;
1489 }
1490
1491 // Bind-mount the directory
1492 r = pakfire_jail_bind(build->jail, path, CCACHE_DIR, MS_NOSUID|MS_NOEXEC|MS_NODEV);
1493 if (r) {
1494 ERROR(build->pakfire, "Could not mount ccache: %m\n");
1495 return r;
1496 }
1497
1498 return 0;
1499 }
1500
1501 static int pakfire_build_setup_repo(struct pakfire_build* build) {
1502 char path[PATH_MAX] = PAKFIRE_TMP_DIR "/pakfire-build-repo.XXXXXX";
1503 char url[PATH_MAX];
1504 int r;
1505
1506 // Create a new repository
1507 r = pakfire_repo_create(&build->repo, build->pakfire, PAKFIRE_REPO_RESULT);
1508 if (r) {
1509 ERROR(build->pakfire, "Could not create repository %s: %m", PAKFIRE_REPO_RESULT);
1510 return r;
1511 }
1512
1513 // Set description
1514 pakfire_repo_set_description(build->repo, _("Build Repository"));
1515
1516 // Create a temporary directory
1517 const char* p = pakfire_mkdtemp(path);
1518 if (!p) {
1519 ERROR(build->pakfire, "Could not create a the build repository: %m\n");
1520 return 1;
1521 }
1522
1523 // Format the URL
1524 r = pakfire_string_format(url, "file://%s", path);
1525 if (r)
1526 return r;
1527
1528 // Set the URL
1529 pakfire_repo_set_baseurl(build->repo, url);
1530
1531 return r;
1532 }
1533
1534 static int pakfire_build_set_time_start(struct pakfire_build* build) {
1535 const time_t now = time(NULL);
1536
1537 if (now < 0) {
1538 ERROR(build->pakfire, "Could not fetch start time: %m\n");
1539 return 1;
1540 }
1541
1542 build->time_start = now;
1543
1544 return 0;
1545 }
1546
1547 PAKFIRE_EXPORT int pakfire_build_create(struct pakfire_build** build,
1548 struct pakfire* pakfire, const char* id, int flags) {
1549 int r;
1550
1551 // Allocate build object
1552 struct pakfire_build* b = calloc(1, sizeof(*b));
1553 if (!b)
1554 return 1;
1555
1556 // Reference pakfire
1557 b->pakfire = pakfire_ref(pakfire);
1558
1559 // Initialize reference counter
1560 b->nrefs = 1;
1561
1562 // Copy flags
1563 b->flags = flags;
1564
1565 // Store start time
1566 r = pakfire_build_set_time_start(b);
1567 if (r)
1568 goto ERROR;
1569
1570 // Parse ID
1571 r = pakfire_build_parse_id(b, id);
1572 if (r)
1573 goto ERROR;
1574
1575 // Setup repo
1576 r = pakfire_build_setup_repo(b);
1577 if (r)
1578 goto ERROR;
1579
1580 // Create cgroup
1581 r = pakfire_build_setup_cgroup(b);
1582 if (r)
1583 goto ERROR;
1584
1585 // Create jail
1586 r = pakfire_build_setup_jail(b);
1587 if (r)
1588 goto ERROR;
1589
1590 // Setup ccache
1591 r = pakfire_build_setup_ccache(b);
1592 if (r)
1593 goto ERROR;
1594
1595 *build = b;
1596 return 0;
1597
1598 ERROR:
1599 pakfire_build_free(b);
1600 return r;
1601 }
1602
1603 PAKFIRE_EXPORT struct pakfire_build* pakfire_build_ref(struct pakfire_build* build) {
1604 ++build->nrefs;
1605
1606 return build;
1607 }
1608
1609 PAKFIRE_EXPORT struct pakfire_build* pakfire_build_unref(struct pakfire_build* build) {
1610 if (--build->nrefs > 0)
1611 return build;
1612
1613 pakfire_build_free(build);
1614 return NULL;
1615 }
1616
1617 PAKFIRE_EXPORT int pakfire_build_set_target(
1618 struct pakfire_build* build, const char* target) {
1619 return pakfire_string_set(build->target, target);
1620 }
1621
1622 static int pakfire_build_install_packages(struct pakfire_build* build,
1623 int* snapshot_needs_update) {
1624 int r;
1625
1626 const char* packages[] = {
1627 "build-essential",
1628 NULL,
1629 };
1630
1631 int changed = 0;
1632
1633 // Install everything
1634 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, 0,
1635 &changed, NULL, NULL);
1636 if (r) {
1637 ERROR(build->pakfire, "Could not install build dependencies: %m\n");
1638 return r;
1639 }
1640
1641 // Mark snapshot as changed if new packages were installed
1642 if (changed)
1643 *snapshot_needs_update = 1;
1644
1645 // Update everything
1646 r = pakfire_sync(build->pakfire, 0, 0, &changed, NULL, NULL);
1647 if (r) {
1648 ERROR(build->pakfire, "Could not update packages: %m\n");
1649 return r;
1650 }
1651
1652 // Has anything changed?
1653 if (changed)
1654 *snapshot_needs_update = 1;
1655
1656 return 0;
1657 }
1658
1659 /*
1660 Initializes the build environment
1661 */
1662 static int pakfire_build_init(struct pakfire_build* build) {
1663 FILE* f = NULL;
1664 char path[PATH_MAX];
1665 int r;
1666
1667 // Don't do it again
1668 if (build->init) {
1669 DEBUG(build->pakfire, "Build environment has already been initialized\n");
1670 return 0;
1671 }
1672
1673 // Compose path for snapshot
1674 r = pakfire_cache_path(build->pakfire, path, "%s", "snapshot.tar.zst");
1675 if (r) {
1676 ERROR(build->pakfire, "Could not compose snapshot path: %m\n");
1677 return 1;
1678 }
1679
1680 // Tells us whether we need to (re-)create the snapshot
1681 int snapshot_needs_update = 0;
1682
1683 // Extract snapshot
1684 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT)) {
1685 // Open the snapshot
1686 f = fopen(path, "r");
1687
1688 // Try restoring the snapshot
1689 if (f) {
1690 r = pakfire_snapshot_restore(build->pakfire, f);
1691 fclose(f);
1692
1693 // Exit on error
1694 if (r)
1695 return r;
1696 }
1697 }
1698
1699 // Install or update any build dependencies
1700 r = pakfire_build_install_packages(build, &snapshot_needs_update);
1701 if (r)
1702 return r;
1703
1704 // Update the snapshot if there were changes
1705 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_SNAPSHOT) && snapshot_needs_update) {
1706 // Open snapshot file for writing
1707 f = fopen(path, "w");
1708 if (!f) {
1709 ERROR(build->pakfire, "Could not open snapshot file for writing: %m\n");
1710 return 1;
1711 }
1712
1713 // Create a new snapshot
1714 r = pakfire_snapshot_create(build->pakfire, f);
1715 fclose(f);
1716
1717 if (r)
1718 return r;
1719 }
1720
1721 // Mark as initialized
1722 build->init = 1;
1723
1724 return 0;
1725 }
1726
1727 static int pakfire_build_read_makefile(struct pakfire_build* build,
1728 struct pakfire_parser** parser, struct pakfire_package* package) {
1729 char path[PATH_MAX];
1730 int r;
1731
1732 struct pakfire_parser_error* error = NULL;
1733
1734 const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA);
1735 const char* name = pakfire_package_get_string(package, PAKFIRE_PKG_NAME);
1736
1737 // Compose path to makefile
1738 r = pakfire_path(build->pakfire, path, "/usr/src/packages/%s/%s.nm", nevra, name);
1739 if (r < 0)
1740 return 1;
1741
1742 // Read makefile
1743 r = pakfire_read_makefile(parser, build->pakfire, path, &error);
1744 if (r) {
1745 if (error) {
1746 ERROR(build->pakfire, "Could not parse makefile %s: %s\n", path,
1747 pakfire_parser_error_get_message(error));
1748 } else {
1749 ERROR(build->pakfire, "Could not parse makefile %s: %m\n", path);
1750 }
1751
1752 goto ERROR;
1753 }
1754
1755 // Set BUILDROOT
1756 const char* buildroot = pakfire_relpath(build->pakfire, build->buildroot);
1757 if (buildroot)
1758 pakfire_parser_set(*parser, NULL, "BUILDROOT", buildroot, 0);
1759
1760 ERROR:
1761 if (error)
1762 pakfire_parser_error_unref(error);
1763
1764 return r;
1765 }
1766
1767 static int pakfire_build_perform(struct pakfire_build* build,
1768 struct pakfire_parser* makefile) {
1769 int r;
1770
1771 // Prepare the build
1772 r = pakfire_build_stage(build, makefile, "prepare");
1773 if (r)
1774 goto ERROR;
1775
1776 // Perform the build
1777 r = pakfire_build_stage(build, makefile, "build");
1778 if (r)
1779 goto ERROR;
1780
1781 // Test the build
1782 if (!pakfire_build_has_flag(build, PAKFIRE_BUILD_DISABLE_TESTS)) {
1783 r = pakfire_build_stage(build, makefile, "test");
1784 if (r)
1785 goto ERROR;
1786 }
1787
1788 // Install everything
1789 r = pakfire_build_stage(build, makefile, "install");
1790 if (r)
1791 goto ERROR;
1792
1793 // Run post build checks
1794 r = pakfire_build_run_post_build_checks(build);
1795 if (r) {
1796 ERROR(build->pakfire, "Post build checks failed\n");
1797 goto ERROR;
1798 }
1799
1800 // Run post build scripts
1801 r = pakfire_build_run_post_build_scripts(build);
1802 if (r)
1803 goto ERROR;
1804
1805 // Done!
1806 return 0;
1807
1808 ERROR:
1809 // Drop to a shell for debugging
1810 if (pakfire_build_has_flag(build, PAKFIRE_BUILD_INTERACTIVE))
1811 pakfire_build_shell(build);
1812
1813 return r;
1814 }
1815
1816 static int __pakfire_build_unpackaged_file(struct pakfire* pakfire,
1817 struct pakfire_file* file, void* p) {
1818 char* s = pakfire_file_dump(file, PAKFIRE_FILE_DUMP_FULL);
1819 if (s) {
1820 ERROR(pakfire, "%s\n", s);
1821 free(s);
1822 }
1823
1824 return 0;
1825 }
1826
1827 static int pakfire_build_check_unpackaged_files(struct pakfire_build* build) {
1828 struct pakfire_filelist* filelist = NULL;
1829 int r;
1830
1831 // Create a new filelist
1832 r = pakfire_filelist_create(&filelist, build->pakfire);
1833 if (r)
1834 goto ERROR;
1835
1836 // Scan for all files in BUILDROOT
1837 r = pakfire_filelist_scan(filelist, build->buildroot, NULL, NULL);
1838 if (r)
1839 goto ERROR;
1840
1841 if (!pakfire_filelist_is_empty(filelist)) {
1842 ERROR(build->pakfire, "Unpackaged files found:\n");
1843
1844 r = pakfire_filelist_walk(filelist, __pakfire_build_unpackaged_file, NULL);
1845 if (r)
1846 goto ERROR;
1847
1848 // Report an error
1849 r = 1;
1850 }
1851
1852 ERROR:
1853 if (filelist)
1854 pakfire_filelist_unref(filelist);
1855
1856 return r;
1857 }
1858
1859 static int pakfire_build_install_package(struct pakfire* pakfire,
1860 struct pakfire_package* pkg, void* p) {
1861 struct pakfire_request* request = (struct pakfire_request*)p;
1862
1863 return pakfire_request_add_package(request, PAKFIRE_REQ_INSTALL, pkg,
1864 PAKFIRE_REQUEST_ESSENTIAL);
1865 }
1866
1867 static int pakfire_build_install_test(struct pakfire_build* build) {
1868 struct pakfire_request* request = NULL;
1869 struct pakfire_problem* problem = NULL;
1870 struct pakfire_solution* solution = NULL;
1871 const char* s = NULL;
1872 int r;
1873
1874 // Create a new request
1875 r = pakfire_request_create(&request, build->pakfire, 0);
1876 if (r)
1877 goto ERROR;
1878
1879 // Add all packages
1880 r = pakfire_packagelist_walk(build->packages, pakfire_build_install_package, request);
1881
1882 // Solve the request
1883 r = pakfire_request_solve(request);
1884 switch (r) {
1885 // All okay
1886 case 0:
1887 break;
1888
1889 // Dependency Error
1890 case 2:
1891 ERROR(build->pakfire, "Install test failed:\n");
1892
1893 // Walk through all problems
1894 for (;;) {
1895 r = pakfire_request_next_problem(request, &problem);
1896 if (r)
1897 goto ERROR;
1898
1899 // There are no more problems
1900 if (!problem)
1901 break;
1902
1903 // Format the problem into something human-readable
1904 s = pakfire_problem_to_string(problem);
1905 if (!s)
1906 continue;
1907
1908 ERROR(build->pakfire, " * %s\n", s);
1909
1910 // Walk through all solutions
1911 for (;;) {
1912 r = pakfire_problem_next_solution(problem, &solution);
1913 if (r)
1914 goto ERROR;
1915
1916 // There are no more solutions
1917 if (!solution)
1918 break;
1919
1920 // Format the solution into something human-readable
1921 s = pakfire_solution_to_string(solution);
1922 if (!s)
1923 continue;
1924
1925 ERROR(build->pakfire, " * %s\n", s);
1926 }
1927 }
1928
1929 break;
1930
1931 // Any other errors
1932 default:
1933 goto ERROR;
1934 }
1935
1936 ERROR:
1937 if (r)
1938 ERROR(build->pakfire, "Install test failed: %m\n");
1939 if (request)
1940 pakfire_request_unref(request);
1941 if (problem)
1942 pakfire_problem_unref(problem);
1943 if (solution)
1944 pakfire_solution_unref(solution);
1945
1946 return r;
1947 }
1948
1949 static int pakfire_build_post_check(struct pakfire_build* build) {
1950 int r;
1951
1952 // Check for unpackaged files
1953 r = pakfire_build_check_unpackaged_files(build);
1954 if (r)
1955 return r;
1956
1957 // Perform install test
1958 r = pakfire_build_install_test(build);
1959 if (r)
1960 return r;
1961
1962 return 0;
1963 }
1964
1965 static int pakfire_build_copy_package(struct pakfire* pakfire,
1966 struct pakfire_package* pkg, void* p) {
1967 struct pakfire_archive* archive = NULL;
1968 char path[PATH_MAX];
1969 int r;
1970
1971 const char* target = (const char*)p;
1972
1973 if (!target) {
1974 errno = EINVAL;
1975 return 1;
1976 }
1977
1978 // Fetch the package filename
1979 const char* filename = pakfire_package_get_string(pkg, PAKFIRE_PKG_FILENAME);
1980 if (!filename) {
1981 r = 1;
1982 goto ERROR;
1983 }
1984
1985 // Format the destination path
1986 r = pakfire_string_format(path, "%s/%s", target, filename);
1987 if (r)
1988 goto ERROR;
1989
1990 // Open the archive
1991 archive = pakfire_package_get_archive(pkg);
1992 if (!archive) {
1993 r = 1;
1994 goto ERROR;
1995 }
1996
1997 // Copy it to its destination
1998 r = pakfire_archive_copy(archive, path);
1999 if (r)
2000 goto ERROR;
2001
2002 ERROR:
2003 if (archive)
2004 pakfire_archive_unref(archive);
2005
2006 return r;
2007 }
2008
2009 static int pakfire_build_copy_packages(struct pakfire_build* build) {
2010 struct pakfire_repo* local = NULL;
2011 int r = 0;
2012
2013 DEBUG(build->pakfire, "Copying built packages\n");
2014
2015 // Fetch local repository
2016 local = pakfire_get_repo(build->pakfire, PAKFIRE_REPO_LOCAL);
2017
2018 // Copy all packages to the target path
2019 if (*build->target) {
2020 r = pakfire_packagelist_walk(build->packages,
2021 pakfire_build_copy_package, build->target);
2022 if (r)
2023 goto ERROR;
2024 }
2025
2026 // If we have a local repository, we copy all packages to it
2027 if (local) {
2028 const char* path = pakfire_repo_get_path(local);
2029 if (path) {
2030 r = pakfire_packagelist_walk(build->packages,
2031 pakfire_build_copy_package, (void*)path);
2032 if (r)
2033 goto ERROR;
2034 }
2035 }
2036
2037 ERROR:
2038 if (local)
2039 pakfire_repo_unref(local);
2040
2041 return r;
2042 }
2043
2044 static int pakfire_build_install_source_package(
2045 struct pakfire_build* build, struct pakfire_package* package) {
2046 struct pakfire_request* request = NULL;
2047 struct pakfire_transaction* transaction = NULL;
2048 int r;
2049
2050 // Create a new request
2051 r = pakfire_request_create(&request, build->pakfire, 0);
2052 if (r)
2053 goto ERROR;
2054
2055 // Add the package
2056 r = pakfire_request_add_package(request, PAKFIRE_REQ_INSTALL, package,
2057 PAKFIRE_REQUEST_ESSENTIAL);
2058 if (r)
2059 goto ERROR;
2060
2061 // Solve the request
2062 r = pakfire_request_solve(request);
2063 if (r)
2064 goto ERROR;
2065
2066 // Fetch the transaction
2067 r = pakfire_request_get_transaction(request, &transaction);
2068 if (r)
2069 goto ERROR;
2070
2071 // Set how many packages have been changed
2072 const size_t changes = pakfire_transaction_count(transaction);
2073
2074 // Sanity check to see if we actually try to install anything
2075 if (!changes) {
2076 ERROR(build->pakfire, "The source package did not get installed\n");
2077 r = 1;
2078 goto ERROR;
2079 }
2080
2081 // Run the transaction
2082 r = pakfire_transaction_run(transaction, 0);
2083 if (r)
2084 goto ERROR;
2085
2086 ERROR:
2087 if (transaction)
2088 pakfire_transaction_unref(transaction);
2089 if (request)
2090 pakfire_request_unref(request);
2091
2092 return r;
2093 }
2094
2095 PAKFIRE_EXPORT int pakfire_build_exec(struct pakfire_build* build, const char* path) {
2096 struct pakfire_package* package = NULL;
2097 struct pakfire_parser* makefile = NULL;
2098 char* buildroot = NULL;
2099 char duration[TIME_STRING_MAX];
2100 int r;
2101
2102 // Set buildroot
2103 r = pakfire_path(build->pakfire, build->buildroot, "%s",
2104 PAKFIRE_TMP_DIR "/pakfire-buildroot.XXXXXX");
2105 if (r)
2106 goto ERROR;
2107
2108 // Open the source package
2109 r = pakfire_commandline_add(build->pakfire, path, &package);
2110 if (r)
2111 goto ERROR;
2112
2113 const char* nevra = pakfire_package_get_string(package, PAKFIRE_PKG_NEVRA);
2114
2115 INFO(build->pakfire, "Building %s...\n", nevra);
2116
2117 // Initialize the build environment
2118 r = pakfire_build_init(build);
2119 if (r)
2120 goto ERROR;
2121
2122 // Install the source package
2123 r = pakfire_build_install_source_package(build, package);
2124 if (r) {
2125 ERROR(build->pakfire, "Could not install the source package: %m\n");
2126 goto ERROR;
2127 }
2128
2129 // Create BUILDROOT
2130 buildroot = pakfire_mkdtemp(build->buildroot);
2131 if (!buildroot) {
2132 ERROR(build->pakfire, "Could not create BUILDROOT: %m\n");
2133 goto ERROR;
2134 }
2135
2136 // Open the makefile
2137 r = pakfire_build_read_makefile(build, &makefile, package);
2138 if (r)
2139 goto ERROR;
2140
2141 // Perform the actual build
2142 r = pakfire_build_perform(build, makefile);
2143 if (r)
2144 goto ERROR;
2145
2146 // Create the packages
2147 r = pakfire_build_packages(build, makefile);
2148 if (r) {
2149 ERROR(build->pakfire, "Could not create packages: %m\n");
2150 goto ERROR;
2151 }
2152
2153 // Perform post build checks
2154 r = pakfire_build_post_check(build);
2155 if (r)
2156 goto ERROR;
2157
2158 // Copy packages to their destination
2159 r = pakfire_build_copy_packages(build);
2160 if (r)
2161 goto ERROR;
2162
2163 // Log duration
2164 r = pakfire_format_time(duration, pakfire_build_duration(build));
2165 if (r)
2166 goto ERROR;
2167
2168 INFO(build->pakfire, "Build successfully completed in %s\n", duration);
2169
2170 ERROR:
2171 if (makefile)
2172 pakfire_parser_unref(makefile);
2173 if (package)
2174 pakfire_package_unref(package);
2175
2176 // Cleanup buildroot
2177 if (buildroot)
2178 pakfire_rmtree(buildroot, 0);
2179
2180 return r;
2181 }
2182
2183 /*
2184 Compatibility function to keep the legacy API.
2185 */
2186 PAKFIRE_EXPORT int pakfire_build(struct pakfire* pakfire, const char* path,
2187 const char* target, const char* id, int flags) {
2188 struct pakfire_build* build = NULL;
2189 int r;
2190
2191 // Check if path is set
2192 if (!path) {
2193 errno = EINVAL;
2194 return 1;
2195 }
2196
2197 // Create a new build environment
2198 r = pakfire_build_create(&build, pakfire, id, flags);
2199 if (r)
2200 goto ERROR;
2201
2202 // Set target
2203 if (target) {
2204 r = pakfire_build_set_target(build, target);
2205 if (r)
2206 goto ERROR;
2207 }
2208
2209 // Run build
2210 r = pakfire_build_exec(build, path);
2211
2212 ERROR:
2213 if (build)
2214 pakfire_build_unref(build);
2215
2216 return r;
2217 }
2218
2219 int pakfire_build_clean(struct pakfire* pakfire, int flags) {
2220 struct pakfire_repo* local = NULL;
2221 int r = 0;
2222
2223 // Fetch local repository
2224 local = pakfire_get_repo(pakfire, PAKFIRE_REPO_LOCAL);
2225 if (!local) {
2226 ERROR(pakfire, "Could not find repository %s: %m\n", PAKFIRE_REPO_LOCAL);
2227 goto ERROR;
2228 }
2229
2230 // Destroy everything in it
2231 r = pakfire_repo_clean(local, PAKFIRE_REPO_CLEAN_FLAGS_DESTROY);
2232 if (r)
2233 goto ERROR;
2234
2235 ERROR:
2236 if (local)
2237 pakfire_repo_unref(local);
2238
2239 return r;
2240 }
2241
2242 /*
2243 This is a convenience function that sets up a build environment and
2244 then drops the user into an interactive shell.
2245 */
2246 PAKFIRE_EXPORT int pakfire_shell(struct pakfire* pakfire, const char** packages, int flags) {
2247 struct pakfire_build* build = NULL;
2248 int r;
2249
2250 // Shells are always interactive
2251 flags |= PAKFIRE_BUILD_INTERACTIVE;
2252
2253 // Create a new build environment
2254 r = pakfire_build_create(&build, pakfire, NULL, flags);
2255 if (r) {
2256 ERROR(pakfire, "Could not create build: %m\n");
2257 goto ERROR;
2258 }
2259
2260 // Initialize the build environment
2261 r = pakfire_build_init(build);
2262 if (r)
2263 goto ERROR;
2264
2265 // Install any additional packages
2266 if (packages) {
2267 r = pakfire_install(build->pakfire, 0, 0, packages, NULL, 0, NULL, NULL, NULL);
2268 if (r)
2269 return r;
2270 }
2271
2272 // Run shell
2273 r = pakfire_build_shell(build);
2274
2275 ERROR:
2276 if (build)
2277 pakfire_build_unref(build);
2278
2279 return r;
2280 }