]> git.ipfire.org Git - people/ms/pakfire.git/blob - src/libpakfire/dist.c
util: Use our custom mktemp function everywhere
[people/ms/pakfire.git] / src / libpakfire / dist.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 <fts.h>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #include <glob.h>
26 #include <stddef.h>
27 #include <stdlib.h>
28
29 #include <pakfire/dist.h>
30 #include <pakfire/downloader.h>
31 #include <pakfire/logging.h>
32 #include <pakfire/package.h>
33 #include <pakfire/packager.h>
34 #include <pakfire/pakfire.h>
35 #include <pakfire/parser.h>
36 #include <pakfire/private.h>
37 #include <pakfire/repo.h>
38 #include <pakfire/types.h>
39 #include <pakfire/util.h>
40
41 // XXX for now
42 #define BASEURL "https://source.ipfire.org/source-3.x/"
43
44 #define PAKFIRE_MACROS_DIR "/usr/lib/pakfire/macros"
45 #define PAKFIRE_MACROS_GLOB_PATTERN PAKFIRE_MACROS_DIR "/*.macro"
46
47 PAKFIRE_EXPORT int pakfire_read_makefile(PakfireParser* parser, Pakfire pakfire,
48 const char* path, struct pakfire_parser_error** error) {
49 int r = 1;
50
51 *parser = pakfire_parser_create(pakfire, NULL, NULL, PAKFIRE_PARSER_FLAGS_EXPAND_COMMANDS);
52 if (!*parser) {
53 r = 1;
54 goto ERROR;
55 }
56
57 // Set BASEDIR
58 char* dirname = pakfire_dirname(path);
59 if (dirname) {
60 const char* root = pakfire_get_path(pakfire);
61
62 pakfire_parser_set(*parser, NULL, "BASEDIR", pakfire_path_relpath(root, dirname));
63 free(dirname);
64 }
65
66 // Find all macros
67
68 DEBUG(pakfire, "Searching for macros in %s\n", PAKFIRE_MACROS_GLOB_PATTERN);
69
70 glob_t globmacros;
71 r = glob(PAKFIRE_MACROS_GLOB_PATTERN, 0, NULL, &globmacros);
72
73 // Handle any errors
74 switch (r) {
75 case 0:
76 case GLOB_NOMATCH:
77 break;
78
79 case GLOB_NOSPACE:
80 errno = ENOMEM;
81 goto ERROR;
82
83 case GLOB_ABORTED:
84 goto ERROR;
85
86 default:
87 ERROR(pakfire, "glob() returned an unhandled error: %d\n", r);
88 goto ERROR;
89 }
90
91 DEBUG(pakfire, "Found %zu macro(s)\n", globmacros.gl_pathc);
92
93 // Read all macros
94 for (unsigned int i = 0; i < globmacros.gl_pathc; i++) {
95 // Parse the file
96 r = pakfire_parser_read_file(*parser, globmacros.gl_pathv[i], error);
97 if (r)
98 goto ERROR;
99 }
100
101 globfree(&globmacros);
102
103 // Finally, parse the makefile
104 r = pakfire_parser_read_file(*parser, path, error);
105 if (r)
106 goto ERROR;
107
108 return 0;
109
110 ERROR:
111 globfree(&globmacros);
112
113 if (*parser) {
114 pakfire_parser_unref(*parser);
115 *parser = NULL;
116 }
117
118 return r;
119 }
120
121 static int pakfire_dist_create_downloader_and_mirrorlist(
122 Pakfire pakfire, PakfireParser makefile,
123 struct pakfire_downloader** downloader, struct pakfire_mirrorlist** mirrorlist) {
124 // Create the downloader
125 int r = pakfire_downloader_create(downloader, pakfire);
126 if (r) {
127 ERROR(pakfire, "Could not initialize downloader\n");
128 return r;
129 }
130
131 // Fetch source_dl
132 char** source_dls = pakfire_parser_get_split(makefile, NULL, "source_dl", ' ');
133
134 // We do not need to create a mirrorlist if this isn't set
135 if (!source_dls)
136 return 0;
137
138 // Create mirrorlist
139 r = pakfire_mirrorlist_create(mirrorlist, pakfire);
140 if (r) {
141 ERROR(pakfire, "Could not create the mirrorlist\n");
142 goto ERROR;
143 }
144
145 // Add all mirrors
146 for (char** source_dl = source_dls; *source_dl; source_dl++) {
147 r = pakfire_mirrorlist_add_mirror(*mirrorlist, *source_dl);
148 if (r)
149 goto ERROR;
150 }
151
152 // Success
153 r = 0;
154
155 ERROR:
156 if (source_dls) {
157 for (char** source_dl = source_dls; *source_dl; source_dl++)
158 free(*source_dl);
159 free(source_dls);
160 }
161
162 return r;
163 }
164
165 static int pakfire_dist_add_source(Pakfire pakfire, struct pakfire_packager* packager,
166 PakfirePackage pkg, struct pakfire_downloader* downloader,
167 struct pakfire_mirrorlist* mirrorlist, const char* filename) {
168 int r;
169 char archive_path[PATH_MAX];
170 char cache_path[PATH_MAX];
171
172 const char* name = pakfire_package_get_name(pkg);
173 pakfire_make_cache_path(pakfire, cache_path, "sources/%s/%s", name, filename);
174
175 // Download the file if it does not exist in the cache
176 if (access(cache_path, R_OK) != 0) {
177 r = pakfire_downloader_retrieve(downloader, BASEURL, mirrorlist,
178 NULL, filename, cache_path, 0);
179 if (r)
180 return r;
181 }
182
183 pakfire_string_format(archive_path, "files/%s", filename);
184
185 // Add file to package
186 return pakfire_packager_add(packager, cache_path, archive_path);
187 }
188
189 static int pakfire_dist_add_sources(Pakfire pakfire, struct pakfire_packager* packager,
190 PakfirePackage pkg, PakfireParser makefile) {
191 // Fetch sources
192 char** sources = pakfire_parser_get_split(makefile, NULL, "sources", ' ');
193
194 // Nothing to do if this variable is empty
195 if (!sources)
196 return 1;
197
198 struct pakfire_downloader* downloader = NULL;
199 struct pakfire_mirrorlist* mirrorlist = NULL;
200
201 // Create a downloader and mirrorlist
202 int r = pakfire_dist_create_downloader_and_mirrorlist(pakfire, makefile,
203 &downloader, &mirrorlist);
204 if (r)
205 goto ERROR;
206
207 // Add all files one by one
208 for (char** source = sources; *source; source++) {
209 DEBUG(pakfire, "Adding source file %s\n", *source);
210
211 r = pakfire_dist_add_source(pakfire, packager, pkg, downloader, mirrorlist, *source);
212 if (r) {
213 ERROR(pakfire, "Could not add '%s' to package: %s\n", *source, strerror(errno));
214 goto ERROR;
215 }
216 }
217
218 // Success
219 r = 0;
220
221 ERROR:
222 if (downloader)
223 pakfire_downloader_unref(downloader);
224 if (mirrorlist)
225 pakfire_mirrorlist_unref(mirrorlist);
226
227 if (sources) {
228 for (char** source = sources; *source; source++)
229 free(*source);
230 free(sources);
231 }
232
233 return r;
234 }
235
236 static int pakfire_dist_add_files(Pakfire pakfire, struct pakfire_packager* packager,
237 const char* path) {
238 int r = 1;
239
240 // Find the parent directory
241 char* dirname = pakfire_dirname(path);
242 if (!dirname)
243 return 1;
244
245 DEBUG(pakfire, "Adding all files in '%s' to package...\n", dirname);
246
247 char* paths[2] = {
248 dirname, NULL,
249 };
250
251 FTS* f = fts_open(paths, FTS_NOCHDIR|FTS_NOSTAT, NULL);
252 if (!f)
253 goto ERROR;
254
255 for (;;) {
256 FTSENT* fent = fts_read(f);
257 if (!fent)
258 break;
259
260 // Only handle files
261 if (fent->fts_info & FTS_F) {
262 const char* filename = pakfire_path_relpath(dirname, fent->fts_path);
263 if (filename)
264 filename++;
265
266 DEBUG(pakfire, "Adding '%s' to package...\n", filename);
267
268 r = pakfire_packager_add(packager, fent->fts_path, filename);
269 if (r)
270 goto ERROR;
271 }
272 }
273
274 // If fts_read() encountered an error, errno will be set
275 if (errno) {
276 r = 1;
277 goto ERROR;
278 }
279
280 // Success
281 r = 0;
282
283 ERROR:
284 if (f)
285 fts_close(f);
286
287 free(dirname);
288
289 return r;
290 }
291
292 PAKFIRE_EXPORT int pakfire_dist(Pakfire pakfire, const char* path, const char* target) {
293 PakfireParser makefile;
294 struct pakfire_parser_error* error = NULL;
295
296 struct pakfire_packager* packager = NULL;
297 PakfirePackage pkg = NULL;
298
299 char tempfile[PATH_MAX] = "";
300
301 // Open the target directory
302 DIR* d = opendir(target);
303 if (!d)
304 return 1;
305
306 int dfd = dirfd(d);
307
308 // Load makefile
309 int r = pakfire_read_makefile(&makefile, pakfire, path, &error);
310 if (r) {
311 if (error)
312 pakfire_parser_error_unref(error);
313
314 return r;
315 }
316
317 PakfireRepo repo = pakfire_get_repo(pakfire, "@dummy");
318 if (!repo)
319 goto ERROR;
320
321 // Get the package object
322 r = pakfire_parser_create_package(makefile, &pkg, repo, NULL, "src");
323 if (r)
324 goto ERROR;
325
326 // Create a packager
327 r = pakfire_packager_create(&packager, pkg);
328 if (r)
329 goto ERROR;
330
331 // Add all files in the directory
332 r = pakfire_dist_add_files(pakfire, packager, path);
333 if (r)
334 goto ERROR;
335
336 // Add all source files (which might need to be downloaded)
337 r = pakfire_dist_add_sources(pakfire, packager, pkg, makefile);
338 if (r)
339 goto ERROR;
340
341 snprintf(tempfile, PATH_MAX - 1, "%s/.pakfire-dist.XXXXXX", target);
342
343 // Create a temporary result file
344 FILE* f = pakfire_mktemp(tempfile);
345 if (!f)
346 goto ERROR;
347
348 // Write the finished package
349 r = pakfire_packager_finish(packager, f);
350 if (r) {
351 ERROR(pakfire, "pakfire_packager_finish() failed: %s\n", strerror(errno));
352 goto ERROR;
353 }
354
355 // Close the file
356 fclose(f);
357
358 const char* filename = pakfire_packager_filename(packager);
359 if (!filename)
360 goto ERROR;
361
362 // Move the temporary file to destination
363 r = renameat(AT_FDCWD, tempfile, dfd, filename);
364 if (r)
365 goto ERROR;
366
367 ERROR:
368 // Remove the temporary file
369 if (*tempfile)
370 unlink(tempfile);
371 closedir(d);
372
373 if (packager)
374 pakfire_packager_unref(packager);
375 if (pkg)
376 pakfire_package_unref(pkg);
377 if (repo)
378 pakfire_repo_unref(repo);
379 pakfire_parser_unref(makefile);
380
381 return r;
382 }