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