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