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 DEFINE_REF(eConsoleAppContainer);
60 eConsoleAppContainer::eConsoleAppContainer()
61 :pid(-1), killstate(0)
63 for (int i=0; i < 3; ++i)
70 int eConsoleAppContainer::setCWD( const char *path )
74 if (stat(path, &dir_stat) == -1)
77 if (!S_ISDIR(dir_stat.st_mode))
84 int eConsoleAppContainer::execute( const char *cmd )
87 const char *argv[argc + 1];
93 return execute(argv[0], argv);
96 int eConsoleAppContainer::execute(const char *cmdline, const char * const argv[])
104 // get one read ,one write and the err pipe to the prog..
105 pid = bidirpipe(fd, cmdline, argv, m_cwd.length() ? m_cwd.c_str() : 0);
110 // eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
112 ::fcntl(fd[0], F_SETFL, O_NONBLOCK);
113 ::fcntl(fd[1], F_SETFL, O_NONBLOCK);
114 ::fcntl(fd[2], F_SETFL, O_NONBLOCK);
115 in = eSocketNotifier::create(eApp, fd[0], eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Hungup );
116 out = eSocketNotifier::create(eApp, fd[1], eSocketNotifier::Write, false);
117 err = eSocketNotifier::create(eApp, fd[2], eSocketNotifier::Read|eSocketNotifier::Priority );
118 CONNECT(in->activated, eConsoleAppContainer::readyRead);
119 CONNECT(out->activated, eConsoleAppContainer::readyWrite);
120 CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
121 in->m_clients.push_back(this);
122 out->m_clients.push_back(this);
123 err->m_clients.push_back(this);
128 eConsoleAppContainer::~eConsoleAppContainer()
133 void eConsoleAppContainer::kill()
135 if ( killstate != -1 && pid != -1 )
137 eDebug("user kill(SIGKILL) console App");
140 * Use a negative pid value, to signal the whole process group
141 * ('pid' might not even be running anymore at this point)
143 ::kill(-pid, SIGKILL);
146 while( outbuf.size() ) // cleanup out buffer
148 queue_data d = outbuf.front();
156 for (int i=0; i < 3; ++i)
163 void eConsoleAppContainer::sendCtrlC()
165 if ( killstate != -1 && pid != -1 )
167 eDebug("user send SIGINT(Ctrl-C) to console App");
169 * Use a negative pid value, to signal the whole process group
170 * ('pid' might not even be running anymore at this point)
172 ::kill(-pid, SIGINT);
176 void eConsoleAppContainer::sendEOF()
187 void eConsoleAppContainer::closePipes()
210 eDebug("pipes closed");
211 while( outbuf.size() ) // cleanup out buffer
213 queue_data d = outbuf.front();
217 in = 0; out = 0; err = 0;
221 void eConsoleAppContainer::readyRead(int what)
223 bool hungup = what & eSocketNotifier::Hungup;
224 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
226 // eDebug("what = %d");
229 while((rd = read(fd[0], buf, 2048)) > 0)
232 /*emit*/ dataAvail(buf);
235 ::write(filefd[1], buf, rd);
240 readyErrRead(eSocketNotifier::Priority|eSocketNotifier::Read); /* be sure to flush all data which might be already written */
243 eDebug("child has terminated");
246 int retval = killstate;
248 * We have to call 'wait' on the child process, in order to avoid zombies.
249 * Also, this gives us the chance to provide better exit status info to appClosed.
251 if (::waitpid(pid, &childstatus, 0) > 0)
253 if (WIFEXITED(childstatus))
255 retval = WEXITSTATUS(childstatus);
258 /*emit*/ appClosed(retval);
262 void eConsoleAppContainer::readyErrRead(int what)
264 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
266 // eDebug("what = %d");
269 while((rd = read(fd[2], buf, 2048)) > 0)
271 /* for ( int i = 0; i < rd; i++ )
272 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
274 /*emit*/ dataAvail(buf);
280 void eConsoleAppContainer::write( const char *data, int len )
282 char *tmp = new char[len];
283 memcpy(tmp, data, len);
284 outbuf.push(queue_data(tmp,len));
289 void eConsoleAppContainer::readyWrite(int what)
291 if (what&eSocketNotifier::Write && outbuf.size() )
293 queue_data &d = outbuf.front();
294 int wr = ::write( fd[1], d.data+d.dataSent, d.len-d.dataSent );
296 eDebug("eConsoleAppContainer write failed (%m)");
299 if (d.dataSent == d.len)
303 if ( filefd[0] == -1 )
304 /* emit */ dataSent(0);
307 if ( !outbuf.size() )
311 char readbuf[32*1024];
312 int rsize = read(filefd[0], readbuf, 32*1024);
314 write(readbuf, rsize);
320 eDebug("readFromFile done - closing eConsoleAppContainer stdin pipe");
331 #include "structmember.h"
338 eConsoleAppContainer *cont;
339 PyObject *in_weakreflist; /* List of weak references */
343 eConsolePy_dataAvail(eConsolePy *self, void *closure)
345 return self->cont->dataAvail.get();
349 eConsolePy_stdoutAvail(eConsolePy *self, void *closure)
351 return self->cont->stdoutAvail.get();
355 eConsolePy_stderrAvail(eConsolePy *self, void *closure)
357 return self->cont->stderrAvail.get();
361 eConsolePy_dataSent(eConsolePy *self, void *closure)
363 return self->cont->dataSent.get();
367 eConsolePy_appClosed(eConsolePy *self, void *closure)
369 return self->cont->appClosed.get();
372 static PyGetSetDef eConsolePy_getseters[] = {
374 (getter)eConsolePy_dataAvail, (setter)0,
375 "dataAvail callback list",
378 (getter)eConsolePy_stdoutAvail, (setter)0,
379 "stdoutAvail callback list",
382 (getter)eConsolePy_stderrAvail, (setter)0,
383 "stderrAvail callback list",
386 (getter)eConsolePy_dataSent, (setter)0,
387 "dataSent callback list",
390 (getter)eConsolePy_appClosed, (setter)0,
391 "appClosed callback list",
393 {NULL} /* Sentinel */
397 eConsolePy_traverse(eConsolePy *self, visitproc visit, void *arg)
399 PyObject *obj = self->cont->dataAvail.getSteal();
403 obj = self->cont->stdoutAvail.getSteal();
407 obj = self->cont->stderrAvail.getSteal();
411 obj = self->cont->dataSent.getSteal();
415 obj = self->cont->appClosed.getSteal();
423 eConsolePy_clear(eConsolePy *self)
425 PyObject *obj = self->cont->dataAvail.getSteal(true);
429 obj = self->cont->stdoutAvail.getSteal(true);
433 obj = self->cont->stderrAvail.getSteal(true);
437 obj = self->cont->dataSent.getSteal(true);
441 obj = self->cont->appClosed.getSteal(true);
449 eConsolePy_dealloc(eConsolePy* self)
451 if (self->in_weakreflist != NULL)
452 PyObject_ClearWeakRefs((PyObject *) self);
453 eConsolePy_clear(self);
454 self->cont->Release();
455 self->ob_type->tp_free((PyObject*)self);
459 eConsolePy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
461 eConsolePy *self = (eConsolePy *)type->tp_alloc(type, 0);
462 self->cont = new eConsoleAppContainer();
463 self->cont->AddRef();
464 self->in_weakreflist = NULL;
465 return (PyObject *)self;
469 eConsolePy_running(eConsolePy* self)
471 PyObject *ret = NULL;
472 ret = self->cont->running() ? Py_True : Py_False;
478 eConsolePy_execute(eConsolePy* self, PyObject *argt)
480 Py_ssize_t argc = PyTuple_Size(argt);
483 const char *argv[argc + 1];
487 PyObject *arg = PyTuple_GET_ITEM(argt, argpos);
488 if (!PyString_Check(arg))
492 snprintf(err, 255, "arg %d is not a string", argpos);
494 snprintf(err, 255, "cmd is not a string!");
495 PyErr_SetString(PyExc_TypeError, err);
498 argv[argpos++] = PyString_AsString(arg);
501 return PyInt_FromLong(self->cont->execute(argv[0], argv+1));
506 if (PyArg_ParseTuple(argt, "s", &str))
507 return PyInt_FromLong(self->cont->execute(str));
508 PyErr_SetString(PyExc_TypeError,
509 "cmd is not a string!");
515 eConsolePy_write(eConsolePy* self, PyObject *args)
520 Py_ssize_t argc = PyTuple_Size(args);
522 ret = PyArg_ParseTuple(args, "si", &data, &len);
526 ret = !PyArg_ParseTuple(args, "O", &ob) || !PyString_Check(ob);
530 if (!PyString_AsStringAndSize(ob, &data, &length))
538 PyErr_SetString(PyExc_TypeError,
539 "1st arg must be a string, optionaly 2nd arg can be the string length");
542 self->cont->write(data, len);
547 eConsolePy_getPID(eConsolePy* self)
549 return PyInt_FromLong(self->cont->getPID());
553 eConsolePy_setCWD(eConsolePy* self, PyObject *args)
556 if (!PyArg_ParseTuple(args, "s", &path))
558 self->cont->setCWD(path);
563 eConsolePy_kill(eConsolePy* self)
570 eConsolePy_sendCtrlC(eConsolePy* self)
572 self->cont->sendCtrlC();
577 eConsolePy_sendEOF(eConsolePy* self)
579 self->cont->sendEOF();
584 eConsolePy_dumpToFile(eConsolePy* self, PyObject *args)
587 if (!PyArg_ParseTuple(args, "s", &filename))
589 PyErr_SetString(PyExc_TypeError,
590 "arg must be a string (filename)");
595 int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
596 self->cont->setFileFD(1, fd);
597 eDebug("eConsoleAppContainer::dumpToFile open(%s, O_WRONLY|O_CREAT|O_TRUNC, 0644)=%d", filename, fd);
603 eConsolePy_readFromFile(eConsolePy* self, PyObject *args)
606 if (!PyArg_ParseTuple(args, "s", &filename))
608 PyErr_SetString(PyExc_TypeError,
609 "arg must be a string (filename)");
614 int fd = open(filename, O_RDONLY);
617 char readbuf[32*1024];
618 int rsize = read(fd, readbuf, 32*1024);
619 self->cont->setFileFD(0, fd);
620 eDebug("eConsoleAppContainer::readFromFile open(%s, O_RDONLY)=%d, read: %d", filename, fd, rsize);
621 self->cont->write(readbuf, rsize);
625 eDebug("eConsoleAppContainer::readFromFile %s not exist!", filename);
626 self->cont->setFileFD(0, -1);
632 static PyMethodDef eConsolePy_methods[] = {
633 {"setCWD", (PyCFunction)eConsolePy_setCWD, METH_VARARGS,
636 {"execute", (PyCFunction)eConsolePy_execute, METH_VARARGS,
639 {"dumpToFile", (PyCFunction)eConsolePy_dumpToFile, METH_VARARGS,
642 {"readFromFile", (PyCFunction)eConsolePy_readFromFile, METH_VARARGS,
645 {"getPID", (PyCFunction)eConsolePy_getPID, METH_NOARGS,
648 {"kill", (PyCFunction)eConsolePy_kill, METH_NOARGS,
651 {"sendCtrlC", (PyCFunction)eConsolePy_sendCtrlC, METH_NOARGS,
652 "send Ctrl-C to application"
654 {"sendEOF", (PyCFunction)eConsolePy_sendEOF, METH_NOARGS,
655 "send EOF to application"
657 {"write", (PyCFunction)eConsolePy_write, METH_VARARGS,
658 "write data to application"
660 {"running", (PyCFunction)eConsolePy_running, METH_NOARGS,
661 "returns the running state"
663 {NULL} /* Sentinel */
666 static PyTypeObject eConsolePyType = {
667 PyObject_HEAD_INIT(NULL)
669 "eConsoleImpl.eConsoleAppContainer", /*tp_name*/
670 sizeof(eConsolePy), /*tp_basicsize*/
672 (destructor)eConsolePy_dealloc, /*tp_dealloc*/
679 0, /*tp_as_sequence*/
687 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
688 "eConsoleAppContainer objects", /* tp_doc */
689 (traverseproc)eConsolePy_traverse, /* tp_traverse */
690 (inquiry)eConsolePy_clear, /* tp_clear */
691 0, /* tp_richcompare */
692 offsetof(eConsolePy, in_weakreflist), /* tp_weaklistoffset */
695 eConsolePy_methods, /* tp_methods */
697 eConsolePy_getseters, /* tp_getset */
700 0, /* tp_descr_get */
701 0, /* tp_descr_set */
702 0, /* tp_dictoffset */
705 eConsolePy_new, /* tp_new */
708 static PyMethodDef module_methods[] = {
709 {NULL} /* Sentinel */
712 void eConsoleInit(void)
714 PyObject* m = Py_InitModule3("eConsoleImpl", module_methods,
715 "Module that implements eConsoleAppContainer with working cyclic garbage collection.");
720 if (!PyType_Ready(&eConsolePyType))
722 Org_Py_INCREF((PyObject*)&eConsolePyType);
723 PyModule_AddObject(m, "eConsoleAppContainer", (PyObject*)&eConsolePyType);