1 #include <lib/base/console.h>
2 #include <lib/base/eerror.h>
3 #include <sys/vfs.h> // for statfs
12 int bidirpipe(int pfd[], const char *cmd , const char * const argv[], const char *cwd )
14 int pfdin[2]; /* from child to parent */
15 int pfdout[2]; /* from parent to child */
16 int pfderr[2]; /* stderr from child to parent */
17 int pid; /* child's pid */
19 if ( pipe(pfdin) == -1 || pipe(pfdout) == -1 || pipe(pfderr) == -1)
22 if ( ( pid = vfork() ) == -1 )
24 else if (pid == 0) /* child process */
27 if ( close(0) == -1 || close(1) == -1 || close(2) == -1 )
30 if (dup(pfdout[0]) != 0 || dup(pfdin[1]) != 1 || dup(pfderr[1]) != 2 )
33 if (close(pfdout[0]) == -1 || close(pfdout[1]) == -1 ||
34 close(pfdin[0]) == -1 || close(pfdin[1]) == -1 ||
35 close(pfderr[0]) == -1 || close(pfderr[1]) == -1 )
38 for (unsigned int i=3; i < 90; ++i )
44 execvp(cmd, (char * const *)argv);
45 /* the vfork will actually suspend the parent thread until execvp is called. thus it's ok to use the shared arg/cmdline pointers here. */
48 if (close(pfdout[0]) == -1 || close(pfdin[1]) == -1 || close(pfderr[1]) == -1)
58 eConsoleAppContainer::eConsoleAppContainer()
59 :pid(-1), killstate(0), in(0), out(0), err(0)
61 for (int i=0; i < 3; ++i)
65 static char brakets[][2] = {
75 static char *find_bracket(char ch)
78 while (idx < sizeof(brakets)/2) {
79 if (brakets[idx][0] == ch)
80 return &brakets[idx][0];
86 int eConsoleAppContainer::setCWD( const char *path )
90 if (stat(path, &dir_stat) == -1)
93 if (!S_ISDIR(dir_stat.st_mode))
100 int eConsoleAppContainer::execute( const char *cmd )
102 int cnt=0, slen=strlen(cmd);
104 char *tmp=0, *argv[64], *path=buf, *cmds = buf;
105 memcpy(buf, cmd, slen+1);
107 // printf("cmd = %s, len %d\n", cmd, slen);
109 // kill spaces at beginning
110 while(path[0] == ' ') {
116 // kill spaces at the end
117 while(slen && path[slen-1] == ' ') {
125 tmp = strchr(path, ' ');
129 while(*cmds && *cmds == ' ')
135 memset(argv, 0, sizeof(argv));
139 char *argb=NULL, *it=NULL;
140 while ( (tmp = strchr(cmds, ' ')) ) {
141 if (!it && *cmds && (it = find_bracket(*cmds)) )
142 *cmds = 'X'; // replace open braket...
143 if (!argb) // not arg begin
145 if (it && *(tmp-1) == it[1]) {
146 *argb = it[0]; // set old char for open braket
149 if (!it) { // end of arg
152 argb=0; // reset arg begin
155 while (*cmds && *cmds == ' ')
158 argv[cnt++] = argb ? argb : cmds;
160 *argv[cnt-1] = it[0]; // set old char for open braket
165 // eDebug("%d is %s", tmp, argv[tmp++]);
166 return execute(argv[0], argv);
169 int eConsoleAppContainer::execute(const char *cmdline, const char * const argv[])
177 // get one read ,one write and the err pipe to the prog..
178 pid = bidirpipe(fd, cmdline, argv, m_cwd.length() ? m_cwd.c_str() : 0);
183 // eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
185 ::fcntl(fd[1], F_SETFL, O_NONBLOCK);
186 ::fcntl(fd[2], F_SETFL, O_NONBLOCK);
187 in = new eSocketNotifier(eApp, fd[0], eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Hungup );
188 out = new eSocketNotifier(eApp, fd[1], eSocketNotifier::Write, false);
189 err = new eSocketNotifier(eApp, fd[2], eSocketNotifier::Read|eSocketNotifier::Priority );
190 CONNECT(in->activated, eConsoleAppContainer::readyRead);
191 CONNECT(out->activated, eConsoleAppContainer::readyWrite);
192 CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
197 int eConsoleAppContainer::execute( PyObject *cmdline, PyObject *args )
199 if (!PyString_Check(cmdline))
201 if (!PyList_Check(args))
203 const char *argv[PyList_Size(args) + 1];
205 for (i = 0; i < PyList_Size(args); ++i)
207 PyObject *arg = PyList_GetItem(args, i); /* borrowed ref */
210 if (!PyString_Check(arg))
212 argv[i] = PyString_AsString(arg); /* borrowed pointer */
216 return execute(PyString_AsString(cmdline), argv); /* borrowed pointer */
219 eConsoleAppContainer::~eConsoleAppContainer()
224 void eConsoleAppContainer::kill()
226 if ( killstate != -1 && pid != -1 )
228 eDebug("user kill(SIGKILL) console App");
231 * Use a negative pid value, to signal the whole process group
232 * ('pid' might not even be running anymore at this point)
234 ::kill(-pid, SIGKILL);
237 while( outbuf.size() ) // cleanup out buffer
239 queue_data d = outbuf.front();
249 void eConsoleAppContainer::sendCtrlC()
251 if ( killstate != -1 && pid != -1 )
253 eDebug("user send SIGINT(Ctrl-C) to console App");
255 * Use a negative pid value, to signal the whole process group
256 * ('pid' might not even be running anymore at this point)
258 ::kill(-pid, SIGINT);
262 void eConsoleAppContainer::closePipes()
285 eDebug("pipes closed");
286 while( outbuf.size() ) // cleanup out buffer
288 queue_data d = outbuf.front();
295 void eConsoleAppContainer::readyRead(int what)
297 bool hungup = what & eSocketNotifier::Hungup;
298 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
300 // eDebug("what = %d");
303 while((rd = read(fd[0], buf, 2048)) > 0)
306 /*emit*/ dataAvail(buf);
313 eDebug("child has terminated");
316 int retval = killstate;
318 * We have to call 'wait' on the child process, in order to avoid zombies.
319 * Also, this gives us the chance to provide better exit status info to appClosed.
321 if (::waitpid(pid, &childstatus, 0) > 0)
323 if (WIFEXITED(childstatus))
325 retval = WEXITSTATUS(childstatus);
328 /*emit*/ appClosed(retval);
332 void eConsoleAppContainer::readyErrRead(int what)
334 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
336 // eDebug("what = %d");
339 while((rd = read(fd[2], buf, 2048)) > 0)
341 /* for ( int i = 0; i < rd; i++ )
342 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
344 /*emit*/ dataAvail(buf);
349 void eConsoleAppContainer::write( const char *data, int len )
351 char *tmp = new char[len];
352 memcpy(tmp, data, len);
353 outbuf.push(queue_data(tmp,len));
358 void eConsoleAppContainer::write( PyObject *data )
362 if (PyString_AsStringAndSize(data, &buffer, &length))
364 if (buffer && length)
365 write(buffer, length);
368 void eConsoleAppContainer::readyWrite(int what)
370 if (what&eSocketNotifier::Write && outbuf.size() )
372 queue_data d = outbuf.front();
374 if ( ::write( fd[1], d.data, d.len ) != d.len )
376 /* emit */ dataSent(-1);
377 // eDebug("writeError");
381 /* emit */ dataSent(0);
382 // eDebug("write ok");
386 if ( !outbuf.size() )