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
30 #include <sys/types.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)
43 if ( ( pid
= fork() ) == -1 )
45 else if (pid
== 0) /* child process */
48 if ( close(0) == -1 || close(1) == -1 || close(2) == -1 )
51 if (dup(pfdout
[0]) != 0 || dup(pfdin
[1]) != 1 || dup(pfderr
[1]) != 2 )
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 )
59 for (unsigned int i
=3; i
< 90; ++i
)
65 if (close(pfdout
[0]) == -1 || close(pfdin
[1]) == -1 || close(pfderr
[1]) == -1)
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
)
81 int cnt
=2; // path to app + terminated 0
82 eString
str(cmd
?cmd
:"");
84 while( str
.length() && str
[0] == ' ' ) // kill spaces at beginning
87 while( str
.length() && str
[str
.length()-1] == ' ' ) // kill spaces at the end
88 str
= str
.left( str
.length() - 1 );
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>('<','>'));
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() );
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
)
118 if (it
== brackets
.end())
121 it
= brackets
.find(cmds
[idx
+1]);
126 // eDebug("idx = %d, %d counted spaces", idx, cnt-2);
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]);
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
)
157 if ( it
== brackets
.end() )
159 eString tmp
= cmds
.left(idx
);
163 tmp
.erase(tmp
.length()-1, 1);
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]);
177 if ( it
!= brackets
.end() )
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() );
189 // get one read ,one write and the err pipe to the prog..
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
--];
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()
222 void eConsoleAppContainer::kill()
224 if ( killstate
!= -1 )
226 eDebug("user kill(SIGKILL) console App");
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
);
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()
262 eDebug("pipes closed");
265 void eConsoleAppContainer::readyRead(int what
)
267 if (what
& POLLPRI
|POLLIN
)
269 // eDebug("what = %d");
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");
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");
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
));
330 void eConsoleAppContainer::readyWrite(int what
)
332 if (what
== POLLOUT
&& outbuf
.size() )
334 queue_data d
= outbuf
.front();
336 if ( ::write( fd
[1], d
.data
, d
.len
) != d
.len
)
338 /* emit */ dataSent(-1);
339 // eDebug("writeError");
343 /* emit */ dataSent(0);
344 // eDebug("write ok");
349 if ( !outbuf
.size() )