cvs20080628 - trunk
[gitenigma.git] / lib / base / console.cpp
blobf96c76bcab99cd64017371873289cc83d09b4305
1 /*
2 * console.cpp
4 * Copyright (C) 2002 Felix Domke <tmbinc@tuxbox.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * $Id: console.cpp,v 1.16 2007/12/15 16:22:55 pieterg Exp $
23 #include <lib/base/console.h>
25 #include <lib/base/estring.h>
26 #include <sys/vfs.h> // for statfs
27 #include <unistd.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
33 int bidirpipe(int pfd[], char *cmd , char *argv[])
35 int pfdin[2]; /* from child to parent */
36 int pfdout[2]; /* from parent to child */
37 int pfderr[2]; /* stderr from child to parent */
38 int pid; /* child's pid */
40 if ( pipe(pfdin) == -1 || pipe(pfdout) == -1 || pipe(pfderr) == -1)
41 return(-1);
43 if ( ( pid = fork() ) == -1 )
44 return(-1);
45 else if (pid == 0) /* child process */
47 setsid();
48 if ( close(0) == -1 || close(1) == -1 || close(2) == -1 )
49 _exit(0);
51 if (dup(pfdout[0]) != 0 || dup(pfdin[1]) != 1 || dup(pfderr[1]) != 2 )
52 _exit(0);
54 if (close(pfdout[0]) == -1 || close(pfdout[1]) == -1 ||
55 close(pfdin[0]) == -1 || close(pfdin[1]) == -1 ||
56 close(pfderr[0]) == -1 || close(pfderr[1]) == -1 )
57 _exit(0);
59 for (unsigned int i=3; i < 90; ++i )
60 close(i);
62 execvp(cmd,argv);
63 _exit(0);
65 if (close(pfdout[0]) == -1 || close(pfdin[1]) == -1 || close(pfderr[1]) == -1)
66 return(-1);
68 pfd[0] = pfdin[0];
69 pfd[1] = pfdout[1];
70 pfd[2] = pfderr[0];
72 return(pid);
75 eConsoleAppContainer::eConsoleAppContainer( const eString &cmd )
76 :pid(-1), killstate(0)
78 // eDebug("cmd = %s", cmd.c_str() );
79 for (int i=0; i < 3; ++i)
80 fd[i]=-1;
81 int cnt=2; // path to app + terminated 0
82 eString str(cmd?cmd:"");
84 while( str.length() && str[0] == ' ' ) // kill spaces at beginning
85 str = str.mid(1);
87 while( str.length() && str[str.length()-1] == ' ' ) // kill spaces at the end
88 str = str.left( str.length() - 1 );
90 if (!str.length())
91 return;
93 std::map<char,char> brackets;
94 brackets.insert(std::pair<char,char>('\'','\''));
95 brackets.insert(std::pair<char,char>('"','"'));
96 brackets.insert(std::pair<char,char>('`','`'));
97 brackets.insert(std::pair<char,char>('(',')'));
98 brackets.insert(std::pair<char,char>('{','}'));
99 brackets.insert(std::pair<char,char>('[',']'));
100 brackets.insert(std::pair<char,char>('<','>'));
102 unsigned int idx=0;
103 eString path = str.left( (idx = str.find(' ')) != eString::npos ? idx : str.length() );
104 // eDebug("path = %s", path.c_str() );
106 eString cmds = str.mid( path.length()+1 );
107 // eDebug("cmds = %s", cmds.c_str() );
109 idx = 0;
110 std::map<char,char>::iterator it = brackets.find(cmds[idx]);
111 while ( (idx = cmds.find(' ',idx) ) != eString::npos ) // count args
113 if (it != brackets.end())
115 if (cmds[idx-1] == it->second)
116 it = brackets.end();
118 if (it == brackets.end())
120 cnt++;
121 it = brackets.find(cmds[idx+1]);
123 idx++;
126 // eDebug("idx = %d, %d counted spaces", idx, cnt-2);
128 if ( cmds.length() )
130 cnt++;
131 // eDebug("increase cnt");
134 // eDebug("%d args", cnt-2);
135 char **argv = new char*[cnt]; // min two args... path and terminating 0
136 argv[0] = new char[ path.length() ];
137 strcpy( argv[0], path.c_str() );
138 argv[cnt-1] = 0; // set terminating null
140 if ( cnt > 2 ) // more then default args?
142 cnt=1; // do not overwrite path in argv[0]
144 it = brackets.find(cmds[0]);
145 idx=0;
146 while ( (idx = cmds.find(' ',idx)) != eString::npos ) // parse all args..
148 bool bracketClosed=false;
149 if ( it != brackets.end() )
151 if (cmds[idx-1]==it->second)
153 it = brackets.end();
154 bracketClosed=true;
157 if ( it == brackets.end() )
159 eString tmp = cmds.left(idx);
160 if (bracketClosed)
162 tmp.erase(0,1);
163 tmp.erase(tmp.length()-1, 1);
164 bracketClosed=false;
166 argv[cnt] = new char[ tmp.length()+1 ];
167 // eDebug("idx=%d, arg = %s", idx, tmp.c_str() );
168 strcpy( argv[cnt++], tmp.c_str() );
169 cmds = cmds.mid(idx+1);
170 // eDebug("str = %s", cmds.c_str() );
171 it = brackets.find(cmds[0]);
172 idx=0;
174 else
175 idx++;
177 if ( it != brackets.end() )
179 cmds.erase(0,1);
180 cmds.erase(cmds.length()-1, 1);
182 // store the last arg
183 argv[cnt] = new char[ cmds.length() ];
184 strcpy( argv[cnt], cmds.c_str() );
186 else
187 cnt=1;
189 // get one read ,one write and the err pipe to the prog..
191 // int tmp=0;
192 // while(argv[tmp])
193 // eDebug("%d is %s", tmp, argv[tmp++]);
195 pid = bidirpipe(fd, argv[0], argv);
197 while ( cnt >= 0 ) // release heap memory
198 delete [] argv[cnt--];
199 delete [] argv;
201 if ( pid == -1 )
202 return;
204 // eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
206 in = new eSocketNotifier(eApp, fd[0], 19 ); // 19 = POLLIN, POLLPRI, POLLHUP
207 out = new eSocketNotifier(eApp, fd[1], eSocketNotifier::Write, false); // POLLOUT
208 err = new eSocketNotifier(eApp, fd[2], 19 ); // 19 = POLLIN, POLLPRI, POLLHUP
209 CONNECT(in->activated, eConsoleAppContainer::readyRead);
210 CONNECT(out->activated, eConsoleAppContainer::readyWrite);
211 CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
214 eConsoleAppContainer::~eConsoleAppContainer()
216 kill();
217 delete in;
218 delete out;
219 delete err;
222 void eConsoleAppContainer::kill()
224 if ( killstate != -1 )
226 eDebug("user kill(SIGKILL) console App");
227 killstate=-1;
229 * Use a negative pid value, to signal the whole process group
230 * ('pid' might not even be running anymore at this point)
232 ::kill(-pid, SIGKILL);
233 closePipes();
237 void eConsoleAppContainer::sendCtrlC()
239 if ( killstate != -1 )
241 eDebug("user send SIGINT(Ctrl-C) to console App");
243 * Use a negative pid value, to signal the whole process group
244 * ('pid' might not even be running anymore at this point)
246 ::kill(-pid, SIGINT);
251 void eConsoleAppContainer::closePipes()
253 in->stop();
254 out->stop();
255 err->stop();
256 ::close(fd[0]);
257 fd[0]=-1;
258 ::close(fd[1]);
259 fd[1]=-1;
260 ::close(fd[2]);
261 fd[2]=-1;
262 eDebug("pipes closed");
265 void eConsoleAppContainer::readyRead(int what)
267 if (what & POLLPRI|POLLIN)
269 // eDebug("what = %d");
270 char buf[2048];
271 int readed = read(fd[0], buf, 2048);
272 // eDebug("%d bytes read", readed);
273 if ( readed != -1 && readed )
275 /* for ( int i = 0; i < readed; i++ )
276 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
277 /*emit*/ dataAvail( eString( buf, readed ) );
279 else if (readed == -1)
280 eDebug("readerror %d", errno);
282 if (what & eSocketNotifier::Hungup)
284 eDebug("child has terminated");
285 closePipes();
286 int childstatus;
287 int retval = killstate;
289 * We have to call 'wait' on the child process, in order to avoid zombies.
290 * Also, this gives us the chance to provide better exit status info to appClosed.
292 if (::waitpid(pid, &childstatus, 0) > 0)
294 if (WIFEXITED(childstatus))
296 retval = WEXITSTATUS(childstatus);
299 /*emit*/ appClosed(retval);
303 void eConsoleAppContainer::readyErrRead(int what)
305 if (what & POLLPRI|POLLIN)
307 // eDebug("what = %d");
308 char buf[2048];
309 int readed = read(fd[2], buf, 2048);
310 // eDebug("%d bytes read", readed);
311 if ( readed != -1 && readed )
313 /* for ( int i = 0; i < readed; i++ )
314 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
315 /*emit*/ dataAvail( eString( buf, readed ) );
317 else if (readed == -1)
318 eDebug("readerror %d", errno);
322 void eConsoleAppContainer::write( const char *data, int len )
324 char *tmp = new char[len];
325 memcpy(tmp, data, len);
326 outbuf.push(queue_data(tmp,len));
327 out->start();
330 void eConsoleAppContainer::readyWrite(int what)
332 if (what == POLLOUT && outbuf.size() )
334 queue_data d = outbuf.front();
335 outbuf.pop();
336 if ( ::write( fd[1], d.data, d.len ) != d.len )
338 /* emit */ dataSent(-1);
339 // eDebug("writeError");
341 else
343 /* emit */ dataSent(0);
344 // eDebug("write ok");
347 delete [] d.data;
349 if ( !outbuf.size() )
350 out->stop();