1 /*
2 * Copyright (c) 2010, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Author:
6 * Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
7 */
8
9
10 #include "PackageInstall.h"
11
12 #include "InstalledPackageInfo.h"
13 #include "PackageItem.h"
14 #include "PackageView.h"
15
16 #include <Alert.h>
17 #include <Catalog.h>
18 #include <Locale.h>
19 #include <stdio.h>
20
21
22 #undef B_TRANSLATION_CONTEXT
23 #define B_TRANSLATION_CONTEXT "PackageInstall"
24
25
26 static int32
install_function(void * data)27 install_function(void* data)
28 {
29 // TODO: Inform if already one thread is running
30 if (data == NULL)
31 return -1;
32
33 PackageInstall* install = static_cast<PackageInstall*>(data);
34 install->Install();
35 return 0;
36 }
37
38
PackageInstall(PackageView * parent)39 PackageInstall::PackageInstall(PackageView* parent)
40 :
41 fParent(parent),
42 fThreadId(-1),
43 fCurrentScript(NULL)
44 {
45 }
46
47
~PackageInstall()48 PackageInstall::~PackageInstall()
49 {
50 }
51
52
53 status_t
Start()54 PackageInstall::Start()
55 {
56 status_t ret = B_OK;
57
58 fIdLocker.Lock();
59 if (fThreadId > -1) {
60 ret = B_BUSY;
61 } else {
62 fThreadId = spawn_thread(install_function, "install_package",
63 B_NORMAL_PRIORITY, this);
64 resume_thread(fThreadId);
65 }
66 fIdLocker.Unlock();
67
68 return ret;
69 }
70
71
72 void
Stop()73 PackageInstall::Stop()
74 {
75 // TODO: Argh! No killing of threads!! That leaks resources which they
76 // allocated. Rather inform them they need to quit, which they do at the
77 // next convenient time, then use wait_for_thread() here.
78 fIdLocker.Lock();
79 if (fThreadId > -1) {
80 kill_thread(fThreadId);
81 fThreadId = -1;
82 }
83 fIdLocker.Unlock();
84
85 fCurrentScriptLocker.Lock();
86 if (fCurrentScript != NULL) {
87 thread_id id = fCurrentScript->GetThreadId();
88 if (id > -1) {
89 fCurrentScript->SetThreadId(-1);
90 kill_thread(id);
91 }
92 fCurrentScript = NULL;
93 }
94 fCurrentScriptLocker.Unlock();
95 }
96
97
98 void
Install()99 PackageInstall::Install()
100 {
101 // A message sending wrapper around _Install()
102 uint32 code = _Install();
103
104 BMessenger messenger(fParent);
105 if (messenger.IsValid()) {
106 BMessage message(code);
107 messenger.SendMessage(&message);
108 }
109 }
110
111
112 static inline BString
get_item_progress_string(uint32 index,uint32 total)113 get_item_progress_string(uint32 index, uint32 total)
114 {
115 BString label(B_TRANSLATE("%index% of %total%"));
116 BString indexString;
117 indexString << (index + 1);
118 BString totalString;
119 totalString << total;
120 label.ReplaceAll("%index%", indexString);
121 label.ReplaceAll("%total%", totalString);
122 return label;
123 }
124
125
126 uint32
_Install()127 PackageInstall::_Install()
128 {
129 PackageInfo* info = fParent->GetPackageInfo();
130 pkg_profile* type = static_cast<pkg_profile*>(info->GetProfile(
131 fParent->CurrentType()));
132 uint32 n = type->items.CountItems();
133 uint32 m = info->GetScriptCount();
134
135 PackageStatus* progress = fParent->StatusWindow();
136 progress->Reset(n + m + 5);
137
138 progress->StageStep(1, B_TRANSLATE("Preparing package"));
139
140 InstalledPackageInfo packageInfo(info->GetName(), info->GetVersion());
141
142 status_t err = packageInfo.InitCheck();
143 if (err == B_OK) {
144 // The package is already installed, inform the user
145 BAlert* reinstall = new BAlert("reinstall",
146 B_TRANSLATE("The given package seems to be already installed on "
147 "your system. Would you like to uninstall the existing one "
148 "and continue the installation?"),
149 B_TRANSLATE("Continue"),
150 B_TRANSLATE("Abort"));
151 reinstall->SetShortcut(1, B_ESCAPE);
152
153 if (reinstall->Go() == 0) {
154 // Uninstall the package
155 err = packageInfo.Uninstall();
156 if (err != B_OK) {
157 fprintf(stderr, "Error uninstalling previously installed "
158 "package: %s\n", strerror(err));
159 // Ignore error
160 }
161
162 err = packageInfo.SetTo(info->GetName(), info->GetVersion(), true);
163 if (err != B_OK) {
164 fprintf(stderr, "Error marking installation of package: "
165 "%s\n", strerror(err));
166 return P_MSG_I_ERROR;
167 }
168 } else {
169 // Abort the installation
170 return P_MSG_I_ABORT;
171 }
172 } else if (err == B_ENTRY_NOT_FOUND) {
173 err = packageInfo.SetTo(info->GetName(), info->GetVersion(), true);
174 if (err != B_OK) {
175 fprintf(stderr, "Error marking installation of package: "
176 "%s\n", strerror(err));
177 return P_MSG_I_ERROR;
178 }
179 } else if (progress->Stopped()) {
180 return P_MSG_I_ABORT;
181 } else {
182 fprintf(stderr, "returning on error\n");
183 return P_MSG_I_ERROR;
184 }
185
186 progress->StageStep(1, B_TRANSLATE("Installing files and folders"));
187
188 // Install files and directories
189
190 packageInfo.SetName(info->GetName());
191 // TODO: Here's a small problem, since right now it's not quite sure
192 // which description is really used as such. The one displayed on
193 // the installer is mostly package installation description, but
194 // most people use it for describing the application in more detail
195 // then in the short description.
196 // For now, we'll use the short description if possible.
197 BString description = info->GetShortDescription();
198 if (description.Length() <= 0)
199 description = info->GetDescription();
200 packageInfo.SetDescription(description.String());
201 packageInfo.SetSpaceNeeded(type->space_needed);
202
203 fItemExistsPolicy = P_EXISTS_NONE;
204
205 const char* installPath = fParent->CurrentPath()->Path();
206 for (uint32 i = 0; i < n; i++) {
207 ItemState state(fItemExistsPolicy);
208 PackageItem* item = static_cast<PackageItem*>(type->items.ItemAt(i));
209
210 err = item->DoInstall(installPath, &state);
211 if (err == B_FILE_EXISTS) {
212 // Writing to path failed because path already exists - ask the user
213 // what to do and retry the writing process
214 int32 choice = fParent->ItemExists(*item, state.destination,
215 fItemExistsPolicy);
216 if (choice != P_EXISTS_ABORT) {
217 state.policy = choice;
218 err = item->DoInstall(installPath, &state);
219 }
220 }
221
222 if (err != B_OK) {
223 fprintf(stderr, "Error '%s' while writing path\n", strerror(err));
224 return P_MSG_I_ERROR;
225 }
226
227 if (progress->Stopped())
228 return P_MSG_I_ABORT;
229
230 // Update progress
231 progress->StageStep(1, NULL, get_item_progress_string(i, n).String());
232
233 // Mark installed item in packageInfo
234 packageInfo.AddItem(state.destination.Path());
235 }
236
237 progress->StageStep(1, B_TRANSLATE("Running post-installation scripts"),
238 "");
239
240 // Run all scripts
241 // TODO: Change current working directory to installation location!
242 for (uint32 i = 0; i < m; i++) {
243 PackageScript* script = info->GetScript(i);
244
245 fCurrentScriptLocker.Lock();
246 fCurrentScript = script;
247
248 status_t status = script->DoInstall(installPath);
249 if (status != B_OK) {
250 fprintf(stderr, "Error while running script: %s\n",
251 strerror(status));
252 fCurrentScriptLocker.Unlock();
253 return P_MSG_I_ERROR;
254 }
255 fCurrentScriptLocker.Unlock();
256
257 wait_for_thread(script->GetThreadId(), &status);
258
259 fCurrentScriptLocker.Lock();
260 script->SetThreadId(-1);
261 fCurrentScript = NULL;
262 fCurrentScriptLocker.Unlock();
263
264 if (progress->Stopped())
265 return P_MSG_I_ABORT;
266
267 progress->StageStep(1, NULL, get_item_progress_string(i, m).String());
268 }
269
270 progress->StageStep(1, B_TRANSLATE("Finishing installation"), "");
271
272 err = packageInfo.Save();
273 if (err != B_OK)
274 return P_MSG_I_ERROR;
275
276 progress->StageStep(1, B_TRANSLATE("Done"));
277
278 // Inform our parent that we finished
279 return P_MSG_I_FINISHED;
280 }
281
282