xref: /haiku/src/apps/expander/ExpanderThread.cpp (revision 750111c96617f378be7fbcdf0192028f96bf757e)
10f11a95aSJérôme Duval /*
2*750111c9SJérôme Duval  * Copyright 2004-2010, 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),
24*750111c9SJérôme Duval 	fExpanderError(NULL)
2555a5a6bdSJérôme Duval {
2655a5a6bdSJérôme Duval 	SetDataStore(new BMessage(* refs_message));  // leak?
2755a5a6bdSJérôme Duval 	// prevents bug with B_SIMPLE_DATA
2855a5a6bdSJérôme Duval 	// (drag&drop messages)
2955a5a6bdSJérôme Duval }
3055a5a6bdSJérôme Duval 
31ebb64aceSPhilippe Saint-Pierre 
3255a5a6bdSJérôme Duval ExpanderThread::~ExpanderThread()
3355a5a6bdSJérôme Duval {
3455a5a6bdSJérôme Duval 	delete fWindowMessenger;
3555a5a6bdSJérôme Duval }
3655a5a6bdSJérôme Duval 
37ebb64aceSPhilippe Saint-Pierre 
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 
76520b37b8SFrançois Revol 	// lower the command priority since it is a background task.
77520b37b8SFrançois Revol 	set_thread_priority(fThreadId, B_LOW_PRIORITY);
78520b37b8SFrançois Revol 
7955a5a6bdSJérôme Duval 	resume_thread(fThreadId);
8055a5a6bdSJérôme Duval 
81*750111c9SJérôme Duval 	int flags = fcntl(fStdOut, F_GETFL, 0);
82*750111c9SJérôme Duval 	flags |= O_NONBLOCK;
83*750111c9SJérôme Duval 	fcntl(fStdOut, F_SETFL, flags);
84*750111c9SJérôme Duval 	flags = fcntl(fStdErr, F_GETFL, 0);
85*750111c9SJérôme Duval 	flags |= O_NONBLOCK;
86*750111c9SJérôme Duval 	fcntl(fStdErr, F_SETFL, flags);
87*750111c9SJérôme Duval 
8855a5a6bdSJérôme Duval 	fExpanderOutput = fdopen(fStdOut, "r");
89*750111c9SJérôme Duval 	fExpanderError = fdopen(fStdErr, "r");
9055a5a6bdSJérôme Duval 
9155a5a6bdSJérôme Duval 	return B_OK;
9255a5a6bdSJérôme Duval }
9355a5a6bdSJérôme Duval 
94ebb64aceSPhilippe Saint-Pierre 
9555a5a6bdSJérôme Duval status_t
9655a5a6bdSJérôme Duval ExpanderThread::ExecuteUnit(void)
9755a5a6bdSJérôme Duval {
98*750111c9SJérôme Duval 	// read output and error from command
9955a5a6bdSJérôme Duval 	// send it to window
10055a5a6bdSJérôme Duval 
10155a5a6bdSJérôme Duval 	BMessage message('outp');
102*750111c9SJérôme Duval 	bool outputAdded = false;
103*750111c9SJérôme Duval 	for (int32 i = 0; i < 50; i++) {
104*750111c9SJérôme Duval 		char *output_string = fgets(fExpanderOutputBuffer , LINE_MAX, fExpanderOutput);
10555a5a6bdSJérôme Duval 		if (!output_string)
10655a5a6bdSJérôme Duval 			break;
10755a5a6bdSJérôme Duval 		message.AddString("output", output_string);
108*750111c9SJérôme Duval 		outputAdded = true;
10955a5a6bdSJérôme Duval 	}
110*750111c9SJérôme Duval 	if (outputAdded)
11155a5a6bdSJérôme Duval 		fWindowMessenger->SendMessage(&message);
112*750111c9SJérôme Duval 	if (feof(fExpanderOutput))
113*750111c9SJérôme Duval 		return EOF;
114*750111c9SJérôme Duval 
115*750111c9SJérôme Duval 	char *error_string = fgets(fExpanderOutputBuffer , LINE_MAX, fExpanderError);
116*750111c9SJérôme Duval 	if (error_string != NULL
117*750111c9SJérôme Duval 		&& strcmp(error_string, "\n")) {
118*750111c9SJérôme Duval 		BMessage message('errp');
119*750111c9SJérôme Duval 		message.AddString("error", error_string);
120*750111c9SJérôme Duval 		fWindowMessenger->SendMessage(&message);
121*750111c9SJérôme Duval 	}
122*750111c9SJérôme Duval 
123*750111c9SJérôme Duval 	// streams are non blocking, sleep every 100ms
124*750111c9SJérôme Duval 	snooze(100000);
12555a5a6bdSJérôme Duval 
12655a5a6bdSJérôme Duval 	return B_OK;
12755a5a6bdSJérôme Duval }
12855a5a6bdSJérôme Duval 
129ebb64aceSPhilippe Saint-Pierre 
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 
140ebb64aceSPhilippe Saint-Pierre 
14155a5a6bdSJérôme Duval void
14255a5a6bdSJérôme Duval ExpanderThread::ThreadStartupFailed(status_t status)
14355a5a6bdSJérôme Duval {
14455a5a6bdSJérôme Duval 	fprintf(stderr, "ExpanderThread::ThreadStartupFailed() : %s\n", strerror(status));
14555a5a6bdSJérôme Duval 
14655a5a6bdSJérôme Duval 	Quit();
14755a5a6bdSJérôme Duval }
14855a5a6bdSJérôme Duval 
149ebb64aceSPhilippe Saint-Pierre 
15055a5a6bdSJérôme Duval void
15155a5a6bdSJérôme Duval ExpanderThread::ExecuteUnitFailed(status_t status)
15255a5a6bdSJérôme Duval {
15355a5a6bdSJérôme Duval 	if (status == EOF) {
15455a5a6bdSJérôme Duval 		// thread has finished, been quit or killed, we don't know
15555a5a6bdSJérôme Duval 		fWindowMessenger->SendMessage(new BMessage('exit'));
15655a5a6bdSJérôme Duval 	} else {
15755a5a6bdSJérôme Duval 		// explicit error - communicate error to Window
15855a5a6bdSJérôme Duval 		fWindowMessenger->SendMessage(new BMessage('exrr'));
15955a5a6bdSJérôme Duval 	}
16055a5a6bdSJérôme Duval 
16155a5a6bdSJérôme Duval 	Quit();
16255a5a6bdSJérôme Duval }
16355a5a6bdSJérôme Duval 
164ebb64aceSPhilippe Saint-Pierre 
16555a5a6bdSJérôme Duval void
16655a5a6bdSJérôme Duval ExpanderThread::ThreadShutdownFailed(status_t status)
16755a5a6bdSJérôme Duval {
16855a5a6bdSJérôme Duval 	fprintf(stderr, "ExpanderThread::ThreadShutdownFailed() %s\n", strerror(status));
16955a5a6bdSJérôme Duval }
17055a5a6bdSJérôme Duval 
17155a5a6bdSJérôme Duval 
17255a5a6bdSJérôme Duval status_t
17355a5a6bdSJérôme Duval ExpanderThread::ProcessRefs(BMessage *msg)
17455a5a6bdSJérôme Duval {
17555a5a6bdSJérôme Duval 	return B_OK;
17655a5a6bdSJérôme Duval }
17755a5a6bdSJérôme Duval 
178ebb64aceSPhilippe Saint-Pierre 
17955a5a6bdSJérôme Duval thread_id
18055a5a6bdSJérôme Duval ExpanderThread::PipeCommand(int argc, const char **argv, int &in, int &out, int &err, const char **envp)
18155a5a6bdSJérôme Duval {
18255a5a6bdSJérôme Duval 	// This function written by Peter Folk <pfolk@uni.uiuc.edu>
18355a5a6bdSJérôme Duval 	// and published in the BeDevTalk FAQ
18455a5a6bdSJérôme Duval 	// http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
18555a5a6bdSJérôme Duval 
18655a5a6bdSJérôme Duval 	// Save current FDs
18755a5a6bdSJérôme Duval 	int old_in  =  dup(0);
18855a5a6bdSJérôme Duval 	int old_out  =  dup(1);
18955a5a6bdSJérôme Duval 	int old_err  =  dup(2);
19055a5a6bdSJérôme Duval 
19155a5a6bdSJérôme Duval 	int filedes[2];
19255a5a6bdSJérôme Duval 
19355a5a6bdSJérôme Duval 	/* Create new pipe FDs as stdin, stdout, stderr */
19455a5a6bdSJérôme Duval 	pipe(filedes);  dup2(filedes[0], 0); close(filedes[0]);
19555a5a6bdSJérôme Duval 	in = filedes[1];  // Write to in, appears on cmd's stdin
19655a5a6bdSJérôme Duval 	pipe(filedes);  dup2(filedes[1], 1); close(filedes[1]);
19755a5a6bdSJérôme Duval 	out = filedes[0]; // Read from out, taken from cmd's stdout
19855a5a6bdSJérôme Duval 	pipe(filedes);  dup2(filedes[1], 2); close(filedes[1]);
19955a5a6bdSJérôme Duval 	err = filedes[0]; // Read from err, taken from cmd's stderr
20055a5a6bdSJérôme Duval 
20155a5a6bdSJérôme Duval 	// "load" command.
20255a5a6bdSJérôme Duval 	thread_id ret  =  load_image(argc, argv, envp);
20355a5a6bdSJérôme Duval 
20455a5a6bdSJérôme Duval 	if (ret < B_OK)
20555a5a6bdSJérôme Duval 		return ret;
20655a5a6bdSJérôme Duval 
20755a5a6bdSJérôme Duval 	// thread ret is now suspended.
20855a5a6bdSJérôme Duval 
20955a5a6bdSJérôme Duval 	setpgid(ret, ret);
21055a5a6bdSJérôme Duval 
21155a5a6bdSJérôme Duval 	// Restore old FDs
21255a5a6bdSJérôme Duval 	close(0); dup(old_in); close(old_in);
21355a5a6bdSJérôme Duval 	close(1); dup(old_out); close(old_out);
21455a5a6bdSJérôme Duval 	close(2); dup(old_err); close(old_err);
21555a5a6bdSJérôme Duval 
21655a5a6bdSJérôme Duval 	/* Theoretically I should do loads of error checking, but
21755a5a6bdSJérôme Duval 	   the calls aren't very likely to fail, and that would
21855a5a6bdSJérôme Duval 	   muddy up the example quite a bit.  YMMV. */
21955a5a6bdSJérôme Duval 
22055a5a6bdSJérôme Duval 	return ret;
22155a5a6bdSJérôme Duval }
22255a5a6bdSJérôme Duval 
22355a5a6bdSJérôme Duval 
22455a5a6bdSJérôme Duval status_t
22555a5a6bdSJérôme Duval ExpanderThread::SuspendExternalExpander()
22655a5a6bdSJérôme Duval {
22755a5a6bdSJérôme Duval 	thread_info thread_info;
22869d0d149SStefano Ceccherini 	status_t status = get_thread_info(fThreadId, &thread_info);
22955a5a6bdSJérôme Duval 
23055a5a6bdSJérôme Duval 	if (status == B_OK)
23155a5a6bdSJérôme Duval 		return send_signal(-fThreadId, SIGSTOP);
23255a5a6bdSJérôme Duval 	else
23355a5a6bdSJérôme Duval 		return status;
23455a5a6bdSJérôme Duval }
23555a5a6bdSJérôme Duval 
236ebb64aceSPhilippe Saint-Pierre 
23755a5a6bdSJérôme Duval status_t
23855a5a6bdSJérôme Duval ExpanderThread::ResumeExternalExpander()
23955a5a6bdSJérôme Duval {
24055a5a6bdSJérôme Duval 	thread_info thread_info;
24169d0d149SStefano Ceccherini 	status_t status = get_thread_info(fThreadId, &thread_info);
24255a5a6bdSJérôme Duval 
24355a5a6bdSJérôme Duval 	if (status == B_OK)
24455a5a6bdSJérôme Duval 		return send_signal(-fThreadId, SIGCONT);
24555a5a6bdSJérôme Duval 	else
24655a5a6bdSJérôme Duval 		return status;
24755a5a6bdSJérôme Duval }
24855a5a6bdSJérôme Duval 
249ebb64aceSPhilippe Saint-Pierre 
25055a5a6bdSJérôme Duval status_t
25155a5a6bdSJérôme Duval ExpanderThread::InterruptExternalExpander()
25255a5a6bdSJérôme Duval {
25355a5a6bdSJérôme Duval 	thread_info thread_info;
25469d0d149SStefano Ceccherini 	status_t status = get_thread_info(fThreadId, &thread_info);
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 
263ebb64aceSPhilippe Saint-Pierre 
26455a5a6bdSJérôme Duval status_t
26555a5a6bdSJérôme Duval ExpanderThread::WaitOnExternalExpander()
26655a5a6bdSJérôme Duval {
26755a5a6bdSJérôme Duval 	thread_info thread_info;
26869d0d149SStefano Ceccherini 	status_t status = get_thread_info(fThreadId, &thread_info);
26955a5a6bdSJérôme Duval 
27055a5a6bdSJérôme Duval 	if (status == B_OK)
27155a5a6bdSJérôme Duval 		return wait_for_thread(fThreadId, &status);
27255a5a6bdSJérôme Duval 	else
27355a5a6bdSJérôme Duval 		return status;
27455a5a6bdSJérôme Duval }
275