1*55a5a6bdSJérôme Duval /*****************************************************************************/ 2*55a5a6bdSJérôme Duval // Expander 3*55a5a6bdSJérôme Duval // Written by Jérôme Duval 4*55a5a6bdSJérôme Duval // 5*55a5a6bdSJérôme Duval // ExpanderThread.cpp 6*55a5a6bdSJérôme Duval // 7*55a5a6bdSJérôme Duval // Copyright (c) 2004 OpenBeOS Project 8*55a5a6bdSJérôme Duval // 9*55a5a6bdSJérôme Duval // Original code from ZipOMatic by jonas.sundstrom@kirilla.com 10*55a5a6bdSJérôme Duval // 11*55a5a6bdSJérôme Duval // Permission is hereby granted, free of charge, to any person obtaining a 12*55a5a6bdSJérôme Duval // copy of this software and associated documentation files (the "Software"), 13*55a5a6bdSJérôme Duval // to deal in the Software without restriction, including without limitation 14*55a5a6bdSJérôme Duval // the rights to use, copy, modify, merge, publish, distribute, sublicense, 15*55a5a6bdSJérôme Duval // and/or sell copies of the Software, and to permit persons to whom the 16*55a5a6bdSJérôme Duval // Software is furnished to do so, subject to the following conditions: 17*55a5a6bdSJérôme Duval // 18*55a5a6bdSJérôme Duval // The above copyright notice and this permission notice shall be included 19*55a5a6bdSJérôme Duval // in all copies or substantial portions of the Software. 20*55a5a6bdSJérôme Duval // 21*55a5a6bdSJérôme Duval // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22*55a5a6bdSJérôme Duval // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23*55a5a6bdSJérôme Duval // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 24*55a5a6bdSJérôme Duval // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25*55a5a6bdSJérôme Duval // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26*55a5a6bdSJérôme Duval // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27*55a5a6bdSJérôme Duval // DEALINGS IN THE SOFTWARE. 28*55a5a6bdSJérôme Duval /*****************************************************************************/ 29*55a5a6bdSJérôme Duval #include <Path.h> 30*55a5a6bdSJérôme Duval #include <ExpanderThread.h> 31*55a5a6bdSJérôme Duval #include <image.h> 32*55a5a6bdSJérôme Duval #include <signal.h> 33*55a5a6bdSJérôme Duval #include <unistd.h> 34*55a5a6bdSJérôme Duval #include <errno.h> 35*55a5a6bdSJérôme Duval 36*55a5a6bdSJérôme Duval const char * ExpanderThreadName = "ExpanderThread"; 37*55a5a6bdSJérôme Duval 38*55a5a6bdSJérôme Duval ExpanderThread::ExpanderThread (BMessage * refs_message, BMessenger * messenger) 39*55a5a6bdSJérôme Duval : GenericThread(ExpanderThreadName, B_NORMAL_PRIORITY, refs_message), 40*55a5a6bdSJérôme Duval fWindowMessenger(messenger), 41*55a5a6bdSJérôme Duval fThreadId(-1), 42*55a5a6bdSJérôme Duval fStdIn(-1), 43*55a5a6bdSJérôme Duval fStdOut(-1), 44*55a5a6bdSJérôme Duval fStdErr(-1), 45*55a5a6bdSJérôme Duval fExpanderOutput(NULL), 46*55a5a6bdSJérôme Duval fExpanderOutputString(), 47*55a5a6bdSJérôme Duval fExpanderOutputBuffer(new char [4096]) 48*55a5a6bdSJérôme Duval { 49*55a5a6bdSJérôme Duval SetDataStore(new BMessage (* refs_message)); // leak? 50*55a5a6bdSJérôme Duval // prevents bug with B_SIMPLE_DATA 51*55a5a6bdSJérôme Duval // (drag&drop messages) 52*55a5a6bdSJérôme Duval } 53*55a5a6bdSJérôme Duval 54*55a5a6bdSJérôme Duval ExpanderThread::~ExpanderThread() 55*55a5a6bdSJérôme Duval { 56*55a5a6bdSJérôme Duval delete fWindowMessenger; 57*55a5a6bdSJérôme Duval delete [] fExpanderOutputBuffer; 58*55a5a6bdSJérôme Duval } 59*55a5a6bdSJérôme Duval 60*55a5a6bdSJérôme Duval status_t 61*55a5a6bdSJérôme Duval ExpanderThread::ThreadStartup() 62*55a5a6bdSJérôme Duval { 63*55a5a6bdSJérôme Duval status_t status = B_OK; 64*55a5a6bdSJérôme Duval entry_ref srcRef, destRef; 65*55a5a6bdSJérôme Duval BString cmd; 66*55a5a6bdSJérôme Duval 67*55a5a6bdSJérôme Duval if ((status = GetDataStore()->FindRef("srcRef", &srcRef))!=B_OK) 68*55a5a6bdSJérôme Duval return status; 69*55a5a6bdSJérôme Duval if ((status = GetDataStore()->FindRef("destRef", &destRef))!=B_OK) 70*55a5a6bdSJérôme Duval return status; 71*55a5a6bdSJérôme Duval if ((status = GetDataStore()->FindString("cmd", &cmd))!=B_OK) 72*55a5a6bdSJérôme Duval return status; 73*55a5a6bdSJérôme Duval 74*55a5a6bdSJérôme Duval BPath path(&srcRef); 75*55a5a6bdSJérôme Duval BString pathString(path.Path()); 76*55a5a6bdSJérôme Duval pathString.CharacterEscape("\"$`", '\\'); 77*55a5a6bdSJérôme Duval pathString.Prepend("\""); 78*55a5a6bdSJérôme Duval pathString.Append("\""); 79*55a5a6bdSJérôme Duval cmd.ReplaceAll("%s", pathString.String()); 80*55a5a6bdSJérôme Duval 81*55a5a6bdSJérôme Duval path.SetTo(&destRef); 82*55a5a6bdSJérôme Duval chdir(path.Path()); 83*55a5a6bdSJérôme Duval 84*55a5a6bdSJérôme Duval int32 argc = 3; 85*55a5a6bdSJérôme Duval const char ** argv = new const char * [argc + 1]; 86*55a5a6bdSJérôme Duval 87*55a5a6bdSJérôme Duval argv[0] = strdup("/bin/sh"); 88*55a5a6bdSJérôme Duval argv[1] = strdup("-c"); 89*55a5a6bdSJérôme Duval argv[2] = strdup(cmd.String()); 90*55a5a6bdSJérôme Duval argv[argc] = NULL; 91*55a5a6bdSJérôme Duval 92*55a5a6bdSJérôme Duval fThreadId = PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr); 93*55a5a6bdSJérôme Duval 94*55a5a6bdSJérôme Duval delete [] argv; 95*55a5a6bdSJérôme Duval 96*55a5a6bdSJérôme Duval if (fThreadId < 0) 97*55a5a6bdSJérôme Duval return fThreadId; 98*55a5a6bdSJérôme Duval 99*55a5a6bdSJérôme Duval resume_thread(fThreadId); 100*55a5a6bdSJérôme Duval 101*55a5a6bdSJérôme Duval fExpanderOutput = fdopen(fStdOut, "r"); 102*55a5a6bdSJérôme Duval 103*55a5a6bdSJérôme Duval return B_OK; 104*55a5a6bdSJérôme Duval } 105*55a5a6bdSJérôme Duval 106*55a5a6bdSJérôme Duval status_t 107*55a5a6bdSJérôme Duval ExpanderThread::ExecuteUnit (void) 108*55a5a6bdSJérôme Duval { 109*55a5a6bdSJérôme Duval // read output from command 110*55a5a6bdSJérôme Duval // send it to window 111*55a5a6bdSJérôme Duval 112*55a5a6bdSJérôme Duval char * output_string; 113*55a5a6bdSJérôme Duval output_string = fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput); 114*55a5a6bdSJérôme Duval 115*55a5a6bdSJérôme Duval if (output_string == NULL) 116*55a5a6bdSJérôme Duval return EOF; 117*55a5a6bdSJérôme Duval 118*55a5a6bdSJérôme Duval BMessage message('outp'); 119*55a5a6bdSJérôme Duval message.AddString("output", output_string); 120*55a5a6bdSJérôme Duval for (int32 i=0; i<5; i++) { 121*55a5a6bdSJérôme Duval output_string = fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput); 122*55a5a6bdSJérôme Duval if(!output_string) 123*55a5a6bdSJérôme Duval break; 124*55a5a6bdSJérôme Duval message.AddString("output", output_string); 125*55a5a6bdSJérôme Duval } 126*55a5a6bdSJérôme Duval fWindowMessenger->SendMessage(&message); 127*55a5a6bdSJérôme Duval 128*55a5a6bdSJérôme Duval return B_OK; 129*55a5a6bdSJérôme Duval } 130*55a5a6bdSJérôme Duval 131*55a5a6bdSJérôme Duval status_t 132*55a5a6bdSJérôme Duval ExpanderThread::ThreadShutdown(void) 133*55a5a6bdSJérôme Duval { 134*55a5a6bdSJérôme Duval close(fStdIn); 135*55a5a6bdSJérôme Duval close(fStdOut); 136*55a5a6bdSJérôme Duval close(fStdErr); 137*55a5a6bdSJérôme Duval 138*55a5a6bdSJérôme Duval return B_OK; 139*55a5a6bdSJérôme Duval } 140*55a5a6bdSJérôme Duval 141*55a5a6bdSJérôme Duval void 142*55a5a6bdSJérôme Duval ExpanderThread::ThreadStartupFailed(status_t status) 143*55a5a6bdSJérôme Duval { 144*55a5a6bdSJérôme Duval fprintf(stderr, "ExpanderThread::ThreadStartupFailed() : %s\n", strerror(status)); 145*55a5a6bdSJérôme Duval 146*55a5a6bdSJérôme Duval Quit(); 147*55a5a6bdSJérôme Duval } 148*55a5a6bdSJérôme Duval 149*55a5a6bdSJérôme Duval void 150*55a5a6bdSJérôme Duval ExpanderThread::ExecuteUnitFailed(status_t status) 151*55a5a6bdSJérôme Duval { 152*55a5a6bdSJérôme Duval if (status == EOF) { 153*55a5a6bdSJérôme Duval // thread has finished, been quit or killed, we don't know 154*55a5a6bdSJérôme Duval fWindowMessenger->SendMessage(new BMessage('exit')); 155*55a5a6bdSJérôme Duval } else { 156*55a5a6bdSJérôme Duval // explicit error - communicate error to Window 157*55a5a6bdSJérôme Duval fWindowMessenger->SendMessage(new BMessage('exrr')); 158*55a5a6bdSJérôme Duval } 159*55a5a6bdSJérôme Duval 160*55a5a6bdSJérôme Duval Quit(); 161*55a5a6bdSJérôme Duval } 162*55a5a6bdSJérôme Duval 163*55a5a6bdSJérôme Duval void 164*55a5a6bdSJérôme Duval ExpanderThread::ThreadShutdownFailed(status_t status) 165*55a5a6bdSJérôme Duval { 166*55a5a6bdSJérôme Duval fprintf(stderr, "ExpanderThread::ThreadShutdownFailed() %s\n", strerror(status)); 167*55a5a6bdSJérôme Duval } 168*55a5a6bdSJérôme Duval 169*55a5a6bdSJérôme Duval 170*55a5a6bdSJérôme Duval status_t 171*55a5a6bdSJérôme Duval ExpanderThread::ProcessRefs(BMessage *msg) 172*55a5a6bdSJérôme Duval { 173*55a5a6bdSJérôme Duval return B_OK; 174*55a5a6bdSJérôme Duval } 175*55a5a6bdSJérôme Duval 176*55a5a6bdSJérôme Duval thread_id 177*55a5a6bdSJérôme Duval ExpanderThread::PipeCommand(int argc, const char **argv, int &in, int &out, int &err, const char **envp) 178*55a5a6bdSJérôme Duval { 179*55a5a6bdSJérôme Duval // This function written by Peter Folk <pfolk@uni.uiuc.edu> 180*55a5a6bdSJérôme Duval // and published in the BeDevTalk FAQ 181*55a5a6bdSJérôme Duval // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209 182*55a5a6bdSJérôme Duval 183*55a5a6bdSJérôme Duval // Save current FDs 184*55a5a6bdSJérôme Duval int old_in = dup(0); 185*55a5a6bdSJérôme Duval int old_out = dup(1); 186*55a5a6bdSJérôme Duval int old_err = dup(2); 187*55a5a6bdSJérôme Duval 188*55a5a6bdSJérôme Duval int filedes[2]; 189*55a5a6bdSJérôme Duval 190*55a5a6bdSJérôme Duval /* Create new pipe FDs as stdin, stdout, stderr */ 191*55a5a6bdSJérôme Duval pipe(filedes); dup2(filedes[0],0); close(filedes[0]); 192*55a5a6bdSJérôme Duval in=filedes[1]; // Write to in, appears on cmd's stdin 193*55a5a6bdSJérôme Duval pipe(filedes); dup2(filedes[1],1); close(filedes[1]); 194*55a5a6bdSJérôme Duval out=filedes[0]; // Read from out, taken from cmd's stdout 195*55a5a6bdSJérôme Duval pipe(filedes); dup2(filedes[1],2); close(filedes[1]); 196*55a5a6bdSJérôme Duval err=filedes[0]; // Read from err, taken from cmd's stderr 197*55a5a6bdSJérôme Duval 198*55a5a6bdSJérôme Duval // "load" command. 199*55a5a6bdSJérôme Duval thread_id ret = load_image(argc, argv, envp); 200*55a5a6bdSJérôme Duval 201*55a5a6bdSJérôme Duval if (ret < B_OK) 202*55a5a6bdSJérôme Duval return ret; 203*55a5a6bdSJérôme Duval 204*55a5a6bdSJérôme Duval // thread ret is now suspended. 205*55a5a6bdSJérôme Duval 206*55a5a6bdSJérôme Duval setpgid(ret, ret); 207*55a5a6bdSJérôme Duval 208*55a5a6bdSJérôme Duval // Restore old FDs 209*55a5a6bdSJérôme Duval close(0); dup(old_in); close(old_in); 210*55a5a6bdSJérôme Duval close(1); dup(old_out); close(old_out); 211*55a5a6bdSJérôme Duval close(2); dup(old_err); close(old_err); 212*55a5a6bdSJérôme Duval 213*55a5a6bdSJérôme Duval /* Theoretically I should do loads of error checking, but 214*55a5a6bdSJérôme Duval the calls aren't very likely to fail, and that would 215*55a5a6bdSJérôme Duval muddy up the example quite a bit. YMMV. */ 216*55a5a6bdSJérôme Duval 217*55a5a6bdSJérôme Duval return ret; 218*55a5a6bdSJérôme Duval } 219*55a5a6bdSJérôme Duval 220*55a5a6bdSJérôme Duval 221*55a5a6bdSJérôme Duval status_t 222*55a5a6bdSJérôme Duval ExpanderThread::SuspendExternalExpander() 223*55a5a6bdSJérôme Duval { 224*55a5a6bdSJérôme Duval status_t status; 225*55a5a6bdSJérôme Duval thread_info thread_info; 226*55a5a6bdSJérôme Duval status = get_thread_info(fThreadId, &thread_info); 227*55a5a6bdSJérôme Duval BString thread_name = thread_info.name; 228*55a5a6bdSJérôme Duval 229*55a5a6bdSJérôme Duval if (status == B_OK) 230*55a5a6bdSJérôme Duval return send_signal(-fThreadId, SIGSTOP); 231*55a5a6bdSJérôme Duval else 232*55a5a6bdSJérôme Duval return status; 233*55a5a6bdSJérôme Duval } 234*55a5a6bdSJérôme Duval 235*55a5a6bdSJérôme Duval status_t 236*55a5a6bdSJérôme Duval ExpanderThread::ResumeExternalExpander() 237*55a5a6bdSJérôme Duval { 238*55a5a6bdSJérôme Duval status_t status = B_OK; 239*55a5a6bdSJérôme Duval thread_info thread_info; 240*55a5a6bdSJérôme Duval status = get_thread_info(fThreadId, &thread_info); 241*55a5a6bdSJérôme Duval BString thread_name = thread_info.name; 242*55a5a6bdSJérôme Duval 243*55a5a6bdSJérôme Duval if (status == B_OK) 244*55a5a6bdSJérôme Duval return send_signal(-fThreadId, SIGCONT); 245*55a5a6bdSJérôme Duval else 246*55a5a6bdSJérôme Duval return status; 247*55a5a6bdSJérôme Duval } 248*55a5a6bdSJérôme Duval 249*55a5a6bdSJérôme Duval status_t 250*55a5a6bdSJérôme Duval ExpanderThread::InterruptExternalExpander() 251*55a5a6bdSJérôme Duval { 252*55a5a6bdSJérôme Duval status_t status = B_OK; 253*55a5a6bdSJérôme Duval thread_info thread_info; 254*55a5a6bdSJérôme Duval status = get_thread_info (fThreadId, &thread_info); 255*55a5a6bdSJérôme Duval BString thread_name = thread_info.name; 256*55a5a6bdSJérôme Duval 257*55a5a6bdSJérôme Duval if (status == B_OK) { 258*55a5a6bdSJérôme Duval status = send_signal(-fThreadId, SIGINT); 259*55a5a6bdSJérôme Duval WaitOnExternalExpander(); 260*55a5a6bdSJérôme Duval } 261*55a5a6bdSJérôme Duval return status; 262*55a5a6bdSJérôme Duval } 263*55a5a6bdSJérôme Duval 264*55a5a6bdSJérôme Duval status_t 265*55a5a6bdSJérôme Duval ExpanderThread::WaitOnExternalExpander() 266*55a5a6bdSJérôme Duval { 267*55a5a6bdSJérôme Duval status_t status; 268*55a5a6bdSJérôme Duval thread_info thread_info; 269*55a5a6bdSJérôme Duval status = get_thread_info(fThreadId, &thread_info); 270*55a5a6bdSJérôme Duval BString thread_name = thread_info.name; 271*55a5a6bdSJérôme Duval 272*55a5a6bdSJérôme Duval if (status == B_OK) 273*55a5a6bdSJérôme Duval return wait_for_thread(fThreadId, &status); 274*55a5a6bdSJérôme Duval else 275*55a5a6bdSJérôme Duval return status; 276*55a5a6bdSJérôme Duval } 277