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