repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / expander / ExpanderThread.cpp
blobd0e9c843c0851c3b34ab7c8de10426c30895502d
1 /*
2 * Copyright 2004-2010, 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 "ExpanderThread.h"
9 #include <errno.h>
10 #include <image.h>
11 #include <signal.h>
12 #include <termios.h>
13 #include <unistd.h>
15 #include <Messenger.h>
16 #include <Path.h>
19 const char* ExpanderThreadName = "ExpanderThread";
22 ExpanderThread::ExpanderThread(BMessage* refs_message, BMessenger* messenger)
24 GenericThread(ExpanderThreadName, B_NORMAL_PRIORITY, refs_message),
25 fWindowMessenger(messenger),
26 fThreadId(-1),
27 fStdIn(-1),
28 fStdOut(-1),
29 fStdErr(-1),
30 fExpanderOutput(NULL),
31 fExpanderError(NULL)
33 SetDataStore(new BMessage(*refs_message));
34 // leak?
35 // prevents bug with B_SIMPLE_DATA
36 // (drag&drop messages)
40 ExpanderThread::~ExpanderThread()
42 delete fWindowMessenger;
46 status_t
47 ExpanderThread::ThreadStartup()
49 status_t status = B_OK;
50 entry_ref srcRef;
51 entry_ref destRef;
52 BString cmd;
54 if ((status = GetDataStore()->FindRef("srcRef", &srcRef)) != B_OK)
55 return status;
57 if ((status = GetDataStore()->FindRef("destRef", &destRef)) == B_OK) {
58 BPath path(&destRef);
59 chdir(path.Path());
62 if ((status = GetDataStore()->FindString("cmd", &cmd)) != B_OK)
63 return status;
65 BPath path(&srcRef);
66 BString pathString(path.Path());
67 pathString.CharacterEscape("\\\"$`", '\\');
68 pathString.Prepend("\"");
69 pathString.Append("\"");
70 cmd.ReplaceAll("%s", pathString.String());
72 int32 argc = 3;
73 const char** argv = new const char * [argc + 1];
75 argv[0] = strdup("/bin/sh");
76 argv[1] = strdup("-c");
77 argv[2] = strdup(cmd.String());
78 argv[argc] = NULL;
80 fThreadId = PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr);
82 delete [] argv;
84 if (fThreadId < 0)
85 return fThreadId;
87 // lower the command priority since it is a background task.
88 set_thread_priority(fThreadId, B_LOW_PRIORITY);
90 resume_thread(fThreadId);
92 int flags = fcntl(fStdOut, F_GETFL, 0);
93 flags |= O_NONBLOCK;
94 fcntl(fStdOut, F_SETFL, flags);
95 flags = fcntl(fStdErr, F_GETFL, 0);
96 flags |= O_NONBLOCK;
97 fcntl(fStdErr, F_SETFL, flags);
99 fExpanderOutput = fdopen(fStdOut, "r");
100 fExpanderError = fdopen(fStdErr, "r");
102 return B_OK;
106 status_t
107 ExpanderThread::ExecuteUnit(void)
109 // read output and error from command
110 // send it to window
112 BMessage message('outp');
113 bool outputAdded = false;
114 for (int32 i = 0; i < 50; i++) {
115 char* output_string = fgets(fExpanderOutputBuffer , LINE_MAX,
116 fExpanderOutput);
117 if (output_string == NULL)
118 break;
120 message.AddString("output", output_string);
121 outputAdded = true;
123 if (outputAdded)
124 fWindowMessenger->SendMessage(&message);
126 if (feof(fExpanderOutput))
127 return EOF;
129 char* error_string = fgets(fExpanderOutputBuffer, LINE_MAX,
130 fExpanderError);
131 if (error_string != NULL && strcmp(error_string, "\n")) {
132 BMessage message('errp');
133 message.AddString("error", error_string);
134 fWindowMessenger->SendMessage(&message);
137 // streams are non blocking, sleep every 100ms
138 snooze(100000);
140 return B_OK;
144 void
145 ExpanderThread::PushInput(BString text)
147 text += "\n";
148 write(fStdIn, text.String(), text.Length());
152 status_t
153 ExpanderThread::ThreadShutdown(void)
155 close(fStdIn);
156 close(fStdOut);
157 close(fStdErr);
159 return B_OK;
163 void
164 ExpanderThread::ThreadStartupFailed(status_t status)
166 fprintf(stderr, "ExpanderThread::ThreadStartupFailed() : %s\n",
167 strerror(status));
169 Quit();
173 void
174 ExpanderThread::ExecuteUnitFailed(status_t status)
176 if (status == EOF) {
177 // thread has finished, been quit or killed, we don't know
178 fWindowMessenger->SendMessage(new BMessage('exit'));
179 } else {
180 // explicit error - communicate error to Window
181 fWindowMessenger->SendMessage(new BMessage('exrr'));
184 Quit();
188 void
189 ExpanderThread::ThreadShutdownFailed(status_t status)
191 fprintf(stderr, "ExpanderThread::ThreadShutdownFailed() %s\n",
192 strerror(status));
196 status_t
197 ExpanderThread::ProcessRefs(BMessage *msg)
199 return B_OK;
203 thread_id
204 ExpanderThread::PipeCommand(int argc, const char** argv, int& in, int& out,
205 int& err, const char** envp)
207 // This function written by Peter Folk <pfolk@uni.uiuc.edu>
208 // and published in the BeDevTalk FAQ
209 // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
211 // Save current FDs
212 int old_out = dup(1);
213 int old_err = dup(2);
215 int filedes[2];
217 // create new pipe FDs as stdout, stderr
218 pipe(filedes); dup2(filedes[1], 1); close(filedes[1]);
219 out = filedes[0]; // Read from out, taken from cmd's stdout
220 pipe(filedes); dup2(filedes[1], 2); close(filedes[1]);
221 err = filedes[0]; // Read from err, taken from cmd's stderr
223 // taken from pty.cpp
224 // create a tty for stdin, as utilities don't generally use stdin
225 int master = posix_openpt(O_RDWR);
226 if (master < 0)
227 return -1;
229 int slave;
230 const char* ttyName;
231 if (grantpt(master) != 0 || unlockpt(master) != 0
232 || (ttyName = ptsname(master)) == NULL
233 || (slave = open(ttyName, O_RDWR | O_NOCTTY)) < 0) {
234 close(master);
235 return -1;
238 int pid = fork();
239 if (pid < 0) {
240 close(master);
241 close(slave);
242 return -1;
245 // child
246 if (pid == 0) {
247 close(master);
249 setsid();
250 if (ioctl(slave, TIOCSCTTY, NULL) != 0)
251 return -1;
253 dup2(slave, 0);
254 close(slave);
256 // "load" command.
257 execv(argv[0], (char *const *)argv);
259 // shouldn't return
260 return -1;
263 // parent
264 close (slave);
265 in = master;
267 // Restore old FDs
268 close(1); dup(old_out); close(old_out);
269 close(2); dup(old_err); close(old_err);
271 // Theoretically I should do loads of error checking, but
272 // the calls aren't very likely to fail, and that would
273 // muddy up the example quite a bit. YMMV.
275 return pid;
279 status_t
280 ExpanderThread::SuspendExternalExpander()
282 thread_info info;
283 status_t status = get_thread_info(fThreadId, &info);
285 if (status == B_OK)
286 return send_signal(-fThreadId, SIGSTOP);
287 else
288 return status;
292 status_t
293 ExpanderThread::ResumeExternalExpander()
295 thread_info info;
296 status_t status = get_thread_info(fThreadId, &info);
298 if (status == B_OK)
299 return send_signal(-fThreadId, SIGCONT);
300 else
301 return status;
305 status_t
306 ExpanderThread::InterruptExternalExpander()
308 thread_info info;
309 status_t status = get_thread_info(fThreadId, &info);
311 if (status == B_OK) {
312 status = send_signal(-fThreadId, SIGINT);
313 WaitOnExternalExpander();
316 return status;
320 status_t
321 ExpanderThread::WaitOnExternalExpander()
323 thread_info info;
324 status_t status = get_thread_info(fThreadId, &info);
326 if (status == B_OK)
327 return wait_for_thread(fThreadId, &status);
328 else
329 return status;