xref: /haiku/src/apps/haikudepot/process/ProcessCoordinatorFactory.cpp (revision 1978089f7cec856677e46204e992c7273d70b9af)
1 /*
2  * Copyright 2018-2023, Andrew Lindesay <apl@lindesay.co.nz>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 #include "ProcessCoordinatorFactory.h"
6 
7 #include <Autolock.h>
8 #include <AutoLocker.h>
9 
10 #include <package/Context.h>
11 #include <package/PackageRoster.h>
12 
13 #include "AbstractServerProcess.h"
14 #include "CacheScreenshotProcess.h"
15 #include "DeskbarLink.h"
16 #include "HaikuDepotConstants.h"
17 #include "IncrementViewCounterProcess.h"
18 #include "InstallPackageProcess.h"
19 #include "LocalPkgDataLoadProcess.h"
20 #include "LocalRepositoryUpdateProcess.h"
21 #include "Logger.h"
22 #include "Model.h"
23 #include "OpenPackageProcess.h"
24 #include "PackageInfoListener.h"
25 #include "PopulatePkgSizesProcess.h"
26 #include "ProcessCoordinator.h"
27 #include "ServerHelper.h"
28 #include "ServerIconExportUpdateProcess.h"
29 #include "ServerPkgDataUpdateProcess.h"
30 #include "ServerReferenceDataUpdateProcess.h"
31 #include "ServerRepositoryDataUpdateProcess.h"
32 #include "ServerSettings.h"
33 #include "StorageUtils.h"
34 #include "ThreadedProcessNode.h"
35 #include "UninstallPackageProcess.h"
36 #include "UserDetailVerifierProcess.h"
37 
38 
39 using namespace BPackageKit;
40 
41 
42 /*static*/ ProcessCoordinator*
43 ProcessCoordinatorFactory::CreateIncrementViewCounter(
44 	Model* model, const PackageInfoRef package)
45 {
46 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
47 		"IncrementViewCounter");
48 	AbstractProcessNode* node = new ThreadedProcessNode(
49 		new IncrementViewCounterProcess(model, package));
50 	processCoordinator->AddNode(node);
51 	return processCoordinator;
52 }
53 
54 
55 /*static*/ ProcessCoordinator*
56 ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator(
57 	UserDetailVerifierListener* userDetailVerifierListener,
58 	Model* model)
59 {
60 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
61 		"UserDetailVerifier");
62 	AbstractProcessNode* userDetailVerifier = new ThreadedProcessNode(
63 		new UserDetailVerifierProcess(model, userDetailVerifierListener));
64 	processCoordinator->AddNode(userDetailVerifier);
65 	return processCoordinator;
66 }
67 
68 
69 /* static */ ProcessCoordinator*
70 ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
71 	PackageInfoListenerRef packageInfoListener,
72 	Model* model, bool forceLocalUpdate)
73 {
74 	bool areWorkingFilesAvailable = StorageUtils::AreWorkingFilesAvailable();
75 	uint32 serverProcessOptions = _CalculateServerProcessOptions();
76 	BAutolock locker(model->Lock());
77 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
78 		"BulkLoad", new BMessage(MSG_BULK_LOAD_DONE));
79 
80 	AbstractProcessNode *localRepositoryUpdate =
81 		new ThreadedProcessNode(new LocalRepositoryUpdateProcess(model,
82 			forceLocalUpdate));
83 	processCoordinator->AddNode(localRepositoryUpdate);
84 
85 	AbstractProcessNode *localPkgDataLoad =
86 		new ThreadedProcessNode(new LocalPkgDataLoadProcess(
87 			packageInfoListener, model, forceLocalUpdate));
88 	localPkgDataLoad->AddPredecessor(localRepositoryUpdate);
89 	processCoordinator->AddNode(localPkgDataLoad);
90 
91 	if (areWorkingFilesAvailable) {
92 		AbstractProcessNode *serverIconExportUpdate =
93 			new ThreadedProcessNode(new ServerIconExportUpdateProcess(model,
94 				serverProcessOptions));
95 		serverIconExportUpdate->AddPredecessor(localPkgDataLoad);
96 		processCoordinator->AddNode(serverIconExportUpdate);
97 
98 		AbstractProcessNode *serverRepositoryDataUpdate =
99 			new ThreadedProcessNode(new ServerRepositoryDataUpdateProcess(model,
100 				serverProcessOptions));
101 		serverRepositoryDataUpdate->AddPredecessor(localPkgDataLoad);
102 		processCoordinator->AddNode(serverRepositoryDataUpdate);
103 
104 		AbstractProcessNode *serverReferenceDataUpdate =
105 			new ThreadedProcessNode(new ServerReferenceDataUpdateProcess(model,
106 				serverProcessOptions));
107 		processCoordinator->AddNode(serverReferenceDataUpdate);
108 
109 		// This one has to run after the server data is taken up because it
110 		// will fill in the gaps based on local data that was not able to be
111 		// sourced from the server. It has all of the
112 		// `ServerPkgDataUpdateProcess` nodes configured as its predecessors.
113 
114 		AbstractProcessNode* populatePkgSizes =
115 			new ThreadedProcessNode(new PopulatePkgSizesProcess(model));
116 
117 		// create a process for each of the repositories that are configured on
118 		// the local system.  Later, only those that have a web-app repository
119 		// server code will be actually processed, but this means that the
120 		// creation of the 'processes' does not need to be dynamic as the
121 		// process coordinator runs.
122 
123 		BPackageRoster roster;
124 		BStringList repoNames;
125 		status_t repoNamesResult = roster.GetRepositoryNames(repoNames);
126 
127 		if (repoNamesResult == B_OK) {
128 			AutoLocker<BLocker> locker(model->Lock());
129 
130 			for (int32 i = 0; i < repoNames.CountStrings(); i++) {
131 				AbstractProcessNode* processNode = new ThreadedProcessNode(
132 					new ServerPkgDataUpdateProcess(
133 						model->Language()->PreferredLanguage()->Code(),
134 						repoNames.StringAt(i), model, serverProcessOptions));
135 				processNode->AddPredecessor(serverRepositoryDataUpdate);
136 				processNode->AddPredecessor(serverReferenceDataUpdate);
137 				processCoordinator->AddNode(processNode);
138 
139 				populatePkgSizes->AddPredecessor(processNode);
140 			}
141 		} else
142 			HDERROR("a problem has arisen getting the repository names.");
143 
144 		processCoordinator->AddNode(populatePkgSizes);
145 	}
146 
147 	return processCoordinator;
148 }
149 
150 
151 /*static*/ ProcessCoordinator*
152 ProcessCoordinatorFactory::CreatePackageActionCoordinator(
153 	Model* model, BMessage* message)
154 {
155 	switch (message->what) {
156 		case MSG_PKG_INSTALL:
157 			return _CreateInstallPackageActionCoordinator(model, message);
158 		case MSG_PKG_UNINSTALL:
159 			return _CreateUninstallPackageActionCoordinator(model, message);
160 		case MSG_PKG_OPEN:
161 			return _CreateOpenPackageActionCoordinator(model, message);
162 		default:
163 			HDFATAL("unexpected package action message what");
164 	}
165 }
166 
167 
168 /*static*/ ProcessCoordinator*
169 ProcessCoordinatorFactory::CacheScreenshotCoordinator(Model* model,
170 	ScreenshotCoordinate& screenshotCoordinate)
171 {
172 	ProcessCoordinator* processCoordinator = new ProcessCoordinator("CacheScreenshot");
173 	AbstractProcessNode* cacheScreenshotNode = new ThreadedProcessNode(
174 		new CacheScreenshotProcess(model, screenshotCoordinate));
175 	processCoordinator->AddNode(cacheScreenshotNode);
176 	return processCoordinator;
177 }
178 
179 
180 /*static*/ PackageInfoRef
181 ProcessCoordinatorFactory::_ExtractPackageFromMessage(
182 	Model* model, BMessage* message)
183 {
184 	BString pkgName;
185 	if (message->FindString(KEY_PACKAGE_NAME, &pkgName) != B_OK)
186 		HDFATAL("malformed message missing key [%s]", KEY_PACKAGE_NAME);
187 	return model->PackageForName(pkgName);
188 }
189 
190 
191 /*static*/ ProcessCoordinator*
192 ProcessCoordinatorFactory::_CreateInstallPackageActionCoordinator(
193 	Model* model, BMessage* message)
194 {
195 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
196 		"InstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
197 	PackageInfoRef package = _ExtractPackageFromMessage(model, message);
198 	if (package.IsSet()) {
199 		AbstractProcessNode *processNode =
200 			new ThreadedProcessNode(
201 				new InstallPackageProcess(package, model), 10);
202 		processCoordinator->AddNode(processNode);
203 	} else {
204 		HDERROR("unable to find the package");
205 	}
206 	return processCoordinator;
207 }
208 
209 
210 /*static*/ ProcessCoordinator*
211 ProcessCoordinatorFactory::_CreateUninstallPackageActionCoordinator(
212 	Model* model, BMessage* message)
213 {
214 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
215 		"UninstallPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
216 	PackageInfoRef package = _ExtractPackageFromMessage(model, message);
217 	if (package.IsSet()) {
218 		AbstractProcessNode *processNode =
219 			new ThreadedProcessNode(
220 				new UninstallPackageProcess(package, model), 10);
221 		processCoordinator->AddNode(processNode);
222 	} else {
223 		HDERROR("unable to find the package");
224 	}
225 	return processCoordinator;
226 }
227 
228 
229 /*static*/ ProcessCoordinator*
230 ProcessCoordinatorFactory::_CreateOpenPackageActionCoordinator(
231 	Model* model, BMessage* message)
232 {
233 	ProcessCoordinator* processCoordinator = new ProcessCoordinator(
234 		"OpenPackage", new BMessage(MSG_PACKAGE_ACTION_DONE));
235 	PackageInfoRef package = _ExtractPackageFromMessage(model, message);
236 	if (package.IsSet()) {
237 		BMessage deskbarLinkMessage;
238 		if (message->FindMessage(KEY_DESKBAR_LINK, &deskbarLinkMessage) != B_OK)
239 			HDFATAL("malformed message missing key [%s]", KEY_DESKBAR_LINK);
240 		DeskbarLink deskbarLink(&deskbarLinkMessage);
241 		AbstractProcessNode *processNode =
242 			new ThreadedProcessNode(new OpenPackageProcess(
243 				package, model, deskbarLink));
244 		processCoordinator->AddNode(processNode);
245 	} else {
246 		HDERROR("unable to find the package");
247 	}
248 	return processCoordinator;
249 }
250 
251 
252 /* static */ uint32
253 ProcessCoordinatorFactory::_CalculateServerProcessOptions()
254 {
255 	uint32 processOptions = 0;
256 
257 	if (ServerSettings::IsClientTooOld()) {
258 		HDINFO("bulk load proceeding without network communications "
259 			"because the client is too old");
260 		processOptions |= SERVER_PROCESS_NO_NETWORKING;
261 	}
262 
263 	if (!ServerHelper::IsNetworkAvailable())
264 		processOptions |= SERVER_PROCESS_NO_NETWORKING;
265 
266 	if (ServerSettings::PreferCache())
267 		processOptions |= SERVER_PROCESS_PREFER_CACHE;
268 
269 	if (ServerSettings::DropCache())
270 		processOptions |= SERVER_PROCESS_DROP_CACHE;
271 
272 	return processOptions;
273 }
274