1 #include <lib/base/console.h>
2 #include <lib/base/eerror.h>
3 #include <sys/vfs.h> // for statfs
11 int bidirpipe(int pfd[], char *cmd , char *argv[])
13 int pfdin[2]; /* from child to parent */
14 int pfdout[2]; /* from parent to child */
15 int pfderr[2]; /* stderr from child to parent */
16 int pid; /* child's pid */
18 if ( pipe(pfdin) == -1 || pipe(pfdout) == -1 || pipe(pfderr) == -1)
21 if ( ( pid = fork() ) == -1 )
23 else if (pid == 0) /* child process */
26 if ( close(0) == -1 || close(1) == -1 || close(2) == -1 )
29 if (dup(pfdout[0]) != 0 || dup(pfdin[1]) != 1 || dup(pfderr[1]) != 2 )
32 if (close(pfdout[0]) == -1 || close(pfdout[1]) == -1 ||
33 close(pfdin[0]) == -1 || close(pfdin[1]) == -1 ||
34 close(pfderr[0]) == -1 || close(pfderr[1]) == -1 )
37 for (unsigned int i=3; i < 90; ++i )
43 if (close(pfdout[0]) == -1 || close(pfdin[1]) == -1 || close(pfderr[1]) == -1)
53 eConsoleAppContainer::eConsoleAppContainer()
54 :pid(-1), killstate(0), in(0), out(0), err(0)
56 for (int i=0; i < 3; ++i)
60 int eConsoleAppContainer::execute( const std::string &cmd )
66 // eDebug("cmd = %s", cmd.c_str() );
67 int cnt=2; // path to app + terminated 0
68 std::string str(cmd.length()?cmd:"");
70 // kill spaces at beginning
71 unsigned int pos = str.find_first_not_of(' ');
72 if (pos != std::string::npos && pos)
73 str = str.substr(pos);
75 // kill spaces at the end
76 pos = str.find_last_not_of(' ');
77 if (pos != std::string::npos && (pos+1) < str.length())
78 str = str.erase(pos+1);
80 unsigned int slen=str.length();
84 std::map<char,char> brackets;
85 brackets.insert(std::pair<char,char>('\'','\''));
86 brackets.insert(std::pair<char,char>('"','"'));
87 brackets.insert(std::pair<char,char>('`','`'));
88 brackets.insert(std::pair<char,char>('(',')'));
89 brackets.insert(std::pair<char,char>('{','}'));
90 brackets.insert(std::pair<char,char>('[',']'));
91 brackets.insert(std::pair<char,char>('<','>'));
93 unsigned int idx=str.find(' ');
94 std::string path = str.substr(0, idx != std::string::npos ? idx : slen );
95 // eDebug("path = %s", path.c_str() );
96 unsigned int plen = path.length();
98 std::string cmds = slen > plen ? str.substr( plen+1 ) : "";
99 unsigned int clen = cmds.length();
100 // eDebug("cmds = %s", cmds.c_str() );
103 std::map<char,char>::iterator it = brackets.find(cmds[idx]);
104 while ( (idx = cmds.find(' ',idx) ) != std::string::npos ) // count args
106 if (it != brackets.end())
108 if (cmds[idx-1] == it->second)
111 if (it == brackets.end())
114 it = brackets.find(cmds[idx+1]);
119 // eDebug("idx = %d, %d counted spaces", idx, cnt-2);
124 // eDebug("increase cnt");
127 // eDebug("%d args", cnt-2);
128 char **argv = new char*[cnt]; // min two args... path and terminating 0
129 // eDebug("%d args", cnt);
130 argv[0] = new char[ plen+1 ];
131 // eDebug("new argv[0] %d bytes (%s)", plen+1, path.c_str());
132 strcpy( argv[0], path.c_str() );
133 argv[cnt-1] = 0; // set terminating null
135 if ( cnt > 2 ) // more then default args?
137 cnt=1; // do not overwrite path in argv[0]
139 it = brackets.find(cmds[0]);
141 while ( (idx = cmds.find(' ',idx)) != std::string::npos ) // parse all args..
143 bool bracketClosed=false;
144 if ( it != brackets.end() )
146 if (cmds[idx-1]==it->second)
152 if ( it == brackets.end() )
154 std::string tmp = cmds.substr(0, idx);
158 tmp.erase(tmp.length()-1, 1);
161 // eDebug("new argv[%d] %d bytes (%s)", cnt, tmp.length()+1, tmp.c_str());
162 argv[cnt] = new char[ tmp.length()+1 ];
163 // eDebug("idx=%d, arg = %s", idx, tmp.c_str() );
164 strcpy( argv[cnt++], tmp.c_str() );
165 cmds.erase(0, idx+1);
166 // eDebug("str = %s", cmds.c_str() );
167 it = brackets.find(cmds[0]);
173 if ( it != brackets.end() )
176 cmds.erase(cmds.length()-1, 1);
178 // store the last arg
179 // eDebug("new argv[%d] %d bytes (%s)", cnt, cmds.length()+1, cmds.c_str());
180 argv[cnt] = new char[ cmds.length()+1 ];
181 strcpy( argv[cnt], cmds.c_str() );
186 // get one read ,one write and the err pipe to the prog..
190 // eDebug("%d is %s", tmp, argv[tmp++]);
192 pid = bidirpipe(fd, argv[0], argv);
194 while ( cnt >= 0 ) // release heap memory
196 // eDebug("delete argv[%d]", cnt);
197 delete [] argv[cnt--];
199 // eDebug("delete argv");
205 // eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
207 in = new eSocketNotifier(eApp, fd[0], eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Hungup );
208 out = new eSocketNotifier(eApp, fd[1], eSocketNotifier::Write, false);
209 err = new eSocketNotifier(eApp, fd[2], eSocketNotifier::Read|eSocketNotifier::Priority );
210 CONNECT(in->activated, eConsoleAppContainer::readyRead);
211 CONNECT(out->activated, eConsoleAppContainer::readyWrite);
212 CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
216 eConsoleAppContainer::~eConsoleAppContainer()
221 void eConsoleAppContainer::kill()
223 if ( killstate != -1 && pid != -1 )
225 eDebug("user kill(SIGKILL) console App");
228 * Use a negative pid value, to signal the whole process group
229 * ('pid' might not even be running anymore at this point)
231 ::kill(-pid, SIGKILL);
234 while( outbuf.size() ) // cleanup out buffer
236 queue_data d = outbuf.front();
246 void eConsoleAppContainer::sendCtrlC()
248 if ( killstate != -1 && pid != -1 )
250 eDebug("user send SIGINT(Ctrl-C) to console App");
252 * Use a negative pid value, to signal the whole process group
253 * ('pid' might not even be running anymore at this point)
255 ::kill(-pid, SIGINT);
259 void eConsoleAppContainer::closePipes()
282 eDebug("pipes closed");
283 while( outbuf.size() ) // cleanup out buffer
285 queue_data d = outbuf.front();
292 void eConsoleAppContainer::readyRead(int what)
294 bool hungup = what & eSocketNotifier::Hungup;
295 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
297 // eDebug("what = %d");
300 while((rd = read(fd[0], buf, 2047)) > 0)
302 /* for ( int i = 0; i < rd; i++ )
303 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
305 /*emit*/ dataAvail(buf);
312 eDebug("child has terminated");
315 int retval = killstate;
317 * We have to call 'wait' on the child process, in order to avoid zombies.
318 * Also, this gives us the chance to provide better exit status info to appClosed.
320 if (::waitpid(pid, &childstatus, 0) > 0)
322 if (WIFEXITED(childstatus))
324 retval = WEXITSTATUS(childstatus);
327 /*emit*/ appClosed(retval);
331 void eConsoleAppContainer::readyErrRead(int what)
333 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
335 // eDebug("what = %d");
338 while((rd = read(fd[2], buf, 2047)) > 0)
340 /* for ( int i = 0; i < rd; i++ )
341 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
343 /*emit*/ dataAvail(buf);
348 void eConsoleAppContainer::write( const char *data, int len )
350 char *tmp = new char[len];
351 memcpy(tmp, data, len);
352 outbuf.push(queue_data(tmp,len));
356 void eConsoleAppContainer::readyWrite(int what)
358 if (what&eSocketNotifier::Write && outbuf.size() )
360 queue_data d = outbuf.front();
362 if ( ::write( fd[1], d.data, d.len ) != d.len )
364 /* emit */ dataSent(-1);
365 // eDebug("writeError");
369 /* emit */ dataSent(0);
370 // eDebug("write ok");
374 if ( !outbuf.size() )