xref: /haiku/src/apps/expander/ExpanderThread.cpp (revision 91d1efffa50fede28f99dedff3cbcdb1e7c57495)
155a5a6bdSJérôme Duval /*****************************************************************************/
255a5a6bdSJérôme Duval // Expander
355a5a6bdSJérôme Duval // Written by Jérôme Duval
455a5a6bdSJérôme Duval //
555a5a6bdSJérôme Duval // ExpanderThread.cpp
655a5a6bdSJérôme Duval //
755a5a6bdSJérôme Duval // Copyright (c) 2004 OpenBeOS Project
855a5a6bdSJérôme Duval //
955a5a6bdSJérôme Duval // Original code from ZipOMatic by jonas.sundstrom@kirilla.com
1055a5a6bdSJérôme Duval //
1155a5a6bdSJérôme Duval // Permission is hereby granted, free of charge, to any person obtaining a
1255a5a6bdSJérôme Duval // copy of this software and associated documentation files (the "Software"),
1355a5a6bdSJérôme Duval // to deal in the Software without restriction, including without limitation
1455a5a6bdSJérôme Duval // the rights to use, copy, modify, merge, publish, distribute, sublicense,
1555a5a6bdSJérôme Duval // and/or sell copies of the Software, and to permit persons to whom the
1655a5a6bdSJérôme Duval // Software is furnished to do so, subject to the following conditions:
1755a5a6bdSJérôme Duval //
1855a5a6bdSJérôme Duval // The above copyright notice and this permission notice shall be included
1955a5a6bdSJérôme Duval // in all copies or substantial portions of the Software.
2055a5a6bdSJérôme Duval //
2155a5a6bdSJérôme Duval // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
2255a5a6bdSJérôme Duval // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2355a5a6bdSJérôme Duval // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2455a5a6bdSJérôme Duval // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2555a5a6bdSJérôme Duval // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2655a5a6bdSJérôme Duval // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2755a5a6bdSJérôme Duval // DEALINGS IN THE SOFTWARE.
2855a5a6bdSJérôme Duval /*****************************************************************************/
2955a5a6bdSJérôme Duval #include <Path.h>
3055a5a6bdSJérôme Duval #include <ExpanderThread.h>
3155a5a6bdSJérôme Duval #include <image.h>
3255a5a6bdSJérôme Duval #include <signal.h>
3355a5a6bdSJérôme Duval #include <unistd.h>
3455a5a6bdSJérôme Duval #include <errno.h>
3555a5a6bdSJérôme Duval 
3655a5a6bdSJérôme Duval const char * ExpanderThreadName	=	"ExpanderThread";
3755a5a6bdSJérôme Duval 
3855a5a6bdSJérôme Duval ExpanderThread::ExpanderThread (BMessage * refs_message, BMessenger * messenger)
3955a5a6bdSJérôme Duval :	GenericThread(ExpanderThreadName, B_NORMAL_PRIORITY, refs_message),
4055a5a6bdSJérôme Duval 	fWindowMessenger(messenger),
4155a5a6bdSJérôme Duval 	fThreadId(-1),
4255a5a6bdSJérôme Duval 	fStdIn(-1),
4355a5a6bdSJérôme Duval 	fStdOut(-1),
4455a5a6bdSJérôme Duval 	fStdErr(-1),
4555a5a6bdSJérôme Duval 	fExpanderOutput(NULL),
4655a5a6bdSJérôme Duval 	fExpanderOutputString(),
4755a5a6bdSJérôme Duval 	fExpanderOutputBuffer(new char [4096])
4855a5a6bdSJérôme Duval {
4955a5a6bdSJérôme Duval 	SetDataStore(new BMessage (* refs_message)); // leak?
5055a5a6bdSJérôme Duval 												  // prevents bug with B_SIMPLE_DATA
5155a5a6bdSJérôme Duval 												  // (drag&drop messages)
5255a5a6bdSJérôme Duval }
5355a5a6bdSJérôme Duval 
5455a5a6bdSJérôme Duval ExpanderThread::~ExpanderThread()
5555a5a6bdSJérôme Duval {
5655a5a6bdSJérôme Duval 	delete fWindowMessenger;
5755a5a6bdSJérôme Duval 	delete [] fExpanderOutputBuffer;
5855a5a6bdSJérôme Duval }
5955a5a6bdSJérôme Duval 
6055a5a6bdSJérôme Duval status_t
6155a5a6bdSJérôme Duval ExpanderThread::ThreadStartup()
6255a5a6bdSJérôme Duval {
6355a5a6bdSJérôme Duval 	status_t	status	=	B_OK;
6455a5a6bdSJérôme Duval 	entry_ref	srcRef, destRef;
6555a5a6bdSJérôme Duval 	BString 	cmd;
6655a5a6bdSJérôme Duval 
6755a5a6bdSJérôme Duval 	if ((status = GetDataStore()->FindRef("srcRef", &srcRef))!=B_OK)
6855a5a6bdSJérôme Duval 		return status;
69*91d1efffSJérôme Duval 	if ((status = GetDataStore()->FindRef("destRef", &destRef))==B_OK) {
70*91d1efffSJérôme Duval 		BPath path(&destRef);
71*91d1efffSJérôme Duval 		chdir(path.Path());
72*91d1efffSJérôme Duval 	}
7355a5a6bdSJérôme Duval 	if ((status = GetDataStore()->FindString("cmd", &cmd))!=B_OK)
7455a5a6bdSJérôme Duval 		return status;
7555a5a6bdSJérôme Duval 
7655a5a6bdSJérôme Duval 	BPath path(&srcRef);
7755a5a6bdSJérôme Duval 	BString pathString(path.Path());
7855a5a6bdSJérôme Duval 	pathString.CharacterEscape("\"$`", '\\');
7955a5a6bdSJérôme Duval 	pathString.Prepend("\"");
8055a5a6bdSJérôme Duval 	pathString.Append("\"");
8155a5a6bdSJérôme Duval 	cmd.ReplaceAll("%s", pathString.String());
8255a5a6bdSJérôme Duval 
8355a5a6bdSJérôme Duval 	int32 argc = 3;
8455a5a6bdSJérôme Duval 	const char ** argv = new const char * [argc + 1];
8555a5a6bdSJérôme Duval 
8655a5a6bdSJérôme Duval 	argv[0]	=	strdup("/bin/sh");
8755a5a6bdSJérôme Duval 	argv[1]	=	strdup("-c");
8855a5a6bdSJérôme Duval 	argv[2]	=	strdup(cmd.String());
8955a5a6bdSJérôme Duval 	argv[argc] = NULL;
9055a5a6bdSJérôme Duval 
9155a5a6bdSJérôme Duval 	fThreadId = PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr);
9255a5a6bdSJérôme Duval 
9355a5a6bdSJérôme Duval 	delete [] argv;
9455a5a6bdSJérôme Duval 
9555a5a6bdSJérôme Duval 	if (fThreadId < 0)
9655a5a6bdSJérôme Duval 		return fThreadId;
9755a5a6bdSJérôme Duval 
9855a5a6bdSJérôme Duval     resume_thread(fThreadId);
9955a5a6bdSJérôme Duval 
10055a5a6bdSJérôme Duval     fExpanderOutput = fdopen(fStdOut, "r");
10155a5a6bdSJérôme Duval 
10255a5a6bdSJérôme Duval 	return B_OK;
10355a5a6bdSJérôme Duval }
10455a5a6bdSJérôme Duval 
10555a5a6bdSJérôme Duval status_t
10655a5a6bdSJérôme Duval ExpanderThread::ExecuteUnit (void)
10755a5a6bdSJérôme Duval {
10855a5a6bdSJérôme Duval 	// read output from command
10955a5a6bdSJérôme Duval 	// send it to window
11055a5a6bdSJérôme Duval 
11155a5a6bdSJérôme Duval 	char * output_string;
11255a5a6bdSJérôme Duval 	output_string =	fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput);
11355a5a6bdSJérôme Duval 
11455a5a6bdSJérôme Duval 	if (output_string == NULL)
11555a5a6bdSJérôme Duval 		return EOF;
11655a5a6bdSJérôme Duval 
11755a5a6bdSJérôme Duval 	BMessage message('outp');
11855a5a6bdSJérôme Duval 	message.AddString("output", output_string);
11955a5a6bdSJérôme Duval 	for (int32 i=0; i<5; i++) {
12055a5a6bdSJérôme Duval 		output_string = fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput);
12155a5a6bdSJérôme Duval 		if(!output_string)
12255a5a6bdSJérôme Duval 			break;
12355a5a6bdSJérôme Duval 		message.AddString("output", output_string);
12455a5a6bdSJérôme Duval 	}
12555a5a6bdSJérôme Duval 	fWindowMessenger->SendMessage(&message);
12655a5a6bdSJérôme Duval 
12755a5a6bdSJérôme Duval 	return B_OK;
12855a5a6bdSJérôme Duval }
12955a5a6bdSJérôme Duval 
13055a5a6bdSJérôme Duval status_t
13155a5a6bdSJérôme Duval ExpanderThread::ThreadShutdown(void)
13255a5a6bdSJérôme Duval {
13355a5a6bdSJérôme Duval 	close(fStdIn);
13455a5a6bdSJérôme Duval     close(fStdOut);
13555a5a6bdSJérôme Duval    	close(fStdErr);
13655a5a6bdSJérôme Duval 
13755a5a6bdSJérôme Duval 	return B_OK;
13855a5a6bdSJérôme Duval }
13955a5a6bdSJérôme Duval 
14055a5a6bdSJérôme Duval void
14155a5a6bdSJérôme Duval ExpanderThread::ThreadStartupFailed(status_t status)
14255a5a6bdSJérôme Duval {
14355a5a6bdSJérôme Duval 	fprintf(stderr, "ExpanderThread::ThreadStartupFailed() : %s\n", strerror(status));
14455a5a6bdSJérôme Duval 
14555a5a6bdSJérôme Duval 	Quit();
14655a5a6bdSJérôme Duval }
14755a5a6bdSJérôme Duval 
14855a5a6bdSJérôme Duval void
14955a5a6bdSJérôme Duval ExpanderThread::ExecuteUnitFailed(status_t status)
15055a5a6bdSJérôme Duval {
15155a5a6bdSJérôme Duval 	if (status == EOF) {
15255a5a6bdSJérôme Duval 		// thread has finished, been quit or killed, we don't know
15355a5a6bdSJérôme Duval 		fWindowMessenger->SendMessage(new BMessage('exit'));
15455a5a6bdSJérôme Duval 	} else {
15555a5a6bdSJérôme Duval 		// explicit error - communicate error to Window
15655a5a6bdSJérôme Duval 		fWindowMessenger->SendMessage(new BMessage('exrr'));
15755a5a6bdSJérôme Duval 	}
15855a5a6bdSJérôme Duval 
15955a5a6bdSJérôme Duval 	Quit();
16055a5a6bdSJérôme Duval }
16155a5a6bdSJérôme Duval 
16255a5a6bdSJérôme Duval void
16355a5a6bdSJérôme Duval ExpanderThread::ThreadShutdownFailed(status_t status)
16455a5a6bdSJérôme Duval {
16555a5a6bdSJérôme Duval 	fprintf(stderr, "ExpanderThread::ThreadShutdownFailed() %s\n", strerror(status));
16655a5a6bdSJérôme Duval }
16755a5a6bdSJérôme Duval 
16855a5a6bdSJérôme Duval 
16955a5a6bdSJérôme Duval status_t
17055a5a6bdSJérôme Duval ExpanderThread::ProcessRefs(BMessage *msg)
17155a5a6bdSJérôme Duval {
17255a5a6bdSJérôme Duval 	return B_OK;
17355a5a6bdSJérôme Duval }
17455a5a6bdSJérôme Duval 
17555a5a6bdSJérôme Duval thread_id
17655a5a6bdSJérôme Duval ExpanderThread::PipeCommand(int argc, const char **argv, int &in, int &out, int &err, const char **envp)
17755a5a6bdSJérôme Duval {
17855a5a6bdSJérôme Duval 	// This function written by Peter Folk <pfolk@uni.uiuc.edu>
17955a5a6bdSJérôme Duval 	// and published in the BeDevTalk FAQ
18055a5a6bdSJérôme Duval 	// http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
18155a5a6bdSJérôme Duval 
18255a5a6bdSJérôme Duval     // Save current FDs
18355a5a6bdSJérôme Duval     int old_in  =  dup(0);
18455a5a6bdSJérôme Duval     int old_out  =  dup(1);
18555a5a6bdSJérôme Duval     int old_err  =  dup(2);
18655a5a6bdSJérôme Duval 
18755a5a6bdSJérôme Duval     int filedes[2];
18855a5a6bdSJérôme Duval 
18955a5a6bdSJérôme Duval     /* Create new pipe FDs as stdin, stdout, stderr */
19055a5a6bdSJérôme Duval     pipe(filedes);  dup2(filedes[0],0); close(filedes[0]);
19155a5a6bdSJérôme Duval     in=filedes[1];  // Write to in, appears on cmd's stdin
19255a5a6bdSJérôme Duval     pipe(filedes);  dup2(filedes[1],1); close(filedes[1]);
19355a5a6bdSJérôme Duval     out=filedes[0]; // Read from out, taken from cmd's stdout
19455a5a6bdSJérôme Duval     pipe(filedes);  dup2(filedes[1],2); close(filedes[1]);
19555a5a6bdSJérôme Duval     err=filedes[0]; // Read from err, taken from cmd's stderr
19655a5a6bdSJérôme Duval 
19755a5a6bdSJérôme Duval     // "load" command.
19855a5a6bdSJérôme Duval     thread_id ret  =  load_image(argc, argv, envp);
19955a5a6bdSJérôme Duval 
20055a5a6bdSJérôme Duval     if (ret < B_OK)
20155a5a6bdSJérôme Duval 		return ret;
20255a5a6bdSJérôme Duval 
20355a5a6bdSJérôme Duval     // thread ret is now suspended.
20455a5a6bdSJérôme Duval 
20555a5a6bdSJérôme Duval 	setpgid(ret, ret);
20655a5a6bdSJérôme Duval 
20755a5a6bdSJérôme Duval     // Restore old FDs
20855a5a6bdSJérôme Duval     close(0); dup(old_in); close(old_in);
20955a5a6bdSJérôme Duval     close(1); dup(old_out); close(old_out);
21055a5a6bdSJérôme Duval     close(2); dup(old_err); close(old_err);
21155a5a6bdSJérôme Duval 
21255a5a6bdSJérôme Duval     /* Theoretically I should do loads of error checking, but
21355a5a6bdSJérôme Duval        the calls aren't very likely to fail, and that would
21455a5a6bdSJérôme Duval        muddy up the example quite a bit.  YMMV. */
21555a5a6bdSJérôme Duval 
21655a5a6bdSJérôme Duval     return ret;
21755a5a6bdSJérôme Duval }
21855a5a6bdSJérôme Duval 
21955a5a6bdSJérôme Duval 
22055a5a6bdSJérôme Duval status_t
22155a5a6bdSJérôme Duval ExpanderThread::SuspendExternalExpander()
22255a5a6bdSJérôme Duval {
22355a5a6bdSJérôme Duval 	status_t status;
22455a5a6bdSJérôme Duval 	thread_info thread_info;
22555a5a6bdSJérôme Duval 	status = get_thread_info(fThreadId, &thread_info);
22655a5a6bdSJérôme Duval 	BString	thread_name = thread_info.name;
22755a5a6bdSJérôme Duval 
22855a5a6bdSJérôme Duval 	if (status == B_OK)
22955a5a6bdSJérôme Duval 		return send_signal(-fThreadId, SIGSTOP);
23055a5a6bdSJérôme Duval 	else
23155a5a6bdSJérôme Duval 		return status;
23255a5a6bdSJérôme Duval }
23355a5a6bdSJérôme Duval 
23455a5a6bdSJérôme Duval status_t
23555a5a6bdSJérôme Duval ExpanderThread::ResumeExternalExpander()
23655a5a6bdSJérôme Duval {
23755a5a6bdSJérôme Duval 	status_t status = B_OK;
23855a5a6bdSJérôme Duval 	thread_info thread_info;
23955a5a6bdSJérôme Duval 	status = get_thread_info(fThreadId, &thread_info);
24055a5a6bdSJérôme Duval 	BString	thread_name = thread_info.name;
24155a5a6bdSJérôme Duval 
24255a5a6bdSJérôme Duval 	if (status == B_OK)
24355a5a6bdSJérôme Duval 		return send_signal(-fThreadId, SIGCONT);
24455a5a6bdSJérôme Duval 	else
24555a5a6bdSJérôme Duval 		return status;
24655a5a6bdSJérôme Duval }
24755a5a6bdSJérôme Duval 
24855a5a6bdSJérôme Duval status_t
24955a5a6bdSJérôme Duval ExpanderThread::InterruptExternalExpander()
25055a5a6bdSJérôme Duval {
25155a5a6bdSJérôme Duval 	status_t status = B_OK;
25255a5a6bdSJérôme Duval 	thread_info thread_info;
25355a5a6bdSJérôme Duval 	status = get_thread_info (fThreadId, &thread_info);
25455a5a6bdSJérôme Duval 	BString	thread_name = thread_info.name;
25555a5a6bdSJérôme Duval 
25655a5a6bdSJérôme Duval 	if (status == B_OK) {
25755a5a6bdSJérôme Duval 		status = send_signal(-fThreadId, SIGINT);
25855a5a6bdSJérôme Duval 		WaitOnExternalExpander();
25955a5a6bdSJérôme Duval 	}
26055a5a6bdSJérôme Duval 	return status;
26155a5a6bdSJérôme Duval }
26255a5a6bdSJérôme Duval 
26355a5a6bdSJérôme Duval status_t
26455a5a6bdSJérôme Duval ExpanderThread::WaitOnExternalExpander()
26555a5a6bdSJérôme Duval {
26655a5a6bdSJérôme Duval 	status_t status;
26755a5a6bdSJérôme Duval 	thread_info thread_info;
26855a5a6bdSJérôme Duval 	status = get_thread_info(fThreadId, &thread_info);
26955a5a6bdSJérôme Duval 	BString	thread_name = thread_info.name;
27055a5a6bdSJérôme Duval 
27155a5a6bdSJérôme Duval 	if (status == B_OK)
27255a5a6bdSJérôme Duval 		return wait_for_thread(fThreadId, &status);
27355a5a6bdSJérôme Duval 	else
27455a5a6bdSJérôme Duval 		return status;
27555a5a6bdSJérôme Duval }
276