1 /* 2 * Copyright 2004-2006, Jérôme Duval. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * Original code from ZipOMatic by jonas.sundstrom@kirilla.com 5 */ 6 #include <Messenger.h> 7 #include <Path.h> 8 #include "ExpanderThread.h" 9 #include <image.h> 10 #include <signal.h> 11 #include <unistd.h> 12 #include <errno.h> 13 14 const char * ExpanderThreadName = "ExpanderThread"; 15 16 ExpanderThread::ExpanderThread (BMessage * refs_message, BMessenger * messenger) 17 : GenericThread(ExpanderThreadName, B_NORMAL_PRIORITY, refs_message), 18 fWindowMessenger(messenger), 19 fThreadId(-1), 20 fStdIn(-1), 21 fStdOut(-1), 22 fStdErr(-1), 23 fExpanderOutput(NULL), 24 fExpanderOutputString(), 25 fExpanderOutputBuffer(new char [4096]) 26 { 27 SetDataStore(new BMessage (* refs_message)); // leak? 28 // prevents bug with B_SIMPLE_DATA 29 // (drag&drop messages) 30 } 31 32 ExpanderThread::~ExpanderThread() 33 { 34 delete fWindowMessenger; 35 delete [] fExpanderOutputBuffer; 36 } 37 38 status_t 39 ExpanderThread::ThreadStartup() 40 { 41 status_t status = B_OK; 42 entry_ref srcRef, destRef; 43 BString cmd; 44 45 if ((status = GetDataStore()->FindRef("srcRef", &srcRef))!=B_OK) 46 return status; 47 if ((status = GetDataStore()->FindRef("destRef", &destRef))==B_OK) { 48 BPath path(&destRef); 49 chdir(path.Path()); 50 } 51 if ((status = GetDataStore()->FindString("cmd", &cmd))!=B_OK) 52 return status; 53 54 BPath path(&srcRef); 55 BString pathString(path.Path()); 56 pathString.CharacterEscape("\"$`", '\\'); 57 pathString.Prepend("\""); 58 pathString.Append("\""); 59 cmd.ReplaceAll("%s", pathString.String()); 60 61 int32 argc = 3; 62 const char ** argv = new const char * [argc + 1]; 63 64 argv[0] = strdup("/bin/sh"); 65 argv[1] = strdup("-c"); 66 argv[2] = strdup(cmd.String()); 67 argv[argc] = NULL; 68 69 fThreadId = PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr); 70 71 delete [] argv; 72 73 if (fThreadId < 0) 74 return fThreadId; 75 76 resume_thread(fThreadId); 77 78 fExpanderOutput = fdopen(fStdOut, "r"); 79 80 return B_OK; 81 } 82 83 status_t 84 ExpanderThread::ExecuteUnit (void) 85 { 86 // read output from command 87 // send it to window 88 89 char * output_string; 90 output_string = fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput); 91 92 if (output_string == NULL) 93 return EOF; 94 95 BMessage message('outp'); 96 message.AddString("output", output_string); 97 for (int32 i=0; i<5; i++) { 98 output_string = fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput); 99 if(!output_string) 100 break; 101 message.AddString("output", output_string); 102 } 103 fWindowMessenger->SendMessage(&message); 104 105 return B_OK; 106 } 107 108 status_t 109 ExpanderThread::ThreadShutdown(void) 110 { 111 close(fStdIn); 112 close(fStdOut); 113 close(fStdErr); 114 115 return B_OK; 116 } 117 118 void 119 ExpanderThread::ThreadStartupFailed(status_t status) 120 { 121 fprintf(stderr, "ExpanderThread::ThreadStartupFailed() : %s\n", strerror(status)); 122 123 Quit(); 124 } 125 126 void 127 ExpanderThread::ExecuteUnitFailed(status_t status) 128 { 129 if (status == EOF) { 130 // thread has finished, been quit or killed, we don't know 131 fWindowMessenger->SendMessage(new BMessage('exit')); 132 } else { 133 // explicit error - communicate error to Window 134 fWindowMessenger->SendMessage(new BMessage('exrr')); 135 } 136 137 Quit(); 138 } 139 140 void 141 ExpanderThread::ThreadShutdownFailed(status_t status) 142 { 143 fprintf(stderr, "ExpanderThread::ThreadShutdownFailed() %s\n", strerror(status)); 144 } 145 146 147 status_t 148 ExpanderThread::ProcessRefs(BMessage *msg) 149 { 150 return B_OK; 151 } 152 153 thread_id 154 ExpanderThread::PipeCommand(int argc, const char **argv, int &in, int &out, int &err, const char **envp) 155 { 156 // This function written by Peter Folk <pfolk@uni.uiuc.edu> 157 // and published in the BeDevTalk FAQ 158 // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209 159 160 // Save current FDs 161 int old_in = dup(0); 162 int old_out = dup(1); 163 int old_err = dup(2); 164 165 int filedes[2]; 166 167 /* Create new pipe FDs as stdin, stdout, stderr */ 168 pipe(filedes); dup2(filedes[0],0); close(filedes[0]); 169 in=filedes[1]; // Write to in, appears on cmd's stdin 170 pipe(filedes); dup2(filedes[1],1); close(filedes[1]); 171 out=filedes[0]; // Read from out, taken from cmd's stdout 172 pipe(filedes); dup2(filedes[1],2); close(filedes[1]); 173 err=filedes[0]; // Read from err, taken from cmd's stderr 174 175 // "load" command. 176 thread_id ret = load_image(argc, argv, envp); 177 178 if (ret < B_OK) 179 return ret; 180 181 // thread ret is now suspended. 182 183 setpgid(ret, ret); 184 185 // Restore old FDs 186 close(0); dup(old_in); close(old_in); 187 close(1); dup(old_out); close(old_out); 188 close(2); dup(old_err); close(old_err); 189 190 /* Theoretically I should do loads of error checking, but 191 the calls aren't very likely to fail, and that would 192 muddy up the example quite a bit. YMMV. */ 193 194 return ret; 195 } 196 197 198 status_t 199 ExpanderThread::SuspendExternalExpander() 200 { 201 status_t status; 202 thread_info thread_info; 203 status = get_thread_info(fThreadId, &thread_info); 204 BString thread_name = thread_info.name; 205 206 if (status == B_OK) 207 return send_signal(-fThreadId, SIGSTOP); 208 else 209 return status; 210 } 211 212 status_t 213 ExpanderThread::ResumeExternalExpander() 214 { 215 status_t status = B_OK; 216 thread_info thread_info; 217 status = get_thread_info(fThreadId, &thread_info); 218 BString thread_name = thread_info.name; 219 220 if (status == B_OK) 221 return send_signal(-fThreadId, SIGCONT); 222 else 223 return status; 224 } 225 226 status_t 227 ExpanderThread::InterruptExternalExpander() 228 { 229 status_t status = B_OK; 230 thread_info thread_info; 231 status = get_thread_info (fThreadId, &thread_info); 232 BString thread_name = thread_info.name; 233 234 if (status == B_OK) { 235 status = send_signal(-fThreadId, SIGINT); 236 WaitOnExternalExpander(); 237 } 238 return status; 239 } 240 241 status_t 242 ExpanderThread::WaitOnExternalExpander() 243 { 244 status_t status; 245 thread_info thread_info; 246 status = get_thread_info(fThreadId, &thread_info); 247 BString thread_name = thread_info.name; 248 249 if (status == B_OK) 250 return wait_for_thread(fThreadId, &status); 251 else 252 return status; 253 } 254