xref: /haiku/src/apps/packageinstaller/PackageInstall.cpp (revision 4fd62caa9acc437534c41bbb7d3fc9d53e915005)
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
27 install_function(void *data)
28 {
29 	// TODO: Inform if already one thread is running
30 	PackageInstall *install = static_cast<PackageInstall *>(data);
31 	if (data == NULL)
32 		return -1;
33 
34 	install->Install();
35 	return 0;
36 }
37 
38 
39 PackageInstall::PackageInstall(PackageView *parent)
40 	: fParent(parent),
41 	fThreadId(-1),
42 	fCurrentScript(NULL)
43 {
44 }
45 
46 
47 PackageInstall::~PackageInstall()
48 {
49 }
50 
51 
52 status_t
53 PackageInstall::Start()
54 {
55 	status_t ret = B_OK;
56 
57 	fIdLocker.Lock();
58 	if (fThreadId > -1) {
59 		ret = B_BUSY;
60 	} else {
61 		fThreadId = spawn_thread(install_function, "install_package", B_NORMAL_PRIORITY,
62 				static_cast<void *>(this));
63 		resume_thread(fThreadId);
64 	}
65 	fIdLocker.Unlock();
66 
67 	return ret;
68 }
69 
70 
71 void
72 PackageInstall::Stop()
73 {
74 	fIdLocker.Lock();
75 	if (fThreadId > -1) {
76 		kill_thread(fThreadId);
77 		fThreadId = -1;
78 	}
79 	fIdLocker.Unlock();
80 
81 	fCurrentScriptLocker.Lock();
82 	if (fCurrentScript != NULL) {
83 		thread_id id = fCurrentScript->GetThreadId();
84 		if (id > -1) {
85 			fCurrentScript->SetThreadId(-1);
86 			kill_thread(id);
87 		}
88 		fCurrentScript = NULL;
89 	}
90 	fCurrentScriptLocker.Unlock();
91 }
92 
93 
94 void
95 PackageInstall::Install()
96 {
97 	// A message sending wrapper around _Install()
98 	uint32 msg = _Install();
99 	if (fParent && fParent->Looper())
100 		fParent->Looper()->PostMessage(new BMessage(msg), fParent);
101 }
102 
103 
104 uint32
105 PackageInstall::_Install()
106 {
107 	PackageInfo *info = fParent->GetPackageInfo();
108 	pkg_profile *type = static_cast<pkg_profile *>(info->GetProfile(
109 		fParent->GetCurrentType()));
110 	uint32 n = type->items.CountItems(), m = info->GetScriptCount();
111 
112 	PackageStatus *progress = fParent->GetStatusWindow();
113 	progress->Reset(n + m + 5);
114 
115 	progress->StageStep(1, B_TRANSLATE("Preparing package"));
116 
117 	InstalledPackageInfo packageInfo(info->GetName(), info->GetVersion());
118 
119 	status_t err = packageInfo.InitCheck();
120 	if (err == B_OK) {
121 		// The package is already installed, inform the user
122 		BAlert *reinstall = new BAlert("reinstall",
123 			B_TRANSLATE("The given package seems to be already installed on "
124 				"your system. Would you like to uninstall the existing one "
125 				"and continue the installation?"),
126 			B_TRANSLATE("Continue"),
127 			B_TRANSLATE("Abort"));
128 		reinstall->SetShortcut(1, B_ESCAPE);
129 
130 		if (reinstall->Go() == 0) {
131 			// Uninstall the package
132 			err = packageInfo.Uninstall();
133 			if (err != B_OK) {
134 				fprintf(stderr, "Error on uninstall\n");
135 				return P_MSG_I_ERROR;
136 			}
137 
138 			err = packageInfo.SetTo(info->GetName(), info->GetVersion(), true);
139 			if (err != B_OK) {
140 				fprintf(stderr, "Error on SetTo\n");
141 				return P_MSG_I_ERROR;
142 			}
143 		} else {
144 			// Abort the installation
145 			return P_MSG_I_ABORT;
146 		}
147 	} else if (err == B_ENTRY_NOT_FOUND) {
148 		err = packageInfo.SetTo(info->GetName(), info->GetVersion(), true);
149 		if (err != B_OK) {
150 			fprintf(stderr, "Error on SetTo\n");
151 			return P_MSG_I_ERROR;
152 		}
153 	} else if (progress->Stopped()) {
154 		return P_MSG_I_ABORT;
155 	} else {
156 		fprintf(stderr, "returning on error\n");
157 		return P_MSG_I_ERROR;
158 	}
159 
160 	progress->StageStep(1, B_TRANSLATE("Installing files and folders"));
161 
162 	// Install files and directories
163 	PackageItem *iter;
164 	ItemState state;
165 	uint32 i;
166 	int32 choice;
167 	BString label;
168 
169 	packageInfo.SetName(info->GetName());
170 	// TODO: Here's a small problem, since right now it's not quite sure
171 	//		which description is really used as such. The one displayed on
172 	//		the installer is mostly package installation description, but
173 	//		most people use it for describing the application in more detail
174 	//		then in the short description.
175 	//		For now, we'll use the short description if possible.
176 	BString description = info->GetShortDescription();
177 	if (description.Length() <= 0)
178 		description = info->GetDescription();
179 	packageInfo.SetDescription(description.String());
180 	packageInfo.SetSpaceNeeded(type->space_needed);
181 
182 	fItemExistsPolicy = P_EXISTS_NONE;
183 
184 	const char *installPath = fParent->GetCurrentPath()->Path();
185 	for (i = 0; i < n; i++) {
186 		state.Reset(fItemExistsPolicy); // Reset the current item state
187 		iter = static_cast<PackageItem *>(type->items.ItemAt(i));
188 
189 		err = iter->DoInstall(installPath, &state);
190 		if (err == B_FILE_EXISTS) {
191 			// Writing to path failed because path already exists - ask the user
192 			// what to do and retry the writing process
193 			choice = fParent->ItemExists(*iter, state.destination,
194 				fItemExistsPolicy);
195 			if (choice != P_EXISTS_ABORT) {
196 				state.policy = choice;
197 				err = iter->DoInstall(installPath, &state);
198 			}
199 		}
200 
201 		if (err != B_OK) {
202 			fprintf(stderr, "Error while writing path\n");
203 			return P_MSG_I_ERROR;
204 		}
205 
206 		if (progress->Stopped())
207 			return P_MSG_I_ABORT;
208 		label = "";
209 		label << (uint32)(i + 1) << " of " << (uint32)n;
210 		progress->StageStep(1, NULL, label.String());
211 
212 		packageInfo.AddItem(state.destination.Path());
213 	}
214 
215 	progress->StageStep(1, B_TRANSLATE("Running post-installation scripts"),
216 		 "");
217 
218 	PackageScript *scr;
219 	status_t status;
220 	// Run all scripts
221 	for (i = 0; i < m; i++) {
222 		scr = info->GetScript(i);
223 
224 		fCurrentScriptLocker.Lock();
225 		fCurrentScript = scr;
226 
227 		if (scr->DoInstall() != B_OK) {
228 			fprintf(stderr, "Error while running script\n");
229 			return P_MSG_I_ERROR;
230 		}
231 		fCurrentScriptLocker.Unlock();
232 
233 		wait_for_thread(scr->GetThreadId(), &status);
234 		fCurrentScriptLocker.Lock();
235 		scr->SetThreadId(-1);
236 		fCurrentScript = NULL;
237 		fCurrentScriptLocker.Unlock();
238 
239 		if (progress->Stopped())
240 			return P_MSG_I_ABORT;
241 		label = "";
242 		label << (uint32)(i + 1) << " of " << (uint32)m;
243 		progress->StageStep(1, NULL, label.String());
244 	}
245 
246 	progress->StageStep(1, B_TRANSLATE("Finishing installation"), "");
247 
248 	err = packageInfo.Save();
249 	if (err != B_OK)
250 		return P_MSG_I_ERROR;
251 
252 	progress->StageStep(1, B_TRANSLATE("Done"));
253 
254 	// Inform our parent that we finished
255 	return P_MSG_I_FINISHED;
256 }
257 
258