]> git.ipfire.org Git - people/stevee/pakfire.git/blob - src/libpakfire/transaction.c
libpakfire: Implement refcounting in Step
[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 <solv/transaction.h>
23
24 #include <pakfire/i18n.h>
25 #include <pakfire/logging.h>
26 #include <pakfire/package.h>
27 #include <pakfire/packagelist.h>
28 #include <pakfire/pool.h>
29 #include <pakfire/private.h>
30 #include <pakfire/repo.h>
31 #include <pakfire/step.h>
32 #include <pakfire/transaction.h>
33 #include <pakfire/types.h>
34 #include <pakfire/util.h>
35
36 struct _PakfireTransaction {
37 PakfirePool pool;
38 Transaction* transaction;
39
40 int nrefs;
41 };
42
43 PAKFIRE_EXPORT PakfireTransaction pakfire_transaction_create(PakfirePool pool, Transaction* trans) {
44 PakfireTransaction transaction = pakfire_calloc(1, sizeof(*transaction));
45 if (transaction) {
46 DEBUG("Allocated Transaction at %p\n", transaction);
47 transaction->nrefs = 1;
48
49 transaction->pool = pakfire_pool_ref(pool);
50
51 // Clone the transaction, so we get independent from what ever called this.
52 if (trans) {
53 transaction->transaction = transaction_create_clone(trans);
54 transaction_order(transaction->transaction, 0);
55 } else {
56 transaction->transaction = transaction_create(trans->pool);
57 }
58 }
59
60 return transaction;
61 }
62
63 PAKFIRE_EXPORT PakfireTransaction pakfire_transaction_ref(PakfireTransaction transaction) {
64 if (!transaction)
65 return NULL;
66
67 transaction->nrefs++;
68 return transaction;
69 }
70
71 void pakfire_transaction_free(PakfireTransaction transaction) {
72 pakfire_pool_unref(transaction->pool);
73
74 transaction_free(transaction->transaction);
75 pakfire_free(transaction);
76
77 DEBUG("Released Transaction at %p\n", transaction);
78 }
79
80 PAKFIRE_EXPORT PakfireTransaction pakfire_transaction_unref(PakfireTransaction transaction) {
81 if (!transaction)
82 return NULL;
83
84 if (--transaction->nrefs > 0)
85 return transaction;
86
87 pakfire_transaction_free(transaction);
88 return NULL;
89 }
90
91 PAKFIRE_EXPORT PakfirePool pakfire_transaction_get_pool(PakfireTransaction transaction) {
92 return pakfire_pool_ref(transaction->pool);
93 }
94
95 Transaction* pakfire_transaction_get_transaction(PakfireTransaction transaction) {
96 return transaction->transaction;
97 }
98
99 PAKFIRE_EXPORT size_t pakfire_transaction_count(PakfireTransaction transaction) {
100 return transaction->transaction->steps.count;
101 }
102
103 PAKFIRE_EXPORT ssize_t pakfire_transaction_installsizechange(PakfireTransaction transaction) {
104 ssize_t sizechange = transaction_calc_installsizechange(transaction->transaction);
105
106 // Convert from kbytes to bytes
107 return sizechange * 1024;
108 }
109
110 PAKFIRE_EXPORT ssize_t pakfire_transaction_downloadsize(PakfireTransaction transaction) {
111 ssize_t size = 0;
112
113 size_t steps = pakfire_transaction_count(transaction);
114 for (unsigned int i = 0; i < steps; i++) {
115 PakfireStep step = pakfire_transaction_get_step(transaction, i);
116 size += pakfire_step_get_downloadsize(step);
117
118 pakfire_step_unref(step);
119 }
120
121 return size;
122 }
123
124 PAKFIRE_EXPORT PakfireStep pakfire_transaction_get_step(PakfireTransaction transaction, int index) {
125 Transaction* trans = transaction->transaction;
126
127 if (index >= trans->steps.count)
128 return NULL;
129
130 return pakfire_step_create(transaction, trans->steps.elements[index]);
131 }
132
133 PAKFIRE_EXPORT PakfirePackageList pakfire_transaction_get_packages(PakfireTransaction transaction, pakfire_step_type_t type) {
134 PakfirePackageList packagelist = pakfire_packagelist_create();
135
136 size_t steps = pakfire_transaction_count(transaction);
137 for (unsigned int i = 0; i < steps; i++) {
138 PakfireStep step = pakfire_transaction_get_step(transaction, i);
139
140 if (pakfire_step_get_type(step) == type) {
141 PakfirePackage package = pakfire_step_get_package(step);
142 pakfire_packagelist_push(packagelist, package);
143
144 pakfire_package_unref(package);
145 }
146
147 pakfire_step_unref(step);
148 }
149
150 // Sort list in place
151 pakfire_packagelist_sort(packagelist);
152
153 return packagelist;
154 }
155
156 static void pakfire_transaction_add_headline(char** str, size_t width, const char* headline) {
157 assert(headline);
158
159 asprintf(str, "%s%s\n", *str, headline);
160 }
161
162 static void pakfire_transaction_add_newline(char** str, size_t width) {
163 asprintf(str, "%s\n", *str);
164 }
165
166 static void pakfire_transaction_add_line(char** str, size_t width, const char* name,
167 const char* arch, const char* version, const char* repo, const char* size) {
168 // XXX need to adapt to size
169 asprintf(str, "%s %-21s %-8s %-21s %-18s %6s \n", *str, name, arch, version, repo, size);
170 }
171
172 static void pakfire_transaction_add_package(char** str, size_t width, PakfirePackage pkg) {
173 PakfireRepo repo = pakfire_package_get_repo(pkg);
174
175 unsigned long long size = pakfire_package_get_size(pkg);
176 char* size_str = pakfire_format_size(size);
177
178 pakfire_transaction_add_line(str, width,
179 pakfire_package_get_name(pkg),
180 pakfire_package_get_arch(pkg),
181 pakfire_package_get_evr(pkg),
182 pakfire_repo_get_name(repo),
183 size_str
184 );
185
186 pakfire_repo_free(repo);
187 pakfire_free(size_str);
188 }
189
190 static void pakfire_transaction_add_separator(char** str, size_t width) {
191 while (width-- > 0)
192 asprintf(str, "%s=", *str);
193
194 // newline
195 asprintf(str, "%s\n", *str);
196 }
197
198 static size_t pakfire_transaction_add_section(char** str, size_t width, PakfireTransaction transaction,
199 const char* headline, pakfire_step_type_t type) {
200 PakfirePackageList list = pakfire_transaction_get_packages(transaction, type);
201
202 // Nothing to do if there are no packages in this stage
203 size_t c = pakfire_packagelist_count(list);
204 if (c == 0)
205 goto END;
206
207 // Headline
208 pakfire_transaction_add_headline(str, width, headline);
209
210 // List each package
211 for (unsigned int i = 0; i < c; i++) {
212 PakfirePackage pkg = pakfire_packagelist_get(list, i);
213 pakfire_transaction_add_package(str, width, pkg);
214 }
215
216 // newline
217 pakfire_transaction_add_newline(str, width);
218
219 END:
220 pakfire_packagelist_free(list);
221
222 return c;
223 }
224
225 static void pakfire_transaction_add_summary_line(char** str, size_t width, const char* headline, size_t pkgs) {
226 if (pkgs > 0)
227 asprintf(str, "%s%-20s %-4zu %s\n", *str, headline, pkgs, _("package(s)"));
228 }
229
230 static void pakfire_transaction_add_usage_line(char** str, size_t width, const char* headline, ssize_t size) {
231 char* s = pakfire_format_size(size);
232
233 asprintf(str, "%s%-21s: %s\n", *str, headline, s);
234
235 pakfire_free(s);
236 }
237
238 PAKFIRE_EXPORT char* pakfire_transaction_dump(PakfireTransaction transaction, size_t width) {
239 char* string = "";
240
241 // Header
242 pakfire_transaction_add_separator(&string, width);
243 pakfire_transaction_add_line(&string, width,
244 _("Package"),
245 _("Arch"),
246 _("Version"),
247 _("Repository"),
248 _("Size")
249 );
250 pakfire_transaction_add_separator(&string, width);
251
252 // Show what we are doing
253 size_t installing = pakfire_transaction_add_section(&string, width, transaction,
254 _("Installing:"), PAKFIRE_STEP_INSTALL);
255 size_t reinstalling = pakfire_transaction_add_section(&string, width, transaction,
256 _("Reinstalling:"), PAKFIRE_STEP_REINSTALL);
257 size_t updating = pakfire_transaction_add_section(&string, width, transaction,
258 _("Updating:"), PAKFIRE_STEP_UPGRADE);
259 size_t downgrading = pakfire_transaction_add_section(&string, width, transaction,
260 _("Downgrading:"), PAKFIRE_STEP_DOWNGRADE);
261 size_t removing = pakfire_transaction_add_section(&string, width, transaction,
262 _("Removing:"), PAKFIRE_STEP_ERASE);
263 size_t obsoleting = pakfire_transaction_add_section(&string, width, transaction,
264 _("Obsoleting:"), PAKFIRE_STEP_OBSOLETE);
265
266 // Summary
267 pakfire_transaction_add_headline(&string, width, _("Transaction Summary"));
268 pakfire_transaction_add_separator(&string, width);
269
270 pakfire_transaction_add_summary_line(&string, width, _("Installing:"), installing);
271 pakfire_transaction_add_summary_line(&string, width, _("Reinstalling:"), reinstalling);
272 pakfire_transaction_add_summary_line(&string, width, _("Updating:"), updating);
273 pakfire_transaction_add_summary_line(&string, width, _("Downgrading:"), downgrading);
274 pakfire_transaction_add_summary_line(&string, width, _("Removing:"), removing);
275 pakfire_transaction_add_summary_line(&string, width, _("Obsoleting:"), obsoleting);
276
277 // How much do we need to download?
278 size_t downloadsize = pakfire_transaction_downloadsize(transaction);
279 if (downloadsize > 0)
280 pakfire_transaction_add_usage_line(&string, width,
281 _("Total Download Size"), downloadsize);
282
283 // How much more space do we need?
284 ssize_t sizechange = pakfire_transaction_installsizechange(transaction);
285
286 pakfire_transaction_add_usage_line(&string, width,
287 (sizechange >= 0) ? _("Installed Size") : _("Freed Size"), sizechange);
288
289 // Remove trailing newline
290 size_t l = strlen(string) - 1;
291
292 if (l > 0 && string[l] == '\n')
293 string[l] = '\0';
294
295 DEBUG("Transaction: %s\n", string);
296
297 return string;
298 }
299
300 static int pakfire_transaction_run_steps(PakfireTransaction transaction, const pakfire_action_type action) {
301 size_t steps = pakfire_transaction_count(transaction);
302
303 // Walk through all steps
304 int r = 0;
305 for (unsigned int i = 0; i < steps; i++) {
306 PakfireStep step = pakfire_transaction_get_step(transaction, i);
307
308 // Verify the step
309 r = pakfire_step_run(step, action);
310
311 // Free memory
312 pakfire_step_unref(step);
313
314 // End loop if action was unsuccessful
315 if (r)
316 break;
317 }
318
319 return r;
320 }
321
322 PAKFIRE_EXPORT int pakfire_transaction_run(PakfireTransaction transaction) {
323 int r = 0;
324
325 // Verify steps
326 r = pakfire_transaction_run_steps(transaction, PAKFIRE_ACTION_VERIFY);
327 if (r)
328 return r;
329
330 // Execute all pre transaction actions
331 r = pakfire_transaction_run_steps(transaction, PAKFIRE_ACTION_PRETRANS);
332 if (r)
333 return r;
334
335 r = pakfire_transaction_run_steps(transaction, PAKFIRE_ACTION_EXECUTE);
336 if (r)
337 return r;
338
339 // Execute all post transaction actions
340 r = pakfire_transaction_run_steps(transaction, PAKFIRE_ACTION_POSTTRANS);
341 if (r)
342 return r;
343
344 return 0;
345 }