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 eConsoleAppContainer::~eConsoleAppContainer()
127 void eConsoleAppContainer::kill()
129 if ( killstate != -1 && pid != -1 )
131 eDebug("user kill(SIGKILL) console App");
134 * Use a negative pid value, to signal the whole process group
135 * ('pid' might not even be running anymore at this point)
137 ::kill(-pid, SIGKILL);
140 while( outbuf.size() ) // cleanup out buffer
142 queue_data d = outbuf.front();
151 for (int i=0; i < 3; ++i)
158 void eConsoleAppContainer::sendCtrlC()
160 if ( killstate != -1 && pid != -1 )
162 eDebug("user send SIGINT(Ctrl-C) to console App");
164 * Use a negative pid value, to signal the whole process group
165 * ('pid' might not even be running anymore at this point)
167 ::kill(-pid, SIGINT);
171 void eConsoleAppContainer::sendEOF()
182 void eConsoleAppContainer::closePipes()
205 eDebug("pipes closed");
206 while( outbuf.size() ) // cleanup out buffer
208 queue_data d = outbuf.front();
215 void eConsoleAppContainer::readyRead(int what)
217 bool hungup = what & eSocketNotifier::Hungup;
218 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
220 // eDebug("what = %d");
223 while((rd = read(fd[0], buf, 2048)) > 0)
226 /*emit*/ dataAvail(buf);
229 ::write(filefd[1], buf, rd);
234 readyErrRead(eSocketNotifier::Priority|eSocketNotifier::Read); /* be sure to flush all data which might be already written */
237 eDebug("child has terminated");
240 int retval = killstate;
242 * We have to call 'wait' on the child process, in order to avoid zombies.
243 * Also, this gives us the chance to provide better exit status info to appClosed.
245 if (::waitpid(pid, &childstatus, 0) > 0)
247 if (WIFEXITED(childstatus))
249 retval = WEXITSTATUS(childstatus);
252 /*emit*/ appClosed(retval);
256 void eConsoleAppContainer::readyErrRead(int what)
258 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
260 // eDebug("what = %d");
263 while((rd = read(fd[2], buf, 2048)) > 0)
265 /* for ( int i = 0; i < rd; i++ )
266 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
268 /*emit*/ dataAvail(buf);
274 void eConsoleAppContainer::write( const char *data, int len )
276 char *tmp = new char[len];
277 memcpy(tmp, data, len);
278 outbuf.push(queue_data(tmp,len));
283 void eConsoleAppContainer::readyWrite(int what)
285 if (what&eSocketNotifier::Write && outbuf.size() )
287 queue_data &d = outbuf.front();
288 int wr = ::write( fd[1], d.data+d.dataSent, d.len-d.dataSent );
290 eDebug("eConsoleAppContainer write failed (%m)");
293 if (d.dataSent == d.len)
297 if ( filefd[0] == -1 )
298 /* emit */ dataSent(0);
301 if ( !outbuf.size() )
305 char readbuf[32*1024];
306 int rsize = read(filefd[0], readbuf, 32*1024);
308 write(readbuf, rsize);
314 eDebug("readFromFile done - closing eConsoleAppContainer stdin pipe");
325 #include "structmember.h"
332 eConsoleAppContainer *cont;
333 PyObject *in_weakreflist; /* List of weak references */
337 eConsolePy_dataAvail(eConsolePy *self, void *closure)
339 return self->cont->dataAvail.get();
343 eConsolePy_stdoutAvail(eConsolePy *self, void *closure)
345 return self->cont->stdoutAvail.get();
349 eConsolePy_stderrAvail(eConsolePy *self, void *closure)
351 return self->cont->stderrAvail.get();
355 eConsolePy_dataSent(eConsolePy *self, void *closure)
357 return self->cont->dataSent.get();
361 eConsolePy_appClosed(eConsolePy *self, void *closure)
363 return self->cont->appClosed.get();
366 static PyGetSetDef eConsolePy_getseters[] = {
368 (getter)eConsolePy_dataAvail, (setter)0,
369 "dataAvail callback list",
372 (getter)eConsolePy_stdoutAvail, (setter)0,
373 "stdoutAvail callback list",
376 (getter)eConsolePy_stderrAvail, (setter)0,
377 "stderrAvail callback list",
380 (getter)eConsolePy_dataSent, (setter)0,
381 "dataSent callback list",
384 (getter)eConsolePy_appClosed, (setter)0,
385 "appClosed callback list",
387 {NULL} /* Sentinel */
391 eConsolePy_traverse(eConsolePy *self, visitproc visit, void *arg)
393 PyObject *obj = self->cont->dataAvail.get(true);
397 obj = self->cont->stdoutAvail.get(true);
401 obj = self->cont->stderrAvail.get(true);
405 obj = self->cont->dataSent.get(true);
409 obj = self->cont->appClosed.get(true);
417 eConsolePy_clear(eConsolePy *self)
419 PyObject *obj = self->cont->dataAvail.get(true);
423 obj = self->cont->stdoutAvail.get(true);
427 obj = self->cont->stderrAvail.get(true);
431 obj = self->cont->dataSent.get(true);
435 obj = self->cont->appClosed.get(true);
443 eConsolePy_dealloc(eConsolePy* self)
445 if (self->in_weakreflist != NULL)
446 PyObject_ClearWeakRefs((PyObject *) self);
447 eConsolePy_clear(self);
449 self->ob_type->tp_free((PyObject*)self);
453 eConsolePy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
455 eConsolePy *self = (eConsolePy *)type->tp_alloc(type, 0);
456 self->cont = new eConsoleAppContainer();
457 self->in_weakreflist = NULL;
458 return (PyObject *)self;
462 eConsolePy_running(eConsolePy* self)
464 PyObject *ret = NULL;
465 ret = self->cont->running() ? Py_True : Py_False;
471 eConsolePy_execute(eConsolePy* self, PyObject *argt)
474 if (PyArg_ParseTuple(argt, "s", &str))
475 return PyInt_FromLong(self->cont->execute(str));
476 PyErr_SetString(PyExc_TypeError,
477 "argument is not a string");
482 eConsolePy_write(eConsolePy* self, PyObject *args)
486 if (PyArg_ParseTuple(args, "si", &data, &len))
491 if (!PyArg_ParseTuple(args, "O", &ob) || !PyString_Check(ob))
493 PyErr_SetString(PyExc_TypeError,
494 "1st arg must be a string, optionaly 2nd arg can be the string length");
500 if (!PyString_AsStringAndSize(ob, &data, &length))
506 self->cont->write(data, len);
511 eConsolePy_getPID(eConsolePy* self)
513 return PyInt_FromLong(self->cont->getPID());
517 eConsolePy_setCWD(eConsolePy* self, PyObject *args)
520 if (!PyArg_ParseTuple(args, "s", &path))
522 self->cont->setCWD(path);
527 eConsolePy_kill(eConsolePy* self)
534 eConsolePy_sendCtrlC(eConsolePy* self)
536 self->cont->sendCtrlC();
541 eConsolePy_sendEOF(eConsolePy* self)
543 self->cont->sendEOF();
548 eConsolePy_dumpToFile(eConsolePy* self, PyObject *args)
551 if (!PyArg_ParseTuple(args, "s", &filename))
553 PyErr_SetString(PyExc_TypeError,
554 "arg must be a string (filename)");
559 int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
560 self->cont->setFileFD(1, fd);
561 eDebug("eConsoleAppContainer::dumpToFile open(%s, O_WRONLY|O_CREAT|O_TRUNC, 0644)=%d", filename, fd);
567 eConsolePy_readFromFile(eConsolePy* self, PyObject *args)
570 if (!PyArg_ParseTuple(args, "s", &filename))
572 PyErr_SetString(PyExc_TypeError,
573 "arg must be a string (filename)");
578 int fd = open(filename, O_RDONLY);
581 char readbuf[32*1024];
582 int rsize = read(fd, readbuf, 32*1024);
583 self->cont->setFileFD(0, fd);
584 eDebug("eConsoleAppContainer::readFromFile open(%s, O_RDONLY)=%d, read: %d", filename, fd, rsize);
585 self->cont->write(readbuf, rsize);
589 eDebug("eConsoleAppContainer::readFromFile %s not exist!", filename);
590 self->cont->setFileFD(0, -1);
596 static PyMethodDef eConsolePy_methods[] = {
597 {"setCWD", (PyCFunction)eConsolePy_setCWD, METH_VARARGS,
600 {"execute", (PyCFunction)eConsolePy_execute, METH_VARARGS,
603 {"dumpToFile", (PyCFunction)eConsolePy_dumpToFile, METH_VARARGS,
606 {"readFromFile", (PyCFunction)eConsolePy_readFromFile, METH_VARARGS,
609 {"getPID", (PyCFunction)eConsolePy_getPID, METH_NOARGS,
612 {"kill", (PyCFunction)eConsolePy_kill, METH_NOARGS,
615 {"sendCtrlC", (PyCFunction)eConsolePy_sendCtrlC, METH_NOARGS,
616 "send Ctrl-C to application"
618 {"sendEOF", (PyCFunction)eConsolePy_sendEOF, METH_NOARGS,
619 "send EOF to application"
621 {"write", (PyCFunction)eConsolePy_write, METH_VARARGS,
622 "write data to application"
624 {"running", (PyCFunction)eConsolePy_running, METH_NOARGS,
625 "returns the running state"
627 {NULL} /* Sentinel */
630 static PyTypeObject eConsolePyType = {
631 PyObject_HEAD_INIT(NULL)
633 "eConsoleImpl.eConsoleAppContainer", /*tp_name*/
634 sizeof(eConsolePy), /*tp_basicsize*/
636 (destructor)eConsolePy_dealloc, /*tp_dealloc*/
643 0, /*tp_as_sequence*/
651 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
652 "eConsoleAppContainer objects", /* tp_doc */
653 (traverseproc)eConsolePy_traverse, /* tp_traverse */
654 (inquiry)eConsolePy_clear, /* tp_clear */
655 0, /* tp_richcompare */
656 offsetof(eConsolePy, in_weakreflist), /* tp_weaklistoffset */
659 eConsolePy_methods, /* tp_methods */
661 eConsolePy_getseters, /* tp_getset */
664 0, /* tp_descr_get */
665 0, /* tp_descr_set */
666 0, /* tp_dictoffset */
669 eConsolePy_new, /* tp_new */
672 static PyMethodDef module_methods[] = {
673 {NULL} /* Sentinel */
676 void eConsoleInit(void)
678 PyObject* m = Py_InitModule3("eConsoleImpl", module_methods,
679 "Module that implements eConsoleAppContainer with working cyclic garbage collection.");
684 if (!PyType_Ready(&eConsolePyType))
686 Org_Py_INCREF((PyObject*)&eConsolePyType);
687 PyModule_AddObject(m, "eConsoleAppContainer", (PyObject*)&eConsolePyType);