xref: /haiku/src/apps/packageinstaller/PackageInstall.cpp (revision f2b4344867e97c3f4e742a1b4a15e6879644601a)
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_TRANSLATE_CONTEXT
23 #define B_TRANSLATE_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 
129 		if (reinstall->Go() == 0) {
130 			// Uninstall the package
131 			err = packageInfo.Uninstall();
132 			if (err != B_OK) {
133 				fprintf(stderr, "Error on uninstall\n");
134 				return P_MSG_I_ERROR;
135 			}
136 
137 			err = packageInfo.SetTo(info->GetName(), info->GetVersion(), true);
138 			if (err != B_OK) {
139 				fprintf(stderr, "Error on SetTo\n");
140 				return P_MSG_I_ERROR;
141 			}
142 		} else {
143 			// Abort the installation
144 			return P_MSG_I_ABORT;
145 		}
146 	} else if (err == B_ENTRY_NOT_FOUND) {
147 		err = packageInfo.SetTo(info->GetName(), info->GetVersion(), true);
148 		if (err != B_OK) {
149 			fprintf(stderr, "Error on SetTo\n");
150 			return P_MSG_I_ERROR;
151 		}
152 	} else if (progress->Stopped()) {
153 		return P_MSG_I_ABORT;
154 	} else {
155 		fprintf(stderr, "returning on error\n");
156 		return P_MSG_I_ERROR;
157 	}
158 
159 	progress->StageStep(1, B_TRANSLATE("Installing files and folders"));
160 
161 	// Install files and directories
162 	PackageItem *iter;
163 	ItemState state;
164 	uint32 i;
165 	int32 choice;
166 	BString label;
167 
168 	packageInfo.SetName(info->GetName());
169 	// TODO: Here's a small problem, since right now it's not quite sure
170 	//		which description is really used as such. The one displayed on
171 	//		the installer is mostly package installation description, but
172 	//		most people use it for describing the application in more detail
173 	//		then in the short description.
174 	//		For now, we'll use the short description if possible.
175 	BString description = info->GetShortDescription();
176 	if (description.Length() <= 0)
177 		description = info->GetDescription();
178 	packageInfo.SetDescription(description.String());
179 	packageInfo.SetSpaceNeeded(type->space_needed);
180 
181 	fItemExistsPolicy = P_EXISTS_NONE;
182 
183 	const char *installPath = fParent->GetCurrentPath()->Path();
184 	for (i = 0; i < n; i++) {
185 		state.Reset(fItemExistsPolicy); // Reset the current item state
186 		iter = static_cast<PackageItem *>(type->items.ItemAt(i));
187 
188 		err = iter->DoInstall(installPath, &state);
189 		if (err == B_FILE_EXISTS) {
190 			// Writing to path failed because path already exists - ask the user
191 			// what to do and retry the writing process
192 			choice = fParent->ItemExists(*iter, state.destination,
193 				fItemExistsPolicy);
194 			if (choice != P_EXISTS_ABORT) {
195 				state.policy = choice;
196 				err = iter->DoInstall(installPath, &state);
197 			}
198 		}
199 
200 		if (err != B_OK) {
201 			fprintf(stderr, "Error while writing path\n");
202 			return P_MSG_I_ERROR;
203 		}
204 
205 		if (progress->Stopped())
206 			return P_MSG_I_ABORT;
207 		label = "";
208 		label << (uint32)(i + 1) << " of " << (uint32)n;
209 		progress->StageStep(1, NULL, label.String());
210 
211 		packageInfo.AddItem(state.destination.Path());
212 	}
213 
214 	progress->StageStep(1, B_TRANSLATE("Running post-installation scripts"),
215 		 "");
216 
217 	PackageScript *scr;
218 	status_t status;
219 	// Run all scripts
220 	for (i = 0; i < m; i++) {
221 		scr = info->GetScript(i);
222 
223 		fCurrentScriptLocker.Lock();
224 		fCurrentScript = scr;
225 
226 		if (scr->DoInstall() != B_OK) {
227 			fprintf(stderr, "Error while running script\n");
228 			return P_MSG_I_ERROR;
229 		}
230 		fCurrentScriptLocker.Unlock();
231 
232 		wait_for_thread(scr->GetThreadId(), &status);
233 		fCurrentScriptLocker.Lock();
234 		scr->SetThreadId(-1);
235 		fCurrentScript = NULL;
236 		fCurrentScriptLocker.Unlock();
237 
238 		if (progress->Stopped())
239 			return P_MSG_I_ABORT;
240 		label = "";
241 		label << (uint32)(i + 1) << " of " << (uint32)m;
242 		progress->StageStep(1, NULL, label.String());
243 	}
244 
245 	progress->StageStep(1, B_TRANSLATE("Finishing installation"), "");
246 
247 	err = packageInfo.Save();
248 	if (err != B_OK)
249 		return P_MSG_I_ERROR;
250 
251 	progress->StageStep(1, B_TRANSLATE("Done"));
252 
253 	// Inform our parent that we finished
254 	return P_MSG_I_FINISHED;
255 }
256 
257