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[1], F_SETFL, O_NONBLOCK);
113 ::fcntl(fd[2], F_SETFL, O_NONBLOCK);
114 in = eSocketNotifier::create(eApp, fd[0], eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Hungup );
115 out = eSocketNotifier::create(eApp, fd[1], eSocketNotifier::Write, false);
116 err = eSocketNotifier::create(eApp, fd[2], eSocketNotifier::Read|eSocketNotifier::Priority );
117 CONNECT(in->activated, eConsoleAppContainer::readyRead);
118 CONNECT(out->activated, eConsoleAppContainer::readyWrite);
119 CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
120 in->m_clients.push_back(this);
121 out->m_clients.push_back(this);
122 err->m_clients.push_back(this);
127 eConsoleAppContainer::~eConsoleAppContainer()
132 void eConsoleAppContainer::kill()
134 if ( killstate != -1 && pid != -1 )
136 eDebug("user kill(SIGKILL) console App");
139 * Use a negative pid value, to signal the whole process group
140 * ('pid' might not even be running anymore at this point)
142 ::kill(-pid, SIGKILL);
145 while( outbuf.size() ) // cleanup out buffer
147 queue_data d = outbuf.front();
155 for (int i=0; i < 3; ++i)
162 void eConsoleAppContainer::sendCtrlC()
164 if ( killstate != -1 && pid != -1 )
166 eDebug("user send SIGINT(Ctrl-C) to console App");
168 * Use a negative pid value, to signal the whole process group
169 * ('pid' might not even be running anymore at this point)
171 ::kill(-pid, SIGINT);
175 void eConsoleAppContainer::sendEOF()
186 void eConsoleAppContainer::closePipes()
209 eDebug("pipes closed");
210 while( outbuf.size() ) // cleanup out buffer
212 queue_data d = outbuf.front();
216 in = 0; out = 0; err = 0;
220 void eConsoleAppContainer::readyRead(int what)
222 bool hungup = what & eSocketNotifier::Hungup;
223 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
225 // eDebug("what = %d");
228 while((rd = read(fd[0], buf, 2048)) > 0)
231 /*emit*/ dataAvail(buf);
234 ::write(filefd[1], buf, rd);
239 readyErrRead(eSocketNotifier::Priority|eSocketNotifier::Read); /* be sure to flush all data which might be already written */
242 eDebug("child has terminated");
245 int retval = killstate;
247 * We have to call 'wait' on the child process, in order to avoid zombies.
248 * Also, this gives us the chance to provide better exit status info to appClosed.
250 if (::waitpid(pid, &childstatus, 0) > 0)
252 if (WIFEXITED(childstatus))
254 retval = WEXITSTATUS(childstatus);
257 /*emit*/ appClosed(retval);
261 void eConsoleAppContainer::readyErrRead(int what)
263 if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
265 // eDebug("what = %d");
268 while((rd = read(fd[2], buf, 2048)) > 0)
270 /* for ( int i = 0; i < rd; i++ )
271 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
273 /*emit*/ dataAvail(buf);
279 void eConsoleAppContainer::write( const char *data, int len )
281 char *tmp = new char[len];
282 memcpy(tmp, data, len);
283 outbuf.push(queue_data(tmp,len));
288 void eConsoleAppContainer::readyWrite(int what)
290 if (what&eSocketNotifier::Write && outbuf.size() )
292 queue_data &d = outbuf.front();
293 int wr = ::write( fd[1], d.data+d.dataSent, d.len-d.dataSent );
295 eDebug("eConsoleAppContainer write failed (%m)");
298 if (d.dataSent == d.len)
302 if ( filefd[0] == -1 )
303 /* emit */ dataSent(0);
306 if ( !outbuf.size() )
310 char readbuf[32*1024];
311 int rsize = read(filefd[0], readbuf, 32*1024);
313 write(readbuf, rsize);
319 eDebug("readFromFile done - closing eConsoleAppContainer stdin pipe");
330 #include "structmember.h"
337 eConsoleAppContainer *cont;
338 PyObject *in_weakreflist; /* List of weak references */
342 eConsolePy_dataAvail(eConsolePy *self, void *closure)
344 return self->cont->dataAvail.get();
348 eConsolePy_stdoutAvail(eConsolePy *self, void *closure)
350 return self->cont->stdoutAvail.get();
354 eConsolePy_stderrAvail(eConsolePy *self, void *closure)
356 return self->cont->stderrAvail.get();
360 eConsolePy_dataSent(eConsolePy *self, void *closure)
362 return self->cont->dataSent.get();
366 eConsolePy_appClosed(eConsolePy *self, void *closure)
368 return self->cont->appClosed.get();
371 static PyGetSetDef eConsolePy_getseters[] = {
373 (getter)eConsolePy_dataAvail, (setter)0,
374 "dataAvail callback list",
377 (getter)eConsolePy_stdoutAvail, (setter)0,
378 "stdoutAvail callback list",
381 (getter)eConsolePy_stderrAvail, (setter)0,
382 "stderrAvail callback list",
385 (getter)eConsolePy_dataSent, (setter)0,
386 "dataSent callback list",
389 (getter)eConsolePy_appClosed, (setter)0,
390 "appClosed callback list",
392 {NULL} /* Sentinel */
396 eConsolePy_traverse(eConsolePy *self, visitproc visit, void *arg)
398 PyObject *obj = self->cont->dataAvail.getSteal();
402 obj = self->cont->stdoutAvail.getSteal();
406 obj = self->cont->stderrAvail.getSteal();
410 obj = self->cont->dataSent.getSteal();
414 obj = self->cont->appClosed.getSteal();
422 eConsolePy_clear(eConsolePy *self)
424 PyObject *obj = self->cont->dataAvail.getSteal(true);
428 obj = self->cont->stdoutAvail.getSteal(true);
432 obj = self->cont->stderrAvail.getSteal(true);
436 obj = self->cont->dataSent.getSteal(true);
440 obj = self->cont->appClosed.getSteal(true);
448 eConsolePy_dealloc(eConsolePy* self)
450 if (self->in_weakreflist != NULL)
451 PyObject_ClearWeakRefs((PyObject *) self);
452 eConsolePy_clear(self);
453 self->cont->Release();
454 self->ob_type->tp_free((PyObject*)self);
458 eConsolePy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
460 eConsolePy *self = (eConsolePy *)type->tp_alloc(type, 0);
461 self->cont = new eConsoleAppContainer();
462 self->cont->AddRef();
463 self->in_weakreflist = NULL;
464 return (PyObject *)self;
468 eConsolePy_running(eConsolePy* self)
470 PyObject *ret = NULL;
471 ret = self->cont->running() ? Py_True : Py_False;
477 eConsolePy_execute(eConsolePy* self, PyObject *argt)
479 Py_ssize_t argc = PyTuple_Size(argt);
482 const char *argv[argc + 1];
486 PyObject *arg = PyTuple_GET_ITEM(argt, argpos);
487 if (!PyString_Check(arg))
491 snprintf(err, 255, "arg %d is not a string", argpos);
493 snprintf(err, 255, "cmd is not a string!");
494 PyErr_SetString(PyExc_TypeError, err);
497 argv[argpos++] = PyString_AsString(arg);
500 return PyInt_FromLong(self->cont->execute(argv[0], argv+1));
505 if (PyArg_ParseTuple(argt, "s", &str))
506 return PyInt_FromLong(self->cont->execute(str));
507 PyErr_SetString(PyExc_TypeError,
508 "cmd is not a string!");
514 eConsolePy_write(eConsolePy* self, PyObject *args)
518 if (PyArg_ParseTuple(args, "si", &data, &len))
523 if (!PyArg_ParseTuple(args, "O", &ob) || !PyString_Check(ob))
525 PyErr_SetString(PyExc_TypeError,
526 "1st arg must be a string, optionaly 2nd arg can be the string length");
532 if (!PyString_AsStringAndSize(ob, &data, &length))
538 self->cont->write(data, len);
543 eConsolePy_getPID(eConsolePy* self)
545 return PyInt_FromLong(self->cont->getPID());
549 eConsolePy_setCWD(eConsolePy* self, PyObject *args)
552 if (!PyArg_ParseTuple(args, "s", &path))
554 self->cont->setCWD(path);
559 eConsolePy_kill(eConsolePy* self)
566 eConsolePy_sendCtrlC(eConsolePy* self)
568 self->cont->sendCtrlC();
573 eConsolePy_sendEOF(eConsolePy* self)
575 self->cont->sendEOF();
580 eConsolePy_dumpToFile(eConsolePy* self, PyObject *args)
583 if (!PyArg_ParseTuple(args, "s", &filename))
585 PyErr_SetString(PyExc_TypeError,
586 "arg must be a string (filename)");
591 int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
592 self->cont->setFileFD(1, fd);
593 eDebug("eConsoleAppContainer::dumpToFile open(%s, O_WRONLY|O_CREAT|O_TRUNC, 0644)=%d", filename, fd);
599 eConsolePy_readFromFile(eConsolePy* self, PyObject *args)
602 if (!PyArg_ParseTuple(args, "s", &filename))
604 PyErr_SetString(PyExc_TypeError,
605 "arg must be a string (filename)");
610 int fd = open(filename, O_RDONLY);
613 char readbuf[32*1024];
614 int rsize = read(fd, readbuf, 32*1024);
615 self->cont->setFileFD(0, fd);
616 eDebug("eConsoleAppContainer::readFromFile open(%s, O_RDONLY)=%d, read: %d", filename, fd, rsize);
617 self->cont->write(readbuf, rsize);
621 eDebug("eConsoleAppContainer::readFromFile %s not exist!", filename);
622 self->cont->setFileFD(0, -1);
628 static PyMethodDef eConsolePy_methods[] = {
629 {"setCWD", (PyCFunction)eConsolePy_setCWD, METH_VARARGS,
632 {"execute", (PyCFunction)eConsolePy_execute, METH_VARARGS,
635 {"dumpToFile", (PyCFunction)eConsolePy_dumpToFile, METH_VARARGS,
638 {"readFromFile", (PyCFunction)eConsolePy_readFromFile, METH_VARARGS,
641 {"getPID", (PyCFunction)eConsolePy_getPID, METH_NOARGS,
644 {"kill", (PyCFunction)eConsolePy_kill, METH_NOARGS,
647 {"sendCtrlC", (PyCFunction)eConsolePy_sendCtrlC, METH_NOARGS,
648 "send Ctrl-C to application"
650 {"sendEOF", (PyCFunction)eConsolePy_sendEOF, METH_NOARGS,
651 "send EOF to application"
653 {"write", (PyCFunction)eConsolePy_write, METH_VARARGS,
654 "write data to application"
656 {"running", (PyCFunction)eConsolePy_running, METH_NOARGS,
657 "returns the running state"
659 {NULL} /* Sentinel */
662 static PyTypeObject eConsolePyType = {
663 PyObject_HEAD_INIT(NULL)
665 "eConsoleImpl.eConsoleAppContainer", /*tp_name*/
666 sizeof(eConsolePy), /*tp_basicsize*/
668 (destructor)eConsolePy_dealloc, /*tp_dealloc*/
675 0, /*tp_as_sequence*/
683 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
684 "eConsoleAppContainer objects", /* tp_doc */
685 (traverseproc)eConsolePy_traverse, /* tp_traverse */
686 (inquiry)eConsolePy_clear, /* tp_clear */
687 0, /* tp_richcompare */
688 offsetof(eConsolePy, in_weakreflist), /* tp_weaklistoffset */
691 eConsolePy_methods, /* tp_methods */
693 eConsolePy_getseters, /* tp_getset */
696 0, /* tp_descr_get */
697 0, /* tp_descr_set */
698 0, /* tp_dictoffset */
701 eConsolePy_new, /* tp_new */
704 static PyMethodDef module_methods[] = {
705 {NULL} /* Sentinel */
708 void eConsoleInit(void)
710 PyObject* m = Py_InitModule3("eConsoleImpl", module_methods,
711 "Module that implements eConsoleAppContainer with working cyclic garbage collection.");
716 if (!PyType_Ready(&eConsolePyType))
718 Org_Py_INCREF((PyObject*)&eConsolePyType);
719 PyModule_AddObject(m, "eConsoleAppContainer", (PyObject*)&eConsolePyType);