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 if (data == NULL) 31 return -1; 32 33 PackageInstall* install = static_cast<PackageInstall*>(data); 34 install->Install(); 35 return 0; 36 } 37 38 39 PackageInstall::PackageInstall(PackageView* parent) 40 : 41 fParent(parent), 42 fThreadId(-1), 43 fCurrentScript(NULL) 44 { 45 } 46 47 48 PackageInstall::~PackageInstall() 49 { 50 } 51 52 53 status_t 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 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 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 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 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