]> git.ipfire.org Git - people/ms/pakfire.git/blob - src/libpakfire/request.c
archive: Change type from PakfireArchive to struct pakfire_archive
[people/ms/pakfire.git] / src / libpakfire / request.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 <ctype.h>
22 #include <errno.h>
23 #include <linux/limits.h>
24 #include <stdlib.h>
25 #include <sys/utsname.h>
26
27 #include <solv/queue.h>
28 #include <solv/selection.h>
29 #include <solv/solver.h>
30 #include <solv/transaction.h>
31
32 #ifdef ENABLE_DEBUG
33 # include <solv/solverdebug.h>
34 #endif
35
36 #include <pakfire/archive.h>
37 #include <pakfire/logging.h>
38 #include <pakfire/package.h>
39 #include <pakfire/pakfire.h>
40 #include <pakfire/private.h>
41 #include <pakfire/problem.h>
42 #include <pakfire/request.h>
43 #include <pakfire/transaction.h>
44 #include <pakfire/types.h>
45 #include <pakfire/util.h>
46
47 struct pakfire_request {
48 Pakfire pakfire;
49 int nrefs;
50
51 Solver* solver;
52 Queue jobs;
53 };
54
55 /*
56 These packages can be installed multiple times simultaneously
57 */
58 const char* pakfire_multiinstall_packages[] = {
59 "kernel",
60 "kernel-devel",
61 NULL,
62 };
63
64 static void pakfire_request_free(struct pakfire_request* request) {
65 if (request->solver)
66 solver_free(request->solver);
67 queue_free(&request->jobs);
68
69 pakfire_unref(request->pakfire);
70 free(request);
71 }
72
73 static void pakfire_request_add_packages(struct pakfire_request* request,
74 int flags, const char** packages) {
75 Pool* pool = pakfire_get_solv_pool(request->pakfire);
76 if (!pool)
77 return;
78
79 for (const char** package = packages; *package; package++) {
80 Id id = pool_str2id(pool, *package, 1);
81 if (id)
82 queue_push2(&request->jobs, flags, id);
83 }
84 }
85
86 static void pakfire_request_lock_running_kernel(struct pakfire_request* request) {
87 struct utsname utsname;
88 char buffer[NAME_MAX];
89
90 // Call uname()
91 int r = uname(&utsname);
92 if (r) {
93 ERROR(request->pakfire, "uname() failed: %m\n");
94 return;
95 }
96
97 DEBUG(request->pakfire, "Locking running kernel %s\n", utsname.release);
98
99 r = pakfire_string_format(buffer, "kernel(%s)", utsname.release);
100 if (r < 0)
101 return;
102
103 Pool* pool = pakfire_get_solv_pool(request->pakfire);
104
105 // Add a locking pool job
106 Id id = pool_str2id(pool, buffer, 1);
107 if (id)
108 queue_push2(&request->jobs, SOLVER_LOCK|SOLVER_SOLVABLE_PROVIDES, id);
109 }
110
111 static int setup_solver(struct pakfire_request* request, int flags) {
112 // Can the solver downgrade packages?
113 if (flags & PAKFIRE_REQUEST_ALLOW_DOWNGRADE)
114 solver_set_flag(request->solver, SOLVER_FLAG_ALLOW_DOWNGRADE, 1);
115
116 // Can the solver uninstall packages?
117 if (flags & PAKFIRE_REQUEST_ALLOW_UNINSTALL)
118 solver_set_flag(request->solver, SOLVER_FLAG_ALLOW_UNINSTALL, 1);
119
120 // Do not install any recommended packages
121 if (flags & PAKFIRE_REQUEST_WITHOUT_RECOMMENDED)
122 solver_set_flag(request->solver, SOLVER_FLAG_IGNORE_RECOMMENDED, 1);
123
124 // Add multiinstall packages
125 pakfire_request_add_packages(request,
126 SOLVER_MULTIVERSION|SOLVER_SOLVABLE_PROVIDES, pakfire_multiinstall_packages);
127
128 // Lock the running kernel
129 if (pakfire_on_root(request->pakfire))
130 pakfire_request_lock_running_kernel(request);
131
132 return 0;
133 }
134
135 PAKFIRE_EXPORT int pakfire_request_create(struct pakfire_request** request,
136 Pakfire pakfire, int flags) {
137 Pool* pool = pakfire_get_solv_pool(pakfire);
138 int r = 1;
139
140 // Cannot create a solver when no installed repository has been set
141 if (!pool->installed)
142 return -EINVAL;
143
144 // Allocate request
145 struct pakfire_request* req = calloc(1, sizeof(*req));
146 if (!req)
147 return ENOMEM;
148
149 req->pakfire = pakfire_ref(pakfire);
150 req->nrefs = 1;
151
152 // Allocate a job queue
153 queue_init(&req->jobs);
154
155 // Allocate solver
156 req->solver = solver_create(pool);
157 if (!req->solver) {
158 ERROR(pakfire, "Could not allocate solver: %m\n");
159 goto ERROR;
160 }
161
162 // Set up solver
163 r = setup_solver(req, flags);
164 if (r)
165 goto ERROR;
166
167 *request = req;
168 return 0;
169
170 ERROR:
171 pakfire_request_free(req);
172 return r;
173 }
174
175 PAKFIRE_EXPORT struct pakfire_request* pakfire_request_ref(struct pakfire_request* request) {
176 request->nrefs++;
177
178 return request;
179 }
180
181 PAKFIRE_EXPORT struct pakfire_request* pakfire_request_unref(struct pakfire_request* request) {
182 if (--request->nrefs > 0)
183 return request;
184
185 pakfire_request_free(request);
186 return NULL;
187 }
188
189 Solver* pakfire_request_get_solver(struct pakfire_request* request) {
190 return request->solver;
191 }
192
193 static int pakfire_request_get_problems(struct pakfire_request* request,
194 struct pakfire_problem*** problems) {
195 struct pakfire_problem* problem;
196
197 unsigned int count = solver_problem_count(request->solver);
198 if (!count)
199 return 0;
200
201 // Allocate array
202 *problems = calloc(count + 1, sizeof(**problems));
203 if (!*problems)
204 return 1;
205
206 Id p = 0;
207 for (unsigned int i = 0; i < count; i++) {
208 p = solver_next_problem(request->solver, p);
209 if (!p)
210 break;
211
212 // Create problem
213 int r = pakfire_problem_create(&problem, request->pakfire, request, p);
214 if (r)
215 goto ERROR;
216
217 // Append to array
218 (*problems)[i] = problem;
219 };
220
221 return 0;
222
223 ERROR:
224 if (*problems) {
225 for (struct pakfire_problem** problem = *problems; *problem; problem++)
226 free(*problem);
227 free(*problems);
228
229 // Reset pointer
230 *problems = NULL;
231 }
232
233 return 1;
234 }
235
236 PAKFIRE_EXPORT int pakfire_request_solve(struct pakfire_request* request,
237 struct pakfire_transaction** transaction, struct pakfire_problem*** problems) {
238 int r;
239
240 pakfire_pool_apply_changes(request->pakfire);
241
242 // Reset pointers
243 *transaction = NULL;
244 *problems = NULL;
245
246 #ifdef ENABLE_DEBUG
247 Pool* pool = pakfire_get_solv_pool(request->pakfire);
248
249 const char* selection = pool_selection2str(pool, &request->jobs, 0);
250 if (selection) {
251 DEBUG(request->pakfire, "Solving: %s\n", selection);
252 }
253 #endif
254
255 // Save time when we starting solving
256 clock_t solving_start = clock();
257
258 if (solver_solve(request->solver, &request->jobs)) {
259 #ifdef ENABLE_DEBUG
260 solver_printallsolutions(request->solver);
261 #endif
262
263 // Fetch problems
264 r = pakfire_request_get_problems(request, problems);
265 if (r)
266 return r;
267
268 return 2;
269 }
270
271 // Save time when we finished solving
272 clock_t solving_end = clock();
273
274 DEBUG(request->pakfire, "Solved request in %.4fms\n",
275 (double)(solving_end - solving_start) * 1000 / CLOCKS_PER_SEC);
276
277 #ifdef ENABLE_DEBUG
278 solver_printdecisions(request->solver);
279 #endif
280
281 // If the solving process was successful, we create the transaction
282 r = pakfire_transaction_create(transaction, request->pakfire, request->solver);
283 if (r)
284 goto ERROR;
285
286 // All okay
287 return 0;
288
289 ERROR:
290 if (*transaction)
291 pakfire_transaction_unref(*transaction);
292
293 return r;
294 }
295
296 static int pakfire_request_is_file(const char* what) {
297 return pakfire_string_endswith(what, ".pfm");
298 }
299
300 static int pakfire_request_is_url(const char* what) {
301 static const char* known_schemas[] = {
302 "https://",
303 "http://",
304 "file://",
305 NULL,
306 };
307
308 for (const char** schema = known_schemas; *schema; schema++) {
309 if (pakfire_string_startswith(what, *schema))
310 return 1;
311 }
312
313 return 0;
314 }
315
316 static int pakfire_request_add_package(struct pakfire_request* request, int action,
317 struct pakfire_package* pkg, int flags) {
318 // Get the solvable ID
319 Id id = pakfire_package_id(pkg);
320
321 // Add it to the job queue
322 queue_push2(&request->jobs, SOLVER_SOLVABLE|action|flags, id);
323
324 return 0;
325 }
326
327 static int pakfire_request_add_job(struct pakfire_request* request, int action,
328 const char* what, int extra_flags) {
329 Pool* pool = pakfire_get_solv_pool(request->pakfire);
330 Id key = 0;
331
332 // Make the pool ready
333 pakfire_pool_apply_changes(request->pakfire);
334
335 Queue jobs;
336 queue_init(&jobs);
337
338 int flags =
339 // Select packages by name
340 SELECTION_NAME|
341 // Select packages by what they provide
342 SELECTION_PROVIDES|
343 // Process globbing patterns
344 SELECTION_GLOB|
345 // Select packages by their canonical name
346 SELECTION_CANON|
347 // Process .arch
348 SELECTION_DOTARCH|
349 // Process -release
350 SELECTION_REL;
351
352 // Process filelists?
353 if (*what == '/')
354 flags |= SELECTION_FILELIST;
355
356 // Is this a group?
357 else if (*what == '@') {
358 flags = SELECTION_MATCH_DEPSTR;
359 key = SOLVABLE_GROUP;
360 what++;
361 }
362
363 // Select all packages
364 if (key)
365 selection_make_matchdeps(pool, &jobs, what, flags, key, 0);
366 else
367 selection_make(pool, &jobs, what, flags);
368
369 // Did we find anything?
370 if (jobs.count == 0) {
371 Id id = pakfire_parse_dep(request->pakfire, what);
372 if (!id)
373 return 1;
374
375 queue_push2(&jobs, SOLVER_SOLVABLE_PROVIDES, id);
376 }
377
378 DEBUG(request->pakfire, "Found %d match(es) for '%s'\n", jobs.count / 2, what);
379
380 // Set action and global flags
381 for (int i = 0; i < jobs.count; i += 2)
382 jobs.elements[i] |= action | extra_flags;
383
384 // Merge jobs into the main job queue
385 queue_insertn(&request->jobs, request->jobs.count, jobs.count, jobs.elements);
386 queue_free(&jobs);
387
388 return 0;
389 }
390
391 static int pakfire_request_add_archive(struct pakfire_request* request, int action,
392 struct pakfire_archive* archive, int extra_flags) {
393 struct pakfire_repo* repo = pakfire_repo_create(request->pakfire, "@commandline");
394 if (!repo)
395 return 1;
396
397 // Add it to the repository
398 struct pakfire_package* pkg = pakfire_repo_add_archive(repo, archive);
399 if (!pkg)
400 goto ERROR;
401
402 int r = pakfire_request_add_package(request, action, pkg, extra_flags);
403 if (r)
404 goto ERROR;
405
406 // Success
407 r = 0;
408
409 ERROR:
410 if (pkg)
411 pakfire_package_unref(pkg);
412 pakfire_repo_unref(repo);
413
414 return r;
415 }
416
417 static int pakfire_request_add_file(struct pakfire_request* request, int action,
418 const char* path, int extra_flags) {
419 struct pakfire_archive* archive;
420
421 // Open the archive
422 int r = pakfire_archive_open(&archive, request->pakfire, path);
423 if (r)
424 goto ERROR;
425
426 // Add it to the request
427 r = pakfire_request_add_archive(request, action, archive, extra_flags);
428 if (r)
429 goto ERROR;
430
431 // Success
432 r = 0;
433
434 ERROR:
435 pakfire_archive_unref(archive);
436
437 return r;
438 }
439
440 static int pakfire_request_add_url(struct pakfire_request* request, int action,
441 const char* url, int extra_flags) {
442 struct pakfire_downloader* downloader;
443 char path[PATH_MAX] = PAKFIRE_CACHE_PATH "/tmp/XXXXXX";
444
445 // Allocate a temporary file name
446 FILE* f = pakfire_mktemp(path);
447 if (!f)
448 return 1;
449
450 // Create a downloader
451 int r = pakfire_downloader_create(&downloader, request->pakfire);
452 if (r)
453 return r;
454
455 // Download the file
456 r = pakfire_downloader_retrieve(downloader, NULL, NULL, NULL,
457 url, path, PAKFIRE_TRANSFER_NOTEMP);
458 if (r)
459 goto ERROR;
460
461 // Try adding the archive
462 r = pakfire_request_add_file(request, action, path, extra_flags);
463 if (r)
464 goto ERROR;
465
466 // Success
467 r = 0;
468
469 ERROR:
470 if (f)
471 fclose(f);
472 pakfire_downloader_unref(downloader);
473
474 return r;
475 }
476
477 static int pakfire_request_add(struct pakfire_request* request, int action,
478 const char* what, int extra_flags) {
479 if (!what) {
480 errno = EINVAL;
481 return 1;
482 }
483
484 // Remove leading whitespace
485 while (*what && isspace(*what))
486 what++;
487
488 // Download and add any remote files
489 if (pakfire_request_is_url(what))
490 return pakfire_request_add_url(request, action, what, extra_flags);
491
492 // Add any local files
493 if (pakfire_request_is_file(what))
494 return pakfire_request_add_file(request, action, what, extra_flags);
495
496 // Add anything else as a job
497 return pakfire_request_add_job(request, action, what, extra_flags);
498 }
499
500 PAKFIRE_EXPORT int pakfire_request_install(struct pakfire_request* request,
501 const char* what, int flags) {
502 return pakfire_request_add(request, SOLVER_INSTALL, what, flags);
503 }
504
505 PAKFIRE_EXPORT int pakfire_request_install_package(
506 struct pakfire_request* request, struct pakfire_package* package) {
507 return pakfire_request_add_package(request, SOLVER_INSTALL, package, 0);
508 }
509
510 static int erase_flags(int flags) {
511 int additional = 0;
512
513 // Keep dependencies
514 if (!(flags & PAKFIRE_REQUEST_KEEP_DEPS))
515 additional |= SOLVER_CLEANDEPS;
516
517 return additional;
518 }
519
520 PAKFIRE_EXPORT int pakfire_request_erase(struct pakfire_request* request, const char* what, int flags) {
521 return pakfire_request_add(request, SOLVER_ERASE, what, erase_flags(flags));
522 }
523
524 PAKFIRE_EXPORT int pakfire_request_erase_package(
525 struct pakfire_request* request, struct pakfire_package* package, int flags) {
526 return pakfire_request_add_package(request, SOLVER_ERASE, package, erase_flags(flags));
527 }
528
529 PAKFIRE_EXPORT int pakfire_request_update(struct pakfire_request* request, const char* what, int flags) {
530 return pakfire_request_add(request, SOLVER_UPDATE, what, flags);
531 }
532
533 PAKFIRE_EXPORT int pakfire_request_update_package(
534 struct pakfire_request* request, struct pakfire_package* package) {
535 return pakfire_request_add_package(request, SOLVER_UPDATE, package, 0);
536 }
537
538 PAKFIRE_EXPORT int pakfire_request_update_all(struct pakfire_request* request, int flags) {
539 queue_push2(&request->jobs, SOLVER_SOLVABLE_ALL|SOLVER_UPDATE|erase_flags(flags), 0);
540
541 return 0;
542 }
543
544 PAKFIRE_EXPORT int pakfire_request_sync(struct pakfire_request* request, int flags) {
545 int solver_flags = SOLVER_SOLVABLE_ALL|SOLVER_DISTUPGRADE;
546
547 // Drop orphans
548 if (!(flags & PAKFIRE_REQUEST_KEEP_ORPHANED))
549 solver_flags |= SOLVER_DROP_ORPHANED;
550
551 queue_push2(&request->jobs, solver_flags, 0);
552
553 return 0;
554 }
555
556 PAKFIRE_EXPORT int pakfire_request_lock(struct pakfire_request* request, const char* what) {
557 return pakfire_request_add(request, SOLVER_LOCK, what, 0);
558 }
559
560 PAKFIRE_EXPORT int pakfire_request_lock_package(struct pakfire_request* request, struct pakfire_package* package) {
561 return pakfire_request_add_package(request, SOLVER_LOCK, package, 0);
562 }
563
564 PAKFIRE_EXPORT int pakfire_request_verify(struct pakfire_request* request, int flags) {
565 queue_push2(&request->jobs, SOLVER_SOLVABLE_ALL|SOLVER_VERIFY, 0);
566
567 return 0;
568 }
569
570 PAKFIRE_EXPORT int pakfire_request_take_solution(struct pakfire_request* request,
571 struct pakfire_solution* solution) {
572 struct pakfire_problem* problem = pakfire_solution_get_problem(solution);
573
574 // Fetch IDs
575 Id problem_id = pakfire_problem_get_id(problem);
576 Id solution_id = pakfire_solution_get_id(solution);
577
578 // Feed the solution into the solver
579 solver_take_solution(request->solver, problem_id, solution_id, &request->jobs);
580
581 pakfire_problem_unref(problem);
582 return 0;
583 }