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