vfs: check userland buffers before reading them.
[haiku.git] / src / bin / pkgman / command_resolve_dependencies.cpp
blob8ef44474ea766f160262313af0233e69eb6aa8e4
1 /*
2 * Copyright 2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
7 */
10 #include <errno.h>
11 #include <getopt.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <sys/stat.h>
16 #include <package/manager/RepositoryBuilder.h>
17 #include <package/solver/SolverPackageSpecifier.h>
18 #include <package/solver/SolverPackageSpecifierList.h>
19 #include <package/solver/SolverProblem.h>
20 #include <package/solver/SolverResult.h>
22 #include <AutoDeleter.h>
24 #include "Command.h"
25 #include "pkgman.h"
28 // TODO: internationalization!
31 using namespace BPackageKit;
32 using BManager::BPrivate::BPackagePathMap;
33 using BManager::BPrivate::BRepositoryBuilder;
36 static const char* const kShortUsage =
37 " %command% <package> ... <repository> [ <priority> ] ...\n"
38 " Resolves all packages the given packages depend on.\n";
40 static const char* const kLongUsage =
41 "Usage: %program% %command% <package> ... <repository> [ <priority> ] ...\n"
42 "Resolves and lists all packages the given packages depend on. Fails, if\n"
43 "not all dependencies could be resolved.\n"
44 "\n"
45 "Options:\n"
46 " --debug <level>\n"
47 " Print debug output. <level> should be between 0 (no debug output,\n"
48 " the default) and 10 (most debug output).\n"
49 "\n"
50 "Arguments:\n"
51 " <package>\n"
52 " The HPKG or package info file of the package for which the\n"
53 " dependencies shall be resolved. Multiple files can be specified.\n"
54 " <repository>\n"
55 " Path to a directory containing packages from which the package's\n"
56 " dependencies shall be resolved. Multiple directories can be\n"
57 " specified.\n"
58 " <priority>\n"
59 " Can follow a <repository> to specify the priority of that\n"
60 " repository. The default priority is 0.\n"
61 "\n";
64 DEFINE_COMMAND(ResolveDependenciesCommand, "resolve-dependencies", kShortUsage,
65 kLongUsage, kCommandCategoryOther)
68 static void
69 check_problems(BSolver* solver, const char* errorContext)
71 if (solver->HasProblems()) {
72 fprintf(stderr,
73 "Encountered problems %s:\n", errorContext);
75 int32 problemCount = solver->CountProblems();
76 for (int32 i = 0; i < problemCount; i++) {
77 printf(" %" B_PRId32 ": %s\n", i + 1,
78 solver->ProblemAt(i)->ToString().String());
80 exit(1);
85 static void
86 verify_result(const BSolverResult& result,
87 const BPackagePathMap& specifiedPackagePaths)
89 // create the solver
90 BSolver* solver;
91 status_t error = BSolver::Create(solver);
92 if (error != B_OK)
93 DIE(error, "failed to create solver");
95 // Add an installation repository and add all of the result packages save
96 // the specified packages.
97 BSolverRepository installation;
98 BRepositoryBuilder installationBuilder(installation, "installation");
100 for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
101 i++) {
102 BSolverPackage* package = element->Package();
103 if (specifiedPackagePaths.find(package) == specifiedPackagePaths.end())
104 installationBuilder.AddPackage(package->Info());
106 installationBuilder.AddToSolver(solver, true);
108 // resolve
109 error = solver->VerifyInstallation();
110 if (error != B_OK)
111 DIE(error, "failed to verify computed package dependencies");
113 check_problems(solver, "verifying computed package dependencies");
118 ResolveDependenciesCommand::Execute(int argc, const char* const* argv)
120 while (true) {
121 static struct option sLongOptions[] = {
122 { "debug", required_argument, 0, OPTION_DEBUG },
123 { "help", no_argument, 0, 'h' },
124 { 0, 0, 0, 0 }
127 opterr = 0; // don't print errors
128 int c = getopt_long(argc, (char**)argv, "h", sLongOptions, NULL);
129 if (c == -1)
130 break;
132 if (fCommonOptions.HandleOption(c))
133 continue;
135 switch (c) {
136 case 'h':
137 PrintUsageAndExit(false);
138 break;
140 default:
141 PrintUsageAndExit(true);
142 break;
146 // The remaining arguments are the package (info) files and the repository
147 // directories (at least one), optionally with priorities.
148 if (argc < optind + 2)
149 PrintUsageAndExit(true);
151 // Determine where the package list ends and the repository list starts.
152 const char* const* specifiedPackages = argv + optind;
153 for (; optind < argc; optind++) {
154 const char* path = argv[optind];
155 struct stat st;
156 if (stat(path, &st) != 0)
157 DIE(errno, "failed to stat() \"%s\"", path);
159 if (S_ISDIR(st.st_mode))
160 break;
163 const char* const* repositoryDirectories = argv + optind;
164 int repositoryDirectoryCount = argc - optind;
165 int specifiedPackageCount = repositoryDirectories - specifiedPackages;
167 // create the solver
168 BSolver* solver;
169 status_t error = BSolver::Create(solver);
170 if (error != B_OK)
171 DIE(error, "failed to create solver");
173 solver->SetDebugLevel(fCommonOptions.DebugLevel());
175 // add repositories
176 BPackagePathMap packagePaths;
177 BObjectList<BSolverRepository> repositories(10, true);
178 int32 repositoryIndex = 0;
179 for (int i = 0; i < repositoryDirectoryCount; i++, repositoryIndex++) {
180 const char* directoryPath = repositoryDirectories[i];
182 BSolverRepository* repository = new(std::nothrow) BSolverRepository;
183 if (repository == NULL || !repositories.AddItem(repository))
184 DIE(B_NO_MEMORY, "failed to create repository");
187 if (i + 1 < repositoryDirectoryCount) {
188 char* end;
189 long priority = strtol(repositoryDirectories[i + 1], &end, 0);
190 if (*end == '\0') {
191 repository->SetPriority((uint8)priority);
192 i++;
196 BRepositoryBuilder(*repository,
197 BString("repository") << repositoryIndex)
198 .SetPackagePathMap(&packagePaths)
199 .AddPackagesDirectory(directoryPath)
200 .AddToSolver(solver);
203 // add a repository with only the specified packages
204 BPackagePathMap specifiedPackagePaths;
205 BSolverRepository dummyRepository;
207 BRepositoryBuilder builder(dummyRepository, "dummy",
208 "specified packages");
209 builder.SetPackagePathMap(&specifiedPackagePaths);
211 for (int i = 0; i < specifiedPackageCount; i++)
212 builder.AddPackage(specifiedPackages[i]);
214 builder.AddToSolver(solver);
217 // resolve
218 BSolverPackageSpecifierList packagesToInstall;
219 for (BPackagePathMap::const_iterator it = specifiedPackagePaths.begin();
220 it != specifiedPackagePaths.end(); ++it) {
221 if (!packagesToInstall.AppendSpecifier(it->first))
222 DIE(B_NO_MEMORY, "failed to add specified package");
225 error = solver->Install(packagesToInstall);
226 if (error != B_OK)
227 DIE(error, "failed to resolve package dependencies");
229 check_problems(solver, "resolving package dependencies");
231 BSolverResult result;
232 error = solver->GetResult(result);
233 if (error != B_OK)
234 DIE(error, "failed to resolve package dependencies");
236 // Verify that the resolved packages don't depend on the specified package.
237 verify_result(result, specifiedPackagePaths);
239 // print packages
240 for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i);
241 i++) {
242 // skip the specified package
243 BSolverPackage* package = element->Package();
244 if (specifiedPackagePaths.find(package) != specifiedPackagePaths.end())
245 continue;
247 // resolve and print the path
248 BPackagePathMap::const_iterator it = packagePaths.find(package);
249 if (it == packagePaths.end()) {
250 DIE(B_ERROR, "ugh, no package %p (%s-%s) not in package path map",
251 package, package->Info().Name().String(),
252 package->Info().Version().ToString().String());
255 printf("%s\n", it->second.String());
258 return 0;