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 eDebug("eListCompatibilityWrapper_dealloc(eListCompatibilityWrapper* self) %p", self);
366 if (self->in_weakreflist != NULL)
367 PyObject_ClearWeakRefs((PyObject *) self);
368 eDebug("wrapper->list is %p",self->list);
369 eListCompatibilityWrapper_clear(self);
370 Org_Py_DECREF(self->list);
371 self->ob_type->tp_free((PyObject*)self);
375 eListCompatibilityWrapper_get(eListCompatibilityWrapper *self, void *closure)
377 eDebug("eListCompatibilityWrapper_get(eListCompatibilityWrapper *self, void *closure)");
378 Org_Py_INCREF(self->list);
382 static PyMethodDef eListCompatibilityWrapper_methods[] = {
383 {"get", (PyCFunction)eListCompatibilityWrapper_get, METH_NOARGS,
386 {NULL} /* Sentinel */
389 static PyGetSetDef eListCompatibilityWrapper_getseters[] = {
390 {NULL} /* Sentinel */
393 static PyTypeObject eListCompatibilityWrapperType = {
394 PyObject_HEAD_INIT(NULL)
396 "eConsoleImpl.eListCompatibilityWrapper", /*tp_name*/
397 sizeof(eListCompatibilityWrapper), /*tp_basicsize*/
399 (destructor)eListCompatibilityWrapper_dealloc, /*tp_dealloc*/
406 0, /*tp_as_sequence*/
414 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
415 "eListCompatibilityWrapper objects", /* tp_doc */
416 (traverseproc)eListCompatibilityWrapper_traverse, /* tp_traverse */
417 (inquiry)eListCompatibilityWrapper_clear, /* tp_clear */
418 0, /* tp_richcompare */
419 offsetof(eListCompatibilityWrapper, in_weakreflist), /* tp_weaklistoffset */
422 eListCompatibilityWrapper_methods, /* tp_methods */
424 eListCompatibilityWrapper_getseters, /* tp_getset */
427 0, /* tp_descr_get */
428 0, /* tp_descr_set */
429 0, /* tp_dictoffset */
436 eConsolePy_dataAvail(eConsolePy *self, void *closure)
438 eListCompatibilityWrapper *wrapper = (eListCompatibilityWrapper *)eListCompatibilityWrapperType.tp_alloc(&eListCompatibilityWrapperType, 0);
439 Org_Py_INCREF((PyObject*)wrapper);
440 wrapper->list = self->cont->dataAvail.get();
441 wrapper->in_weakreflist = NULL;
442 return (PyObject*)wrapper;
446 eConsolePy_stdoutAvail(eConsolePy *self, void *closure)
448 eListCompatibilityWrapper *wrapper = (eListCompatibilityWrapper *)eListCompatibilityWrapperType.tp_alloc(&eListCompatibilityWrapperType, 0);
449 Org_Py_INCREF((PyObject*)wrapper);
450 wrapper->list = self->cont->stdoutAvail.get();
451 wrapper->in_weakreflist = NULL;
452 return (PyObject*)wrapper;
456 eConsolePy_stderrAvail(eConsolePy *self, void *closure)
458 eListCompatibilityWrapper *wrapper = (eListCompatibilityWrapper *)eListCompatibilityWrapperType.tp_alloc(&eListCompatibilityWrapperType, 0);
459 Org_Py_INCREF((PyObject*)wrapper);
460 wrapper->list = self->cont->stderrAvail.get();
461 wrapper->in_weakreflist = NULL;
462 return (PyObject*)wrapper;
466 eConsolePy_dataSent(eConsolePy *self, void *closure)
468 eListCompatibilityWrapper *wrapper = (eListCompatibilityWrapper *)eListCompatibilityWrapperType.tp_alloc(&eListCompatibilityWrapperType, 0);
469 Org_Py_INCREF((PyObject*)wrapper);
470 wrapper->list = self->cont->dataSent.get();
471 wrapper->in_weakreflist = NULL;
472 return (PyObject*)wrapper;
476 eConsolePy_appClosed(eConsolePy *self, void *closure)
478 eListCompatibilityWrapper *wrapper = (eListCompatibilityWrapper *)eListCompatibilityWrapperType.tp_alloc(&eListCompatibilityWrapperType, 0);
479 Org_Py_INCREF((PyObject*)wrapper);
480 wrapper->list = self->cont->appClosed.get();
481 wrapper->in_weakreflist = NULL;
482 return (PyObject*)wrapper;
486 eConsolePy_dataAvail(eConsolePy *self, void *closure)
488 return self->cont->dataAvail.get();
492 eConsolePy_stdoutAvail(eConsolePy *self, void *closure)
494 return self->cont->stdoutAvail.get();
498 eConsolePy_stderrAvail(eConsolePy *self, void *closure)
500 return self->cont->stderrAvail.get();
504 eConsolePy_dataSent(eConsolePy *self, void *closure)
506 return self->cont->dataSent.get();
510 eConsolePy_appClosed(eConsolePy *self, void *closure)
512 return self->cont->appClosed.get();
516 static PyGetSetDef eConsolePy_getseters[] = {
518 (getter)eConsolePy_dataAvail, (setter)0,
519 "dataAvail callback list",
522 (getter)eConsolePy_stdoutAvail, (setter)0,
523 "stdoutAvail callback list",
526 (getter)eConsolePy_stderrAvail, (setter)0,
527 "stderrAvail callback list",
530 (getter)eConsolePy_dataSent, (setter)0,
531 "dataSent callback list",
534 (getter)eConsolePy_appClosed, (setter)0,
535 "appClosed callback list",
537 {NULL} /* Sentinel */
541 eConsolePy_traverse(eConsolePy *self, visitproc visit, void *arg)
543 PyObject *obj = self->cont->dataAvail.get(true);
547 obj = self->cont->stdoutAvail.get(true);
551 obj = self->cont->stderrAvail.get(true);
555 obj = self->cont->dataSent.get(true);
559 obj = self->cont->appClosed.get(true);
567 eConsolePy_clear(eConsolePy *self)
569 PyObject *obj = self->cont->dataAvail.get(true);
573 obj = self->cont->stdoutAvail.get(true);
577 obj = self->cont->stderrAvail.get(true);
581 obj = self->cont->dataSent.get(true);
585 obj = self->cont->appClosed.get(true);
593 eConsolePy_dealloc(eConsolePy* self)
595 if (self->in_weakreflist != NULL)
596 PyObject_ClearWeakRefs((PyObject *) self);
597 eConsolePy_clear(self);
599 self->ob_type->tp_free((PyObject*)self);
603 eConsolePy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
605 eConsolePy *self = (eConsolePy *)type->tp_alloc(type, 0);
606 self->cont = new eConsoleAppContainer();
607 self->in_weakreflist = NULL;
608 return (PyObject *)self;
612 eConsolePy_running(eConsolePy* self)
614 PyObject *ret = NULL;
615 ret = self->cont->running() ? Py_True : Py_False;
621 eConsolePy_execute(eConsolePy* self, PyObject *argt)
623 if (PyTuple_Size(argt) > 1)
625 PyObject *cmdline, *args;
626 PyArg_ParseTuple(args, "OO", &cmdline, &args);
627 if (!PyString_Check(cmdline) || !PyList_Check(args))
628 return PyInt_FromLong(-2);
631 const char *argv[PyList_Size(args) + 1];
633 for (; i < PyList_Size(args); ++i)
635 PyObject *arg = PyList_GetItem(args, i); /* borrowed ref */
636 if (!PyString_Check(arg))
637 return PyInt_FromLong(-3);
638 argv[i] = PyString_AsString(arg); /* borrowed pointer */
641 return PyInt_FromLong(self->cont->execute(PyString_AsString(cmdline), argv)); /* borrowed pointer */
647 if (PyArg_ParseTuple(argt, "s", &str))
648 return PyInt_FromLong(self->cont->execute(str));
654 eConsolePy_write(eConsolePy* self, PyObject *args)
658 if (PyArg_ParseTuple(args, "si", &data, &len))
663 if (!PyArg_ParseTuple(args, "O", &ob) || !PyString_Check(ob))
668 if (!PyString_AsStringAndSize(ob, &data, &length))
674 self->cont->write(data, len);
679 eConsolePy_getPID(eConsolePy* self)
681 return PyInt_FromLong(self->cont->getPID());
685 eConsolePy_setCWD(eConsolePy* self, PyObject *args)
688 if (!PyArg_ParseTuple(args, "s", &path))
690 self->cont->setCWD(path);
695 eConsolePy_kill(eConsolePy* self)
702 eConsolePy_sendCtrlC(eConsolePy* self)
704 self->cont->sendCtrlC();
709 eConsolePy_sendEOF(eConsolePy* self)
711 self->cont->sendEOF();
716 eConsolePy_dumpToFile(eConsolePy* self, PyObject *args)
719 if (!PyArg_ParseTuple(args, "s", &filename))
723 int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
724 self->cont->setFileFD(1, fd);
725 eDebug("eConsoleAppContainer::dumpToFile open(%s, O_WRONLY|O_CREAT|O_TRUNC, 0644)=%d", filename, fd);
731 eConsolePy_readFromFile(eConsolePy* self, PyObject *args)
734 if (!PyArg_ParseTuple(args, "s", &filename))
738 int fd = open(filename, O_RDONLY);
741 char readbuf[32*1024];
742 int rsize = read(fd, readbuf, 32*1024);
743 self->cont->setFileFD(0, fd);
744 eDebug("eConsoleAppContainer::readFromFile open(%s, O_RDONLY)=%d, read: %d", filename, fd, rsize);
745 self->cont->write(readbuf, rsize);
749 eDebug("eConsoleAppContainer::readFromFile %s not exist!", filename);
750 self->cont->setFileFD(0, -1);
756 static PyMethodDef eConsolePy_methods[] = {
757 {"setCWD", (PyCFunction)eConsolePy_setCWD, METH_VARARGS,
760 {"execute", (PyCFunction)eConsolePy_execute, METH_VARARGS,
763 {"dumpToFile", (PyCFunction)eConsolePy_dumpToFile, METH_VARARGS,
766 {"readFromFile", (PyCFunction)eConsolePy_readFromFile, METH_VARARGS,
769 {"getPID", (PyCFunction)eConsolePy_getPID, METH_NOARGS,
772 {"kill", (PyCFunction)eConsolePy_kill, METH_NOARGS,
775 {"sendCtrlC", (PyCFunction)eConsolePy_sendCtrlC, METH_NOARGS,
776 "send Ctrl-C to application"
778 {"sendEOF", (PyCFunction)eConsolePy_sendEOF, METH_NOARGS,
779 "send EOF to application"
781 {"write", (PyCFunction)eConsolePy_write, METH_VARARGS,
782 "write data to application"
784 {"running", (PyCFunction)eConsolePy_running, METH_NOARGS,
785 "returns the running state"
787 {NULL} /* Sentinel */
790 static PyTypeObject eConsolePyType = {
791 PyObject_HEAD_INIT(NULL)
793 "eConsoleImpl.eConsoleAppContainer", /*tp_name*/
794 sizeof(eConsolePy), /*tp_basicsize*/
796 (destructor)eConsolePy_dealloc, /*tp_dealloc*/
803 0, /*tp_as_sequence*/
811 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
812 "eConsoleAppContainer objects", /* tp_doc */
813 (traverseproc)eConsolePy_traverse, /* tp_traverse */
814 (inquiry)eConsolePy_clear, /* tp_clear */
815 0, /* tp_richcompare */
816 offsetof(eConsolePy, in_weakreflist), /* tp_weaklistoffset */
819 eConsolePy_methods, /* tp_methods */
821 eConsolePy_getseters, /* tp_getset */
824 0, /* tp_descr_get */
825 0, /* tp_descr_set */
826 0, /* tp_dictoffset */
829 eConsolePy_new, /* tp_new */
832 static PyMethodDef module_methods[] = {
833 {NULL} /* Sentinel */
836 void eConsoleInit(void)
840 m = Py_InitModule3("eConsoleImpl", module_methods,
841 "Module that implements eConsoleAppContainer with working cyclic garbage collection.");
846 #ifdef COMPATIBILITY_MODE
847 if (!PyType_Ready(&eListCompatibilityWrapperType))
849 Org_Py_INCREF((PyObject*)&eListCompatibilityWrapperType);
850 PyModule_AddObject(m, "eListCompatibilityWrapper", (PyObject*)&eListCompatibilityWrapperType);
853 if (!PyType_Ready(&eConsolePyType))
855 Org_Py_INCREF((PyObject*)&eConsolePyType);
856 PyModule_AddObject(m, "eConsoleAppContainer", (PyObject*)&eConsolePyType);