xref: /haiku/src/apps/expander/ExpanderThread.cpp (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
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