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)
68 int eConsoleAppContainer::setCWD( const char *path )
72 if (stat(path, &dir_stat) == -1)
75 if (!S_ISDIR(dir_stat.st_mode))
82 int eConsoleAppContainer::execute( const char *cmd )
85 const char *argv[argc + 1];
91 return execute(argv[0], argv);
94 int eConsoleAppContainer::execute(const char *cmdline, const char * const argv[])
102 // get one read ,one write and the err pipe to the prog..
103 pid = bidirpipe(fd, cmdline, argv, m_cwd.length() ? m_cwd.c_str() : 0);
108 // eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
110 ::fcntl(fd[1], F_SETFL, O_NONBLOCK);
111 ::fcntl(fd[2], F_SETFL, O_NONBLOCK);
112 in = new eSocketNotifier(eApp, fd[0], eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Hungup );
113 out = new eSocketNotifier(eApp, fd[1], eSocketNotifier::Write, false);
114 err = new eSocketNotifier(eApp, fd[2], eSocketNotifier::Read|eSocketNotifier::Priority );
115 CONNECT(in->activated, eConsoleAppContainer::readyRead);
116 CONNECT(out->activated, eConsoleAppContainer::readyWrite);
117 CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
122 int eConsoleAppContainer::execute( PyObject *cmdline, PyObject *args )
124 if (!PyString_Check(cmdline))
126 if (!PyList_Check(args))
128 const char *argv[PyList_Size(args) + 1];
130 for (i = 0; i < PyList_Size(args); ++i)
132 PyObject *arg = PyList_GetItem(args, i); /* borrowed ref */
135 if (!PyString_Check(arg))
137 argv[i] = PyString_AsString(arg); /* borrowed pointer */
141 return execute(PyString_AsString(cmdline), argv); /* borrowed pointer */
144 eConsoleAppContainer::~eConsoleAppContainer()
149 void eConsoleAppContainer::kill()
151 if ( killstate != -1 && pid != -1 )
153 eDebug("user kill(SIGKILL) console App");
156 * Use a negative pid value, to signal the whole process group
157 * ('pid' might not even be running anymore at this point)
159 ::kill(-pid, SIGKILL);
162 while( outbuf.size() ) // cleanup out buffer
164 queue_data d = outbuf.front();
173 for (int i=0; i < 3; ++i)
180 void eConsoleAppContainer::sendCtrlC()
182 if ( killstate != -1 && pid != -1 )
184 eDebug("user send SIGINT(Ctrl-C) to console App");
186 * Use a negative pid value, to signal the whole process group
187 * ('pid' might not even be running anymore at this point)
189 ::kill(-pid, SIGINT);
193 void eConsoleAppContainer::sendEOF()
204 void eConsoleAppContainer::closePipes()
227 eDebug("pipes closed");
228 while( outbuf.size() ) // cleanup out buffer
230 queue_data d = outbuf.front();
237 void eConsoleAppContainer::readyRead(int what)
239 bool hungup = what & eSocketNotifier::Hungup;
240 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
242 // eDebug("what = %d");
245 while((rd = read(fd[0], buf, 2048)) > 0)
248 /*emit*/ dataAvail(buf);
251 ::write(filefd[1], buf, rd);
256 readyErrRead(eSocketNotifier::Priority|eSocketNotifier::Read); /* be sure to flush all data which might be already written */
259 eDebug("child has terminated");
262 int retval = killstate;
264 * We have to call 'wait' on the child process, in order to avoid zombies.
265 * Also, this gives us the chance to provide better exit status info to appClosed.
267 if (::waitpid(pid, &childstatus, 0) > 0)
269 if (WIFEXITED(childstatus))
271 retval = WEXITSTATUS(childstatus);
274 /*emit*/ appClosed(retval);
278 void eConsoleAppContainer::readyErrRead(int what)
280 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
282 // eDebug("what = %d");
285 while((rd = read(fd[2], buf, 2048)) > 0)
287 /* for ( int i = 0; i < rd; i++ )
288 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
290 /*emit*/ dataAvail(buf);
296 void eConsoleAppContainer::dumpToFile( PyObject *py_filename )
298 char *filename = PyString_AsString(py_filename);
299 filefd[1] = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
300 eDebug("eConsoleAppContainer::dumpToFile open(%s, O_WRONLY|O_CREAT|O_TRUNC, 0644)=%i", filename, filefd[1]);
303 void eConsoleAppContainer::readFromFile( PyObject *py_filename )
305 char *filename = PyString_AsString(py_filename);
306 char readbuf[32*1024];
307 filefd[0] = open(filename, O_RDONLY);
308 int rsize = read(filefd[0], readbuf, 32*1024);
309 eDebug("eConsoleAppContainer::readFromFile open(%s, O_RDONLY)=%i, read: %i", filename, filefd[0], rsize);
310 if ( filefd[0] > 0 && rsize > 0 )
311 write(readbuf, rsize);
314 void eConsoleAppContainer::write( const char *data, int len )
316 char *tmp = new char[len];
317 memcpy(tmp, data, len);
318 outbuf.push(queue_data(tmp,len));
323 void eConsoleAppContainer::write( PyObject *data )
327 if (PyString_AsStringAndSize(data, &buffer, &length))
329 if (buffer && length)
330 write(buffer, length);
333 void eConsoleAppContainer::readyWrite(int what)
335 if (what&eSocketNotifier::Write && outbuf.size() )
337 queue_data &d = outbuf.front();
338 int wr = ::write( fd[1], d.data+d.dataSent, d.len-d.dataSent );
340 eDebug("eConsoleAppContainer write failed (%m)");
343 if (d.dataSent == d.len)
347 if ( filefd[0] == -1 )
348 /* emit */ dataSent(0);
351 if ( !outbuf.size() )
355 char readbuf[32*1024];
356 int rsize = read(filefd[0], readbuf, 32*1024);
358 write(readbuf, rsize);
364 eDebug("readFromFile done - closing eConsoleAppContainer stdin pipe");