1/*
2* Copyright 2010, Haiku. All rights reserved.
3* Distributed under the terms of the MIT License.
4*
5* Authors:
6*		Ithamar R. Adema <ithamar.adema@team-embedded.nl>
7*/
8
9
10#include "FilterIO.h"
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include <image.h>
17
18#include <String.h>
19
20
21FilterIO::FilterIO(const BString& cmdline)
22	:
23	BDataIO()
24{
25	BString cmd(cmdline);
26	const char* argv[4];
27
28	argv[0] = strdup("/bin/sh");
29	argv[1] = strdup("-c");
30	argv[2] = strdup(cmd.String());
31	argv[3] = NULL;
32
33	InitData(3, argv);
34
35	free((void*)argv[0]);
36	free((void*)argv[1]);
37	free((void*)argv[2]);
38}
39
40
41FilterIO::FilterIO(int argc, const char **argv, const char **envp)
42	:
43	BDataIO()
44{
45	InitData(argc, argv, envp);
46}
47
48
49status_t
50FilterIO::InitData(int argc, const char** argv, const char** envp)
51{
52	fStdIn = fStdOut = fStdErr = -1;
53	fInitErr = B_OK;
54
55	fThreadId = PipeCommand(argc, argv, fStdIn, fStdOut, fStdErr, envp);
56	if (fThreadId < 0)
57		fInitErr = fThreadId;
58
59	// lower the command priority since it is a background task.
60	set_thread_priority(fThreadId, B_LOW_PRIORITY);
61	resume_thread(fThreadId);
62
63	return fInitErr;
64}
65
66
67FilterIO::~FilterIO()
68{
69	::close(fStdIn);
70	::close(fStdOut);
71	::close(fStdErr);
72}
73
74
75ssize_t
76FilterIO::Read(void* buffer, size_t size)
77{
78	return ::read(fStdOut, buffer, size);
79}
80
81
82ssize_t
83FilterIO::Write(const void* buffer, size_t size)
84{
85	return ::write(fStdIn, buffer, size);
86}
87
88
89thread_id
90FilterIO::PipeCommand(int argc, const char** argv, int& in, int& out, int& err,
91	const char** envp)
92{
93	// This function written by Peter Folk <pfolk@uni.uiuc.edu>
94	// and published in the BeDevTalk FAQ
95	// http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
96
97	if (!envp)
98		envp = (const char**)environ;
99
100	// Save current FDs
101	int old_in  =  dup(0);
102	int old_out  =  dup(1);
103	int old_err  =  dup(2);
104
105	int filedes[2];
106
107	// Create new pipe FDs as stdin, stdout, stderr
108	pipe(filedes);  dup2(filedes[0], 0); close(filedes[0]);
109	in = filedes[1];  // Write to in, appears on cmd's stdin
110	pipe(filedes);  dup2(filedes[1], 1); close(filedes[1]);
111	out = filedes[0]; // Read from out, taken from cmd's stdout
112	pipe(filedes);  dup2(filedes[1], 2); close(filedes[1]);
113	err = filedes[0]; // Read from err, taken from cmd's stderr
114
115	// "load" command.
116	thread_id ret  =  load_image(argc, argv, envp);
117	if (ret < B_OK)
118		goto cleanup;
119
120	// thread ret is now suspended.
121
122	setpgid(ret, ret);
123
124cleanup:
125	// Restore old FDs
126	close(0); dup(old_in); close(old_in);
127	close(1); dup(old_out); close(old_out);
128	close(2); dup(old_err); close(old_err);
129
130	/* Theoretically I should do loads of error checking, but
131	   the calls aren't very likely to fail, and that would
132	   muddy up the example quite a bit.  YMMV. */
133
134	return ret;
135}
136