xref: /haiku/src/kits/package/manager/RepositoryBuilder.cpp (revision 36aafa560b2cf96aa39cc753c6dec7f54431d564)
1 /*
2  * Copyright 2013-2020, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ingo Weinhold <ingo_weinhold@gmx.de>
7  *		Andrew Lindesay <apl@lindesay.co.nz>
8  */
9 
10 
11 #include <package/manager/RepositoryBuilder.h>
12 
13 #include <errno.h>
14 #include <dirent.h>
15 
16 #include <Entry.h>
17 #include <package/RepositoryCache.h>
18 #include <Path.h>
19 
20 #include <AutoDeleter.h>
21 
22 #include "PackageManagerUtils.h"
23 
24 
25 namespace BPackageKit {
26 
27 namespace BManager {
28 
29 namespace BPrivate {
30 
31 
32 namespace {
33 
34 
35 class PackageInfoErrorListener : public BPackageInfo::ParseErrorListener {
36 public:
37 	PackageInfoErrorListener(const char* context)
38 		:
39 		fContext(context)
40 	{
41 	}
42 
43 	virtual void OnError(const BString& message, int line, int column)
44 	{
45 		fErrors << BString().SetToFormat("%s: parse error in line %d:%d: %s\n",
46 			fContext, line, column, message.String());
47 	}
48 
49 	const BString& Errors() const
50 	{
51 		return fErrors;
52 	}
53 
54 private:
55 	const char*	fContext;
56 	BString		fErrors;
57 };
58 
59 
60 } // unnamed namespace
61 
62 
63 BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository)
64 	:
65 	fRepository(repository),
66 	fErrorName(repository.Name()),
67 	fPackagePaths(NULL)
68 {
69 }
70 
71 
72 BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository,
73 	const BString& name, const BString& errorName)
74 	:
75 	fRepository(repository),
76 	fErrorName(errorName.IsEmpty() ? name : errorName),
77 	fPackagePaths(NULL)
78 {
79 	status_t error = fRepository.SetTo(name);
80 	if (error != B_OK)
81 		DIE(error, "failed to init %s repository", fErrorName.String());
82 }
83 
84 
85 BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository,
86 	const BRepositoryConfig& config)
87 	:
88 	fRepository(repository),
89 	fErrorName(fRepository.Name()),
90 	fPackagePaths(NULL)
91 {
92 	status_t error = fRepository.SetTo(config);
93 	if (error != B_OK)
94 		DIE(error, "failed to init %s repository", fErrorName.String());
95 }
96 
97 
98 BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository,
99 	const BRepositoryCache& cache, const BString& errorName)
100 	:
101 	fRepository(repository),
102 	fErrorName(errorName.IsEmpty() ? cache.Info().Name() : errorName),
103 	fPackagePaths(NULL)
104 {
105 	status_t error = fRepository.SetTo(cache);
106 	if (error != B_OK)
107 		DIE(error, "failed to init %s repository", fErrorName.String());
108 	fErrorName = fRepository.Name();
109 }
110 
111 
112 BRepositoryBuilder&
113 BRepositoryBuilder::SetPackagePathMap(BPackagePathMap* packagePaths)
114 {
115 	fPackagePaths = packagePaths;
116 	return *this;
117 }
118 
119 
120 BRepositoryBuilder&
121 BRepositoryBuilder::AddPackage(const BPackageInfo& info,
122 	const char* packageErrorName, BSolverPackage** _package)
123 {
124 	status_t error = fRepository.AddPackage(info, _package);
125 	if (error != B_OK) {
126 		DIE(error, "failed to add %s to %s repository",
127 			packageErrorName != NULL
128 				? packageErrorName
129 				: (BString("package ") << info.Name()).String(),
130 			fErrorName.String());
131 	}
132 	return *this;
133 }
134 
135 
136 BRepositoryBuilder&
137 BRepositoryBuilder::AddPackage(const char* path, BSolverPackage** _package)
138 {
139 	// read a package info from the (HPKG or package info) file
140 	BPackageInfo packageInfo;
141 
142 	size_t pathLength = strlen(path);
143 	status_t error;
144 	PackageInfoErrorListener errorListener(path);
145 	BEntry entry(path, true);
146 
147 	if (!entry.Exists()) {
148 		DIE_DETAILS(errorListener.Errors(), B_FILE_NOT_FOUND,
149 			"the package data file does not exist at \"%s\"", path);
150 	}
151 
152 	struct stat entryStat;
153 	error = entry.GetStat(&entryStat);
154 
155 	if (error != B_OK) {
156 		DIE_DETAILS(errorListener.Errors(), error,
157 			"failed to access the package data file at \"%s\"", path);
158 	}
159 
160 	if (entryStat.st_size == 0) {
161 		DIE_DETAILS(errorListener.Errors(), B_BAD_DATA,
162 			"empty package data file at \"%s\"", path);
163 	}
164 
165 	if (pathLength > 5 && strcmp(path + pathLength - 5, ".hpkg") == 0) {
166 		// a package file
167 		error = packageInfo.ReadFromPackageFile(path);
168 	} else {
169 		// a package info file (supposedly)
170 		error = packageInfo.ReadFromConfigFile(entry, &errorListener);
171 	}
172 
173 	if (error != B_OK) {
174 		DIE_DETAILS(errorListener.Errors(), error,
175 			"failed to read package data file at \"%s\"", path);
176 	}
177 
178 	// add the package
179 	BSolverPackage* package;
180 	AddPackage(packageInfo, path, &package);
181 
182 	// enter the package path in the path map, if given
183 	if (fPackagePaths != NULL)
184 		(*fPackagePaths)[package] = path;
185 
186 	if (_package != NULL)
187 		*_package = package;
188 
189 	return *this;
190 }
191 
192 
193 BRepositoryBuilder&
194 BRepositoryBuilder::AddPackages(BPackageInstallationLocation location,
195 	const char* locationErrorName)
196 {
197 	status_t error = fRepository.AddPackages(location);
198 	if (error != B_OK) {
199 		DIE(error, "failed to add %s packages to %s repository",
200 			locationErrorName, fErrorName.String());
201 	}
202 	return *this;
203 }
204 
205 
206 BRepositoryBuilder&
207 BRepositoryBuilder::AddPackagesDirectory(const char* path)
208 {
209 	// open directory
210 	DIR* dir = opendir(path);
211 	if (dir == NULL)
212 		DIE(errno, "failed to open package directory \"%s\"", path);
213 	CObjectDeleter<DIR, int, closedir> dirCloser(dir);
214 
215 	// iterate through directory entries
216 	while (dirent* entry = readdir(dir)) {
217 		// skip "." and ".."
218 		const char* name = entry->d_name;
219 		if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
220 			continue;
221 
222 		// stat() the entry and skip any non-file
223 		BPath entryPath;
224 		status_t error = entryPath.SetTo(path, name);
225 		if (error != B_OK)
226 			DIE(errno, "failed to construct path");
227 
228 		struct stat st;
229 		if (stat(entryPath.Path(), &st) != 0)
230 			DIE(errno, "failed to stat() %s", entryPath.Path());
231 
232 		if (!S_ISREG(st.st_mode))
233 			continue;
234 
235 		AddPackage(entryPath.Path());
236 	}
237 
238 	return *this;
239 }
240 
241 
242 BRepositoryBuilder&
243 BRepositoryBuilder::AddToSolver(BSolver* solver, bool isInstalled)
244 {
245 	fRepository.SetInstalled(isInstalled);
246 
247 	status_t error = solver->AddRepository(&fRepository);
248 	if (error != B_OK) {
249 		DIE(error, "failed to add %s repository to solver",
250 			fErrorName.String());
251 	}
252 	return *this;
253 }
254 
255 
256 }	// namespace BPrivate
257 
258 }	// namespace BManager
259 
260 }	// namespace BPackageKit
261