1 /* 2 * Copyright 2003-2009, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Jonas Sundström, jonas@kirilla.com 7 * Peter Folk <pfolk@uni.uiuc.edu> 8 */ 9 10 11 #include "ZipperThread.h" 12 13 #include <errno.h> 14 #include <signal.h> 15 #include <unistd.h> 16 17 #include <FindDirectory.h> 18 #include <Message.h> 19 #include <Path.h> 20 #include <Volume.h> 21 22 #include "ZipOMaticMisc.h" 23 #include "ZipOMaticWindow.h" 24 25 26 ZipperThread::ZipperThread(BMessage* refsMessage, BWindow* window) 27 : 28 GenericThread("ZipperThread", B_NORMAL_PRIORITY, refsMessage), 29 fWindowMessenger(window), 30 fZipProcess(-1), 31 fStdIn(-1), 32 fStdOut(-1), 33 fStdErr(-1), 34 fOutputFile(NULL) 35 { 36 fThreadDataStore = new BMessage(*refsMessage); 37 // leak? 38 // prevents bug with B_SIMPLE_DATA 39 // (drag&drop messages) 40 } 41 42 43 ZipperThread::~ZipperThread() 44 { 45 } 46 47 48 status_t 49 ZipperThread::ThreadStartup() 50 { 51 BString archiveName = "Archive.zip"; 52 53 // do all refs have the same parent dir? 54 type_code type = B_REF_TYPE; 55 int32 refCount = 0; 56 entry_ref ref; 57 entry_ref lastRef; 58 bool sameFolder = true; 59 60 status_t status = fThreadDataStore->GetInfo("refs", &type, &refCount); 61 if (status != B_OK) 62 return status; 63 64 for (int index = 0; index < refCount; index++) { 65 fThreadDataStore->FindRef("refs", index, &ref); 66 67 if (index > 0) { 68 BEntry entry(&ref); 69 if (entry.IsSymLink()) { 70 entry.SetTo(&ref, true); 71 entry_ref target; 72 entry.GetRef(&target); 73 if (lastRef.directory != target.directory) { 74 sameFolder = false; 75 break; 76 } 77 } else if (lastRef.directory != ref.directory) { 78 sameFolder = false; 79 break; 80 } 81 } 82 lastRef = ref; 83 } 84 85 // change active dir 86 if (sameFolder) { 87 BEntry entry(&lastRef); 88 BPath path; 89 entry.GetParent(&entry); 90 entry.GetPath(&path); 91 chdir(path.Path()); 92 } else { 93 BPath path; 94 if (find_directory(B_DESKTOP_DIRECTORY, &path) == B_OK) 95 chdir(path.Path()); 96 } 97 98 // archive filename 99 if (refCount == 1) { 100 archiveName = lastRef.name; 101 archiveName += ".zip"; 102 } 103 104 int32 argc = refCount + 3; 105 const char** argv = new const char* [argc + 1]; 106 107 argv[0] = strdup("/bin/zip"); 108 argv[1] = strdup("-ry"); 109 argv[2] = strdup(archiveName.String()); 110 111 // files to zip 112 for (int index = 0; index < refCount; index++) { 113 fThreadDataStore->FindRef("refs", index, &ref); 114 115 if (sameFolder) { 116 // just the file name 117 argv[3 + index] = strdup(ref.name); 118 } else { 119 // full path 120 BPath path(&ref); 121 BString file = path.Path(); 122 argv[3 + index] = strdup(path.Path()); 123 } 124 } 125 126 argv[argc] = NULL; 127 128 fZipProcess = _PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr); 129 130 delete [] argv; 131 132 if (fZipProcess < 0) 133 return fZipProcess; 134 135 resume_thread(fZipProcess); 136 137 fOutputFile = fdopen(fStdOut, "r"); 138 if (fOutputFile == NULL) 139 return errno; 140 141 archiveName.Prepend("Creating archive: "); 142 143 _SendMessageToWindow(ZIPPO_TASK_DESCRIPTION, "archive_filename", 144 archiveName.String()); 145 _SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", 146 "Preparing to archive"); 147 148 return B_OK; 149 } 150 151 152 status_t 153 ZipperThread::ExecuteUnit() 154 { 155 // read output from /bin/zip 156 // send it to window 157 char buffer[4096]; 158 159 char* output = fgets(buffer, sizeof(buffer) - 1, fOutputFile); 160 if (output == NULL) 161 return EOF; 162 163 char* newLine = strrchr(output, '\n'); 164 if (newLine != NULL) 165 *newLine = '\0'; 166 167 if (!strncmp(" a", output, 3)) { 168 output[2] = 'A'; 169 _SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", output + 2); 170 } else if (!strncmp("up", output, 2)) { 171 output[0] = 'U'; 172 _SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", output); 173 } else { 174 _SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", output); 175 } 176 177 return B_OK; 178 } 179 180 181 status_t 182 ZipperThread::ThreadShutdown() 183 { 184 close(fStdIn); 185 close(fStdOut); 186 close(fStdErr); 187 188 return B_OK; 189 } 190 191 192 void 193 ZipperThread::ThreadStartupFailed(status_t status) 194 { 195 ErrorMessage("ZipperThread::ThreadStartupFailed() \n", status); 196 Quit(); 197 } 198 199 200 void 201 ZipperThread::ExecuteUnitFailed(status_t status) 202 { 203 ErrorMessage("ZipperThread::ExecuteUnitFailed() \n", status); 204 205 if (status == EOF) { 206 // thread has finished, been quit or killed, we don't know 207 _SendMessageToWindow(ZIPPO_THREAD_EXIT); 208 } else { 209 // explicit error - communicate error to Window 210 _SendMessageToWindow(ZIPPO_THREAD_EXIT_ERROR); 211 } 212 213 Quit(); 214 } 215 216 217 void 218 ZipperThread::ThreadShutdownFailed(status_t status) 219 { 220 ErrorMessage("ZipperThread::ThreadShutdownFailed() \n", status); 221 } 222 223 224 void 225 ZipperThread::_MakeShellSafe(BString* string) 226 { 227 string->CharacterEscape("\"$`", '\\'); 228 string->Prepend("\""); 229 string->Append("\""); 230 } 231 232 233 thread_id 234 ZipperThread::_PipeCommand(int argc, const char** argv, int& in, int& out, 235 int& err, const char** envp) 236 { 237 // This function was originally written by Peter Folk <pfolk@uni.uiuc.edu> 238 // and published in the BeDevTalk FAQ 239 // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209 240 241 thread_id thread; 242 243 // Save current FDs 244 int oldIn = dup(STDIN_FILENO); 245 int oldOut = dup(STDOUT_FILENO); 246 int oldErr = dup(STDERR_FILENO); 247 248 int inPipe[2], outPipe[2], errPipe[2]; 249 250 // Create new pipe FDs as stdin, stdout, stderr 251 if (pipe(inPipe) < 0) 252 goto err1; 253 if (pipe(outPipe) < 0) 254 goto err2; 255 if (pipe(errPipe) < 0) 256 goto err3; 257 258 errno = 0; 259 260 // replace old stdin/stderr/stdout 261 dup2(inPipe[0], STDIN_FILENO); 262 close(inPipe[0]); 263 dup2(outPipe[1], STDOUT_FILENO); 264 close(outPipe[1]); 265 dup2(errPipe[1], STDERR_FILENO); 266 close(errPipe[1]); 267 268 if (errno == 0) { 269 in = inPipe[1]; // Write to in, appears on cmd's stdin 270 out = outPipe[0]; // Read from out, taken from cmd's stdout 271 err = errPipe[0]; // Read from err, taken from cmd's stderr 272 273 // execute command 274 thread = load_image(argc, argv, envp); 275 } else { 276 thread = errno; 277 } 278 279 // Restore old FDs 280 dup2(oldIn, STDIN_FILENO); 281 close(oldIn); 282 dup2(oldOut, STDOUT_FILENO); 283 close(oldOut); 284 dup2(oldErr, STDERR_FILENO); 285 close(oldErr); 286 return thread; 287 288 err3: 289 close(outPipe[0]); 290 close(outPipe[1]); 291 err2: 292 close(inPipe[0]); 293 close(inPipe[1]); 294 err1: 295 close(oldIn); 296 close(oldOut); 297 close(oldErr); 298 return errno; 299 } 300 301 302 void 303 ZipperThread::_SendMessageToWindow(uint32 what, const char* name, 304 const char* value) 305 { 306 BMessage msg(what); 307 if (name != NULL && value != NULL) 308 msg.AddString(name, value); 309 310 fWindowMessenger.SendMessage(&msg); 311 } 312 313 314 status_t 315 ZipperThread::SuspendExternalZip() 316 { 317 thread_info info; 318 status_t status = get_thread_info(fZipProcess, &info); 319 320 if (status == B_OK && !strcmp(info.name, "zip")) 321 return suspend_thread(fZipProcess); 322 323 return status; 324 } 325 326 327 status_t 328 ZipperThread::ResumeExternalZip() 329 { 330 thread_info info; 331 status_t status = get_thread_info(fZipProcess, &info); 332 333 if (status == B_OK && !strcmp(info.name, "zip")) 334 return resume_thread(fZipProcess); 335 336 return status; 337 } 338 339 340 status_t 341 ZipperThread::InterruptExternalZip() 342 { 343 thread_info info; 344 status_t status = get_thread_info(fZipProcess, &info); 345 346 if (status == B_OK && !strcmp(info.name, "zip")) { 347 status = B_OK; 348 status = send_signal(fZipProcess, SIGINT); 349 WaitOnExternalZip(); 350 return status; 351 } 352 353 return status; 354 } 355 356 357 status_t 358 ZipperThread::WaitOnExternalZip() 359 { 360 thread_info info; 361 status_t status = get_thread_info(fZipProcess, &info); 362 363 if (status == B_OK && !strcmp(info.name, "zip")) 364 return wait_for_thread(fZipProcess, &status); 365 366 return status; 367 } 368 369