]> git.ipfire.org Git - people/stevee/pakfire.git/blob - src/libpakfire/transaction.c
Create a unified downloader for all repositories
[people/stevee/pakfire.git] / src / libpakfire / transaction.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2013 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 <assert.h>
22 #include <errno.h>
23 #include <stdlib.h>
24
25 #include <solv/transaction.h>
26
27 #include <pakfire/db.h>
28 #include <pakfire/downloader.h>
29 #include <pakfire/i18n.h>
30 #include <pakfire/logging.h>
31 #include <pakfire/package.h>
32 #include <pakfire/packagelist.h>
33 #include <pakfire/pakfire.h>
34 #include <pakfire/private.h>
35 #include <pakfire/repo.h>
36 #include <pakfire/step.h>
37 #include <pakfire/transaction.h>
38 #include <pakfire/types.h>
39 #include <pakfire/util.h>
40
41 struct _PakfireTransaction {
42 Pakfire pakfire;
43 Transaction* transaction;
44 PakfireStep* steps;
45 size_t num_steps;
46 int nrefs;
47 };
48
49 static pakfire_step_type_t transaction_get_step_type(Transaction* transaction, Id id) {
50 int type = transaction_type(transaction, id,
51 SOLVER_TRANSACTION_SHOW_ACTIVE|SOLVER_TRANSACTION_CHANGE_IS_REINSTALL);
52
53 // Translate solver types into our own types
54 switch (type) {
55 case SOLVER_TRANSACTION_INSTALL:
56 case SOLVER_TRANSACTION_MULTIINSTALL:
57 return PAKFIRE_STEP_INSTALL;
58
59 case SOLVER_TRANSACTION_REINSTALL:
60 case SOLVER_TRANSACTION_MULTIREINSTALL:
61 return PAKFIRE_STEP_REINSTALL;
62
63 case SOLVER_TRANSACTION_ERASE:
64 return PAKFIRE_STEP_ERASE;
65
66 case SOLVER_TRANSACTION_DOWNGRADE:
67 return PAKFIRE_STEP_DOWNGRADE;
68
69 case SOLVER_TRANSACTION_UPGRADE:
70 return PAKFIRE_STEP_UPGRADE;
71
72 case SOLVER_TRANSACTION_OBSOLETES:
73 return PAKFIRE_STEP_OBSOLETE;
74
75 // Anything we don't care about
76 case SOLVER_TRANSACTION_IGNORE:
77 case SOLVER_TRANSACTION_REINSTALLED:
78 case SOLVER_TRANSACTION_DOWNGRADED:
79 default:
80 return PAKFIRE_STEP_IGNORE;
81 }
82 }
83
84 PAKFIRE_EXPORT PakfireTransaction pakfire_transaction_create(Pakfire pakfire, Transaction* trans) {
85 PakfireTransaction transaction = calloc(1, sizeof(*transaction));
86 if (transaction) {
87 DEBUG(pakfire, "Allocated Transaction at %p\n", transaction);
88 transaction->nrefs = 1;
89
90 transaction->pakfire = pakfire_ref(pakfire);
91
92 // Clone the transaction, so we get independent from what ever called this.
93 if (trans) {
94 transaction->transaction = transaction_create_clone(trans);
95 transaction_order(transaction->transaction, 0);
96 } else {
97 transaction->transaction = transaction_create(trans->pool);
98 }
99
100 // Save total number of steps
101 transaction->num_steps = transaction->transaction->steps.count;
102
103 // Import all steps
104 PakfireStep* steps = transaction->steps = calloc(transaction->num_steps + 1, sizeof(*steps));
105 for (unsigned int i = 0; i < transaction->num_steps; i++) {
106 Id id = transaction->transaction->steps.elements[i];
107
108 // Get the type
109 pakfire_step_type_t type = transaction_get_step_type(transaction->transaction, id);
110
111 // Fetch the package
112 PakfirePackage pkg = pakfire_package_create_from_solvable(pakfire, id);
113
114 // Append a new step
115 *steps++ = pakfire_step_create(transaction, type, pkg);
116
117 pakfire_package_unref(pkg);
118 }
119 }
120
121 return transaction;
122 }
123
124 PAKFIRE_EXPORT PakfireTransaction pakfire_transaction_ref(PakfireTransaction transaction) {
125 if (!transaction)
126 return NULL;
127
128 transaction->nrefs++;
129 return transaction;
130 }
131
132 void pakfire_transaction_free(PakfireTransaction transaction) {
133 DEBUG(transaction->pakfire, "Releasing Transaction at %p\n", transaction);
134 pakfire_unref(transaction->pakfire);
135
136 // Release all steps
137 while (*transaction->steps)
138 pakfire_step_unref(*transaction->steps++);
139
140 transaction_free(transaction->transaction);
141 free(transaction);
142 }
143
144 PAKFIRE_EXPORT PakfireTransaction pakfire_transaction_unref(PakfireTransaction transaction) {
145 if (!transaction)
146 return NULL;
147
148 if (--transaction->nrefs > 0)
149 return transaction;
150
151 pakfire_transaction_free(transaction);
152 return NULL;
153 }
154
155 PAKFIRE_EXPORT Pakfire pakfire_transaction_get_pakfire(PakfireTransaction transaction) {
156 return pakfire_ref(transaction->pakfire);
157 }
158
159 Transaction* pakfire_transaction_get_transaction(PakfireTransaction transaction) {
160 return transaction->transaction;
161 }
162
163 PAKFIRE_EXPORT size_t pakfire_transaction_count(PakfireTransaction transaction) {
164 return transaction->num_steps;
165 }
166
167 PAKFIRE_EXPORT ssize_t pakfire_transaction_installsizechange(PakfireTransaction transaction) {
168 ssize_t sizechange = transaction_calc_installsizechange(transaction->transaction);
169
170 // Convert from kbytes to bytes
171 return sizechange * 1024;
172 }
173
174 PAKFIRE_EXPORT ssize_t pakfire_transaction_downloadsize(PakfireTransaction transaction) {
175 ssize_t size = 0;
176
177 PakfireStep* steps = transaction->steps;
178 while (*steps) {
179 PakfireStep step = *steps++;
180
181 size += pakfire_step_get_downloadsize(step);
182 }
183
184 return size;
185 }
186
187 PAKFIRE_EXPORT PakfireStep pakfire_transaction_get_step(PakfireTransaction transaction, unsigned int index) {
188 PakfireStep* steps = transaction->steps;
189
190 while (index-- && *steps)
191 steps++;
192
193 if (*steps)
194 return pakfire_step_ref(*steps);
195
196 return NULL;
197 }
198
199 PAKFIRE_EXPORT PakfirePackageList pakfire_transaction_get_packages(PakfireTransaction transaction, pakfire_step_type_t type) {
200 PakfirePackageList packagelist = pakfire_packagelist_create(transaction->pakfire);
201
202 PakfireStep* steps = transaction->steps;
203 while (*steps) {
204 PakfireStep step = *steps++;
205
206 if (pakfire_step_get_type(step) == type) {
207 PakfirePackage package = pakfire_step_get_package(step);
208 pakfire_packagelist_push(packagelist, package);
209
210 pakfire_package_unref(package);
211 }
212 }
213
214 // Sort list in place
215 pakfire_packagelist_sort(packagelist);
216
217 return packagelist;
218 }
219
220 static void pakfire_transaction_add_headline(char** str, size_t width, const char* headline) {
221 assert(headline);
222
223 asprintf(str, "%s%s\n", *str, headline);
224 }
225
226 static void pakfire_transaction_add_newline(char** str, size_t width) {
227 asprintf(str, "%s\n", *str);
228 }
229
230 static void pakfire_transaction_add_line(char** str, size_t width, const char* name,
231 const char* arch, const char* version, const char* repo, const char* size) {
232 // XXX need to adapt to size
233 asprintf(str, "%s %-21s %-8s %-21s %-18s %6s \n", *str, name, arch, version, repo, size);
234 }
235
236 static void pakfire_transaction_add_package(char** str, size_t width, PakfirePackage pkg) {
237 PakfireRepo repo = pakfire_package_get_repo(pkg);
238
239 unsigned long long size = pakfire_package_get_size(pkg);
240 char* size_str = pakfire_format_size(size);
241
242 pakfire_transaction_add_line(str, width,
243 pakfire_package_get_name(pkg),
244 pakfire_package_get_arch(pkg),
245 pakfire_package_get_evr(pkg),
246 pakfire_repo_get_name(repo),
247 size_str
248 );
249
250 pakfire_repo_unref(repo);
251 free(size_str);
252 }
253
254 static void pakfire_transaction_add_separator(char** str, size_t width) {
255 while (width-- > 0)
256 asprintf(str, "%s=", *str);
257
258 // newline
259 asprintf(str, "%s\n", *str);
260 }
261
262 static size_t pakfire_transaction_add_section(char** str, size_t width, PakfireTransaction transaction,
263 const char* headline, pakfire_step_type_t type) {
264 PakfirePackageList list = pakfire_transaction_get_packages(transaction, type);
265
266 // Nothing to do if there are no packages in this stage
267 size_t c = pakfire_packagelist_count(list);
268 if (c == 0)
269 goto END;
270
271 // Headline
272 pakfire_transaction_add_headline(str, width, headline);
273
274 // List each package
275 for (unsigned int i = 0; i < c; i++) {
276 PakfirePackage pkg = pakfire_packagelist_get(list, i);
277 pakfire_transaction_add_package(str, width, pkg);
278 pakfire_package_unref(pkg);
279 }
280
281 // newline
282 pakfire_transaction_add_newline(str, width);
283
284 END:
285 pakfire_packagelist_unref(list);
286
287 return c;
288 }
289
290 static void pakfire_transaction_add_summary_line(char** str, size_t width, const char* headline, size_t pkgs) {
291 if (pkgs > 0)
292 asprintf(str, "%s%-20s %-4zu %s\n", *str, headline, pkgs, _("package(s)"));
293 }
294
295 static void pakfire_transaction_add_usage_line(char** str, size_t width, const char* headline, ssize_t size) {
296 char* s = pakfire_format_size(size);
297
298 asprintf(str, "%s%-21s: %s\n", *str, headline, s);
299
300 free(s);
301 }
302
303 PAKFIRE_EXPORT char* pakfire_transaction_dump(PakfireTransaction transaction, size_t width) {
304 char* string = "";
305
306 // Header
307 pakfire_transaction_add_separator(&string, width);
308 pakfire_transaction_add_line(&string, width,
309 _("Package"),
310 _("Arch"),
311 _("Version"),
312 _("Repository"),
313 _("Size")
314 );
315 pakfire_transaction_add_separator(&string, width);
316
317 // Show what we are doing
318 size_t installing = pakfire_transaction_add_section(&string, width, transaction,
319 _("Installing:"), PAKFIRE_STEP_INSTALL);
320 size_t reinstalling = pakfire_transaction_add_section(&string, width, transaction,
321 _("Reinstalling:"), PAKFIRE_STEP_REINSTALL);
322 size_t updating = pakfire_transaction_add_section(&string, width, transaction,
323 _("Updating:"), PAKFIRE_STEP_UPGRADE);
324 size_t downgrading = pakfire_transaction_add_section(&string, width, transaction,
325 _("Downgrading:"), PAKFIRE_STEP_DOWNGRADE);
326 size_t removing = pakfire_transaction_add_section(&string, width, transaction,
327 _("Removing:"), PAKFIRE_STEP_ERASE);
328 size_t obsoleting = pakfire_transaction_add_section(&string, width, transaction,
329 _("Obsoleting:"), PAKFIRE_STEP_OBSOLETE);
330
331 // Summary
332 pakfire_transaction_add_headline(&string, width, _("Transaction Summary"));
333 pakfire_transaction_add_separator(&string, width);
334
335 pakfire_transaction_add_summary_line(&string, width, _("Installing:"), installing);
336 pakfire_transaction_add_summary_line(&string, width, _("Reinstalling:"), reinstalling);
337 pakfire_transaction_add_summary_line(&string, width, _("Updating:"), updating);
338 pakfire_transaction_add_summary_line(&string, width, _("Downgrading:"), downgrading);
339 pakfire_transaction_add_summary_line(&string, width, _("Removing:"), removing);
340 pakfire_transaction_add_summary_line(&string, width, _("Obsoleting:"), obsoleting);
341
342 // How much do we need to download?
343 size_t downloadsize = pakfire_transaction_downloadsize(transaction);
344 if (downloadsize > 0)
345 pakfire_transaction_add_usage_line(&string, width,
346 _("Total Download Size"), downloadsize);
347
348 // How much more space do we need?
349 ssize_t sizechange = pakfire_transaction_installsizechange(transaction);
350
351 pakfire_transaction_add_usage_line(&string, width,
352 (sizechange >= 0) ? _("Installed Size") : _("Freed Size"), sizechange);
353
354 // Remove trailing newline
355 size_t l = strlen(string) - 1;
356
357 if (l > 0 && string[l] == '\n')
358 string[l] = '\0';
359
360 DEBUG(transaction->pakfire, "Transaction: %s\n", string);
361
362 return string;
363 }
364
365 static int pakfire_transaction_run_steps(PakfireTransaction transaction,
366 struct pakfire_db* db, const pakfire_action_type_t action) {
367 int r = 0;
368
369 // Walk through all steps
370 PakfireStep* steps = transaction->steps;
371 while (*steps) {
372 PakfireStep step = *steps++;
373
374 // Verify the step
375 r = pakfire_step_run(step, db, action);
376
377 // End loop if action was unsuccessful
378 if (r) {
379 DEBUG(transaction->pakfire, "Step %p failed with code %d\n", step, r);
380 break;
381 }
382 }
383
384 return r;
385 }
386
387 PAKFIRE_EXPORT int pakfire_transaction_run(PakfireTransaction transaction) {
388 struct pakfire_db* db;
389 int r;
390
391 DEBUG(transaction->pakfire, "Running Transaction %p\n", transaction);
392
393 // Open the database
394 r = pakfire_db_open(&db, transaction->pakfire, PAKFIRE_DB_READWRITE);
395 if (r) {
396 ERROR(transaction->pakfire, "Could not open the database\n");
397 return r;
398 }
399
400 // Verify steps
401 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_VERIFY);
402 if (r)
403 goto ERROR;
404
405 // Execute all pre transaction actions
406 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_PRETRANS);
407 if (r)
408 goto ERROR;
409
410 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_EXECUTE);
411 if (r)
412 goto ERROR;
413
414 // Execute all post transaction actions
415 r = pakfire_transaction_run_steps(transaction, db, PAKFIRE_ACTION_POSTTRANS);
416 if (r)
417 goto ERROR;
418
419 DEBUG(transaction->pakfire, "The transaction has finished successfully\n");
420
421 ERROR:
422 // Free the database
423 pakfire_db_unref(db);
424
425 return r;
426 }
427
428 static int pakfire_transaction_download_package(PakfireTransaction transaction,
429 struct pakfire_downloader* downloader, PakfirePackage pkg) {
430 int r = 1;
431 PakfireRepo repo = NULL;
432 struct pakfire_mirrorlist* mirrors = NULL;
433 char* cache_path = NULL;
434
435 // Fetch the repository to download from
436 repo = pakfire_package_get_repo(pkg);
437 if (!repo)
438 goto ERROR;
439
440 // Fetch mirrorlist
441 mirrors = pakfire_repo_get_mirrors(repo);
442 if (!mirrors)
443 goto ERROR;
444
445 // Where to store the package?
446 cache_path = pakfire_package_get_cache_path(pkg);
447 if (!cache_path)
448 goto ERROR;
449
450 // What file to download?
451 const char* filename = pakfire_package_get_filename(pkg);
452 if (!filename)
453 goto ERROR;
454
455 // Add transfer to downloader
456 r = pakfire_downloader_add_transfer(downloader, mirrors, filename, cache_path);
457
458 ERROR:
459 if (cache_path)
460 free(cache_path);
461 if (mirrors)
462 pakfire_mirrorlist_unref(mirrors);
463 if (repo)
464 pakfire_repo_unref(repo);
465
466 return r;
467 }
468
469 PAKFIRE_EXPORT int pakfire_transaction_download(PakfireTransaction transaction) {
470 struct pakfire_downloader* downloader;
471 int r;
472
473 // Initialize the downloader
474 r = pakfire_downloader_create(&downloader, transaction->pakfire);
475 if (r) {
476 ERROR(transaction->pakfire, "Could not initialize downloader: %s\n",
477 strerror(errno));
478 return 1;
479 }
480
481 // Add all packages that need to be downloaded
482 for (unsigned int i = 0; i < transaction->num_steps; i++) {
483 PakfireStep step = transaction->steps[i];
484
485 // Skip all steps that do not require a download
486 if (!pakfire_step_needs_download(step))
487 continue;
488
489 // Fetch the package
490 PakfirePackage pkg = pakfire_step_get_package(step);
491
492 // Enqueue download
493 r = pakfire_transaction_download_package(transaction, downloader, pkg);
494 pakfire_package_unref(pkg);
495
496 if (r)
497 goto ERROR;
498 }
499
500 // Run the downloader
501 r = pakfire_downloader_run(downloader);
502
503 ERROR:
504 pakfire_downloader_unref(downloader);
505
506 return r;
507 }