1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2013 Pakfire development team #
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. #
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. #
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/>. #
19 #############################################################################*/
23 #include <linux/limits.h>
25 #include <sys/utsname.h>
27 #include <solv/queue.h>
28 #include <solv/selection.h>
29 #include <solv/solver.h>
30 #include <solv/transaction.h>
33 # include <solv/solverdebug.h>
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>
47 struct pakfire_request
{
56 These packages can be installed multiple times simultaneously
58 const char* pakfire_multiinstall_packages
[] = {
64 static void pakfire_request_free(struct pakfire_request
* request
) {
66 solver_free(request
->solver
);
67 queue_free(&request
->jobs
);
69 pakfire_unref(request
->pakfire
);
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
);
79 for (const char** package
= packages
; *package
; package
++) {
80 Id id
= pool_str2id(pool
, *package
, 1);
82 queue_push2(&request
->jobs
, flags
, id
);
86 static void pakfire_request_lock_running_kernel(struct pakfire_request
* request
) {
87 struct utsname utsname
;
88 char buffer
[NAME_MAX
];
91 int r
= uname(&utsname
);
93 ERROR(request
->pakfire
, "uname() failed: %m\n");
97 DEBUG(request
->pakfire
, "Locking running kernel %s\n", utsname
.release
);
99 r
= pakfire_string_format(buffer
, "kernel(%s)", utsname
.release
);
103 Pool
* pool
= pakfire_get_solv_pool(request
->pakfire
);
105 // Add a locking pool job
106 Id id
= pool_str2id(pool
, buffer
, 1);
108 queue_push2(&request
->jobs
, SOLVER_LOCK
|SOLVER_SOLVABLE_PROVIDES
, id
);
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);
116 // Can the solver uninstall packages?
117 if (flags
& PAKFIRE_REQUEST_ALLOW_UNINSTALL
)
118 solver_set_flag(request
->solver
, SOLVER_FLAG_ALLOW_UNINSTALL
, 1);
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);
124 // Add multiinstall packages
125 pakfire_request_add_packages(request
,
126 SOLVER_MULTIVERSION
|SOLVER_SOLVABLE_PROVIDES
, pakfire_multiinstall_packages
);
128 // Lock the running kernel
129 if (pakfire_on_root(request
->pakfire
))
130 pakfire_request_lock_running_kernel(request
);
135 PAKFIRE_EXPORT
int pakfire_request_create(struct pakfire_request
** request
,
136 Pakfire pakfire
, int flags
) {
137 Pool
* pool
= pakfire_get_solv_pool(pakfire
);
140 // Cannot create a solver when no installed repository has been set
141 if (!pool
->installed
)
145 struct pakfire_request
* req
= calloc(1, sizeof(*req
));
149 req
->pakfire
= pakfire_ref(pakfire
);
152 // Allocate a job queue
153 queue_init(&req
->jobs
);
156 req
->solver
= solver_create(pool
);
158 ERROR(pakfire
, "Could not allocate solver: %m\n");
163 r
= setup_solver(req
, flags
);
171 pakfire_request_free(req
);
175 PAKFIRE_EXPORT
struct pakfire_request
* pakfire_request_ref(struct pakfire_request
* request
) {
181 PAKFIRE_EXPORT
struct pakfire_request
* pakfire_request_unref(struct pakfire_request
* request
) {
182 if (--request
->nrefs
> 0)
185 pakfire_request_free(request
);
189 Solver
* pakfire_request_get_solver(struct pakfire_request
* request
) {
190 return request
->solver
;
193 static int pakfire_request_get_problems(struct pakfire_request
* request
,
194 struct pakfire_problem
*** problems
) {
195 struct pakfire_problem
* problem
;
197 unsigned int count
= solver_problem_count(request
->solver
);
202 *problems
= calloc(count
+ 1, sizeof(**problems
));
207 for (unsigned int i
= 0; i
< count
; i
++) {
208 p
= solver_next_problem(request
->solver
, p
);
213 int r
= pakfire_problem_create(&problem
, request
->pakfire
, request
, p
);
218 (*problems
)[i
] = problem
;
225 for (struct pakfire_problem
** problem
= *problems
; *problem
; problem
++)
236 PAKFIRE_EXPORT
int pakfire_request_solve(struct pakfire_request
* request
,
237 struct pakfire_transaction
** transaction
, struct pakfire_problem
*** problems
) {
240 pakfire_pool_apply_changes(request
->pakfire
);
247 Pool
* pool
= pakfire_get_solv_pool(request
->pakfire
);
249 const char* selection
= pool_selection2str(pool
, &request
->jobs
, 0);
251 DEBUG(request
->pakfire
, "Solving: %s\n", selection
);
255 // Save time when we starting solving
256 clock_t solving_start
= clock();
258 if (solver_solve(request
->solver
, &request
->jobs
)) {
260 solver_printallsolutions(request
->solver
);
264 r
= pakfire_request_get_problems(request
, problems
);
271 // Save time when we finished solving
272 clock_t solving_end
= clock();
274 DEBUG(request
->pakfire
, "Solved request in %.4fms\n",
275 (double)(solving_end
- solving_start
) * 1000 / CLOCKS_PER_SEC
);
278 solver_printdecisions(request
->solver
);
281 // If the solving process was successful, we create the transaction
282 r
= pakfire_transaction_create(transaction
, request
->pakfire
, request
->solver
);
291 pakfire_transaction_unref(*transaction
);
296 static int pakfire_request_is_file(const char* what
) {
297 return pakfire_string_endswith(what
, ".pfm");
300 static int pakfire_request_is_url(const char* what
) {
301 static const char* known_schemas
[] = {
308 for (const char** schema
= known_schemas
; *schema
; schema
++) {
309 if (pakfire_string_startswith(what
, *schema
))
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
);
321 // Add it to the job queue
322 queue_push2(&request
->jobs
, SOLVER_SOLVABLE
|action
|flags
, id
);
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
);
332 // Make the pool ready
333 pakfire_pool_apply_changes(request
->pakfire
);
339 // Select packages by name
341 // Select packages by what they provide
343 // Process globbing patterns
345 // Select packages by their canonical name
352 // Process filelists?
354 flags
|= SELECTION_FILELIST
;
357 else if (*what
== '@') {
358 flags
= SELECTION_MATCH_DEPSTR
;
359 key
= SOLVABLE_GROUP
;
363 // Select all packages
365 selection_make_matchdeps(pool
, &jobs
, what
, flags
, key
, 0);
367 selection_make(pool
, &jobs
, what
, flags
);
369 // Did we find anything?
370 if (jobs
.count
== 0) {
371 Id id
= pakfire_parse_dep(request
->pakfire
, what
);
375 queue_push2(&jobs
, SOLVER_SOLVABLE_PROVIDES
, id
);
378 DEBUG(request
->pakfire
, "Found %d match(es) for '%s'\n", jobs
.count
/ 2, what
);
380 // Set action and global flags
381 for (int i
= 0; i
< jobs
.count
; i
+= 2)
382 jobs
.elements
[i
] |= action
| extra_flags
;
384 // Merge jobs into the main job queue
385 queue_insertn(&request
->jobs
, request
->jobs
.count
, jobs
.count
, jobs
.elements
);
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");
397 // Add it to the repository
398 struct pakfire_package
* pkg
= pakfire_repo_add_archive(repo
, archive
);
402 int r
= pakfire_request_add_package(request
, action
, pkg
, extra_flags
);
411 pakfire_package_unref(pkg
);
412 pakfire_repo_unref(repo
);
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
;
422 int r
= pakfire_archive_open(&archive
, request
->pakfire
, path
);
426 // Add it to the request
427 r
= pakfire_request_add_archive(request
, action
, archive
, extra_flags
);
435 pakfire_archive_unref(archive
);
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";
445 // Allocate a temporary file name
446 FILE* f
= pakfire_mktemp(path
);
450 // Create a downloader
451 int r
= pakfire_downloader_create(&downloader
, request
->pakfire
);
456 r
= pakfire_downloader_retrieve(downloader
, NULL
, NULL
, NULL
,
457 url
, path
, PAKFIRE_TRANSFER_NOTEMP
);
461 // Try adding the archive
462 r
= pakfire_request_add_file(request
, action
, path
, extra_flags
);
472 pakfire_downloader_unref(downloader
);
477 static int pakfire_request_add(struct pakfire_request
* request
, int action
,
478 const char* what
, int extra_flags
) {
484 // Remove leading whitespace
485 while (*what
&& isspace(*what
))
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
);
492 // Add any local files
493 if (pakfire_request_is_file(what
))
494 return pakfire_request_add_file(request
, action
, what
, extra_flags
);
496 // Add anything else as a job
497 return pakfire_request_add_job(request
, action
, what
, extra_flags
);
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
);
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);
510 static int erase_flags(int flags
) {
514 if (!(flags
& PAKFIRE_REQUEST_KEEP_DEPS
))
515 additional
|= SOLVER_CLEANDEPS
;
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
));
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
));
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
);
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);
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);
544 PAKFIRE_EXPORT
int pakfire_request_sync(struct pakfire_request
* request
, int flags
) {
545 int solver_flags
= SOLVER_SOLVABLE_ALL
|SOLVER_DISTUPGRADE
;
548 if (!(flags
& PAKFIRE_REQUEST_KEEP_ORPHANED
))
549 solver_flags
|= SOLVER_DROP_ORPHANED
;
551 queue_push2(&request
->jobs
, solver_flags
, 0);
556 PAKFIRE_EXPORT
int pakfire_request_lock(struct pakfire_request
* request
, const char* what
) {
557 return pakfire_request_add(request
, SOLVER_LOCK
, what
, 0);
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);
564 PAKFIRE_EXPORT
int pakfire_request_verify(struct pakfire_request
* request
, int flags
) {
565 queue_push2(&request
->jobs
, SOLVER_SOLVABLE_ALL
|SOLVER_VERIFY
, 0);
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
);
575 Id problem_id
= pakfire_problem_get_id(problem
);
576 Id solution_id
= pakfire_solution_get_id(solution
);
578 // Feed the solution into the solver
579 solver_take_solution(request
->solver
, problem_id
, solution_id
, &request
->jobs
);
581 pakfire_problem_unref(problem
);