xref: /haiku/src/apps/expander/ExpanderThread.cpp (revision cbf3fb528f6a7742bfed45a1e84feb0b47a561fb)
10f11a95aSJérôme Duval /*
2750111c9SJé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  */
6338b8dc3SIngo Weinhold #include "ExpanderThread.h"
7e8c006b9SJérôme Duval 
8e326cef6SJohn Scipione 
9e8c006b9SJérôme Duval #include <errno.h>
1055a5a6bdSJérôme Duval #include <image.h>
1155a5a6bdSJérôme Duval #include <signal.h>
12e8c006b9SJérôme Duval #include <termios.h>
1355a5a6bdSJérôme Duval #include <unistd.h>
14e8c006b9SJérôme Duval 
15e8c006b9SJérôme Duval #include <Messenger.h>
16e8c006b9SJérôme Duval #include <Path.h>
17e8c006b9SJérôme Duval 
1855a5a6bdSJérôme Duval 
1955a5a6bdSJérôme Duval const char* ExpanderThreadName = "ExpanderThread";
2055a5a6bdSJérôme Duval 
21e8c006b9SJérôme Duval 
ExpanderThread(BMessage * refs_message,BMessenger * messenger)2255a5a6bdSJérôme Duval ExpanderThread::ExpanderThread(BMessage* refs_message, BMessenger* messenger)
23e326cef6SJohn Scipione 	:
24e326cef6SJohn Scipione 	GenericThread(ExpanderThreadName, B_NORMAL_PRIORITY, refs_message),
2555a5a6bdSJérôme Duval 	fWindowMessenger(messenger),
2655a5a6bdSJérôme Duval 	fThreadId(-1),
2755a5a6bdSJérôme Duval 	fStdIn(-1),
2855a5a6bdSJérôme Duval 	fStdOut(-1),
2955a5a6bdSJérôme Duval 	fStdErr(-1),
3055a5a6bdSJérôme Duval 	fExpanderOutput(NULL),
31750111c9SJérôme Duval 	fExpanderError(NULL)
3255a5a6bdSJérôme Duval {
33e326cef6SJohn Scipione 	SetDataStore(new BMessage(*refs_message));
34e326cef6SJohn Scipione 		// leak?
3555a5a6bdSJérôme Duval 	// prevents bug with B_SIMPLE_DATA
3655a5a6bdSJérôme Duval 	// (drag&drop messages)
3755a5a6bdSJérôme Duval }
3855a5a6bdSJérôme Duval 
39ebb64aceSPhilippe Saint-Pierre 
~ExpanderThread()4055a5a6bdSJérôme Duval ExpanderThread::~ExpanderThread()
4155a5a6bdSJérôme Duval {
4255a5a6bdSJérôme Duval 	delete fWindowMessenger;
4355a5a6bdSJérôme Duval }
4455a5a6bdSJérôme Duval 
45ebb64aceSPhilippe Saint-Pierre 
4655a5a6bdSJérôme Duval status_t
ThreadStartup()4755a5a6bdSJérôme Duval ExpanderThread::ThreadStartup()
4855a5a6bdSJérôme Duval {
4955a5a6bdSJérôme Duval 	status_t status = B_OK;
50e326cef6SJohn Scipione 	entry_ref srcRef;
51e326cef6SJohn Scipione 	entry_ref destRef;
5255a5a6bdSJérôme Duval 	BString cmd;
5355a5a6bdSJérôme Duval 
5455a5a6bdSJérôme Duval 	if ((status = GetDataStore()->FindRef("srcRef", &srcRef)) != B_OK)
5555a5a6bdSJérôme Duval 		return status;
56e326cef6SJohn Scipione 
57b9e10b07SMurai Takashi 	if (GetDataStore()->FindRef("destRef", &destRef) == B_OK) {
5891d1efffSJérôme Duval 		BPath path(&destRef);
5991d1efffSJérôme Duval 		chdir(path.Path());
6091d1efffSJérôme Duval 	}
61e326cef6SJohn Scipione 
6255a5a6bdSJérôme Duval 	if ((status = GetDataStore()->FindString("cmd", &cmd)) != B_OK)
6355a5a6bdSJérôme Duval 		return status;
6455a5a6bdSJérôme Duval 
6555a5a6bdSJérôme Duval 	BPath path(&srcRef);
6655a5a6bdSJérôme Duval 	BString pathString(path.Path());
677291c0a8SAdrien Destugues 	pathString.CharacterEscape("\\\"$`", '\\');
6855a5a6bdSJérôme Duval 	pathString.Prepend("\"");
6955a5a6bdSJérôme Duval 	pathString.Append("\"");
7055a5a6bdSJérôme Duval 	cmd.ReplaceAll("%s", pathString.String());
7155a5a6bdSJérôme Duval 
7255a5a6bdSJérôme Duval 	int32 argc = 3;
7355a5a6bdSJérôme Duval 	const char** argv = new const char * [argc + 1];
7455a5a6bdSJérôme Duval 
7555a5a6bdSJérôme Duval 	argv[0] = strdup("/bin/sh");
7655a5a6bdSJérôme Duval 	argv[1] = strdup("-c");
7755a5a6bdSJérôme Duval 	argv[2] = strdup(cmd.String());
7855a5a6bdSJérôme Duval 	argv[argc] = NULL;
7955a5a6bdSJérôme Duval 
8055a5a6bdSJérôme Duval 	fThreadId = PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr);
8155a5a6bdSJérôme Duval 
8255a5a6bdSJérôme Duval 	delete [] argv;
8355a5a6bdSJérôme Duval 
8455a5a6bdSJérôme Duval 	if (fThreadId < 0)
8555a5a6bdSJérôme Duval 		return fThreadId;
8655a5a6bdSJérôme Duval 
87520b37b8SFrançois Revol 	// lower the command priority since it is a background task.
88520b37b8SFrançois Revol 	set_thread_priority(fThreadId, B_LOW_PRIORITY);
89520b37b8SFrançois Revol 
9055a5a6bdSJérôme Duval 	resume_thread(fThreadId);
9155a5a6bdSJérôme Duval 
92750111c9SJérôme Duval 	int flags = fcntl(fStdOut, F_GETFL, 0);
93750111c9SJérôme Duval 	flags |= O_NONBLOCK;
94750111c9SJérôme Duval 	fcntl(fStdOut, F_SETFL, flags);
95750111c9SJérôme Duval 	flags = fcntl(fStdErr, F_GETFL, 0);
96750111c9SJérôme Duval 	flags |= O_NONBLOCK;
97750111c9SJérôme Duval 	fcntl(fStdErr, F_SETFL, flags);
98750111c9SJérôme Duval 
9955a5a6bdSJérôme Duval 	fExpanderOutput = fdopen(fStdOut, "r");
100750111c9SJérôme Duval 	fExpanderError = fdopen(fStdErr, "r");
10155a5a6bdSJérôme Duval 
10255a5a6bdSJérôme Duval 	return B_OK;
10355a5a6bdSJérôme Duval }
10455a5a6bdSJérôme Duval 
105ebb64aceSPhilippe Saint-Pierre 
10655a5a6bdSJérôme Duval status_t
ExecuteUnit(void)10755a5a6bdSJérôme Duval ExpanderThread::ExecuteUnit(void)
10855a5a6bdSJérôme Duval {
109750111c9SJérôme Duval 	// read output and error from command
11055a5a6bdSJérôme Duval 	// send it to window
11155a5a6bdSJérôme Duval 
11255a5a6bdSJérôme Duval 	BMessage message('outp');
113750111c9SJérôme Duval 	bool outputAdded = false;
114750111c9SJérôme Duval 	for (int32 i = 0; i < 50; i++) {
115e326cef6SJohn Scipione 		char* output_string = fgets(fExpanderOutputBuffer , LINE_MAX,
116e326cef6SJohn Scipione 			fExpanderOutput);
117e326cef6SJohn Scipione 		if (output_string == NULL)
11855a5a6bdSJérôme Duval 			break;
119e326cef6SJohn Scipione 
12055a5a6bdSJérôme Duval 		message.AddString("output", output_string);
121750111c9SJérôme Duval 		outputAdded = true;
12255a5a6bdSJérôme Duval 	}
123750111c9SJérôme Duval 	if (outputAdded)
12455a5a6bdSJérôme Duval 		fWindowMessenger->SendMessage(&message);
125e326cef6SJohn Scipione 
126750111c9SJérôme Duval 	if (feof(fExpanderOutput))
127750111c9SJérôme Duval 		return EOF;
128750111c9SJérôme Duval 
129e326cef6SJohn Scipione 	char* error_string = fgets(fExpanderOutputBuffer, LINE_MAX,
130e326cef6SJohn Scipione 		fExpanderError);
131e326cef6SJohn Scipione 	if (error_string != NULL && strcmp(error_string, "\n")) {
132750111c9SJérôme Duval 		BMessage message('errp');
133750111c9SJérôme Duval 		message.AddString("error", error_string);
134750111c9SJérôme Duval 		fWindowMessenger->SendMessage(&message);
135750111c9SJérôme Duval 	}
136750111c9SJérôme Duval 
137750111c9SJérôme Duval 	// streams are non blocking, sleep every 100ms
138750111c9SJérôme Duval 	snooze(100000);
13955a5a6bdSJérôme Duval 
14055a5a6bdSJérôme Duval 	return B_OK;
14155a5a6bdSJérôme Duval }
14255a5a6bdSJérôme Duval 
143ebb64aceSPhilippe Saint-Pierre 
144e8c006b9SJérôme Duval void
PushInput(BString text)145e8c006b9SJérôme Duval ExpanderThread::PushInput(BString text)
146e8c006b9SJérôme Duval {
147e8c006b9SJérôme Duval 	text += "\n";
148e8c006b9SJérôme Duval 	write(fStdIn, text.String(), text.Length());
149e8c006b9SJérôme Duval }
150e8c006b9SJérôme Duval 
151e8c006b9SJérôme Duval 
15255a5a6bdSJérôme Duval status_t
ThreadShutdown(void)15355a5a6bdSJérôme Duval ExpanderThread::ThreadShutdown(void)
15455a5a6bdSJérôme Duval {
15555a5a6bdSJérôme Duval 	close(fStdIn);
15655a5a6bdSJérôme Duval 	close(fStdOut);
15755a5a6bdSJérôme Duval 	close(fStdErr);
15855a5a6bdSJérôme Duval 
15955a5a6bdSJérôme Duval 	return B_OK;
16055a5a6bdSJérôme Duval }
16155a5a6bdSJérôme Duval 
162ebb64aceSPhilippe Saint-Pierre 
16355a5a6bdSJérôme Duval void
ThreadStartupFailed(status_t status)16455a5a6bdSJérôme Duval ExpanderThread::ThreadStartupFailed(status_t status)
16555a5a6bdSJérôme Duval {
166e326cef6SJohn Scipione 	fprintf(stderr, "ExpanderThread::ThreadStartupFailed() : %s\n",
167e326cef6SJohn Scipione 		strerror(status));
16855a5a6bdSJérôme Duval 
16955a5a6bdSJérôme Duval 	Quit();
17055a5a6bdSJérôme Duval }
17155a5a6bdSJérôme Duval 
172ebb64aceSPhilippe Saint-Pierre 
17355a5a6bdSJérôme Duval void
ExecuteUnitFailed(status_t status)17455a5a6bdSJérôme Duval ExpanderThread::ExecuteUnitFailed(status_t status)
17555a5a6bdSJérôme Duval {
17655a5a6bdSJérôme Duval 	if (status == EOF) {
17755a5a6bdSJérôme Duval 		// thread has finished, been quit or killed, we don't know
17855a5a6bdSJérôme Duval 		fWindowMessenger->SendMessage(new BMessage('exit'));
17955a5a6bdSJérôme Duval 	} else {
18055a5a6bdSJérôme Duval 		// explicit error - communicate error to Window
18155a5a6bdSJérôme Duval 		fWindowMessenger->SendMessage(new BMessage('exrr'));
18255a5a6bdSJérôme Duval 	}
18355a5a6bdSJérôme Duval 
18455a5a6bdSJérôme Duval 	Quit();
18555a5a6bdSJérôme Duval }
18655a5a6bdSJérôme Duval 
187ebb64aceSPhilippe Saint-Pierre 
18855a5a6bdSJérôme Duval void
ThreadShutdownFailed(status_t status)18955a5a6bdSJérôme Duval ExpanderThread::ThreadShutdownFailed(status_t status)
19055a5a6bdSJérôme Duval {
191e326cef6SJohn Scipione 	fprintf(stderr, "ExpanderThread::ThreadShutdownFailed() %s\n",
192e326cef6SJohn Scipione 		strerror(status));
19355a5a6bdSJérôme Duval }
19455a5a6bdSJérôme Duval 
19555a5a6bdSJérôme Duval 
19655a5a6bdSJérôme Duval status_t
ProcessRefs(BMessage * msg)19755a5a6bdSJérôme Duval ExpanderThread::ProcessRefs(BMessage *msg)
19855a5a6bdSJérôme Duval {
19955a5a6bdSJérôme Duval 	return B_OK;
20055a5a6bdSJérôme Duval }
20155a5a6bdSJérôme Duval 
202ebb64aceSPhilippe Saint-Pierre 
20355a5a6bdSJérôme Duval thread_id
PipeCommand(int argc,const char ** argv,int & in,int & out,int & err,const char ** envp)204e326cef6SJohn Scipione ExpanderThread::PipeCommand(int argc, const char** argv, int& in, int& out,
205e326cef6SJohn Scipione 	int& err, const char** envp)
20655a5a6bdSJérôme Duval {
20755a5a6bdSJérôme Duval 	// This function written by Peter Folk <pfolk@uni.uiuc.edu>
20855a5a6bdSJérôme Duval 	// and published in the BeDevTalk FAQ
20955a5a6bdSJérôme Duval 	// http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
21055a5a6bdSJérôme Duval 
21155a5a6bdSJérôme Duval 	// Save current FDs
21255a5a6bdSJérôme Duval 	int old_out = dup(1);
21355a5a6bdSJérôme Duval 	int old_err = dup(2);
21455a5a6bdSJérôme Duval 
21555a5a6bdSJérôme Duval 	int filedes[2];
21655a5a6bdSJérôme Duval 
217e326cef6SJohn Scipione 	// create new pipe FDs as stdout, stderr
21855a5a6bdSJérôme Duval 	pipe(filedes);  dup2(filedes[1], 1); close(filedes[1]);
21955a5a6bdSJérôme Duval 	out = filedes[0]; // Read from out, taken from cmd's stdout
22055a5a6bdSJérôme Duval 	pipe(filedes);  dup2(filedes[1], 2); close(filedes[1]);
22155a5a6bdSJérôme Duval 	err = filedes[0]; // Read from err, taken from cmd's stderr
22255a5a6bdSJérôme Duval 
223e8c006b9SJérôme Duval 	// taken from pty.cpp
224e326cef6SJohn Scipione 	// create a tty for stdin, as utilities don't generally use stdin
225e8c006b9SJérôme Duval 	int master = posix_openpt(O_RDWR);
226e8c006b9SJérôme Duval 	if (master < 0)
227e8c006b9SJérôme Duval 		return -1;
228e8c006b9SJérôme Duval 
229e8c006b9SJérôme Duval 	int slave;
230e8c006b9SJérôme Duval 	const char* ttyName;
231e8c006b9SJérôme Duval 	if (grantpt(master) != 0 || unlockpt(master) != 0
232e8c006b9SJérôme Duval 		|| (ttyName = ptsname(master)) == NULL
233e8c006b9SJérôme Duval 		|| (slave = open(ttyName, O_RDWR | O_NOCTTY)) < 0) {
234e8c006b9SJérôme Duval 		close(master);
235e8c006b9SJérôme Duval 		return -1;
236e8c006b9SJérôme Duval 	}
237e8c006b9SJérôme Duval 
238e8c006b9SJérôme Duval 	int pid = fork();
239e8c006b9SJérôme Duval 	if (pid < 0) {
240e8c006b9SJérôme Duval 		close(master);
241e8c006b9SJérôme Duval 		close(slave);
242e8c006b9SJérôme Duval 		return -1;
243e8c006b9SJérôme Duval 	}
244e326cef6SJohn Scipione 
245e8c006b9SJérôme Duval 	// child
246e8c006b9SJérôme Duval 	if (pid == 0) {
247e8c006b9SJérôme Duval 		close(master);
248e8c006b9SJérôme Duval 
249e8c006b9SJérôme Duval 		setsid();
250*cbf3fb52SMurai Takashi 		if (ioctl(slave, TIOCSCTTY, NULL) != 0) {
251*cbf3fb52SMurai Takashi 			close(slave);
252e8c006b9SJérôme Duval 			return -1;
253*cbf3fb52SMurai Takashi 		}
254e326cef6SJohn Scipione 
255e8c006b9SJérôme Duval 		dup2(slave, 0);
256e8c006b9SJérôme Duval 		close(slave);
257e8c006b9SJérôme Duval 
25855a5a6bdSJérôme Duval 		// "load" command.
259e8c006b9SJérôme Duval 		execv(argv[0], (char *const *)argv);
26055a5a6bdSJérôme Duval 
261e8c006b9SJérôme Duval 		// shouldn't return
262e8c006b9SJérôme Duval 		return -1;
263e8c006b9SJérôme Duval 	}
26455a5a6bdSJérôme Duval 
265e8c006b9SJérôme Duval 	// parent
266e8c006b9SJérôme Duval 	close (slave);
267e8c006b9SJérôme Duval 	in = master;
26855a5a6bdSJérôme Duval 
26955a5a6bdSJérôme Duval 	// Restore old FDs
27055a5a6bdSJérôme Duval 	close(1); dup(old_out); close(old_out);
27155a5a6bdSJérôme Duval 	close(2); dup(old_err); close(old_err);
27255a5a6bdSJérôme Duval 
273e326cef6SJohn Scipione 	// Theoretically I should do loads of error checking, but
274e326cef6SJohn Scipione 	// the calls aren't very likely to fail, and that would
275e326cef6SJohn Scipione 	// muddy up the example quite a bit. YMMV.
27655a5a6bdSJérôme Duval 
277e8c006b9SJérôme Duval 	return pid;
27855a5a6bdSJérôme Duval }
27955a5a6bdSJérôme Duval 
28055a5a6bdSJérôme Duval 
28155a5a6bdSJérôme Duval status_t
SuspendExternalExpander()28255a5a6bdSJérôme Duval ExpanderThread::SuspendExternalExpander()
28355a5a6bdSJérôme Duval {
284e326cef6SJohn Scipione 	thread_info info;
285e326cef6SJohn Scipione 	status_t status = get_thread_info(fThreadId, &info);
28655a5a6bdSJérôme Duval 
28755a5a6bdSJérôme Duval 	if (status == B_OK)
28855a5a6bdSJérôme Duval 		return send_signal(-fThreadId, SIGSTOP);
28955a5a6bdSJérôme Duval 	else
29055a5a6bdSJérôme Duval 		return status;
29155a5a6bdSJérôme Duval }
29255a5a6bdSJérôme Duval 
293ebb64aceSPhilippe Saint-Pierre 
29455a5a6bdSJérôme Duval status_t
ResumeExternalExpander()29555a5a6bdSJérôme Duval ExpanderThread::ResumeExternalExpander()
29655a5a6bdSJérôme Duval {
297e326cef6SJohn Scipione 	thread_info info;
298e326cef6SJohn Scipione 	status_t status = get_thread_info(fThreadId, &info);
29955a5a6bdSJérôme Duval 
30055a5a6bdSJérôme Duval 	if (status == B_OK)
30155a5a6bdSJérôme Duval 		return send_signal(-fThreadId, SIGCONT);
30255a5a6bdSJérôme Duval 	else
30355a5a6bdSJérôme Duval 		return status;
30455a5a6bdSJérôme Duval }
30555a5a6bdSJérôme Duval 
306ebb64aceSPhilippe Saint-Pierre 
30755a5a6bdSJérôme Duval status_t
InterruptExternalExpander()30855a5a6bdSJérôme Duval ExpanderThread::InterruptExternalExpander()
30955a5a6bdSJérôme Duval {
310e326cef6SJohn Scipione 	thread_info info;
311e326cef6SJohn Scipione 	status_t status = get_thread_info(fThreadId, &info);
31255a5a6bdSJérôme Duval 
31355a5a6bdSJérôme Duval 	if (status == B_OK) {
31455a5a6bdSJérôme Duval 		status = send_signal(-fThreadId, SIGINT);
31555a5a6bdSJérôme Duval 		WaitOnExternalExpander();
31655a5a6bdSJérôme Duval 	}
317e326cef6SJohn Scipione 
31855a5a6bdSJérôme Duval 	return status;
31955a5a6bdSJérôme Duval }
32055a5a6bdSJérôme Duval 
321ebb64aceSPhilippe Saint-Pierre 
32255a5a6bdSJérôme Duval status_t
WaitOnExternalExpander()32355a5a6bdSJérôme Duval ExpanderThread::WaitOnExternalExpander()
32455a5a6bdSJérôme Duval {
325e326cef6SJohn Scipione 	thread_info info;
326e326cef6SJohn Scipione 	status_t status = get_thread_info(fThreadId, &info);
32755a5a6bdSJérôme Duval 
32855a5a6bdSJérôme Duval 	if (status == B_OK)
32955a5a6bdSJérôme Duval 		return wait_for_thread(fThreadId, &status);
33055a5a6bdSJérôme Duval 	else
33155a5a6bdSJérôme Duval 		return status;
33255a5a6bdSJérôme Duval }
333