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 */
336 #define COMPATIBILITY_MODE
337 // with COMPATIBILITY_MODE enabled the callback list is accessed via console.appClosed.get()
338 // we remove this code after next enigma2 release... then the list should be accessed via console.appClosed ( without .get() )
340 #ifdef COMPATIBILITY_MODE
341 struct eListCompatibilityWrapper
345 PyObject *in_weakreflist; /* List of weak references */
349 eListCompatibilityWrapper_traverse(eListCompatibilityWrapper *self, visitproc visit, void *arg)
351 Py_VISIT(self->list);
356 eListCompatibilityWrapper_clear(eListCompatibilityWrapper *self)
358 Py_CLEAR(self->list);
363 eListCompatibilityWrapper_dealloc(eListCompatibilityWrapper* self)
365 if (self->in_weakreflist != NULL)
366 PyObject_ClearWeakRefs((PyObject *) self);
367 eListCompatibilityWrapper_clear(self);
368 Org_Py_DECREF(self->list);
369 self->ob_type->tp_free((PyObject*)self);
373 eListCompatibilityWrapper_get(eListCompatibilityWrapper *self, void *closure)
375 Org_Py_INCREF(self->list);
379 static PyMethodDef eListCompatibilityWrapper_methods[] = {
380 {"get", (PyCFunction)eListCompatibilityWrapper_get, METH_NOARGS,
383 {NULL} /* Sentinel */
386 static PyGetSetDef eListCompatibilityWrapper_getseters[] = {
387 {NULL} /* Sentinel */
390 static PyTypeObject eListCompatibilityWrapperType = {
391 PyObject_HEAD_INIT(NULL)
393 "eConsoleImpl.eListCompatibilityWrapper", /*tp_name*/
394 sizeof(eListCompatibilityWrapper), /*tp_basicsize*/
396 (destructor)eListCompatibilityWrapper_dealloc, /*tp_dealloc*/
403 0, /*tp_as_sequence*/
411 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
412 "eListCompatibilityWrapper objects", /* tp_doc */
413 (traverseproc)eListCompatibilityWrapper_traverse, /* tp_traverse */
414 (inquiry)eListCompatibilityWrapper_clear, /* tp_clear */
415 0, /* tp_richcompare */
416 offsetof(eListCompatibilityWrapper, in_weakreflist), /* tp_weaklistoffset */
419 eListCompatibilityWrapper_methods, /* tp_methods */
421 eListCompatibilityWrapper_getseters, /* tp_getset */
424 0, /* tp_descr_get */
425 0, /* tp_descr_set */
426 0, /* tp_dictoffset */
433 eConsolePy_dataAvail(eConsolePy *self, void *closure)
435 eListCompatibilityWrapper *wrapper = (eListCompatibilityWrapper *)eListCompatibilityWrapperType.tp_alloc(&eListCompatibilityWrapperType, 0);
436 Org_Py_INCREF((PyObject*)wrapper);
437 wrapper->list = self->cont->dataAvail.get();
438 wrapper->in_weakreflist = NULL;
439 return (PyObject*)wrapper;
443 eConsolePy_stdoutAvail(eConsolePy *self, void *closure)
445 eListCompatibilityWrapper *wrapper = (eListCompatibilityWrapper *)eListCompatibilityWrapperType.tp_alloc(&eListCompatibilityWrapperType, 0);
446 Org_Py_INCREF((PyObject*)wrapper);
447 wrapper->list = self->cont->stdoutAvail.get();
448 wrapper->in_weakreflist = NULL;
449 return (PyObject*)wrapper;
453 eConsolePy_stderrAvail(eConsolePy *self, void *closure)
455 eListCompatibilityWrapper *wrapper = (eListCompatibilityWrapper *)eListCompatibilityWrapperType.tp_alloc(&eListCompatibilityWrapperType, 0);
456 Org_Py_INCREF((PyObject*)wrapper);
457 wrapper->list = self->cont->stderrAvail.get();
458 wrapper->in_weakreflist = NULL;
459 return (PyObject*)wrapper;
463 eConsolePy_dataSent(eConsolePy *self, void *closure)
465 eListCompatibilityWrapper *wrapper = (eListCompatibilityWrapper *)eListCompatibilityWrapperType.tp_alloc(&eListCompatibilityWrapperType, 0);
466 Org_Py_INCREF((PyObject*)wrapper);
467 wrapper->list = self->cont->dataSent.get();
468 wrapper->in_weakreflist = NULL;
469 return (PyObject*)wrapper;
473 eConsolePy_appClosed(eConsolePy *self, void *closure)
475 eListCompatibilityWrapper *wrapper = (eListCompatibilityWrapper *)eListCompatibilityWrapperType.tp_alloc(&eListCompatibilityWrapperType, 0);
476 Org_Py_INCREF((PyObject*)wrapper);
477 wrapper->list = self->cont->appClosed.get();
478 wrapper->in_weakreflist = NULL;
479 return (PyObject*)wrapper;
483 eConsolePy_dataAvail(eConsolePy *self, void *closure)
485 return self->cont->dataAvail.get();
489 eConsolePy_stdoutAvail(eConsolePy *self, void *closure)
491 return self->cont->stdoutAvail.get();
495 eConsolePy_stderrAvail(eConsolePy *self, void *closure)
497 return self->cont->stderrAvail.get();
501 eConsolePy_dataSent(eConsolePy *self, void *closure)
503 return self->cont->dataSent.get();
507 eConsolePy_appClosed(eConsolePy *self, void *closure)
509 return self->cont->appClosed.get();
513 static PyGetSetDef eConsolePy_getseters[] = {
515 (getter)eConsolePy_dataAvail, (setter)0,
516 "dataAvail callback list",
519 (getter)eConsolePy_stdoutAvail, (setter)0,
520 "stdoutAvail callback list",
523 (getter)eConsolePy_stderrAvail, (setter)0,
524 "stderrAvail callback list",
527 (getter)eConsolePy_dataSent, (setter)0,
528 "dataSent callback list",
531 (getter)eConsolePy_appClosed, (setter)0,
532 "appClosed callback list",
534 {NULL} /* Sentinel */
538 eConsolePy_traverse(eConsolePy *self, visitproc visit, void *arg)
540 PyObject *obj = self->cont->dataAvail.get(true);
544 obj = self->cont->stdoutAvail.get(true);
548 obj = self->cont->stderrAvail.get(true);
552 obj = self->cont->dataSent.get(true);
556 obj = self->cont->appClosed.get(true);
564 eConsolePy_clear(eConsolePy *self)
566 PyObject *obj = self->cont->dataAvail.get(true);
570 obj = self->cont->stdoutAvail.get(true);
574 obj = self->cont->stderrAvail.get(true);
578 obj = self->cont->dataSent.get(true);
582 obj = self->cont->appClosed.get(true);
590 eConsolePy_dealloc(eConsolePy* self)
592 if (self->in_weakreflist != NULL)
593 PyObject_ClearWeakRefs((PyObject *) self);
594 eConsolePy_clear(self);
596 self->ob_type->tp_free((PyObject*)self);
600 eConsolePy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
602 eConsolePy *self = (eConsolePy *)type->tp_alloc(type, 0);
603 self->cont = new eConsoleAppContainer();
604 self->in_weakreflist = NULL;
605 return (PyObject *)self;
609 eConsolePy_running(eConsolePy* self)
611 PyObject *ret = NULL;
612 ret = self->cont->running() ? Py_True : Py_False;
618 eConsolePy_execute(eConsolePy* self, PyObject *argt)
621 if (PyArg_ParseTuple(argt, "s", &str))
622 return PyInt_FromLong(self->cont->execute(str));
623 PyErr_SetString(PyExc_TypeError,
624 "argument is not a string");
629 eConsolePy_write(eConsolePy* self, PyObject *args)
633 if (PyArg_ParseTuple(args, "si", &data, &len))
638 if (!PyArg_ParseTuple(args, "O", &ob) || !PyString_Check(ob))
640 PyErr_SetString(PyExc_TypeError,
641 "1st arg must be a string, optionaly 2nd arg can be the string length");
647 if (!PyString_AsStringAndSize(ob, &data, &length))
653 self->cont->write(data, len);
658 eConsolePy_getPID(eConsolePy* self)
660 return PyInt_FromLong(self->cont->getPID());
664 eConsolePy_setCWD(eConsolePy* self, PyObject *args)
667 if (!PyArg_ParseTuple(args, "s", &path))
669 self->cont->setCWD(path);
674 eConsolePy_kill(eConsolePy* self)
681 eConsolePy_sendCtrlC(eConsolePy* self)
683 self->cont->sendCtrlC();
688 eConsolePy_sendEOF(eConsolePy* self)
690 self->cont->sendEOF();
695 eConsolePy_dumpToFile(eConsolePy* self, PyObject *args)
698 if (!PyArg_ParseTuple(args, "s", &filename))
700 PyErr_SetString(PyExc_TypeError,
701 "arg must be a string (filename)");
706 int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
707 self->cont->setFileFD(1, fd);
708 eDebug("eConsoleAppContainer::dumpToFile open(%s, O_WRONLY|O_CREAT|O_TRUNC, 0644)=%d", filename, fd);
714 eConsolePy_readFromFile(eConsolePy* self, PyObject *args)
717 if (!PyArg_ParseTuple(args, "s", &filename))
719 PyErr_SetString(PyExc_TypeError,
720 "arg must be a string (filename)");
725 int fd = open(filename, O_RDONLY);
728 char readbuf[32*1024];
729 int rsize = read(fd, readbuf, 32*1024);
730 self->cont->setFileFD(0, fd);
731 eDebug("eConsoleAppContainer::readFromFile open(%s, O_RDONLY)=%d, read: %d", filename, fd, rsize);
732 self->cont->write(readbuf, rsize);
736 eDebug("eConsoleAppContainer::readFromFile %s not exist!", filename);
737 self->cont->setFileFD(0, -1);
743 static PyMethodDef eConsolePy_methods[] = {
744 {"setCWD", (PyCFunction)eConsolePy_setCWD, METH_VARARGS,
747 {"execute", (PyCFunction)eConsolePy_execute, METH_VARARGS,
750 {"dumpToFile", (PyCFunction)eConsolePy_dumpToFile, METH_VARARGS,
753 {"readFromFile", (PyCFunction)eConsolePy_readFromFile, METH_VARARGS,
756 {"getPID", (PyCFunction)eConsolePy_getPID, METH_NOARGS,
759 {"kill", (PyCFunction)eConsolePy_kill, METH_NOARGS,
762 {"sendCtrlC", (PyCFunction)eConsolePy_sendCtrlC, METH_NOARGS,
763 "send Ctrl-C to application"
765 {"sendEOF", (PyCFunction)eConsolePy_sendEOF, METH_NOARGS,
766 "send EOF to application"
768 {"write", (PyCFunction)eConsolePy_write, METH_VARARGS,
769 "write data to application"
771 {"running", (PyCFunction)eConsolePy_running, METH_NOARGS,
772 "returns the running state"
774 {NULL} /* Sentinel */
777 static PyTypeObject eConsolePyType = {
778 PyObject_HEAD_INIT(NULL)
780 "eConsoleImpl.eConsoleAppContainer", /*tp_name*/
781 sizeof(eConsolePy), /*tp_basicsize*/
783 (destructor)eConsolePy_dealloc, /*tp_dealloc*/
790 0, /*tp_as_sequence*/
798 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
799 "eConsoleAppContainer objects", /* tp_doc */
800 (traverseproc)eConsolePy_traverse, /* tp_traverse */
801 (inquiry)eConsolePy_clear, /* tp_clear */
802 0, /* tp_richcompare */
803 offsetof(eConsolePy, in_weakreflist), /* tp_weaklistoffset */
806 eConsolePy_methods, /* tp_methods */
808 eConsolePy_getseters, /* tp_getset */
811 0, /* tp_descr_get */
812 0, /* tp_descr_set */
813 0, /* tp_dictoffset */
816 eConsolePy_new, /* tp_new */
819 static PyMethodDef module_methods[] = {
820 {NULL} /* Sentinel */
823 void eConsoleInit(void)
825 PyObject* m = Py_InitModule3("eConsoleImpl", module_methods,
826 "Module that implements eConsoleAppContainer with working cyclic garbage collection.");
831 #ifdef COMPATIBILITY_MODE
832 if (!PyType_Ready(&eListCompatibilityWrapperType))
834 Org_Py_INCREF((PyObject*)&eListCompatibilityWrapperType);
835 PyModule_AddObject(m, "eListCompatibilityWrapper", (PyObject*)&eListCompatibilityWrapperType);
838 if (!PyType_Ready(&eConsolePyType))
840 Org_Py_INCREF((PyObject*)&eConsolePyType);
841 PyModule_AddObject(m, "eConsoleAppContainer", (PyObject*)&eConsolePyType);