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 <string.h> 16 #include <unistd.h> 17 18 #include <Catalog.h> 19 #include <FindDirectory.h> 20 #include <Locale.h> 21 #include <Locker.h> 22 #include <Message.h> 23 #include <Path.h> 24 #include <Volume.h> 25 26 #include "ZipOMaticMisc.h" 27 #include "ZipOMaticWindow.h" 28 29 30 #define TR_CONTEXT "file:ZipperThread.cpp" 31 32 33 ZipperThread::ZipperThread(BMessage* refsMessage, BWindow* window) 34 : 35 GenericThread("ZipperThread", B_NORMAL_PRIORITY, refsMessage), 36 fWindowMessenger(window), 37 fZipProcess(-1), 38 fStdIn(-1), 39 fStdOut(-1), 40 fStdErr(-1), 41 fOutputFile(NULL) 42 { 43 fThreadDataStore = new BMessage(*refsMessage); 44 } 45 46 47 ZipperThread::~ZipperThread() 48 { 49 } 50 51 52 status_t 53 ZipperThread::ThreadStartup() 54 { 55 type_code type = B_REF_TYPE; 56 int32 refCount = 0; 57 entry_ref ref; 58 entry_ref lastRef; 59 bool sameFolder = true; 60 61 status_t status = fThreadDataStore->GetInfo("refs", &type, &refCount); 62 if (status != B_OK || refCount < 1) { 63 _SendMessageToWindow(ZIPPO_THREAD_EXIT_ERROR); 64 Quit(); 65 return B_ERROR; 66 } 67 68 for (int index = 0; index < refCount; index++) { 69 fThreadDataStore->FindRef("refs", index, &ref); 70 71 if (index > 0) { 72 if (lastRef.directory != ref.directory) { 73 sameFolder = false; 74 break; 75 } 76 } 77 lastRef = ref; 78 } 79 80 entry_ref dirRef; 81 bool gotDirRef = false; 82 83 status = fThreadDataStore->FindRef("dir_ref", 0, &dirRef); 84 if (status == B_OK) { 85 BEntry dirEntry(&dirRef); 86 BNode dirNode(&dirRef); 87 88 if (dirEntry.InitCheck() == B_OK 89 && dirEntry.Exists() 90 && dirNode.InitCheck() == B_OK 91 && dirNode.IsDirectory()) 92 gotDirRef = true; 93 } 94 95 if (gotDirRef) { 96 BEntry entry(&dirRef); 97 BPath path; 98 entry.GetPath(&path); 99 chdir(path.Path()); 100 } else if (sameFolder) { 101 BEntry entry(&lastRef); 102 BPath path; 103 entry.GetParent(&entry); 104 entry.GetPath(&path); 105 chdir(path.Path()); 106 } else { 107 BPath path; 108 if (find_directory(B_DESKTOP_DIRECTORY, &path) == B_OK) 109 chdir(path.Path()); 110 } 111 112 BString archiveName; 113 114 if (refCount > 1) 115 archiveName = TR("Archive"); 116 else 117 archiveName = lastRef.name; 118 119 int index = 1; 120 for (;; index++) { 121 BString tryName = archiveName; 122 123 if (index != 1) 124 tryName << " " << index; 125 126 tryName << ".zip"; 127 128 BEntry entry(tryName.String()); 129 if (!entry.Exists()) { 130 archiveName = tryName; 131 entry.GetRef(&fOutputEntryRef); 132 break; 133 } 134 } 135 136 int32 argc = refCount + 3; 137 const char** argv = new const char* [argc + 1]; 138 139 argv[0] = strdup("/bin/zip"); 140 argv[1] = strdup("-ry"); 141 argv[2] = strdup(archiveName.String()); 142 143 for (int index = 0; index < refCount; index++) { 144 fThreadDataStore->FindRef("refs", index, &ref); 145 146 if (gotDirRef || sameFolder) { 147 argv[3 + index] = strdup(ref.name); 148 } else { 149 BPath path(&ref); 150 BString file = path.Path(); 151 argv[3 + index] = strdup(path.Path()); 152 } 153 } 154 155 argv[argc] = NULL; 156 157 fZipProcess = _PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr); 158 159 delete [] argv; 160 161 if (fZipProcess < 0) 162 return fZipProcess; 163 164 resume_thread(fZipProcess); 165 166 fOutputFile = fdopen(fStdOut, "r"); 167 if (fOutputFile == NULL) 168 return errno; 169 170 _SendMessageToWindow(ZIPPO_TASK_DESCRIPTION, "archive_filename", 171 archiveName.String()); 172 _SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", 173 TR("Preparing to archive")); 174 175 return B_OK; 176 } 177 178 179 status_t 180 ZipperThread::ExecuteUnit() 181 { 182 char buffer[4096]; 183 184 char* output = fgets(buffer, sizeof(buffer) - 1, fOutputFile); 185 if (output == NULL) 186 return EOF; 187 188 char* newLine = strrchr(output, '\n'); 189 if (newLine != NULL) 190 *newLine = '\0'; 191 192 if (!strncmp(" a", output, 3)) { 193 output[2] = 'A'; 194 _SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", output + 2); 195 } else if (!strncmp("up", output, 2)) { 196 output[0] = 'U'; 197 _SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", output); 198 } else { 199 _SendMessageToWindow(ZIPPO_LINE_OF_STDOUT, "zip_output", output); 200 } 201 202 return B_OK; 203 } 204 205 206 status_t 207 ZipperThread::ThreadShutdown() 208 { 209 close(fStdIn); 210 close(fStdOut); 211 close(fStdErr); 212 213 // _SelectInTracker(); 214 215 return B_OK; 216 } 217 218 219 void 220 ZipperThread::ThreadStartupFailed(status_t status) 221 { 222 Quit(); 223 } 224 225 226 void 227 ZipperThread::ExecuteUnitFailed(status_t status) 228 { 229 if (status == EOF) { 230 // thread has finished, been quit or killed, we don't know 231 _SendMessageToWindow(ZIPPO_THREAD_EXIT); 232 } else { 233 // explicit error - communicate error to Window 234 _SendMessageToWindow(ZIPPO_THREAD_EXIT_ERROR); 235 } 236 237 Quit(); 238 } 239 240 241 void 242 ZipperThread::ThreadShutdownFailed(status_t status) 243 { 244 fprintf(stderr, "ZipperThread::ThreadShutdownFailed(): %s\n", 245 strerror(status)); 246 } 247 248 249 void 250 ZipperThread::_MakeShellSafe(BString* string) 251 { 252 string->CharacterEscape("\"$`", '\\'); 253 string->Prepend("\""); 254 string->Append("\""); 255 } 256 257 258 thread_id 259 ZipperThread::_PipeCommand(int argc, const char** argv, int& in, int& out, 260 int& err, const char** envp) 261 { 262 static BLocker lock; 263 264 if (lock.Lock()) { 265 // This function was originally written by Peter Folk 266 // <pfolk@uni.uiuc.edu> and published in the BeDevTalk FAQ 267 // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209 268 269 thread_id thread; 270 271 // Save current FDs 272 int oldIn = dup(STDIN_FILENO); 273 int oldOut = dup(STDOUT_FILENO); 274 int oldErr = dup(STDERR_FILENO); 275 276 int inPipe[2], outPipe[2], errPipe[2]; 277 278 // Create new pipe FDs as stdin, stdout, stderr 279 if (pipe(inPipe) < 0) 280 goto err1; 281 if (pipe(outPipe) < 0) 282 goto err2; 283 if (pipe(errPipe) < 0) 284 goto err3; 285 286 errno = 0; 287 288 // replace old stdin/stderr/stdout 289 dup2(inPipe[0], STDIN_FILENO); 290 close(inPipe[0]); 291 dup2(outPipe[1], STDOUT_FILENO); 292 close(outPipe[1]); 293 dup2(errPipe[1], STDERR_FILENO); 294 close(errPipe[1]); 295 296 if (errno == 0) { 297 in = inPipe[1]; // Write to in, appears on cmd's stdin 298 out = outPipe[0]; // Read from out, taken from cmd's stdout 299 err = errPipe[0]; // Read from err, taken from cmd's stderr 300 301 // execute command 302 thread = load_image(argc, argv, envp); 303 } else { 304 thread = errno; 305 } 306 307 // Restore old FDs 308 dup2(oldIn, STDIN_FILENO); 309 close(oldIn); 310 dup2(oldOut, STDOUT_FILENO); 311 close(oldOut); 312 dup2(oldErr, STDERR_FILENO); 313 close(oldErr); 314 315 lock.Unlock(); 316 return thread; 317 318 err3: 319 close(outPipe[0]); 320 close(outPipe[1]); 321 err2: 322 close(inPipe[0]); 323 close(inPipe[1]); 324 err1: 325 close(oldIn); 326 close(oldOut); 327 close(oldErr); 328 329 lock.Unlock(); 330 return errno; 331 } else { 332 return B_ERROR; 333 } 334 } 335 336 337 void 338 ZipperThread::_SendMessageToWindow(uint32 what, const char* name, 339 const char* value) 340 { 341 BMessage msg(what); 342 if (name != NULL && value != NULL) 343 msg.AddString(name, value); 344 345 fWindowMessenger.SendMessage(&msg); 346 } 347 348 349 status_t 350 ZipperThread::SuspendExternalZip() 351 { 352 thread_info info; 353 status_t status = get_thread_info(fZipProcess, &info); 354 355 if (status == B_OK && !strcmp(info.name, "zip")) 356 return suspend_thread(fZipProcess); 357 358 return status; 359 } 360 361 362 status_t 363 ZipperThread::ResumeExternalZip() 364 { 365 thread_info info; 366 status_t status = get_thread_info(fZipProcess, &info); 367 368 if (status == B_OK && !strcmp(info.name, "zip")) 369 return resume_thread(fZipProcess); 370 371 return status; 372 } 373 374 375 status_t 376 ZipperThread::InterruptExternalZip() 377 { 378 thread_info info; 379 status_t status = get_thread_info(fZipProcess, &info); 380 381 if (status == B_OK && !strcmp(info.name, "zip")) { 382 status = B_OK; 383 status = send_signal(fZipProcess, SIGINT); 384 WaitOnExternalZip(); 385 return status; 386 } 387 388 return status; 389 } 390 391 392 status_t 393 ZipperThread::WaitOnExternalZip() 394 { 395 thread_info info; 396 status_t status = get_thread_info(fZipProcess, &info); 397 398 if (status == B_OK && !strcmp(info.name, "zip")) 399 return wait_for_thread(fZipProcess, &status); 400 401 return status; 402 } 403 404 405 status_t 406 ZipperThread::_SelectInTracker(int32 tryNumber) 407 { 408 // work in progress - unreliable - not ready to be used 409 410 entry_ref parentRef; 411 BEntry entry(&fOutputEntryRef); 412 413 if (!entry.Exists()) 414 return B_FILE_NOT_FOUND; 415 416 entry.GetParent(&entry); 417 entry.GetRef(&parentRef); 418 419 BMessenger trackerMessenger("application/x-vnd.Be-TRAK"); 420 if (!trackerMessenger.IsValid()) 421 return B_ERROR; 422 423 BMessage request; 424 BMessage reply; 425 status_t status; 426 427 if (tryNumber == 0) { 428 request.MakeEmpty(); 429 request.what = B_REFS_RECEIVED; 430 request.AddRef("refs", &parentRef); 431 trackerMessenger.SendMessage(&request, &reply); 432 } 433 434 if (tryNumber > 20) 435 return B_ERROR; 436 437 snooze(200000); 438 439 // find out the number of Tracker windows 440 request.MakeEmpty(); 441 request.what = B_COUNT_PROPERTIES; 442 request.AddSpecifier("Window"); 443 reply.MakeEmpty(); 444 445 status = trackerMessenger.SendMessage(&request, &reply); 446 if (status != B_OK) 447 return status; 448 449 int32 windowCount; 450 status = reply.FindInt32("result", &windowCount); 451 if (status != B_OK) 452 return status; 453 454 // find a likely parent window 455 bool foundWindow = false; 456 int32 index = 0; 457 for (; index < windowCount; index++) { 458 request.MakeEmpty(); 459 request.what = B_GET_PROPERTY; 460 request.AddSpecifier("Path"); 461 request.AddSpecifier("Poses"); 462 request.AddSpecifier("Window", index); 463 reply.MakeEmpty(); 464 465 status = trackerMessenger.SendMessage(&request, &reply); 466 if (status != B_OK) 467 continue; 468 469 entry_ref windowRef; 470 status = reply.FindRef("result", &windowRef); 471 if (status != B_OK) 472 continue; 473 474 if (windowRef == parentRef) { 475 foundWindow = true; 476 break; 477 } 478 } 479 480 if (!foundWindow) 481 return _SelectInTracker(tryNumber + 1); 482 483 // find entry_ref in window - a newly opened window might 484 // be filling and the entry_ref perhaps not there yet? 485 request.MakeEmpty(); 486 request.what = B_GET_PROPERTY; 487 request.AddSpecifier("Entry"); 488 request.AddSpecifier("Poses"); 489 request.AddSpecifier("Window", index); 490 reply.MakeEmpty(); 491 492 status = trackerMessenger.SendMessage(&request, &reply); 493 if (status != B_OK) 494 return _SelectInTracker(tryNumber + 1); 495 496 bool foundRef = false; 497 entry_ref ref; 498 for (int32 m = 0;; m++) { 499 status = reply.FindRef("result", m, &ref); 500 if (status != B_OK) 501 break; 502 if (ref == fOutputEntryRef) 503 foundRef = true; 504 } 505 506 // if entry_ref not found in window, start over 507 if (!foundRef) 508 return _SelectInTracker(tryNumber + 1); 509 510 // select archive file in Tracker window 511 request.MakeEmpty(); 512 request.what = B_SET_PROPERTY; 513 request.AddRef("data", &fOutputEntryRef); 514 request.AddSpecifier("Selection"); 515 request.AddSpecifier("Poses"); 516 request.AddSpecifier("Window", index); 517 reply.MakeEmpty(); 518 519 status = trackerMessenger.SendMessage(&request, &reply); 520 521 return status; 522 } 523 524