xref: /haiku/src/kits/package/manager/RepositoryBuilder.cpp (revision 46b7da1f4f40f7157d74fc7fb26ff9ec7f2416f2)
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 #include <AutoDeleterPosix.h>
22 
23 #include "PackageManagerUtils.h"
24 
25 
26 namespace BPackageKit {
27 
28 namespace BManager {
29 
30 namespace BPrivate {
31 
32 
33 namespace {
34 
35 
36 class PackageInfoErrorListener : public BPackageInfo::ParseErrorListener {
37 public:
PackageInfoErrorListener(const char * context)38 	PackageInfoErrorListener(const char* context)
39 		:
40 		fContext(context)
41 	{
42 	}
43 
OnError(const BString & message,int line,int column)44 	virtual void OnError(const BString& message, int line, int column)
45 	{
46 		fErrors << BString().SetToFormat("%s: parse error in line %d:%d: %s\n",
47 			fContext, line, column, message.String());
48 	}
49 
Errors() const50 	const BString& Errors() const
51 	{
52 		return fErrors;
53 	}
54 
55 private:
56 	const char*	fContext;
57 	BString		fErrors;
58 };
59 
60 
61 } // unnamed namespace
62 
63 
BRepositoryBuilder(BSolverRepository & repository)64 BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository)
65 	:
66 	fRepository(repository),
67 	fErrorName(repository.Name()),
68 	fPackagePaths(NULL)
69 {
70 }
71 
72 
BRepositoryBuilder(BSolverRepository & repository,const BString & name,const BString & errorName)73 BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository,
74 	const BString& name, const BString& errorName)
75 	:
76 	fRepository(repository),
77 	fErrorName(errorName.IsEmpty() ? name : errorName),
78 	fPackagePaths(NULL)
79 {
80 	status_t error = fRepository.SetTo(name);
81 	if (error != B_OK)
82 		DIE(error, "failed to init %s repository", fErrorName.String());
83 }
84 
85 
BRepositoryBuilder(BSolverRepository & repository,const BRepositoryConfig & config)86 BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository,
87 	const BRepositoryConfig& config)
88 	:
89 	fRepository(repository),
90 	fErrorName(fRepository.Name()),
91 	fPackagePaths(NULL)
92 {
93 	status_t error = fRepository.SetTo(config);
94 	if (error != B_OK)
95 		DIE(error, "failed to init %s repository", fErrorName.String());
96 }
97 
98 
BRepositoryBuilder(BSolverRepository & repository,const BRepositoryCache & cache,const BString & errorName)99 BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository,
100 	const BRepositoryCache& cache, const BString& errorName)
101 	:
102 	fRepository(repository),
103 	fErrorName(errorName.IsEmpty() ? cache.Info().Name() : errorName),
104 	fPackagePaths(NULL)
105 {
106 	status_t error = fRepository.SetTo(cache);
107 	if (error != B_OK)
108 		DIE(error, "failed to init %s repository", fErrorName.String());
109 	fErrorName = fRepository.Name();
110 }
111 
112 
113 BRepositoryBuilder&
SetPackagePathMap(BPackagePathMap * packagePaths)114 BRepositoryBuilder::SetPackagePathMap(BPackagePathMap* packagePaths)
115 {
116 	fPackagePaths = packagePaths;
117 	return *this;
118 }
119 
120 
121 BRepositoryBuilder&
AddPackage(const BPackageInfo & info,const char * packageErrorName,BSolverPackage ** _package)122 BRepositoryBuilder::AddPackage(const BPackageInfo& info,
123 	const char* packageErrorName, BSolverPackage** _package)
124 {
125 	status_t error = fRepository.AddPackage(info, _package);
126 	if (error != B_OK) {
127 		DIE(error, "failed to add %s to %s repository",
128 			packageErrorName != NULL
129 				? packageErrorName
130 				: (BString("package ") << info.Name()).String(),
131 			fErrorName.String());
132 	}
133 	return *this;
134 }
135 
136 
137 BRepositoryBuilder&
AddPackage(const char * path,BSolverPackage ** _package)138 BRepositoryBuilder::AddPackage(const char* path, BSolverPackage** _package)
139 {
140 	// read a package info from the (HPKG or package info) file
141 	BPackageInfo packageInfo;
142 
143 	size_t pathLength = strlen(path);
144 	status_t error;
145 	PackageInfoErrorListener errorListener(path);
146 	BEntry entry(path, true);
147 
148 	if (!entry.Exists()) {
149 		DIE_DETAILS(errorListener.Errors(), B_ENTRY_NOT_FOUND,
150 			"the package data file does not exist at \"%s\"", path);
151 	}
152 
153 	struct stat entryStat;
154 	error = entry.GetStat(&entryStat);
155 
156 	if (error != B_OK) {
157 		DIE_DETAILS(errorListener.Errors(), error,
158 			"failed to access the package data file at \"%s\"", path);
159 	}
160 
161 	if (entryStat.st_size == 0) {
162 		DIE_DETAILS(errorListener.Errors(), B_BAD_DATA,
163 			"empty package data file at \"%s\"", path);
164 	}
165 
166 	if (pathLength > 5 && strcmp(path + pathLength - 5, ".hpkg") == 0) {
167 		// a package file
168 		error = packageInfo.ReadFromPackageFile(path);
169 	} else {
170 		// a package info file (supposedly)
171 		error = packageInfo.ReadFromConfigFile(entry, &errorListener);
172 	}
173 
174 	if (error != B_OK) {
175 		DIE_DETAILS(errorListener.Errors(), error,
176 			"failed to read package data file at \"%s\"", path);
177 	}
178 
179 	// add the package
180 	BSolverPackage* package;
181 	AddPackage(packageInfo, path, &package);
182 
183 	// enter the package path in the path map, if given
184 	if (fPackagePaths != NULL)
185 		(*fPackagePaths)[package] = path;
186 
187 	if (_package != NULL)
188 		*_package = package;
189 
190 	return *this;
191 }
192 
193 
194 BRepositoryBuilder&
AddPackages(BPackageInstallationLocation location,const char * locationErrorName)195 BRepositoryBuilder::AddPackages(BPackageInstallationLocation location,
196 	const char* locationErrorName)
197 {
198 	status_t error = fRepository.AddPackages(location);
199 	if (error != B_OK) {
200 		DIE(error, "failed to add %s packages to %s repository",
201 			locationErrorName, fErrorName.String());
202 	}
203 	return *this;
204 }
205 
206 
207 BRepositoryBuilder&
AddPackagesDirectory(const char * path)208 BRepositoryBuilder::AddPackagesDirectory(const char* path)
209 {
210 	// open directory
211 	DirCloser dir(opendir(path));
212 	if (!dir.IsSet())
213 		DIE(errno, "failed to open package directory \"%s\"", path);
214 
215 	// iterate through directory entries
216 	while (dirent* entry = readdir(dir.Get())) {
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&
AddToSolver(BSolver * solver,bool isInstalled)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