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