xref: /haiku/src/apps/expander/ExpanderThread.cpp (revision 69d0d1491437abd3245abfd8523dadb6934c5d8f)
10f11a95aSJérôme Duval /*
20f11a95aSJérôme Duval  * Copyright 2004-2006, Jérôme Duval. All rights reserved.
30f11a95aSJérôme Duval  * Distributed under the terms of the MIT License.
40f11a95aSJérôme Duval  * Original code from ZipOMatic by jonas.sundstrom@kirilla.com
50f11a95aSJérôme Duval  */
60f11a95aSJérôme Duval #include <Messenger.h>
755a5a6bdSJérôme Duval #include <Path.h>
8338b8dc3SIngo Weinhold #include "ExpanderThread.h"
955a5a6bdSJérôme Duval #include <image.h>
1055a5a6bdSJérôme Duval #include <signal.h>
1155a5a6bdSJérôme Duval #include <unistd.h>
1255a5a6bdSJérôme Duval #include <errno.h>
1355a5a6bdSJérôme Duval 
1455a5a6bdSJérôme Duval const char * ExpanderThreadName	=	"ExpanderThread";
1555a5a6bdSJérôme Duval 
1655a5a6bdSJérôme Duval ExpanderThread::ExpanderThread (BMessage * refs_message, BMessenger * messenger)
1755a5a6bdSJérôme Duval :	GenericThread(ExpanderThreadName, B_NORMAL_PRIORITY, refs_message),
1855a5a6bdSJérôme Duval 	fWindowMessenger(messenger),
1955a5a6bdSJérôme Duval 	fThreadId(-1),
2055a5a6bdSJérôme Duval 	fStdIn(-1),
2155a5a6bdSJérôme Duval 	fStdOut(-1),
2255a5a6bdSJérôme Duval 	fStdErr(-1),
2355a5a6bdSJérôme Duval 	fExpanderOutput(NULL),
2455a5a6bdSJérôme Duval 	fExpanderOutputString(),
2555a5a6bdSJérôme Duval 	fExpanderOutputBuffer(new char [4096])
2655a5a6bdSJérôme Duval {
2755a5a6bdSJérôme Duval 	SetDataStore(new BMessage (* refs_message)); // leak?
2855a5a6bdSJérôme Duval 												  // prevents bug with B_SIMPLE_DATA
2955a5a6bdSJérôme Duval 												  // (drag&drop messages)
3055a5a6bdSJérôme Duval }
3155a5a6bdSJérôme Duval 
3255a5a6bdSJérôme Duval ExpanderThread::~ExpanderThread()
3355a5a6bdSJérôme Duval {
3455a5a6bdSJérôme Duval 	delete fWindowMessenger;
3555a5a6bdSJérôme Duval 	delete [] fExpanderOutputBuffer;
3655a5a6bdSJérôme Duval }
3755a5a6bdSJérôme Duval 
3855a5a6bdSJérôme Duval status_t
3955a5a6bdSJérôme Duval ExpanderThread::ThreadStartup()
4055a5a6bdSJérôme Duval {
4155a5a6bdSJérôme Duval 	status_t	status	=	B_OK;
4255a5a6bdSJérôme Duval 	entry_ref	srcRef, destRef;
4355a5a6bdSJérôme Duval 	BString 	cmd;
4455a5a6bdSJérôme Duval 
4555a5a6bdSJérôme Duval 	if ((status = GetDataStore()->FindRef("srcRef", &srcRef))!=B_OK)
4655a5a6bdSJérôme Duval 		return status;
4791d1efffSJérôme Duval 	if ((status = GetDataStore()->FindRef("destRef", &destRef))==B_OK) {
4891d1efffSJérôme Duval 		BPath path(&destRef);
4991d1efffSJérôme Duval 		chdir(path.Path());
5091d1efffSJérôme Duval 	}
5155a5a6bdSJérôme Duval 	if ((status = GetDataStore()->FindString("cmd", &cmd))!=B_OK)
5255a5a6bdSJérôme Duval 		return status;
5355a5a6bdSJérôme Duval 
5455a5a6bdSJérôme Duval 	BPath path(&srcRef);
5555a5a6bdSJérôme Duval 	BString pathString(path.Path());
5655a5a6bdSJérôme Duval 	pathString.CharacterEscape("\"$`", '\\');
5755a5a6bdSJérôme Duval 	pathString.Prepend("\"");
5855a5a6bdSJérôme Duval 	pathString.Append("\"");
5955a5a6bdSJérôme Duval 	cmd.ReplaceAll("%s", pathString.String());
6055a5a6bdSJérôme Duval 
6155a5a6bdSJérôme Duval 	int32 argc = 3;
6255a5a6bdSJérôme Duval 	const char ** argv = new const char * [argc + 1];
6355a5a6bdSJérôme Duval 
6455a5a6bdSJérôme Duval 	argv[0]	=	strdup("/bin/sh");
6555a5a6bdSJérôme Duval 	argv[1]	=	strdup("-c");
6655a5a6bdSJérôme Duval 	argv[2]	=	strdup(cmd.String());
6755a5a6bdSJérôme Duval 	argv[argc] = NULL;
6855a5a6bdSJérôme Duval 
6955a5a6bdSJérôme Duval 	fThreadId = PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr);
7055a5a6bdSJérôme Duval 
7155a5a6bdSJérôme Duval 	delete [] argv;
7255a5a6bdSJérôme Duval 
7355a5a6bdSJérôme Duval 	if (fThreadId < 0)
7455a5a6bdSJérôme Duval 		return fThreadId;
7555a5a6bdSJérôme Duval 
7655a5a6bdSJérôme Duval     	resume_thread(fThreadId);
7755a5a6bdSJérôme Duval 
7855a5a6bdSJérôme Duval    	fExpanderOutput = fdopen(fStdOut, "r");
7955a5a6bdSJérôme Duval 
8055a5a6bdSJérôme Duval 	return B_OK;
8155a5a6bdSJérôme Duval }
8255a5a6bdSJérôme Duval 
8355a5a6bdSJérôme Duval status_t
8455a5a6bdSJérôme Duval ExpanderThread::ExecuteUnit (void)
8555a5a6bdSJérôme Duval {
8655a5a6bdSJérôme Duval 	// read output from command
8755a5a6bdSJérôme Duval 	// send it to window
8855a5a6bdSJérôme Duval 
89*69d0d149SStefano Ceccherini 	char *output_string = fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput);
9055a5a6bdSJérôme Duval 
9155a5a6bdSJérôme Duval 	if (output_string == NULL)
9255a5a6bdSJérôme Duval 		return EOF;
9355a5a6bdSJérôme Duval 
9455a5a6bdSJérôme Duval 	BMessage message('outp');
9555a5a6bdSJérôme Duval 	message.AddString("output", output_string);
9655a5a6bdSJérôme Duval 	for (int32 i=0; i<5; i++) {
9755a5a6bdSJérôme Duval 		output_string = fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput);
9855a5a6bdSJérôme Duval 		if(!output_string)
9955a5a6bdSJérôme Duval 			break;
10055a5a6bdSJérôme Duval 		message.AddString("output", output_string);
10155a5a6bdSJérôme Duval 	}
10255a5a6bdSJérôme Duval 	fWindowMessenger->SendMessage(&message);
10355a5a6bdSJérôme Duval 
10455a5a6bdSJérôme Duval 	return B_OK;
10555a5a6bdSJérôme Duval }
10655a5a6bdSJérôme Duval 
10755a5a6bdSJérôme Duval status_t
10855a5a6bdSJérôme Duval ExpanderThread::ThreadShutdown(void)
10955a5a6bdSJérôme Duval {
11055a5a6bdSJérôme Duval 	close(fStdIn);
11155a5a6bdSJérôme Duval     	close(fStdOut);
11255a5a6bdSJérôme Duval    	close(fStdErr);
11355a5a6bdSJérôme Duval 
11455a5a6bdSJérôme Duval 	return B_OK;
11555a5a6bdSJérôme Duval }
11655a5a6bdSJérôme Duval 
11755a5a6bdSJérôme Duval void
11855a5a6bdSJérôme Duval ExpanderThread::ThreadStartupFailed(status_t status)
11955a5a6bdSJérôme Duval {
12055a5a6bdSJérôme Duval 	fprintf(stderr, "ExpanderThread::ThreadStartupFailed() : %s\n", strerror(status));
12155a5a6bdSJérôme Duval 
12255a5a6bdSJérôme Duval 	Quit();
12355a5a6bdSJérôme Duval }
12455a5a6bdSJérôme Duval 
12555a5a6bdSJérôme Duval void
12655a5a6bdSJérôme Duval ExpanderThread::ExecuteUnitFailed(status_t status)
12755a5a6bdSJérôme Duval {
12855a5a6bdSJérôme Duval 	if (status == EOF) {
12955a5a6bdSJérôme Duval 		// thread has finished, been quit or killed, we don't know
13055a5a6bdSJérôme Duval 		fWindowMessenger->SendMessage(new BMessage('exit'));
13155a5a6bdSJérôme Duval 	} else {
13255a5a6bdSJérôme Duval 		// explicit error - communicate error to Window
13355a5a6bdSJérôme Duval 		fWindowMessenger->SendMessage(new BMessage('exrr'));
13455a5a6bdSJérôme Duval 	}
13555a5a6bdSJérôme Duval 
13655a5a6bdSJérôme Duval 	Quit();
13755a5a6bdSJérôme Duval }
13855a5a6bdSJérôme Duval 
13955a5a6bdSJérôme Duval void
14055a5a6bdSJérôme Duval ExpanderThread::ThreadShutdownFailed(status_t status)
14155a5a6bdSJérôme Duval {
14255a5a6bdSJérôme Duval 	fprintf(stderr, "ExpanderThread::ThreadShutdownFailed() %s\n", strerror(status));
14355a5a6bdSJérôme Duval }
14455a5a6bdSJérôme Duval 
14555a5a6bdSJérôme Duval 
14655a5a6bdSJérôme Duval status_t
14755a5a6bdSJérôme Duval ExpanderThread::ProcessRefs(BMessage *msg)
14855a5a6bdSJérôme Duval {
14955a5a6bdSJérôme Duval 	return B_OK;
15055a5a6bdSJérôme Duval }
15155a5a6bdSJérôme Duval 
15255a5a6bdSJérôme Duval thread_id
15355a5a6bdSJérôme Duval ExpanderThread::PipeCommand(int argc, const char **argv, int &in, int &out, int &err, const char **envp)
15455a5a6bdSJérôme Duval {
15555a5a6bdSJérôme Duval 	// This function written by Peter Folk <pfolk@uni.uiuc.edu>
15655a5a6bdSJérôme Duval 	// and published in the BeDevTalk FAQ
15755a5a6bdSJérôme Duval 	// http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
15855a5a6bdSJérôme Duval 
15955a5a6bdSJérôme Duval     // Save current FDs
16055a5a6bdSJérôme Duval     int old_in  =  dup(0);
16155a5a6bdSJérôme Duval     int old_out  =  dup(1);
16255a5a6bdSJérôme Duval     int old_err  =  dup(2);
16355a5a6bdSJérôme Duval 
16455a5a6bdSJérôme Duval     int filedes[2];
16555a5a6bdSJérôme Duval 
16655a5a6bdSJérôme Duval     /* Create new pipe FDs as stdin, stdout, stderr */
16755a5a6bdSJérôme Duval     pipe(filedes);  dup2(filedes[0],0); close(filedes[0]);
16855a5a6bdSJérôme Duval     in=filedes[1];  // Write to in, appears on cmd's stdin
16955a5a6bdSJérôme Duval     pipe(filedes);  dup2(filedes[1],1); close(filedes[1]);
17055a5a6bdSJérôme Duval     out=filedes[0]; // Read from out, taken from cmd's stdout
17155a5a6bdSJérôme Duval     pipe(filedes);  dup2(filedes[1],2); close(filedes[1]);
17255a5a6bdSJérôme Duval     err=filedes[0]; // Read from err, taken from cmd's stderr
17355a5a6bdSJérôme Duval 
17455a5a6bdSJérôme Duval     // "load" command.
17555a5a6bdSJérôme Duval     thread_id ret  =  load_image(argc, argv, envp);
17655a5a6bdSJérôme Duval 
17755a5a6bdSJérôme Duval     if (ret < B_OK)
17855a5a6bdSJérôme Duval 	return ret;
17955a5a6bdSJérôme Duval 
18055a5a6bdSJérôme Duval     // thread ret is now suspended.
18155a5a6bdSJérôme Duval 
18255a5a6bdSJérôme Duval 	setpgid(ret, ret);
18355a5a6bdSJérôme Duval 
18455a5a6bdSJérôme Duval     // Restore old FDs
18555a5a6bdSJérôme Duval     close(0); dup(old_in); close(old_in);
18655a5a6bdSJérôme Duval     close(1); dup(old_out); close(old_out);
18755a5a6bdSJérôme Duval     close(2); dup(old_err); close(old_err);
18855a5a6bdSJérôme Duval 
18955a5a6bdSJérôme Duval     /* Theoretically I should do loads of error checking, but
19055a5a6bdSJérôme Duval        the calls aren't very likely to fail, and that would
19155a5a6bdSJérôme Duval        muddy up the example quite a bit.  YMMV. */
19255a5a6bdSJérôme Duval 
19355a5a6bdSJérôme Duval     return ret;
19455a5a6bdSJérôme Duval }
19555a5a6bdSJérôme Duval 
19655a5a6bdSJérôme Duval 
19755a5a6bdSJérôme Duval status_t
19855a5a6bdSJérôme Duval ExpanderThread::SuspendExternalExpander()
19955a5a6bdSJérôme Duval {
20055a5a6bdSJérôme Duval 	thread_info thread_info;
201*69d0d149SStefano Ceccherini 	status_t status = get_thread_info(fThreadId, &thread_info);
20255a5a6bdSJérôme Duval 
20355a5a6bdSJérôme Duval 	if (status == B_OK)
20455a5a6bdSJérôme Duval 		return send_signal(-fThreadId, SIGSTOP);
20555a5a6bdSJérôme Duval 	else
20655a5a6bdSJérôme Duval 		return status;
20755a5a6bdSJérôme Duval }
20855a5a6bdSJérôme Duval 
20955a5a6bdSJérôme Duval status_t
21055a5a6bdSJérôme Duval ExpanderThread::ResumeExternalExpander()
21155a5a6bdSJérôme Duval {
21255a5a6bdSJérôme Duval 	thread_info thread_info;
213*69d0d149SStefano Ceccherini 	status_t status = get_thread_info(fThreadId, &thread_info);
21455a5a6bdSJérôme Duval 
21555a5a6bdSJérôme Duval 	if (status == B_OK)
21655a5a6bdSJérôme Duval 		return send_signal(-fThreadId, SIGCONT);
21755a5a6bdSJérôme Duval 	else
21855a5a6bdSJérôme Duval 		return status;
21955a5a6bdSJérôme Duval }
22055a5a6bdSJérôme Duval 
22155a5a6bdSJérôme Duval status_t
22255a5a6bdSJérôme Duval ExpanderThread::InterruptExternalExpander()
22355a5a6bdSJérôme Duval {
22455a5a6bdSJérôme Duval 	thread_info thread_info;
225*69d0d149SStefano Ceccherini 	status_t status = get_thread_info (fThreadId, &thread_info);
22655a5a6bdSJérôme Duval 
22755a5a6bdSJérôme Duval 	if (status == B_OK) {
22855a5a6bdSJérôme Duval 		status = send_signal(-fThreadId, SIGINT);
22955a5a6bdSJérôme Duval 		WaitOnExternalExpander();
23055a5a6bdSJérôme Duval 	}
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::WaitOnExternalExpander()
23655a5a6bdSJérôme Duval {
23755a5a6bdSJérôme Duval 	thread_info thread_info;
238*69d0d149SStefano Ceccherini 	status_t status = get_thread_info(fThreadId, &thread_info);
23955a5a6bdSJérôme Duval 
24055a5a6bdSJérôme Duval 	if (status == B_OK)
24155a5a6bdSJérôme Duval 		return wait_for_thread(fThreadId, &status);
24255a5a6bdSJérôme Duval 	else
24355a5a6bdSJérôme Duval 		return status;
24455a5a6bdSJérôme Duval }
245