libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / preferences / repositories / TaskLooper.cpp
blobef9fb3d65b9724fa4f301790e01489efdd2b3cf1
1 /*
2 * Copyright 2017 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Brian Hill
7 */
10 #include "TaskLooper.h"
12 #include <Catalog.h>
13 #include <MessageQueue.h>
14 #include <package/AddRepositoryRequest.h>
15 #include <package/DropRepositoryRequest.h>
16 #include <package/RefreshRepositoryRequest.h>
17 #include <package/PackageRoster.h>
18 #include <package/RepositoryConfig.h>
20 #include "constants.h"
22 #define DEBUGTASK 0
24 #undef B_TRANSLATION_CONTEXT
25 #define B_TRANSLATION_CONTEXT "TaskLooper"
27 static const BString kLogResultIndicator = "***";
28 static const BString kCompletedText =
29 B_TRANSLATE_COMMENT("Completed", "Completed task status message");
30 static const BString kFailedText =
31 B_TRANSLATE_COMMENT("Failed", "Failed task status message");
32 static const BString kAbortedText =
33 B_TRANSLATE_COMMENT("Aborted", "Aborted task status message");
34 static const BString kDescriptionText =
35 B_TRANSLATE_COMMENT("Description", "Failed task error description");
36 static const BString kDetailsText =
37 B_TRANSLATE_COMMENT("Details", "Job log details header");
39 using BSupportKit::BJob;
42 void
43 JobStateListener::JobStarted(BJob* job)
45 fJobLog.Add(job->Title());
49 void
50 JobStateListener::JobSucceeded(BJob* job)
52 BString resultText(kLogResultIndicator);
53 fJobLog.Add(resultText.Append(kCompletedText));
57 void
58 JobStateListener::JobFailed(BJob* job)
60 BString resultText(kLogResultIndicator);
61 resultText.Append(kFailedText).Append(": ")
62 .Append(strerror(job->Result()));
63 fJobLog.Add(resultText);
64 if (job->ErrorString().Length() > 0) {
65 resultText.SetTo(kLogResultIndicator);
66 resultText.Append(kDescriptionText).Append(": ")
67 .Append(job->ErrorString());
68 fJobLog.Add(resultText);
73 void
74 JobStateListener::JobAborted(BJob* job)
76 BString resultText(kLogResultIndicator);
77 resultText.Append(kAbortedText).Append(": ")
78 .Append(strerror(job->Result()));
79 fJobLog.Add(resultText);
80 if (job->ErrorString().Length() > 0) {
81 resultText.SetTo(kLogResultIndicator);
82 resultText.Append(kDescriptionText).Append(": ")
83 .Append(job->ErrorString());
84 fJobLog.Add(resultText);
89 BString
90 JobStateListener::GetJobLog()
92 return fJobLog.Join("\n");
96 TaskLooper::TaskLooper(const BMessenger& target)
98 BLooper("TaskLooper"),
99 fReplyTarget(target)
101 Run();
102 fMessenger.SetTo(this);
106 bool
107 TaskLooper::QuitRequested()
109 return MessageQueue()->IsEmpty();
113 void
114 TaskLooper::MessageReceived(BMessage* message)
116 switch (message->what)
118 case DO_TASK:
120 RepoRow* rowItem;
121 status_t result = message->FindPointer(key_rowptr, (void**)&rowItem);
122 if (result == B_OK) {
123 // Check to make sure there isn't already an existing task for this
124 int16 queueCount = fTaskQueue.CountItems();
125 for (int16 index = 0; index<queueCount; index++) {
126 Task* task = fTaskQueue.ItemAt(index);
127 if (rowItem == task->rowItem)
128 break;
131 // Initialize task
132 Task* newTask = new Task();
133 newTask->rowItem = rowItem;
134 newTask->name = rowItem->Name();
135 newTask->resultName = newTask->name;
136 if (rowItem->IsEnabled()) {
137 newTask->taskType = DISABLE_REPO;
138 newTask->taskParam = newTask->name;
139 } else {
140 newTask->taskType = ENABLE_REPO;
141 newTask->taskParam = rowItem->Url();
143 newTask->owner = this;
144 newTask->fTimer = NULL;
146 // Add to queue and start
147 fTaskQueue.AddItem(newTask);
148 BString threadName(newTask->taskType == ENABLE_REPO ?
149 "enable_task" : "disable_task");
150 newTask->threadId = spawn_thread(_DoTask, threadName.String(),
151 B_NORMAL_PRIORITY, (void*)newTask);
152 status_t threadResult;
153 if (newTask->threadId < B_OK)
154 threadResult = B_ERROR;
155 else {
156 threadResult = resume_thread(newTask->threadId);
157 if (threadResult == B_OK) {
158 newTask->fTimer = new TaskTimer(fMessenger, newTask);
159 newTask->fTimer->Start(newTask->name);
160 // Reply to view
161 BMessage reply(*message);
162 reply.what = TASK_STARTED;
163 reply.AddInt16(key_count, fTaskQueue.CountItems());
164 fReplyTarget.SendMessage(&reply);
165 } else
166 kill_thread(newTask->threadId);
168 if (threadResult != B_OK) {
169 _RemoveAndDelete(newTask);
172 break;
175 case TASK_COMPLETED:
176 case TASK_COMPLETED_WITH_ERRORS:
177 case TASK_CANCELED:
179 Task* task;
180 status_t result = message->FindPointer(key_taskptr, (void**)&task);
181 if (result == B_OK && fTaskQueue.HasItem(task)) {
182 task->fTimer->Stop(task->resultName);
183 BMessage reply(message->what);
184 reply.AddInt16(key_count, fTaskQueue.CountItems()-1);
185 reply.AddPointer(key_rowptr, task->rowItem);
186 if (message->what == TASK_COMPLETED_WITH_ERRORS)
187 reply.AddString(key_details, task->resultErrorDetails);
188 if (task->taskType == ENABLE_REPO
189 && task->name.Compare(task->resultName) != 0) {
190 reply.AddString(key_name, task->resultName);
192 fReplyTarget.SendMessage(&reply);
193 _RemoveAndDelete(task);
195 break;
198 case TASK_KILL_REQUEST:
200 Task* task;
201 status_t result = message->FindPointer(key_taskptr, (void**)&task);
202 if (result == B_OK && fTaskQueue.HasItem(task)) {
203 kill_thread(task->threadId);
204 BMessage reply(TASK_CANCELED);
205 reply.AddInt16(key_count, fTaskQueue.CountItems()-1);
206 reply.AddPointer(key_rowptr, task->rowItem);
207 fReplyTarget.SendMessage(&reply);
208 _RemoveAndDelete(task);
210 break;
216 void
217 TaskLooper::_RemoveAndDelete(Task* task)
219 fTaskQueue.RemoveItem(task);
220 if (task->fTimer) {
221 task->fTimer->Lock();
222 task->fTimer->Quit();
223 task->fTimer = NULL;
225 delete task;
229 status_t
230 TaskLooper::_DoTask(void* data)
232 Task* task = (Task*)data;
233 BString errorDetails, repoName("");
234 status_t returnResult = B_OK;
235 DecisionProvider decisionProvider;
236 JobStateListener listener;
237 switch (task->taskType)
239 case DISABLE_REPO:
241 BString nameParam(task->taskParam);
242 BPackageKit::BContext context(decisionProvider, listener);
243 BPackageKit::DropRepositoryRequest dropRequest(context, nameParam);
244 status_t result = dropRequest.Process();
245 if (result != B_OK) {
246 returnResult = result;
247 if (result != B_CANCELED) {
248 errorDetails.Append(B_TRANSLATE_COMMENT("There was an "
249 "error disabling the repository %name%",
250 "Error message, do not translate %name%"));
251 BString nameString("\"");
252 nameString.Append(nameParam).Append("\"");
253 errorDetails.ReplaceFirst("%name%", nameString);
254 _AppendErrorDetails(errorDetails, &listener);
257 break;
260 case ENABLE_REPO:
262 BString urlParam(task->taskParam);
263 BPackageKit::BContext context(decisionProvider, listener);
264 // Add repository
265 bool asUserRepository = false;
266 // TODO does this ever change?
267 BPackageKit::AddRepositoryRequest addRequest(context, urlParam,
268 asUserRepository);
269 status_t result = addRequest.Process();
270 if (result != B_OK) {
271 returnResult = result;
272 if (result != B_CANCELED) {
273 errorDetails.Append(B_TRANSLATE_COMMENT("There was an "
274 "error enabling the repository %url%",
275 "Error message, do not translate %url%"));
276 errorDetails.ReplaceFirst("%url%", urlParam);
277 _AppendErrorDetails(errorDetails, &listener);
279 break;
281 // Continue on to refresh repo cache
282 repoName = addRequest.RepositoryName();
283 BPackageKit::BPackageRoster roster;
284 BPackageKit::BRepositoryConfig repoConfig;
285 roster.GetRepositoryConfig(repoName, &repoConfig);
286 BPackageKit::BRefreshRepositoryRequest refreshRequest(context,
287 repoConfig);
288 result = refreshRequest.Process();
289 if (result != B_OK) {
290 returnResult = result;
291 if (result != B_CANCELED) {
292 errorDetails.Append(B_TRANSLATE_COMMENT("There was an "
293 "error refreshing the repository cache for %name%",
294 "Error message, do not translate %name%"));
295 BString nameString("\"");
296 nameString.Append(repoName).Append("\"");
297 errorDetails.ReplaceFirst("%name%", nameString);
298 _AppendErrorDetails(errorDetails, &listener);
301 break;
304 // Report completion status
305 BMessage reply;
306 if (returnResult == B_OK) {
307 reply.what = TASK_COMPLETED;
308 // Add the repo name if we need to update the list row value
309 if (task->taskType == ENABLE_REPO)
310 task->resultName = repoName;
311 } else if (returnResult == B_CANCELED)
312 reply.what = TASK_CANCELED;
313 else {
314 reply.what = TASK_COMPLETED_WITH_ERRORS;
315 task->resultErrorDetails = errorDetails;
316 if (task->taskType == ENABLE_REPO)
317 task->resultName = repoName;
319 reply.AddPointer(key_taskptr, task);
320 task->owner->PostMessage(&reply);
321 #if DEBUGTASK
322 if (returnResult == B_OK || returnResult == B_CANCELED) {
323 BString degubDetails("Debug info:\n");
324 degubDetails.Append(listener.GetJobLog());
325 (new BAlert("debug", degubDetails, "OK"))->Go(NULL);
327 #endif // DEBUGTASK
328 return 0;
332 void
333 TaskLooper::_AppendErrorDetails(BString& details, JobStateListener* listener)
335 details.Append("\n\n").Append(kDetailsText).Append(":\n");
336 details.Append(listener->GetJobLog());