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: 38 PackageInfoErrorListener(const char* context) 39 : 40 fContext(context) 41 { 42 } 43 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 50 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 64 BRepositoryBuilder::BRepositoryBuilder(BSolverRepository& repository) 65 : 66 fRepository(repository), 67 fErrorName(repository.Name()), 68 fPackagePaths(NULL) 69 { 70 } 71 72 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 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 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& 114 BRepositoryBuilder::SetPackagePathMap(BPackagePathMap* packagePaths) 115 { 116 fPackagePaths = packagePaths; 117 return *this; 118 } 119 120 121 BRepositoryBuilder& 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& 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& 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& 208 BRepositoryBuilder::AddPackagesDirectory(const char* path) 209 { 210 // open directory 211 DIR* dir = opendir(path); 212 if (dir == NULL) 213 DIE(errno, "failed to open package directory \"%s\"", path); 214 DirCloser dirCloser(dir); 215 216 // iterate through directory entries 217 while (dirent* entry = readdir(dir)) { 218 // skip "." and ".." 219 const char* name = entry->d_name; 220 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) 221 continue; 222 223 // stat() the entry and skip any non-file 224 BPath entryPath; 225 status_t error = entryPath.SetTo(path, name); 226 if (error != B_OK) 227 DIE(errno, "failed to construct path"); 228 229 struct stat st; 230 if (stat(entryPath.Path(), &st) != 0) 231 DIE(errno, "failed to stat() %s", entryPath.Path()); 232 233 if (!S_ISREG(st.st_mode)) 234 continue; 235 236 AddPackage(entryPath.Path()); 237 } 238 239 return *this; 240 } 241 242 243 BRepositoryBuilder& 244 BRepositoryBuilder::AddToSolver(BSolver* solver, bool isInstalled) 245 { 246 fRepository.SetInstalled(isInstalled); 247 248 status_t error = solver->AddRepository(&fRepository); 249 if (error != B_OK) { 250 DIE(error, "failed to add %s repository to solver", 251 fErrorName.String()); 252 } 253 return *this; 254 } 255 256 257 } // namespace BPrivate 258 259 } // namespace BManager 260 261 } // namespace BPackageKit 262