add support for cyclic garbage collection to eConsoleAppContainer
[vuplus_dvbapp] / lib / base / console.cpp
1 #include <lib/base/console.h>
2 #include <lib/base/eerror.h>
3 #include <sys/vfs.h> // for statfs
4 #include <unistd.h>
5 #include <signal.h>
6 #include <errno.h>
7 #include <poll.h>
8 #include <sys/types.h>
9 #include <sys/wait.h>
10 #include <fcntl.h>
11
12 int bidirpipe(int pfd[], const char *cmd , const char * const argv[], const char *cwd )
13 {
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 */
18
19         if ( pipe(pfdin) == -1 || pipe(pfdout) == -1 || pipe(pfderr) == -1)
20                 return(-1);
21
22         if ( ( pid = vfork() ) == -1 )
23                 return(-1);
24         else if (pid == 0) /* child process */
25         {
26                 setsid();
27                 if ( close(0) == -1 || close(1) == -1 || close(2) == -1 )
28                         _exit(0);
29
30                 if (dup(pfdout[0]) != 0 || dup(pfdin[1]) != 1 || dup(pfderr[1]) != 2 )
31                         _exit(0);
32
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 )
36                         _exit(0);
37
38                 for (unsigned int i=3; i < 90; ++i )
39                         close(i);
40
41                 if (cwd)
42                         chdir(cwd);
43
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. */
46                 _exit(0);
47         }
48         if (close(pfdout[0]) == -1 || close(pfdin[1]) == -1 || close(pfderr[1]) == -1)
49                         return(-1);
50
51         pfd[0] = pfdin[0];
52         pfd[1] = pfdout[1];
53         pfd[2] = pfderr[0];
54
55         return(pid);
56 }
57
58 eConsoleAppContainer::eConsoleAppContainer()
59 :pid(-1), killstate(0), in(0), out(0), err(0)
60 {
61         for (int i=0; i < 3; ++i)
62         {
63                 fd[i]=-1;
64                 filefd[i]=-1;
65         }
66 }
67
68 int eConsoleAppContainer::setCWD( const char *path )
69 {
70         struct stat dir_stat;
71
72         if (stat(path, &dir_stat) == -1)
73                 return -1;
74
75         if (!S_ISDIR(dir_stat.st_mode))
76                 return -2;
77
78         m_cwd = path;
79         return 0;
80 }
81
82 int eConsoleAppContainer::execute( const char *cmd )
83 {
84         int argc = 3;
85         const char *argv[argc + 1];
86         argv[0] = "/bin/sh";
87         argv[1] = "-c";
88         argv[2] = cmd;
89         argv[argc] = NULL;
90
91         return execute(argv[0], argv);
92 }
93
94 int eConsoleAppContainer::execute(const char *cmdline, const char * const argv[])
95 {
96         if (running())
97                 return -1;
98
99         pid=-1;
100         killstate=0;
101
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);
104
105         if ( pid == -1 )
106                 return -3;
107
108 //      eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
109
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);
118
119         return 0;
120 }
121
122 eConsoleAppContainer::~eConsoleAppContainer()
123 {
124         kill();
125 }
126
127 void eConsoleAppContainer::kill()
128 {
129         if ( killstate != -1 && pid != -1 )
130         {
131                 eDebug("user kill(SIGKILL) console App");
132                 killstate=-1;
133                 /*
134                  * Use a negative pid value, to signal the whole process group
135                  * ('pid' might not even be running anymore at this point)
136                  */
137                 ::kill(-pid, SIGKILL);
138                 closePipes();
139         }
140         while( outbuf.size() ) // cleanup out buffer
141         {
142                 queue_data d = outbuf.front();
143                 outbuf.pop();
144                 delete [] d.data;
145         }
146         delete in;
147         delete out;
148         delete err;
149         in=out=err=0;
150
151         for (int i=0; i < 3; ++i)
152         {
153                 if ( filefd[i] > 0 )
154                         close(filefd[i]);
155         }
156 }
157
158 void eConsoleAppContainer::sendCtrlC()
159 {
160         if ( killstate != -1 && pid != -1 )
161         {
162                 eDebug("user send SIGINT(Ctrl-C) to console App");
163                 /*
164                  * Use a negative pid value, to signal the whole process group
165                  * ('pid' might not even be running anymore at this point)
166                  */
167                 ::kill(-pid, SIGINT);
168         }
169 }
170
171 void eConsoleAppContainer::sendEOF()
172 {
173         if (out)
174                 out->stop();
175         if (fd[1] != -1)
176         {
177                 ::close(fd[1]);
178                 fd[1]=-1;
179         }
180 }
181
182 void eConsoleAppContainer::closePipes()
183 {
184         if (in)
185                 in->stop();
186         if (out)
187                 out->stop();
188         if (err)
189                 err->stop();
190         if (fd[0] != -1)
191         {
192                 ::close(fd[0]);
193                 fd[0]=-1;
194         }
195         if (fd[1] != -1)
196         {
197                 ::close(fd[1]);
198                 fd[1]=-1;
199         }
200         if (fd[2] != -1)
201         {
202                 ::close(fd[2]);
203                 fd[2]=-1;
204         }
205         eDebug("pipes closed");
206         while( outbuf.size() ) // cleanup out buffer
207         {
208                 queue_data d = outbuf.front();
209                 outbuf.pop();
210                 delete [] d.data;
211         }
212         pid = -1;
213 }
214
215 void eConsoleAppContainer::readyRead(int what)
216 {
217         bool hungup = what & eSocketNotifier::Hungup;
218         if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
219         {
220 //              eDebug("what = %d");
221                 char buf[2049];
222                 int rd;
223                 while((rd = read(fd[0], buf, 2048)) > 0)
224                 {
225                         buf[rd]=0;
226                         /*emit*/ dataAvail(buf);
227                         stdoutAvail(buf);
228                         if ( filefd[1] > 0 )
229                                 ::write(filefd[1], buf, rd);
230                         if (!hungup)
231                                 break;
232                 }
233         }
234         readyErrRead(eSocketNotifier::Priority|eSocketNotifier::Read); /* be sure to flush all data which might be already written */
235         if (hungup)
236         {
237                 eDebug("child has terminated");
238                 closePipes();
239                 int childstatus;
240                 int retval = killstate;
241                 /*
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.
244                  */
245                 if (::waitpid(pid, &childstatus, 0) > 0)
246                 {
247                         if (WIFEXITED(childstatus))
248                         {
249                                 retval = WEXITSTATUS(childstatus);
250                         }
251                 }
252                 /*emit*/ appClosed(retval);
253         }
254 }
255
256 void eConsoleAppContainer::readyErrRead(int what)
257 {
258         if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
259         {
260 //              eDebug("what = %d");
261                 char buf[2049];
262                 int rd;
263                 while((rd = read(fd[2], buf, 2048)) > 0)
264                 {
265 /*                      for ( int i = 0; i < rd; i++ )
266                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
267                         buf[rd]=0;
268                         /*emit*/ dataAvail(buf);
269                         stderrAvail(buf);
270                 }
271         }
272 }
273
274 void eConsoleAppContainer::write( const char *data, int len )
275 {
276         char *tmp = new char[len];
277         memcpy(tmp, data, len);
278         outbuf.push(queue_data(tmp,len));
279         if (out)
280                 out->start();
281 }
282
283 void eConsoleAppContainer::readyWrite(int what)
284 {
285         if (what&eSocketNotifier::Write && outbuf.size() )
286         {
287                 queue_data &d = outbuf.front();
288                 int wr = ::write( fd[1], d.data+d.dataSent, d.len-d.dataSent );
289                 if (wr < 0)
290                         eDebug("eConsoleAppContainer write failed (%m)");
291                 else
292                         d.dataSent += wr;
293                 if (d.dataSent == d.len)
294                 {
295                         outbuf.pop();
296                         delete [] d.data;
297                         if ( filefd[0] == -1 )
298                         /* emit */ dataSent(0);
299                 }
300         }
301         if ( !outbuf.size() )
302         {
303                 if ( filefd[0] > 0 )
304                 {
305                         char readbuf[32*1024];
306                         int rsize = read(filefd[0], readbuf, 32*1024);
307                         if ( rsize > 0 )
308                                 write(readbuf, rsize);
309                         else
310                         {
311                                 close(filefd[0]);
312                                 filefd[0] = -1;
313                                 ::close(fd[1]);
314                                 eDebug("readFromFile done - closing eConsoleAppContainer stdin pipe");
315                                 fd[1]=-1;
316                                 dataSent(0);
317                                 out->stop();
318                         }
319                 }
320                 else
321                         out->stop();
322         }
323 }
324
325 #include "structmember.h"
326
327 extern "C" {
328
329 struct eConsolePy
330 {
331         PyObject_HEAD
332         eConsoleAppContainer *cont;
333         PyObject *in_weakreflist; /* List of weak references */
334 };
335
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() )
339
340 #ifdef COMPATIBILITY_MODE
341 struct eListCompatibilityWrapper
342 {
343         PyObject_HEAD
344         PyObject *list;
345         PyObject *in_weakreflist; /* List of weak references */
346 };
347
348 static int
349 eListCompatibilityWrapper_traverse(eListCompatibilityWrapper *self, visitproc visit, void *arg)
350 {
351         Py_VISIT(self->list);
352         return 0;
353 }
354
355 static int
356 eListCompatibilityWrapper_clear(eListCompatibilityWrapper *self)
357 {
358         Py_CLEAR(self->list);
359         return 0;
360 }
361
362 static void
363 eListCompatibilityWrapper_dealloc(eListCompatibilityWrapper* self)
364 {
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);
372 }
373
374 static PyObject *
375 eListCompatibilityWrapper_get(eListCompatibilityWrapper *self, void *closure)
376 {
377         eDebug("eListCompatibilityWrapper_get(eListCompatibilityWrapper *self, void *closure)");
378         Org_Py_INCREF(self->list);
379         return self->list;
380 }
381
382 static PyMethodDef eListCompatibilityWrapper_methods[] = {
383         {"get", (PyCFunction)eListCompatibilityWrapper_get, METH_NOARGS,
384          "returns the list"
385         },
386         {NULL}  /* Sentinel */
387 };
388
389 static PyGetSetDef eListCompatibilityWrapper_getseters[] = {
390         {NULL} /* Sentinel */
391 };
392
393 static PyTypeObject eListCompatibilityWrapperType = {
394         PyObject_HEAD_INIT(NULL)
395         0, /*ob_size*/
396         "eConsoleImpl.eListCompatibilityWrapper", /*tp_name*/
397         sizeof(eListCompatibilityWrapper), /*tp_basicsize*/
398         0, /*tp_itemsize*/
399         (destructor)eListCompatibilityWrapper_dealloc, /*tp_dealloc*/
400         0, /*tp_print*/
401         0, /*tp_getattr*/
402         0, /*tp_setattr*/
403         0, /*tp_compare*/
404         0, /*tp_repr*/
405         0, /*tp_as_number*/
406         0, /*tp_as_sequence*/
407         0, /*tp_as_mapping*/
408         0, /*tp_hash */
409         0, /*tp_call*/
410         0, /*tp_str*/
411         0, /*tp_getattro*/
412         0, /*tp_setattro*/
413         0, /*tp_as_buffer*/
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 */
420         0, /* tp_iter */
421         0, /* tp_iternext */
422         eListCompatibilityWrapper_methods, /* tp_methods */
423         0, /* tp_members */
424         eListCompatibilityWrapper_getseters, /* tp_getset */
425         0, /* tp_base */
426         0, /* tp_dict */
427         0, /* tp_descr_get */
428         0, /* tp_descr_set */
429         0, /* tp_dictoffset */
430         0, /* tp_init */
431         0, /* tp_alloc */
432         0, /* tp_new */
433 };
434
435 static PyObject *
436 eConsolePy_dataAvail(eConsolePy *self, void *closure)
437 {
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;
443 }
444
445 static PyObject *
446 eConsolePy_stdoutAvail(eConsolePy *self, void *closure)
447 {
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;
453 }
454
455 static PyObject *
456 eConsolePy_stderrAvail(eConsolePy *self, void *closure)
457 {
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;
463 }
464
465 static PyObject *
466 eConsolePy_dataSent(eConsolePy *self, void *closure)
467 {
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;
473 }
474
475 static PyObject *
476 eConsolePy_appClosed(eConsolePy *self, void *closure)
477 {
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;
483 }
484 #else
485 static PyObject *
486 eConsolePy_dataAvail(eConsolePy *self, void *closure)
487 {
488         return self->cont->dataAvail.get();
489 }
490
491 static PyObject *
492 eConsolePy_stdoutAvail(eConsolePy *self, void *closure)
493 {
494         return self->cont->stdoutAvail.get();
495 }
496
497 static PyObject *
498 eConsolePy_stderrAvail(eConsolePy *self, void *closure)
499 {
500         return self->cont->stderrAvail.get();
501 }
502
503 static PyObject *
504 eConsolePy_dataSent(eConsolePy *self, void *closure)
505 {
506         return self->cont->dataSent.get();
507 }
508
509 static PyObject *
510 eConsolePy_appClosed(eConsolePy *self, void *closure)
511 {
512         return self->cont->appClosed.get();
513 }
514 #endif
515
516 static PyGetSetDef eConsolePy_getseters[] = {
517         {"dataAvail",
518          (getter)eConsolePy_dataAvail, (setter)0,
519          "dataAvail callback list",
520          NULL},
521         {"stdoutAvail",
522          (getter)eConsolePy_stdoutAvail, (setter)0,
523          "stdoutAvail callback list",
524          NULL},
525         {"stderrAvail",
526          (getter)eConsolePy_stderrAvail, (setter)0,
527          "stderrAvail callback list",
528          NULL},
529         {"dataSent",
530          (getter)eConsolePy_dataSent, (setter)0,
531          "dataSent callback list",
532          NULL},
533         {"appClosed",
534          (getter)eConsolePy_appClosed, (setter)0,
535          "appClosed callback list",
536          NULL},
537         {NULL} /* Sentinel */
538 };
539
540 static int
541 eConsolePy_traverse(eConsolePy *self, visitproc visit, void *arg)
542 {
543         PyObject *obj = self->cont->dataAvail.get(true);
544         if (obj) {
545                 Py_VISIT(obj);
546         }
547         obj = self->cont->stdoutAvail.get(true);
548         if (obj) {
549                 Py_VISIT(obj);
550         }
551         obj = self->cont->stderrAvail.get(true);
552         if (obj) {
553                 Py_VISIT(obj);
554         }
555         obj = self->cont->dataSent.get(true);
556         if (obj) {
557                 Py_VISIT(obj);
558         }
559         obj = self->cont->appClosed.get(true);
560         if (obj) {
561                 Py_VISIT(obj);
562         }
563         return 0;
564 }
565
566 static int
567 eConsolePy_clear(eConsolePy *self)
568 {
569         PyObject *obj = self->cont->dataAvail.get(true);
570         if (obj) {
571                 Py_CLEAR(obj);
572         }
573         obj = self->cont->stdoutAvail.get(true);
574         if (obj) {
575                 Py_CLEAR(obj);
576         }
577         obj = self->cont->stderrAvail.get(true);
578         if (obj) {
579                 Py_CLEAR(obj);
580         }
581         obj = self->cont->dataSent.get(true);
582         if (obj) {
583                 Py_CLEAR(obj);
584         }
585         obj = self->cont->appClosed.get(true);
586         if (obj) {
587                 Py_CLEAR(obj);
588         }
589         return 0;
590 }
591
592 static void
593 eConsolePy_dealloc(eConsolePy* self)
594 {
595         if (self->in_weakreflist != NULL)
596                 PyObject_ClearWeakRefs((PyObject *) self);
597         eConsolePy_clear(self);
598         delete self->cont;
599         self->ob_type->tp_free((PyObject*)self);
600 }
601
602 static PyObject *
603 eConsolePy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
604 {
605         eConsolePy *self = (eConsolePy *)type->tp_alloc(type, 0);
606         self->cont = new eConsoleAppContainer();
607         self->in_weakreflist = NULL;
608         return (PyObject *)self;
609 }
610
611 static PyObject *
612 eConsolePy_running(eConsolePy* self)
613 {
614         PyObject *ret = NULL;
615         ret = self->cont->running() ? Py_True : Py_False;
616         Org_Py_INCREF(ret);
617         return ret;
618 }
619
620 static PyObject *
621 eConsolePy_execute(eConsolePy* self, PyObject *argt)
622 {
623         if (PyTuple_Size(argt) > 1)
624         {
625                 PyObject *cmdline, *args;
626                 PyArg_ParseTuple(args, "OO", &cmdline, &args);
627                 if (!PyString_Check(cmdline) || !PyList_Check(args))
628                         return PyInt_FromLong(-2);
629                 else
630                 {
631                         const char *argv[PyList_Size(args) + 1];
632                         int i=0;
633                         for (; i < PyList_Size(args); ++i)
634                         {
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 */
639                         }
640                         argv[i] = 0;
641                         return PyInt_FromLong(self->cont->execute(PyString_AsString(cmdline), argv)); /* borrowed pointer */
642                 }
643         }
644         else
645         {
646                 const char *str;
647                 if (PyArg_ParseTuple(argt, "s", &str))
648                         return PyInt_FromLong(self->cont->execute(str));
649         }
650         return NULL;
651 }
652
653 static PyObject *
654 eConsolePy_write(eConsolePy* self, PyObject *args)
655 {
656         int len;
657         char *data;
658         if (PyArg_ParseTuple(args, "si", &data, &len))
659                 ;
660         else
661         {
662                 PyObject *ob;
663                 if (!PyArg_ParseTuple(args, "O", &ob) || !PyString_Check(ob))
664                         return NULL;
665                 else
666                 {
667                         Py_ssize_t length;
668                         if (!PyString_AsStringAndSize(ob, &data, &length))
669                                 len = length;
670                         else
671                                 len = 0;
672                 }
673         }
674         self->cont->write(data, len);
675         Py_RETURN_NONE;
676 }
677
678 static PyObject *
679 eConsolePy_getPID(eConsolePy* self)
680 {
681         return PyInt_FromLong(self->cont->getPID());
682 }
683
684 static PyObject *
685 eConsolePy_setCWD(eConsolePy* self, PyObject *args)
686 {
687         const char *path=0;
688         if (!PyArg_ParseTuple(args, "s", &path))
689                 return NULL;
690         self->cont->setCWD(path);
691         Py_RETURN_NONE;
692 }
693
694 static PyObject *
695 eConsolePy_kill(eConsolePy* self)
696 {
697         self->cont->kill();
698         Py_RETURN_NONE;
699 }
700
701 static PyObject *
702 eConsolePy_sendCtrlC(eConsolePy* self)
703 {
704         self->cont->sendCtrlC();
705         Py_RETURN_NONE;
706 }
707
708 static PyObject *
709 eConsolePy_sendEOF(eConsolePy* self)
710 {
711         self->cont->sendEOF();
712         Py_RETURN_NONE;
713 }
714
715 static PyObject *
716 eConsolePy_dumpToFile(eConsolePy* self, PyObject *args)
717 {
718         char *filename;
719         if (!PyArg_ParseTuple(args, "s", &filename))
720                 return NULL;
721         else
722         {
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);
726         }
727         Py_RETURN_NONE;
728 }
729
730 static PyObject *
731 eConsolePy_readFromFile(eConsolePy* self, PyObject *args)
732 {
733         char *filename;
734         if (!PyArg_ParseTuple(args, "s", &filename))
735                 return NULL;
736         else
737         {
738                 int fd = open(filename, O_RDONLY);
739                 if (fd >= 0)
740                 {
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);
746                 }
747                 else
748                 {
749                         eDebug("eConsoleAppContainer::readFromFile %s not exist!", filename);
750                         self->cont->setFileFD(0, -1);
751                 }
752         }
753         Py_RETURN_NONE;
754 }
755
756 static PyMethodDef eConsolePy_methods[] = {
757         {"setCWD", (PyCFunction)eConsolePy_setCWD, METH_VARARGS,
758          "set working dir"
759         },
760         {"execute", (PyCFunction)eConsolePy_execute, METH_VARARGS,
761          "execute command"
762         },
763         {"dumpToFile", (PyCFunction)eConsolePy_dumpToFile, METH_VARARGS,
764          "set output file"
765         },
766         {"readFromFile", (PyCFunction)eConsolePy_readFromFile, METH_VARARGS,
767          "set input file"
768         },
769         {"getPID", (PyCFunction)eConsolePy_getPID, METH_NOARGS,
770          "execute command"
771         },
772         {"kill", (PyCFunction)eConsolePy_kill, METH_NOARGS,
773          "kill application"
774         },
775         {"sendCtrlC", (PyCFunction)eConsolePy_sendCtrlC, METH_NOARGS,
776          "send Ctrl-C to application"
777         },
778         {"sendEOF", (PyCFunction)eConsolePy_sendEOF, METH_NOARGS,
779          "send EOF to application"
780         },
781         {"write", (PyCFunction)eConsolePy_write, METH_VARARGS,
782          "write data to application"
783         },
784         {"running", (PyCFunction)eConsolePy_running, METH_NOARGS,
785          "returns the running state"
786         },
787         {NULL}  /* Sentinel */
788 };
789
790 static PyTypeObject eConsolePyType = {
791         PyObject_HEAD_INIT(NULL)
792         0, /*ob_size*/
793         "eConsoleImpl.eConsoleAppContainer", /*tp_name*/
794         sizeof(eConsolePy), /*tp_basicsize*/
795         0, /*tp_itemsize*/
796         (destructor)eConsolePy_dealloc, /*tp_dealloc*/
797         0, /*tp_print*/
798         0, /*tp_getattr*/
799         0, /*tp_setattr*/
800         0, /*tp_compare*/
801         0, /*tp_repr*/
802         0, /*tp_as_number*/
803         0, /*tp_as_sequence*/
804         0, /*tp_as_mapping*/
805         0, /*tp_hash */
806         0, /*tp_call*/
807         0, /*tp_str*/
808         0, /*tp_getattro*/
809         0, /*tp_setattro*/
810         0, /*tp_as_buffer*/
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 */
817         0, /* tp_iter */
818         0, /* tp_iternext */
819         eConsolePy_methods, /* tp_methods */
820         0, /* tp_members */
821         eConsolePy_getseters, /* tp_getset */
822         0, /* tp_base */
823         0, /* tp_dict */
824         0, /* tp_descr_get */
825         0, /* tp_descr_set */
826         0, /* tp_dictoffset */
827         0, /* tp_init */
828         0, /* tp_alloc */
829         eConsolePy_new, /* tp_new */
830 };
831
832 static PyMethodDef module_methods[] = {
833         {NULL}  /* Sentinel */
834 };
835
836 void eConsoleInit(void)
837 {
838         PyObject* m;
839
840         m = Py_InitModule3("eConsoleImpl", module_methods,
841                 "Module that implements eConsoleAppContainer with working cyclic garbage collection.");
842
843         if (m == NULL)
844                 return;
845
846 #ifdef COMPATIBILITY_MODE
847         if (!PyType_Ready(&eListCompatibilityWrapperType))
848         {
849                 Org_Py_INCREF((PyObject*)&eListCompatibilityWrapperType);
850                 PyModule_AddObject(m, "eListCompatibilityWrapper", (PyObject*)&eListCompatibilityWrapperType);
851         }
852 #endif
853         if (!PyType_Ready(&eConsolePyType))
854         {
855                 Org_Py_INCREF((PyObject*)&eConsolePyType);
856                 PyModule_AddObject(m, "eConsoleAppContainer", (PyObject*)&eConsolePyType);
857         }
858 }
859 }