]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/build.c
scripts: Add check for hardening
[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>
23
24#include <pakfire/build.h>
25#include <pakfire/execute.h>
26#include <pakfire/dist.h>
ae7968a7 27#include <pakfire/file.h>
1a276007 28#include <pakfire/logging.h>
4c07774f 29#include <pakfire/package.h>
48c6f2e7 30#include <pakfire/packager.h>
1a276007
MT
31#include <pakfire/parser.h>
32#include <pakfire/private.h>
33#include <pakfire/types.h>
34#include <pakfire/util.h>
35
36static const char* stages[] = {
37 "prepare",
38 "build",
39 "test",
40 "install",
41 NULL,
42};
43
44#define TEMPLATE \
45 "#!/bin/bash --login\n" \
46 "\n" \
47 "set -e\n" \
48 "set -x\n" \
49 "\n" \
50 "%%{_%s}\n" \
51 "\n" \
52 "exit 0\n"
53
29f2151d 54static int pakfire_build_run_script(Pakfire pakfire, const char* filename, const char* args[],
8d0f3a35
MT
55 pakfire_execute_logging_callback logging_callback, void* data) {
56 char* script = NULL;
57 size_t size = 0;
58 char path[PATH_MAX];
59
60 DEBUG(pakfire, "Running build script '%s'...\n", filename);
61
62 // Make the source path
63 pakfire_path_join(path, PAKFIRE_SCRIPTS_DIR, filename);
64
65 // Open the source script
66 FILE* f = fopen(path, "r");
67 if (!f) {
68 ERROR(pakfire, "Could not open %s: %s\n", path, strerror(errno));
69 return 1;
70 }
71
72 // Read the script into memory
73 int r = pakfire_read_file_into_buffer(f, &script, &size);
74 if (r) {
75 ERROR(pakfire, "Could not read script into memory: %s\n", strerror(errno));
76 goto ERROR;
77 }
78
79 // Execute it
29f2151d 80 r = pakfire_execute_script(pakfire, script, size, args, NULL, 0, logging_callback, data);
8d0f3a35
MT
81 if (r) {
82 ERROR(pakfire, "Script '%s' failed with status %d\n", filename, r);
83 }
84
85ERROR:
86 if (script)
87 free(script);
88
89 return r;
90}
91
73543ae3
MT
92static int append_to_array(const char*** array, const char* s) {
93 unsigned int length = 0;
94
95 // Determine the length of the existing array
96 if (*array) {
97 for (const char** element = *array; *element; element++)
98 length++;
99 }
100
101 // Allocate space
102 *array = reallocarray(*array, length + 2, sizeof(**array));
103 if (!*array)
104 return 1;
105
106 // Append s and terminate the array
107 (*array)[length] = s;
108 (*array)[length + 1] = NULL;
109
110 return 0;
111}
112
113static int pakfire_build_package_add_files(Pakfire pakfire, PakfireParser makefile,
114 const char* namespace, struct pakfire_packager* packager) {
115 PakfireFilelist filelist = NULL;
116 char path[PATH_MAX];
117 int r = 1;
118
119 char** files = NULL;
120 const char** includes = NULL;
121 const char** excludes = NULL;
122
123 // Fetch filelist from makefile
124 files = pakfire_parser_get_split(makefile, namespace, "files", '\n');
125
126 // No files to package?
127 if (!files)
128 return 0;
129
130 // Fetch buildroot
131 char* buildroot = pakfire_parser_get(makefile, NULL, "BUILDROOT");
132 if (!buildroot)
133 goto ERROR;
134
135 // Convert to absolute path
136 pakfire_make_path(pakfire, path, buildroot);
137
138 // Split into includes and excludes
139 for (char** file = files; *file; file++) {
140 if (**file == '!')
141 r = append_to_array(&excludes, *file);
142 else
143 r = append_to_array(&includes, *file);
144 if (r)
145 goto ERROR;
146 }
147
148 // Allocate a new filelist
149 r = pakfire_filelist_create(&filelist, pakfire);
150 if (r)
151 goto ERROR;
152
153 // Scan for files
154 r = pakfire_filelist_scan(filelist, path, includes, excludes);
155 if (r)
156 goto ERROR;
157
ae7968a7 158 const size_t length = pakfire_filelist_size(filelist);
73543ae3
MT
159 DEBUG(pakfire, "%zu file(s) found\n", length);
160
ae7968a7
MT
161 // Add all files to the package
162 for (unsigned int i = 0; i < length; i++) {
163 PakfireFile file = pakfire_filelist_get(filelist, i);
164
165 // Add the file to the package
166 r = pakfire_packager_add(
167 packager,
168 pakfire_file_get_abspath(file),
169 pakfire_file_get_path(file)
170 );
171 if (r) {
172 pakfire_file_unref(file);
173 goto ERROR;
174 }
175
176 pakfire_file_unref(file);
177 }
73543ae3
MT
178
179ERROR:
180 if (filelist)
181 pakfire_filelist_unref(filelist);
182 if (files) {
183 for (char** file = files; *file; file++)
184 free(*file);
185 free(files);
186 }
187 if (includes)
188 free(includes);
189 if (excludes)
190 free(excludes);
191 if (buildroot)
192 free(buildroot);
193
194 return r;
195}
196
48c6f2e7
MT
197static int pakfire_build_package(Pakfire pakfire, PakfireParser makefile,
198 const char* namespace, const char* target) {
4c07774f
MT
199 PakfireRepo repo = NULL;
200 PakfirePackage pkg = NULL;
48c6f2e7 201 struct pakfire_packager* packager = NULL;
73543ae3 202
a50bde9c
MT
203 int r = 1;
204
205 // Expand the handle into the package name
4c07774f 206 char* name = pakfire_parser_expand(makefile, "packages", namespace);
a50bde9c
MT
207 if (!name) {
208 ERROR(pakfire, "Could not get package name: %s\n", strerror(errno));
209 goto ERROR;
210 }
211
212 INFO(pakfire, "Building package '%s'...\n", name);
213
4c07774f
MT
214 // Fetch build architecture
215 const char* arch = pakfire_get_arch(pakfire);
216 if (!arch)
217 goto ERROR;
218
219 // Fetch the dummy repository
220 repo = pakfire_get_repo(pakfire, "@dummy");
221 if (!repo)
222 goto ERROR;
223
224 // Fetch package from makefile
225 r = pakfire_parser_create_package(makefile, &pkg, repo, namespace, arch);
226 if (r) {
227 ERROR(pakfire, "Could not create package from makefile: %s\n", strerror(errno));
228 goto ERROR;
229 }
230
48c6f2e7
MT
231 // Create a packager
232 r = pakfire_packager_create(&packager, pkg);
233 if (r)
234 goto ERROR;
235
73543ae3
MT
236 // Add files
237 r = pakfire_build_package_add_files(pakfire, makefile, namespace, packager);
238 if (r)
239 goto ERROR;
240
ae7968a7
MT
241 // Write the finished package
242 r = pakfire_packager_finish_to_directory(packager, target);
243 if (r) {
244 ERROR(pakfire, "pakfire_packager_finish() failed: %s\n", strerror(errno));
245 goto ERROR;
246 }
247
4c07774f 248#ifdef ENABLE_DEBUG
23e22751 249 char* dump = pakfire_package_dump(pkg, PAKFIRE_PKG_DUMP_LONG);
4c07774f
MT
250 if (dump) {
251 DEBUG(pakfire, "%s\n", dump);
252 free(dump);
253 }
254#endif
a50bde9c
MT
255
256 // Success
257 r = 0;
258
259ERROR:
48c6f2e7
MT
260 if (packager)
261 pakfire_packager_unref(packager);
4c07774f
MT
262 if (repo)
263 pakfire_repo_unref(repo);
264 if (pkg)
265 pakfire_package_unref(pkg);
a50bde9c
MT
266 if (name)
267 free(name);
268
269 return r;
270}
271
48c6f2e7 272static int pakfire_build_packages(Pakfire pakfire, PakfireParser makefile, const char* target) {
a50bde9c
MT
273 DEBUG(pakfire, "Creating packages...");
274 int r = 1;
275
276 // Fetch a list all all packages
277 char** packages = pakfire_parser_list_namespaces(makefile, "packages.package:*");
278 if (!packages) {
279 ERROR(pakfire, "Could not find any packages: %s\n", strerror(errno));
280 goto ERROR;
281 }
282
283 unsigned int num_packages = 0;
284
285 // Count how many packages we have
286 for (char** package = packages; *package; package++)
287 num_packages++;
288
289 DEBUG(pakfire, "Found %d package(s)\n", num_packages);
290
291 // Build packages in reverse order
292 for (int i = num_packages - 1; i >= 0; i--) {
48c6f2e7 293 r = pakfire_build_package(pakfire, makefile, packages[i], target);
a50bde9c
MT
294 if (r)
295 goto ERROR;
296 }
297
298 // Success
299 r = 0;
300
301ERROR:
302 if (packages)
303 free(packages);
304
305 return r;
306}
307
7d999600
MT
308static int pakfire_build_stage(Pakfire pakfire, PakfireParser makefile, const char* stage,
309 pakfire_execute_logging_callback logging_callback, void* data) {
1a276007
MT
310 char template[1024];
311
312 // Prepare template for this stage
313 int r = pakfire_string_format(template, TEMPLATE, stage);
314 if (r < 0)
315 return r;
316
317 // Create the build script
318 char* script = pakfire_parser_expand(makefile, "build", template);
319 if (!script) {
320 ERROR(pakfire, "Could not generate the build script for stage '%s': %s\n",
321 stage, strerror(errno));
322 goto ERROR;
323 }
324
325 INFO(pakfire, "Running build stage '%s'\n", stage);
326
29f2151d 327 r = pakfire_execute_script(pakfire, script, strlen(script), NULL, NULL, 0,
7d999600 328 logging_callback, data);
1a276007
MT
329 if (r) {
330 ERROR(pakfire, "Build stage '%s' failed with status %d\n", stage, r);
331 }
332
333ERROR:
334 if (script)
335 free(script);
336
337 return r;
338}
339
39f45b30
MT
340static const char* post_build_scripts[] = {
341 "remove-static-libs",
1f62ffae 342 "check-symlinks",
022b794e 343 "check-unsafe-files",
99aee237 344 "check-buildroot",
7e1fec6f 345 "check-include",
dd864160 346 "check-hardening",
39f45b30 347 "compress-man-pages",
ffb65de6 348 "strip",
39f45b30
MT
349 NULL,
350};
351
352static int pakfire_build_run_post_build_scripts(Pakfire pakfire, const char* buildroot,
353 pakfire_execute_logging_callback logging_callback, void* data) {
354 // Set default arguments for build scripts
355 const char* args[] = {
356 buildroot, NULL
357 };
358
359 // Run them one by one
360 for (const char** script = post_build_scripts; *script; script++) {
361 int r = pakfire_build_run_script(pakfire, *script, args, logging_callback, data);
362 if (r)
363 return r;
364 }
365
366 return 0;
367}
368
1a276007 369PAKFIRE_EXPORT int pakfire_build(Pakfire pakfire, const char* path,
7d999600
MT
370 const char* target, int flags,
371 pakfire_execute_logging_callback logging_callback, void* data) {
1a276007 372 PakfireParser makefile = NULL;
3d4011eb 373 char buildroot[PATH_MAX];
1a276007 374 struct pakfire_parser_error* error = NULL;
3d4011eb 375 int r;
1a276007 376
48c6f2e7
MT
377 // Check for valid input
378 if (!path) {
379 errno = EINVAL;
380 return 1;
381 }
382
383 // The default target is the local repository path
384 if (!target) {
385 PakfireRepo repo = pakfire_get_repo(pakfire, "local");
386 if (!repo) {
387 errno = EINVAL;
388 return 1;
389 }
390
391 target = pakfire_repo_get_path(repo);
392 pakfire_repo_unref(repo);
393 }
394
3d4011eb
MT
395 const char* root = pakfire_get_path(pakfire);
396
397 // Create BUILDROOT
398 pakfire_make_path(pakfire, buildroot, "/tmp/.buildroot.XXXXXX");
399 if (!pakfire_mkdtemp(buildroot))
400 return 1;
401
402 // Make relative BUILDROOT path
403 const char* buildroot_rel = pakfire_path_relpath(root, buildroot);
404 if (!buildroot_rel)
405 return 1;
406
1a276007 407 // Read makefile
3d4011eb 408 r = pakfire_read_makefile(&makefile, pakfire, path, &error);
1a276007
MT
409 if (r) {
410 if (error) {
411 ERROR(pakfire, "Could not parse makefile %s: %s\n", path,
412 pakfire_parser_error_get_message(error));
413 pakfire_parser_error_unref(error);
414 } else {
415 ERROR(pakfire, "Could not parse makefile %s: %s\n", path,
416 strerror(errno));
417 }
418
419 goto ERROR;
420 }
421
3d4011eb
MT
422 // Set BUILDROOT
423 pakfire_parser_set(makefile, NULL, "BUILDROOT", buildroot_rel);
424
1a276007
MT
425 // Run through all build stages
426 for (const char** stage = stages; *stage; stage++) {
7d999600 427 int r = pakfire_build_stage(pakfire, makefile, *stage, logging_callback, data);
1a276007
MT
428 if (r) {
429 // Drop to a shell for debugging
430 if (flags & PAKFIRE_BUILD_INTERACTIVE)
431 pakfire_shell(pakfire);
432
433 goto ERROR;
434 }
435 }
436
39f45b30
MT
437 // Run post build scripts
438 r = pakfire_build_run_post_build_scripts(pakfire, buildroot_rel, logging_callback, data);
8d0f3a35
MT
439 if (r)
440 goto ERROR;
441
a50bde9c 442 // Create the packages
48c6f2e7 443 r = pakfire_build_packages(pakfire, makefile, target);
a50bde9c
MT
444 if (r) {
445 ERROR(pakfire, "Could not create packages: %s\n", strerror(errno));
446 goto ERROR;
447 }
1a276007
MT
448
449ERROR:
450 if (makefile)
451 pakfire_parser_unref(makefile);
452
3d4011eb
MT
453 // Remove buildroot
454 pakfire_rmtree(buildroot, 0);
455
1a276007
MT
456 return r;
457}
458
459PAKFIRE_EXPORT int pakfire_shell(Pakfire pakfire) {
460 const char* argv[] = {
461 "/bin/bash", "--login", NULL,
462 };
463
464 const int flags =
465 PAKFIRE_EXECUTE_INTERACTIVE | PAKFIRE_EXECUTE_ENABLE_NETWORK;
466
467 return pakfire_execute(pakfire, argv, NULL, flags, NULL, NULL);
468}