10f11a95aSJérôme Duval /* 2*750111c9SJérôme Duval * Copyright 2004-2010, Jérôme Duval. All rights reserved. 30f11a95aSJérôme Duval * Distributed under the terms of the MIT License. 40f11a95aSJérôme Duval * Original code from ZipOMatic by jonas.sundstrom@kirilla.com 50f11a95aSJérôme Duval */ 60f11a95aSJérôme Duval #include <Messenger.h> 755a5a6bdSJérôme Duval #include <Path.h> 8338b8dc3SIngo Weinhold #include "ExpanderThread.h" 955a5a6bdSJérôme Duval #include <image.h> 1055a5a6bdSJérôme Duval #include <signal.h> 1155a5a6bdSJérôme Duval #include <unistd.h> 1255a5a6bdSJérôme Duval #include <errno.h> 1355a5a6bdSJérôme Duval 1455a5a6bdSJérôme Duval const char * ExpanderThreadName = "ExpanderThread"; 1555a5a6bdSJérôme Duval 1655a5a6bdSJérôme Duval ExpanderThread::ExpanderThread(BMessage * refs_message, BMessenger * messenger) 1755a5a6bdSJérôme Duval : GenericThread(ExpanderThreadName, B_NORMAL_PRIORITY, refs_message), 1855a5a6bdSJérôme Duval fWindowMessenger(messenger), 1955a5a6bdSJérôme Duval fThreadId(-1), 2055a5a6bdSJérôme Duval fStdIn(-1), 2155a5a6bdSJérôme Duval fStdOut(-1), 2255a5a6bdSJérôme Duval fStdErr(-1), 2355a5a6bdSJérôme Duval fExpanderOutput(NULL), 24*750111c9SJérôme Duval fExpanderError(NULL) 2555a5a6bdSJérôme Duval { 2655a5a6bdSJérôme Duval SetDataStore(new BMessage(* refs_message)); // leak? 2755a5a6bdSJérôme Duval // prevents bug with B_SIMPLE_DATA 2855a5a6bdSJérôme Duval // (drag&drop messages) 2955a5a6bdSJérôme Duval } 3055a5a6bdSJérôme Duval 31ebb64aceSPhilippe Saint-Pierre 3255a5a6bdSJérôme Duval ExpanderThread::~ExpanderThread() 3355a5a6bdSJérôme Duval { 3455a5a6bdSJérôme Duval delete fWindowMessenger; 3555a5a6bdSJérôme Duval } 3655a5a6bdSJérôme Duval 37ebb64aceSPhilippe Saint-Pierre 3855a5a6bdSJérôme Duval status_t 3955a5a6bdSJérôme Duval ExpanderThread::ThreadStartup() 4055a5a6bdSJérôme Duval { 4155a5a6bdSJérôme Duval status_t status = B_OK; 4255a5a6bdSJérôme Duval entry_ref srcRef, destRef; 4355a5a6bdSJérôme Duval BString cmd; 4455a5a6bdSJérôme Duval 4555a5a6bdSJérôme Duval if ((status = GetDataStore()->FindRef("srcRef", &srcRef)) != B_OK) 4655a5a6bdSJérôme Duval return status; 4791d1efffSJérôme Duval if ((status = GetDataStore()->FindRef("destRef", &destRef)) == B_OK) { 4891d1efffSJérôme Duval BPath path(&destRef); 4991d1efffSJérôme Duval chdir(path.Path()); 5091d1efffSJérôme Duval } 5155a5a6bdSJérôme Duval if ((status = GetDataStore()->FindString("cmd", &cmd)) != B_OK) 5255a5a6bdSJérôme Duval return status; 5355a5a6bdSJérôme Duval 5455a5a6bdSJérôme Duval BPath path(&srcRef); 5555a5a6bdSJérôme Duval BString pathString(path.Path()); 5655a5a6bdSJérôme Duval pathString.CharacterEscape("\"$`", '\\'); 5755a5a6bdSJérôme Duval pathString.Prepend("\""); 5855a5a6bdSJérôme Duval pathString.Append("\""); 5955a5a6bdSJérôme Duval cmd.ReplaceAll("%s", pathString.String()); 6055a5a6bdSJérôme Duval 6155a5a6bdSJérôme Duval int32 argc = 3; 6255a5a6bdSJérôme Duval const char ** argv = new const char * [argc + 1]; 6355a5a6bdSJérôme Duval 6455a5a6bdSJérôme Duval argv[0] = strdup("/bin/sh"); 6555a5a6bdSJérôme Duval argv[1] = strdup("-c"); 6655a5a6bdSJérôme Duval argv[2] = strdup(cmd.String()); 6755a5a6bdSJérôme Duval argv[argc] = NULL; 6855a5a6bdSJérôme Duval 6955a5a6bdSJérôme Duval fThreadId = PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr); 7055a5a6bdSJérôme Duval 7155a5a6bdSJérôme Duval delete [] argv; 7255a5a6bdSJérôme Duval 7355a5a6bdSJérôme Duval if (fThreadId < 0) 7455a5a6bdSJérôme Duval return fThreadId; 7555a5a6bdSJérôme Duval 76520b37b8SFrançois Revol // lower the command priority since it is a background task. 77520b37b8SFrançois Revol set_thread_priority(fThreadId, B_LOW_PRIORITY); 78520b37b8SFrançois Revol 7955a5a6bdSJérôme Duval resume_thread(fThreadId); 8055a5a6bdSJérôme Duval 81*750111c9SJérôme Duval int flags = fcntl(fStdOut, F_GETFL, 0); 82*750111c9SJérôme Duval flags |= O_NONBLOCK; 83*750111c9SJérôme Duval fcntl(fStdOut, F_SETFL, flags); 84*750111c9SJérôme Duval flags = fcntl(fStdErr, F_GETFL, 0); 85*750111c9SJérôme Duval flags |= O_NONBLOCK; 86*750111c9SJérôme Duval fcntl(fStdErr, F_SETFL, flags); 87*750111c9SJérôme Duval 8855a5a6bdSJérôme Duval fExpanderOutput = fdopen(fStdOut, "r"); 89*750111c9SJérôme Duval fExpanderError = fdopen(fStdErr, "r"); 9055a5a6bdSJérôme Duval 9155a5a6bdSJérôme Duval return B_OK; 9255a5a6bdSJérôme Duval } 9355a5a6bdSJérôme Duval 94ebb64aceSPhilippe Saint-Pierre 9555a5a6bdSJérôme Duval status_t 9655a5a6bdSJérôme Duval ExpanderThread::ExecuteUnit(void) 9755a5a6bdSJérôme Duval { 98*750111c9SJérôme Duval // read output and error from command 9955a5a6bdSJérôme Duval // send it to window 10055a5a6bdSJérôme Duval 10155a5a6bdSJérôme Duval BMessage message('outp'); 102*750111c9SJérôme Duval bool outputAdded = false; 103*750111c9SJérôme Duval for (int32 i = 0; i < 50; i++) { 104*750111c9SJérôme Duval char *output_string = fgets(fExpanderOutputBuffer , LINE_MAX, fExpanderOutput); 10555a5a6bdSJérôme Duval if (!output_string) 10655a5a6bdSJérôme Duval break; 10755a5a6bdSJérôme Duval message.AddString("output", output_string); 108*750111c9SJérôme Duval outputAdded = true; 10955a5a6bdSJérôme Duval } 110*750111c9SJérôme Duval if (outputAdded) 11155a5a6bdSJérôme Duval fWindowMessenger->SendMessage(&message); 112*750111c9SJérôme Duval if (feof(fExpanderOutput)) 113*750111c9SJérôme Duval return EOF; 114*750111c9SJérôme Duval 115*750111c9SJérôme Duval char *error_string = fgets(fExpanderOutputBuffer , LINE_MAX, fExpanderError); 116*750111c9SJérôme Duval if (error_string != NULL 117*750111c9SJérôme Duval && strcmp(error_string, "\n")) { 118*750111c9SJérôme Duval BMessage message('errp'); 119*750111c9SJérôme Duval message.AddString("error", error_string); 120*750111c9SJérôme Duval fWindowMessenger->SendMessage(&message); 121*750111c9SJérôme Duval } 122*750111c9SJérôme Duval 123*750111c9SJérôme Duval // streams are non blocking, sleep every 100ms 124*750111c9SJérôme Duval snooze(100000); 12555a5a6bdSJérôme Duval 12655a5a6bdSJérôme Duval return B_OK; 12755a5a6bdSJérôme Duval } 12855a5a6bdSJérôme Duval 129ebb64aceSPhilippe Saint-Pierre 13055a5a6bdSJérôme Duval status_t 13155a5a6bdSJérôme Duval ExpanderThread::ThreadShutdown(void) 13255a5a6bdSJérôme Duval { 13355a5a6bdSJérôme Duval close(fStdIn); 13455a5a6bdSJérôme Duval close(fStdOut); 13555a5a6bdSJérôme Duval close(fStdErr); 13655a5a6bdSJérôme Duval 13755a5a6bdSJérôme Duval return B_OK; 13855a5a6bdSJérôme Duval } 13955a5a6bdSJérôme Duval 140ebb64aceSPhilippe Saint-Pierre 14155a5a6bdSJérôme Duval void 14255a5a6bdSJérôme Duval ExpanderThread::ThreadStartupFailed(status_t status) 14355a5a6bdSJérôme Duval { 14455a5a6bdSJérôme Duval fprintf(stderr, "ExpanderThread::ThreadStartupFailed() : %s\n", strerror(status)); 14555a5a6bdSJérôme Duval 14655a5a6bdSJérôme Duval Quit(); 14755a5a6bdSJérôme Duval } 14855a5a6bdSJérôme Duval 149ebb64aceSPhilippe Saint-Pierre 15055a5a6bdSJérôme Duval void 15155a5a6bdSJérôme Duval ExpanderThread::ExecuteUnitFailed(status_t status) 15255a5a6bdSJérôme Duval { 15355a5a6bdSJérôme Duval if (status == EOF) { 15455a5a6bdSJérôme Duval // thread has finished, been quit or killed, we don't know 15555a5a6bdSJérôme Duval fWindowMessenger->SendMessage(new BMessage('exit')); 15655a5a6bdSJérôme Duval } else { 15755a5a6bdSJérôme Duval // explicit error - communicate error to Window 15855a5a6bdSJérôme Duval fWindowMessenger->SendMessage(new BMessage('exrr')); 15955a5a6bdSJérôme Duval } 16055a5a6bdSJérôme Duval 16155a5a6bdSJérôme Duval Quit(); 16255a5a6bdSJérôme Duval } 16355a5a6bdSJérôme Duval 164ebb64aceSPhilippe Saint-Pierre 16555a5a6bdSJérôme Duval void 16655a5a6bdSJérôme Duval ExpanderThread::ThreadShutdownFailed(status_t status) 16755a5a6bdSJérôme Duval { 16855a5a6bdSJérôme Duval fprintf(stderr, "ExpanderThread::ThreadShutdownFailed() %s\n", strerror(status)); 16955a5a6bdSJérôme Duval } 17055a5a6bdSJérôme Duval 17155a5a6bdSJérôme Duval 17255a5a6bdSJérôme Duval status_t 17355a5a6bdSJérôme Duval ExpanderThread::ProcessRefs(BMessage *msg) 17455a5a6bdSJérôme Duval { 17555a5a6bdSJérôme Duval return B_OK; 17655a5a6bdSJérôme Duval } 17755a5a6bdSJérôme Duval 178ebb64aceSPhilippe Saint-Pierre 17955a5a6bdSJérôme Duval thread_id 18055a5a6bdSJérôme Duval ExpanderThread::PipeCommand(int argc, const char **argv, int &in, int &out, int &err, const char **envp) 18155a5a6bdSJérôme Duval { 18255a5a6bdSJérôme Duval // This function written by Peter Folk <pfolk@uni.uiuc.edu> 18355a5a6bdSJérôme Duval // and published in the BeDevTalk FAQ 18455a5a6bdSJérôme Duval // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209 18555a5a6bdSJérôme Duval 18655a5a6bdSJérôme Duval // Save current FDs 18755a5a6bdSJérôme Duval int old_in = dup(0); 18855a5a6bdSJérôme Duval int old_out = dup(1); 18955a5a6bdSJérôme Duval int old_err = dup(2); 19055a5a6bdSJérôme Duval 19155a5a6bdSJérôme Duval int filedes[2]; 19255a5a6bdSJérôme Duval 19355a5a6bdSJérôme Duval /* Create new pipe FDs as stdin, stdout, stderr */ 19455a5a6bdSJérôme Duval pipe(filedes); dup2(filedes[0], 0); close(filedes[0]); 19555a5a6bdSJérôme Duval in = filedes[1]; // Write to in, appears on cmd's stdin 19655a5a6bdSJérôme Duval pipe(filedes); dup2(filedes[1], 1); close(filedes[1]); 19755a5a6bdSJérôme Duval out = filedes[0]; // Read from out, taken from cmd's stdout 19855a5a6bdSJérôme Duval pipe(filedes); dup2(filedes[1], 2); close(filedes[1]); 19955a5a6bdSJérôme Duval err = filedes[0]; // Read from err, taken from cmd's stderr 20055a5a6bdSJérôme Duval 20155a5a6bdSJérôme Duval // "load" command. 20255a5a6bdSJérôme Duval thread_id ret = load_image(argc, argv, envp); 20355a5a6bdSJérôme Duval 20455a5a6bdSJérôme Duval if (ret < B_OK) 20555a5a6bdSJérôme Duval return ret; 20655a5a6bdSJérôme Duval 20755a5a6bdSJérôme Duval // thread ret is now suspended. 20855a5a6bdSJérôme Duval 20955a5a6bdSJérôme Duval setpgid(ret, ret); 21055a5a6bdSJérôme Duval 21155a5a6bdSJérôme Duval // Restore old FDs 21255a5a6bdSJérôme Duval close(0); dup(old_in); close(old_in); 21355a5a6bdSJérôme Duval close(1); dup(old_out); close(old_out); 21455a5a6bdSJérôme Duval close(2); dup(old_err); close(old_err); 21555a5a6bdSJérôme Duval 21655a5a6bdSJérôme Duval /* Theoretically I should do loads of error checking, but 21755a5a6bdSJérôme Duval the calls aren't very likely to fail, and that would 21855a5a6bdSJérôme Duval muddy up the example quite a bit. YMMV. */ 21955a5a6bdSJérôme Duval 22055a5a6bdSJérôme Duval return ret; 22155a5a6bdSJérôme Duval } 22255a5a6bdSJérôme Duval 22355a5a6bdSJérôme Duval 22455a5a6bdSJérôme Duval status_t 22555a5a6bdSJérôme Duval ExpanderThread::SuspendExternalExpander() 22655a5a6bdSJérôme Duval { 22755a5a6bdSJérôme Duval thread_info thread_info; 22869d0d149SStefano Ceccherini status_t status = get_thread_info(fThreadId, &thread_info); 22955a5a6bdSJérôme Duval 23055a5a6bdSJérôme Duval if (status == B_OK) 23155a5a6bdSJérôme Duval return send_signal(-fThreadId, SIGSTOP); 23255a5a6bdSJérôme Duval else 23355a5a6bdSJérôme Duval return status; 23455a5a6bdSJérôme Duval } 23555a5a6bdSJérôme Duval 236ebb64aceSPhilippe Saint-Pierre 23755a5a6bdSJérôme Duval status_t 23855a5a6bdSJérôme Duval ExpanderThread::ResumeExternalExpander() 23955a5a6bdSJérôme Duval { 24055a5a6bdSJérôme Duval thread_info thread_info; 24169d0d149SStefano Ceccherini status_t status = get_thread_info(fThreadId, &thread_info); 24255a5a6bdSJérôme Duval 24355a5a6bdSJérôme Duval if (status == B_OK) 24455a5a6bdSJérôme Duval return send_signal(-fThreadId, SIGCONT); 24555a5a6bdSJérôme Duval else 24655a5a6bdSJérôme Duval return status; 24755a5a6bdSJérôme Duval } 24855a5a6bdSJérôme Duval 249ebb64aceSPhilippe Saint-Pierre 25055a5a6bdSJérôme Duval status_t 25155a5a6bdSJérôme Duval ExpanderThread::InterruptExternalExpander() 25255a5a6bdSJérôme Duval { 25355a5a6bdSJérôme Duval thread_info thread_info; 25469d0d149SStefano Ceccherini status_t status = get_thread_info(fThreadId, &thread_info); 25555a5a6bdSJérôme Duval 25655a5a6bdSJérôme Duval if (status == B_OK) { 25755a5a6bdSJérôme Duval status = send_signal(-fThreadId, SIGINT); 25855a5a6bdSJérôme Duval WaitOnExternalExpander(); 25955a5a6bdSJérôme Duval } 26055a5a6bdSJérôme Duval return status; 26155a5a6bdSJérôme Duval } 26255a5a6bdSJérôme Duval 263ebb64aceSPhilippe Saint-Pierre 26455a5a6bdSJérôme Duval status_t 26555a5a6bdSJérôme Duval ExpanderThread::WaitOnExternalExpander() 26655a5a6bdSJérôme Duval { 26755a5a6bdSJérôme Duval thread_info thread_info; 26869d0d149SStefano Ceccherini status_t status = get_thread_info(fThreadId, &thread_info); 26955a5a6bdSJérôme Duval 27055a5a6bdSJérôme Duval if (status == B_OK) 27155a5a6bdSJérôme Duval return wait_for_thread(fThreadId, &status); 27255a5a6bdSJérôme Duval else 27355a5a6bdSJérôme Duval return status; 27455a5a6bdSJérôme Duval } 275