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