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