xref: /haiku/src/apps/expander/ExpanderThread.cpp (revision 1d9d47fc72028bb71b5f232a877231e59cfe2438)
1 /*
2  * Copyright 2004-2006, Jérôme Duval. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  * Original code from ZipOMatic by jonas.sundstrom@kirilla.com
5  */
6 #include <Messenger.h>
7 #include <Path.h>
8 #include "ExpanderThread.h"
9 #include <image.h>
10 #include <signal.h>
11 #include <unistd.h>
12 #include <errno.h>
13 
14 const char * ExpanderThreadName	=	"ExpanderThread";
15 
16 ExpanderThread::ExpanderThread (BMessage * refs_message, BMessenger * messenger)
17 :	GenericThread(ExpanderThreadName, B_NORMAL_PRIORITY, refs_message),
18 	fWindowMessenger(messenger),
19 	fThreadId(-1),
20 	fStdIn(-1),
21 	fStdOut(-1),
22 	fStdErr(-1),
23 	fExpanderOutput(NULL),
24 	fExpanderOutputString(),
25 	fExpanderOutputBuffer(new char [4096])
26 {
27 	SetDataStore(new BMessage (* refs_message)); // leak?
28 												  // prevents bug with B_SIMPLE_DATA
29 												  // (drag&drop messages)
30 }
31 
32 ExpanderThread::~ExpanderThread()
33 {
34 	delete fWindowMessenger;
35 	delete [] fExpanderOutputBuffer;
36 }
37 
38 status_t
39 ExpanderThread::ThreadStartup()
40 {
41 	status_t	status	=	B_OK;
42 	entry_ref	srcRef, destRef;
43 	BString 	cmd;
44 
45 	if ((status = GetDataStore()->FindRef("srcRef", &srcRef))!=B_OK)
46 		return status;
47 	if ((status = GetDataStore()->FindRef("destRef", &destRef))==B_OK) {
48 		BPath path(&destRef);
49 		chdir(path.Path());
50 	}
51 	if ((status = GetDataStore()->FindString("cmd", &cmd))!=B_OK)
52 		return status;
53 
54 	BPath path(&srcRef);
55 	BString pathString(path.Path());
56 	pathString.CharacterEscape("\"$`", '\\');
57 	pathString.Prepend("\"");
58 	pathString.Append("\"");
59 	cmd.ReplaceAll("%s", pathString.String());
60 
61 	int32 argc = 3;
62 	const char ** argv = new const char * [argc + 1];
63 
64 	argv[0]	=	strdup("/bin/sh");
65 	argv[1]	=	strdup("-c");
66 	argv[2]	=	strdup(cmd.String());
67 	argv[argc] = NULL;
68 
69 	fThreadId = PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr);
70 
71 	delete [] argv;
72 
73 	if (fThreadId < 0)
74 		return fThreadId;
75 
76     resume_thread(fThreadId);
77 
78     fExpanderOutput = fdopen(fStdOut, "r");
79 
80 	return B_OK;
81 }
82 
83 status_t
84 ExpanderThread::ExecuteUnit (void)
85 {
86 	// read output from command
87 	// send it to window
88 
89 	char * output_string;
90 	output_string =	fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput);
91 
92 	if (output_string == NULL)
93 		return EOF;
94 
95 	BMessage message('outp');
96 	message.AddString("output", output_string);
97 	for (int32 i=0; i<5; i++) {
98 		output_string = fgets(fExpanderOutputBuffer , 4096-1, fExpanderOutput);
99 		if(!output_string)
100 			break;
101 		message.AddString("output", output_string);
102 	}
103 	fWindowMessenger->SendMessage(&message);
104 
105 	return B_OK;
106 }
107 
108 status_t
109 ExpanderThread::ThreadShutdown(void)
110 {
111 	close(fStdIn);
112     close(fStdOut);
113    	close(fStdErr);
114 
115 	return B_OK;
116 }
117 
118 void
119 ExpanderThread::ThreadStartupFailed(status_t status)
120 {
121 	fprintf(stderr, "ExpanderThread::ThreadStartupFailed() : %s\n", strerror(status));
122 
123 	Quit();
124 }
125 
126 void
127 ExpanderThread::ExecuteUnitFailed(status_t status)
128 {
129 	if (status == EOF) {
130 		// thread has finished, been quit or killed, we don't know
131 		fWindowMessenger->SendMessage(new BMessage('exit'));
132 	} else {
133 		// explicit error - communicate error to Window
134 		fWindowMessenger->SendMessage(new BMessage('exrr'));
135 	}
136 
137 	Quit();
138 }
139 
140 void
141 ExpanderThread::ThreadShutdownFailed(status_t status)
142 {
143 	fprintf(stderr, "ExpanderThread::ThreadShutdownFailed() %s\n", strerror(status));
144 }
145 
146 
147 status_t
148 ExpanderThread::ProcessRefs(BMessage *msg)
149 {
150 	return B_OK;
151 }
152 
153 thread_id
154 ExpanderThread::PipeCommand(int argc, const char **argv, int &in, int &out, int &err, const char **envp)
155 {
156 	// This function written by Peter Folk <pfolk@uni.uiuc.edu>
157 	// and published in the BeDevTalk FAQ
158 	// http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
159 
160     // Save current FDs
161     int old_in  =  dup(0);
162     int old_out  =  dup(1);
163     int old_err  =  dup(2);
164 
165     int filedes[2];
166 
167     /* Create new pipe FDs as stdin, stdout, stderr */
168     pipe(filedes);  dup2(filedes[0],0); close(filedes[0]);
169     in=filedes[1];  // Write to in, appears on cmd's stdin
170     pipe(filedes);  dup2(filedes[1],1); close(filedes[1]);
171     out=filedes[0]; // Read from out, taken from cmd's stdout
172     pipe(filedes);  dup2(filedes[1],2); close(filedes[1]);
173     err=filedes[0]; // Read from err, taken from cmd's stderr
174 
175     // "load" command.
176     thread_id ret  =  load_image(argc, argv, envp);
177 
178     if (ret < B_OK)
179 		return ret;
180 
181     // thread ret is now suspended.
182 
183 	setpgid(ret, ret);
184 
185     // Restore old FDs
186     close(0); dup(old_in); close(old_in);
187     close(1); dup(old_out); close(old_out);
188     close(2); dup(old_err); close(old_err);
189 
190     /* Theoretically I should do loads of error checking, but
191        the calls aren't very likely to fail, and that would
192        muddy up the example quite a bit.  YMMV. */
193 
194     return ret;
195 }
196 
197 
198 status_t
199 ExpanderThread::SuspendExternalExpander()
200 {
201 	status_t status;
202 	thread_info thread_info;
203 	status = get_thread_info(fThreadId, &thread_info);
204 	BString	thread_name = thread_info.name;
205 
206 	if (status == B_OK)
207 		return send_signal(-fThreadId, SIGSTOP);
208 	else
209 		return status;
210 }
211 
212 status_t
213 ExpanderThread::ResumeExternalExpander()
214 {
215 	status_t status = B_OK;
216 	thread_info thread_info;
217 	status = get_thread_info(fThreadId, &thread_info);
218 	BString	thread_name = thread_info.name;
219 
220 	if (status == B_OK)
221 		return send_signal(-fThreadId, SIGCONT);
222 	else
223 		return status;
224 }
225 
226 status_t
227 ExpanderThread::InterruptExternalExpander()
228 {
229 	status_t status = B_OK;
230 	thread_info thread_info;
231 	status = get_thread_info (fThreadId, &thread_info);
232 	BString	thread_name = thread_info.name;
233 
234 	if (status == B_OK) {
235 		status = send_signal(-fThreadId, SIGINT);
236 		WaitOnExternalExpander();
237 	}
238 	return status;
239 }
240 
241 status_t
242 ExpanderThread::WaitOnExternalExpander()
243 {
244 	status_t status;
245 	thread_info thread_info;
246 	status = get_thread_info(fThreadId, &thread_info);
247 	BString	thread_name = thread_info.name;
248 
249 	if (status == B_OK)
250 		return wait_for_thread(fThreadId, &status);
251 	else
252 		return status;
253 }
254