10f11a95aSJérôme Duval /* 20f11a95aSJérôme Duval * Copyright 2004-2006, 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), 2455a5a6bdSJérôme Duval fExpanderOutputString(), 2555a5a6bdSJérôme Duval fExpanderOutputBuffer(new char [4096]) 2655a5a6bdSJérôme Duval { 2755a5a6bdSJérôme Duval SetDataStore(new BMessage (* refs_message)); // leak? 2855a5a6bdSJérôme Duval // prevents bug with B_SIMPLE_DATA 2955a5a6bdSJérôme Duval // (drag&drop messages) 3055a5a6bdSJérôme Duval } 3155a5a6bdSJérôme Duval 3255a5a6bdSJérôme Duval ExpanderThread::~ExpanderThread() 3355a5a6bdSJérôme Duval { 3455a5a6bdSJérôme Duval delete fWindowMessenger; 3555a5a6bdSJérôme Duval delete [] fExpanderOutputBuffer; 3655a5a6bdSJérôme Duval } 3755a5a6bdSJérôme Duval 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 7655a5a6bdSJérôme Duval resume_thread(fThreadId); 7755a5a6bdSJérôme Duval 7855a5a6bdSJérôme Duval fExpanderOutput = fdopen(fStdOut, "r"); 7955a5a6bdSJérôme Duval 8055a5a6bdSJérôme Duval return B_OK; 8155a5a6bdSJérôme Duval } 8255a5a6bdSJérôme Duval 8355a5a6bdSJérôme Duval status_t 8455a5a6bdSJérôme Duval ExpanderThread::ExecuteUnit (void) 8555a5a6bdSJérôme Duval { 8655a5a6bdSJérôme Duval // read output from command 8755a5a6bdSJérôme Duval // send it to window 8855a5a6bdSJérôme Duval 89*69d0d149SStefano Ceccherini char *output_string = fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput); 9055a5a6bdSJérôme Duval 9155a5a6bdSJérôme Duval if (output_string == NULL) 9255a5a6bdSJérôme Duval return EOF; 9355a5a6bdSJérôme Duval 9455a5a6bdSJérôme Duval BMessage message('outp'); 9555a5a6bdSJérôme Duval message.AddString("output", output_string); 9655a5a6bdSJérôme Duval for (int32 i=0; i<5; i++) { 9755a5a6bdSJérôme Duval output_string = fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput); 9855a5a6bdSJérôme Duval if(!output_string) 9955a5a6bdSJérôme Duval break; 10055a5a6bdSJérôme Duval message.AddString("output", output_string); 10155a5a6bdSJérôme Duval } 10255a5a6bdSJérôme Duval fWindowMessenger->SendMessage(&message); 10355a5a6bdSJérôme Duval 10455a5a6bdSJérôme Duval return B_OK; 10555a5a6bdSJérôme Duval } 10655a5a6bdSJérôme Duval 10755a5a6bdSJérôme Duval status_t 10855a5a6bdSJérôme Duval ExpanderThread::ThreadShutdown(void) 10955a5a6bdSJérôme Duval { 11055a5a6bdSJérôme Duval close(fStdIn); 11155a5a6bdSJérôme Duval close(fStdOut); 11255a5a6bdSJérôme Duval close(fStdErr); 11355a5a6bdSJérôme Duval 11455a5a6bdSJérôme Duval return B_OK; 11555a5a6bdSJérôme Duval } 11655a5a6bdSJérôme Duval 11755a5a6bdSJérôme Duval void 11855a5a6bdSJérôme Duval ExpanderThread::ThreadStartupFailed(status_t status) 11955a5a6bdSJérôme Duval { 12055a5a6bdSJérôme Duval fprintf(stderr, "ExpanderThread::ThreadStartupFailed() : %s\n", strerror(status)); 12155a5a6bdSJérôme Duval 12255a5a6bdSJérôme Duval Quit(); 12355a5a6bdSJérôme Duval } 12455a5a6bdSJérôme Duval 12555a5a6bdSJérôme Duval void 12655a5a6bdSJérôme Duval ExpanderThread::ExecuteUnitFailed(status_t status) 12755a5a6bdSJérôme Duval { 12855a5a6bdSJérôme Duval if (status == EOF) { 12955a5a6bdSJérôme Duval // thread has finished, been quit or killed, we don't know 13055a5a6bdSJérôme Duval fWindowMessenger->SendMessage(new BMessage('exit')); 13155a5a6bdSJérôme Duval } else { 13255a5a6bdSJérôme Duval // explicit error - communicate error to Window 13355a5a6bdSJérôme Duval fWindowMessenger->SendMessage(new BMessage('exrr')); 13455a5a6bdSJérôme Duval } 13555a5a6bdSJérôme Duval 13655a5a6bdSJérôme Duval Quit(); 13755a5a6bdSJérôme Duval } 13855a5a6bdSJérôme Duval 13955a5a6bdSJérôme Duval void 14055a5a6bdSJérôme Duval ExpanderThread::ThreadShutdownFailed(status_t status) 14155a5a6bdSJérôme Duval { 14255a5a6bdSJérôme Duval fprintf(stderr, "ExpanderThread::ThreadShutdownFailed() %s\n", strerror(status)); 14355a5a6bdSJérôme Duval } 14455a5a6bdSJérôme Duval 14555a5a6bdSJérôme Duval 14655a5a6bdSJérôme Duval status_t 14755a5a6bdSJérôme Duval ExpanderThread::ProcessRefs(BMessage *msg) 14855a5a6bdSJérôme Duval { 14955a5a6bdSJérôme Duval return B_OK; 15055a5a6bdSJérôme Duval } 15155a5a6bdSJérôme Duval 15255a5a6bdSJérôme Duval thread_id 15355a5a6bdSJérôme Duval ExpanderThread::PipeCommand(int argc, const char **argv, int &in, int &out, int &err, const char **envp) 15455a5a6bdSJérôme Duval { 15555a5a6bdSJérôme Duval // This function written by Peter Folk <pfolk@uni.uiuc.edu> 15655a5a6bdSJérôme Duval // and published in the BeDevTalk FAQ 15755a5a6bdSJérôme Duval // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209 15855a5a6bdSJérôme Duval 15955a5a6bdSJérôme Duval // Save current FDs 16055a5a6bdSJérôme Duval int old_in = dup(0); 16155a5a6bdSJérôme Duval int old_out = dup(1); 16255a5a6bdSJérôme Duval int old_err = dup(2); 16355a5a6bdSJérôme Duval 16455a5a6bdSJérôme Duval int filedes[2]; 16555a5a6bdSJérôme Duval 16655a5a6bdSJérôme Duval /* Create new pipe FDs as stdin, stdout, stderr */ 16755a5a6bdSJérôme Duval pipe(filedes); dup2(filedes[0],0); close(filedes[0]); 16855a5a6bdSJérôme Duval in=filedes[1]; // Write to in, appears on cmd's stdin 16955a5a6bdSJérôme Duval pipe(filedes); dup2(filedes[1],1); close(filedes[1]); 17055a5a6bdSJérôme Duval out=filedes[0]; // Read from out, taken from cmd's stdout 17155a5a6bdSJérôme Duval pipe(filedes); dup2(filedes[1],2); close(filedes[1]); 17255a5a6bdSJérôme Duval err=filedes[0]; // Read from err, taken from cmd's stderr 17355a5a6bdSJérôme Duval 17455a5a6bdSJérôme Duval // "load" command. 17555a5a6bdSJérôme Duval thread_id ret = load_image(argc, argv, envp); 17655a5a6bdSJérôme Duval 17755a5a6bdSJérôme Duval if (ret < B_OK) 17855a5a6bdSJérôme Duval return ret; 17955a5a6bdSJérôme Duval 18055a5a6bdSJérôme Duval // thread ret is now suspended. 18155a5a6bdSJérôme Duval 18255a5a6bdSJérôme Duval setpgid(ret, ret); 18355a5a6bdSJérôme Duval 18455a5a6bdSJérôme Duval // Restore old FDs 18555a5a6bdSJérôme Duval close(0); dup(old_in); close(old_in); 18655a5a6bdSJérôme Duval close(1); dup(old_out); close(old_out); 18755a5a6bdSJérôme Duval close(2); dup(old_err); close(old_err); 18855a5a6bdSJérôme Duval 18955a5a6bdSJérôme Duval /* Theoretically I should do loads of error checking, but 19055a5a6bdSJérôme Duval the calls aren't very likely to fail, and that would 19155a5a6bdSJérôme Duval muddy up the example quite a bit. YMMV. */ 19255a5a6bdSJérôme Duval 19355a5a6bdSJérôme Duval return ret; 19455a5a6bdSJérôme Duval } 19555a5a6bdSJérôme Duval 19655a5a6bdSJérôme Duval 19755a5a6bdSJérôme Duval status_t 19855a5a6bdSJérôme Duval ExpanderThread::SuspendExternalExpander() 19955a5a6bdSJérôme Duval { 20055a5a6bdSJérôme Duval thread_info thread_info; 201*69d0d149SStefano Ceccherini status_t status = get_thread_info(fThreadId, &thread_info); 20255a5a6bdSJérôme Duval 20355a5a6bdSJérôme Duval if (status == B_OK) 20455a5a6bdSJérôme Duval return send_signal(-fThreadId, SIGSTOP); 20555a5a6bdSJérôme Duval else 20655a5a6bdSJérôme Duval return status; 20755a5a6bdSJérôme Duval } 20855a5a6bdSJérôme Duval 20955a5a6bdSJérôme Duval status_t 21055a5a6bdSJérôme Duval ExpanderThread::ResumeExternalExpander() 21155a5a6bdSJérôme Duval { 21255a5a6bdSJérôme Duval thread_info thread_info; 213*69d0d149SStefano Ceccherini status_t status = get_thread_info(fThreadId, &thread_info); 21455a5a6bdSJérôme Duval 21555a5a6bdSJérôme Duval if (status == B_OK) 21655a5a6bdSJérôme Duval return send_signal(-fThreadId, SIGCONT); 21755a5a6bdSJérôme Duval else 21855a5a6bdSJérôme Duval return status; 21955a5a6bdSJérôme Duval } 22055a5a6bdSJérôme Duval 22155a5a6bdSJérôme Duval status_t 22255a5a6bdSJérôme Duval ExpanderThread::InterruptExternalExpander() 22355a5a6bdSJérôme Duval { 22455a5a6bdSJérôme Duval thread_info thread_info; 225*69d0d149SStefano Ceccherini status_t status = get_thread_info (fThreadId, &thread_info); 22655a5a6bdSJérôme Duval 22755a5a6bdSJérôme Duval if (status == B_OK) { 22855a5a6bdSJérôme Duval status = send_signal(-fThreadId, SIGINT); 22955a5a6bdSJérôme Duval WaitOnExternalExpander(); 23055a5a6bdSJérôme Duval } 23155a5a6bdSJérôme Duval return status; 23255a5a6bdSJérôme Duval } 23355a5a6bdSJérôme Duval 23455a5a6bdSJérôme Duval status_t 23555a5a6bdSJérôme Duval ExpanderThread::WaitOnExternalExpander() 23655a5a6bdSJérôme Duval { 23755a5a6bdSJérôme Duval thread_info thread_info; 238*69d0d149SStefano Ceccherini status_t status = get_thread_info(fThreadId, &thread_info); 23955a5a6bdSJérôme Duval 24055a5a6bdSJérôme Duval if (status == B_OK) 24155a5a6bdSJérôme Duval return wait_for_thread(fThreadId, &status); 24255a5a6bdSJérôme Duval else 24355a5a6bdSJérôme Duval return status; 24455a5a6bdSJérôme Duval } 245