1 /*
2 * Copyright 2015-2016, Rene Gollent, rene@gollent.com.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "ReportUserInterface.h"
8
9 #include <stdio.h>
10
11 #include <Entry.h>
12 #include <FindDirectory.h>
13 #include <Path.h>
14
15 #include <AutoLocker.h>
16
17 #include "MessageCodes.h"
18 #include "UiUtils.h"
19
20
ReportUserInterface(thread_id targetThread,const char * reportPath)21 ReportUserInterface::ReportUserInterface(thread_id targetThread,
22 const char* reportPath)
23 :
24 fTeam(NULL),
25 fListener(NULL),
26 fTargetThread(targetThread),
27 fReportPath(reportPath),
28 fShowSemaphore(-1),
29 fReportSemaphore(-1),
30 fShown(false),
31 fTerminating(false)
32 {
33 }
34
35
~ReportUserInterface()36 ReportUserInterface::~ReportUserInterface()
37 {
38 if (fShowSemaphore >= 0)
39 delete_sem(fShowSemaphore);
40
41 if (fTeam != NULL)
42 fTeam->RemoveListener(this);
43 }
44
45
46 const char*
ID() const47 ReportUserInterface::ID() const
48 {
49 return "ReportUserInterface";
50 }
51
52
53 status_t
Init(Team * team,UserInterfaceListener * listener)54 ReportUserInterface::Init(Team* team, UserInterfaceListener* listener)
55 {
56 fShowSemaphore = create_sem(0, "show report");
57 if (fShowSemaphore < 0)
58 return fShowSemaphore;
59
60 fReportSemaphore = create_sem(0, "report generator wait");
61 if (fReportSemaphore < 0)
62 return fReportSemaphore;
63
64 fTeam = team;
65 fListener = listener;
66
67 fTeam->AddListener(this);
68
69 return B_OK;
70 }
71
72
73 void
Show()74 ReportUserInterface::Show()
75 {
76 fShown = true;
77 release_sem(fShowSemaphore);
78 }
79
80
81 void
Terminate()82 ReportUserInterface::Terminate()
83 {
84 fTerminating = true;
85 }
86
87
88 UserInterface*
Clone() const89 ReportUserInterface::Clone() const
90 {
91 // the report interface does not support cloning, since
92 // it won't ever be asked to interactively restart.
93 return NULL;
94 }
95
96
97 bool
IsInteractive() const98 ReportUserInterface::IsInteractive() const
99 {
100 return false;
101 }
102
103
104 status_t
LoadSettings(const TeamUiSettings * settings)105 ReportUserInterface::LoadSettings(const TeamUiSettings* settings)
106 {
107 return B_OK;
108 }
109
110
111 status_t
SaveSettings(TeamUiSettings * & settings) const112 ReportUserInterface::SaveSettings(TeamUiSettings*& settings) const
113 {
114 return B_OK;
115 }
116
117
118 void
NotifyUser(const char * title,const char * message,user_notification_type type)119 ReportUserInterface::NotifyUser(const char* title, const char* message,
120 user_notification_type type)
121 {
122 }
123
124
125 void
NotifyBackgroundWorkStatus(const char * message)126 ReportUserInterface::NotifyBackgroundWorkStatus(const char* message)
127 {
128 }
129
130
131 int32
SynchronouslyAskUser(const char * title,const char * message,const char * choice1,const char * choice2,const char * choice3)132 ReportUserInterface::SynchronouslyAskUser(const char* title,
133 const char* message, const char* choice1, const char* choice2,
134 const char* choice3)
135 {
136 return -1;
137 }
138
139
140 status_t
SynchronouslyAskUserForFile(entry_ref * _ref)141 ReportUserInterface::SynchronouslyAskUserForFile(entry_ref* _ref)
142 {
143 return B_UNSUPPORTED;
144 }
145
146
147 void
Run()148 ReportUserInterface::Run()
149 {
150 // Wait for the Show() semaphore to be released.
151 status_t error;
152 do {
153 error = acquire_sem(fShowSemaphore);
154 } while (error == B_INTERRUPTED);
155
156 if (error != B_OK)
157 return;
158
159 bool waitNeeded = false;
160 if (fTargetThread > 0) {
161 AutoLocker< ::Team> teamLocker(fTeam);
162 ::Thread* thread = fTeam->ThreadByID(fTargetThread);
163 if (thread == NULL)
164 waitNeeded = true;
165 else if (thread->State() != THREAD_STATE_STOPPED) {
166 waitNeeded = true;
167 fListener->ThreadActionRequested(fTargetThread, MSG_THREAD_STOP);
168 }
169 }
170
171 if (waitNeeded) {
172 do {
173 error = acquire_sem(fShowSemaphore);
174 } while (error == B_INTERRUPTED);
175
176 if (error != B_OK)
177 return;
178 }
179
180 entry_ref ref;
181 if (fReportPath != NULL && fReportPath[0] == '/') {
182 error = get_ref_for_path(fReportPath, &ref);
183 } else {
184 char filename[B_FILE_NAME_LENGTH];
185 if (fReportPath != NULL)
186 strlcpy(filename, fReportPath, sizeof(filename));
187 else
188 UiUtils::ReportNameForTeam(fTeam, filename, sizeof(filename));
189
190 BPath path;
191 error = find_directory(B_DESKTOP_DIRECTORY, &path);
192 if (error == B_OK)
193 error = path.Append(filename);
194 if (error == B_OK)
195 error = get_ref_for_path(path.Path(), &ref);
196 }
197
198 if (error != B_OK)
199 printf("Unable to get ref for report path %s\n", strerror(error));
200 else {
201 fListener->DebugReportRequested(&ref);
202
203 do {
204 error = acquire_sem(fReportSemaphore);
205 } while (error == B_INTERRUPTED);
206 }
207
208 fListener->UserInterfaceQuitRequested(
209 UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM);
210 }
211
212
213 void
ThreadAdded(const Team::ThreadEvent & event)214 ReportUserInterface::ThreadAdded(const Team::ThreadEvent& event)
215 {
216 ::Thread* thread = event.GetThread();
217 if (thread->ID() != fTargetThread)
218 return;
219
220 if (thread->State() != THREAD_STATE_STOPPED)
221 fListener->ThreadActionRequested(thread->ID(), MSG_THREAD_STOP);
222 else
223 release_sem(fShowSemaphore);
224 }
225
226
227 void
ThreadStateChanged(const Team::ThreadEvent & event)228 ReportUserInterface::ThreadStateChanged(const Team::ThreadEvent& event)
229 {
230 ::Thread* thread = event.GetThread();
231 if (thread->ID() != fTargetThread)
232 return;
233 else if (thread->State() == THREAD_STATE_STOPPED)
234 release_sem(fShowSemaphore);
235 }
236
237
238 void
DebugReportChanged(const Team::DebugReportEvent & event)239 ReportUserInterface::DebugReportChanged(const Team::DebugReportEvent& event)
240 {
241 if (event.GetFinalStatus() == B_OK)
242 printf("Debug report saved to %s\n", event.GetReportPath());
243 else {
244 fprintf(stderr, "Failed to write debug report: %s\n", strerror(
245 event.GetFinalStatus()));
246 }
247 release_sem(fReportSemaphore);
248 }
249