remove non working wrapper class to add a (unneeded) .get() call to
[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 static PyObject *
337 eConsolePy_dataAvail(eConsolePy *self, void *closure)
338 {
339         return self->cont->dataAvail.get();
340 }
341
342 static PyObject *
343 eConsolePy_stdoutAvail(eConsolePy *self, void *closure)
344 {
345         return self->cont->stdoutAvail.get();
346 }
347
348 static PyObject *
349 eConsolePy_stderrAvail(eConsolePy *self, void *closure)
350 {
351         return self->cont->stderrAvail.get();
352 }
353
354 static PyObject *
355 eConsolePy_dataSent(eConsolePy *self, void *closure)
356 {
357         return self->cont->dataSent.get();
358 }
359
360 static PyObject *
361 eConsolePy_appClosed(eConsolePy *self, void *closure)
362 {
363         return self->cont->appClosed.get();
364 }
365
366 static PyGetSetDef eConsolePy_getseters[] = {
367         {"dataAvail",
368          (getter)eConsolePy_dataAvail, (setter)0,
369          "dataAvail callback list",
370          NULL},
371         {"stdoutAvail",
372          (getter)eConsolePy_stdoutAvail, (setter)0,
373          "stdoutAvail callback list",
374          NULL},
375         {"stderrAvail",
376          (getter)eConsolePy_stderrAvail, (setter)0,
377          "stderrAvail callback list",
378          NULL},
379         {"dataSent",
380          (getter)eConsolePy_dataSent, (setter)0,
381          "dataSent callback list",
382          NULL},
383         {"appClosed",
384          (getter)eConsolePy_appClosed, (setter)0,
385          "appClosed callback list",
386          NULL},
387         {NULL} /* Sentinel */
388 };
389
390 static int
391 eConsolePy_traverse(eConsolePy *self, visitproc visit, void *arg)
392 {
393         PyObject *obj = self->cont->dataAvail.get(true);
394         if (obj) {
395                 Py_VISIT(obj);
396         }
397         obj = self->cont->stdoutAvail.get(true);
398         if (obj) {
399                 Py_VISIT(obj);
400         }
401         obj = self->cont->stderrAvail.get(true);
402         if (obj) {
403                 Py_VISIT(obj);
404         }
405         obj = self->cont->dataSent.get(true);
406         if (obj) {
407                 Py_VISIT(obj);
408         }
409         obj = self->cont->appClosed.get(true);
410         if (obj) {
411                 Py_VISIT(obj);
412         }
413         return 0;
414 }
415
416 static int
417 eConsolePy_clear(eConsolePy *self)
418 {
419         PyObject *obj = self->cont->dataAvail.get(true);
420         if (obj) {
421                 Py_CLEAR(obj);
422         }
423         obj = self->cont->stdoutAvail.get(true);
424         if (obj) {
425                 Py_CLEAR(obj);
426         }
427         obj = self->cont->stderrAvail.get(true);
428         if (obj) {
429                 Py_CLEAR(obj);
430         }
431         obj = self->cont->dataSent.get(true);
432         if (obj) {
433                 Py_CLEAR(obj);
434         }
435         obj = self->cont->appClosed.get(true);
436         if (obj) {
437                 Py_CLEAR(obj);
438         }
439         return 0;
440 }
441
442 static void
443 eConsolePy_dealloc(eConsolePy* self)
444 {
445         if (self->in_weakreflist != NULL)
446                 PyObject_ClearWeakRefs((PyObject *) self);
447         eConsolePy_clear(self);
448         delete self->cont;
449         self->ob_type->tp_free((PyObject*)self);
450 }
451
452 static PyObject *
453 eConsolePy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
454 {
455         eConsolePy *self = (eConsolePy *)type->tp_alloc(type, 0);
456         self->cont = new eConsoleAppContainer();
457         self->in_weakreflist = NULL;
458         return (PyObject *)self;
459 }
460
461 static PyObject *
462 eConsolePy_running(eConsolePy* self)
463 {
464         PyObject *ret = NULL;
465         ret = self->cont->running() ? Py_True : Py_False;
466         Org_Py_INCREF(ret);
467         return ret;
468 }
469
470 static PyObject *
471 eConsolePy_execute(eConsolePy* self, PyObject *argt)
472 {
473         const char *str;
474         if (PyArg_ParseTuple(argt, "s", &str))
475                 return PyInt_FromLong(self->cont->execute(str));
476         PyErr_SetString(PyExc_TypeError,
477                 "argument is not a string");
478         return NULL;
479 }
480
481 static PyObject *
482 eConsolePy_write(eConsolePy* self, PyObject *args)
483 {
484         int len;
485         char *data;
486         if (PyArg_ParseTuple(args, "si", &data, &len))
487                 ;
488         else
489         {
490                 PyObject *ob;
491                 if (!PyArg_ParseTuple(args, "O", &ob) || !PyString_Check(ob))
492                 {
493                         PyErr_SetString(PyExc_TypeError,
494                                 "1st arg must be a string, optionaly 2nd arg can be the string length");
495                         return NULL;
496                 }
497                 else
498                 {
499                         Py_ssize_t length;
500                         if (!PyString_AsStringAndSize(ob, &data, &length))
501                                 len = length;
502                         else
503                                 len = 0;
504                 }
505         }
506         self->cont->write(data, len);
507         Py_RETURN_NONE;
508 }
509
510 static PyObject *
511 eConsolePy_getPID(eConsolePy* self)
512 {
513         return PyInt_FromLong(self->cont->getPID());
514 }
515
516 static PyObject *
517 eConsolePy_setCWD(eConsolePy* self, PyObject *args)
518 {
519         const char *path=0;
520         if (!PyArg_ParseTuple(args, "s", &path))
521                 return NULL;
522         self->cont->setCWD(path);
523         Py_RETURN_NONE;
524 }
525
526 static PyObject *
527 eConsolePy_kill(eConsolePy* self)
528 {
529         self->cont->kill();
530         Py_RETURN_NONE;
531 }
532
533 static PyObject *
534 eConsolePy_sendCtrlC(eConsolePy* self)
535 {
536         self->cont->sendCtrlC();
537         Py_RETURN_NONE;
538 }
539
540 static PyObject *
541 eConsolePy_sendEOF(eConsolePy* self)
542 {
543         self->cont->sendEOF();
544         Py_RETURN_NONE;
545 }
546
547 static PyObject *
548 eConsolePy_dumpToFile(eConsolePy* self, PyObject *args)
549 {
550         char *filename;
551         if (!PyArg_ParseTuple(args, "s", &filename))
552         {
553                 PyErr_SetString(PyExc_TypeError,
554                         "arg must be a string (filename)");
555                 return NULL;
556         }
557         else
558         {
559                 int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
560                 self->cont->setFileFD(1, fd);
561                 eDebug("eConsoleAppContainer::dumpToFile open(%s, O_WRONLY|O_CREAT|O_TRUNC, 0644)=%d", filename, fd);
562         }
563         Py_RETURN_NONE;
564 }
565
566 static PyObject *
567 eConsolePy_readFromFile(eConsolePy* self, PyObject *args)
568 {
569         char *filename;
570         if (!PyArg_ParseTuple(args, "s", &filename))
571         {
572                 PyErr_SetString(PyExc_TypeError,
573                         "arg must be a string (filename)");
574                 return NULL;
575         }
576         else
577         {
578                 int fd = open(filename, O_RDONLY);
579                 if (fd >= 0)
580                 {
581                         char readbuf[32*1024];
582                         int rsize = read(fd, readbuf, 32*1024);
583                         self->cont->setFileFD(0, fd);
584                         eDebug("eConsoleAppContainer::readFromFile open(%s, O_RDONLY)=%d, read: %d", filename, fd, rsize);
585                         self->cont->write(readbuf, rsize);
586                 }
587                 else
588                 {
589                         eDebug("eConsoleAppContainer::readFromFile %s not exist!", filename);
590                         self->cont->setFileFD(0, -1);
591                 }
592         }
593         Py_RETURN_NONE;
594 }
595
596 static PyMethodDef eConsolePy_methods[] = {
597         {"setCWD", (PyCFunction)eConsolePy_setCWD, METH_VARARGS,
598          "set working dir"
599         },
600         {"execute", (PyCFunction)eConsolePy_execute, METH_VARARGS,
601          "execute command"
602         },
603         {"dumpToFile", (PyCFunction)eConsolePy_dumpToFile, METH_VARARGS,
604          "set output file"
605         },
606         {"readFromFile", (PyCFunction)eConsolePy_readFromFile, METH_VARARGS,
607          "set input file"
608         },
609         {"getPID", (PyCFunction)eConsolePy_getPID, METH_NOARGS,
610          "execute command"
611         },
612         {"kill", (PyCFunction)eConsolePy_kill, METH_NOARGS,
613          "kill application"
614         },
615         {"sendCtrlC", (PyCFunction)eConsolePy_sendCtrlC, METH_NOARGS,
616          "send Ctrl-C to application"
617         },
618         {"sendEOF", (PyCFunction)eConsolePy_sendEOF, METH_NOARGS,
619          "send EOF to application"
620         },
621         {"write", (PyCFunction)eConsolePy_write, METH_VARARGS,
622          "write data to application"
623         },
624         {"running", (PyCFunction)eConsolePy_running, METH_NOARGS,
625          "returns the running state"
626         },
627         {NULL}  /* Sentinel */
628 };
629
630 static PyTypeObject eConsolePyType = {
631         PyObject_HEAD_INIT(NULL)
632         0, /*ob_size*/
633         "eConsoleImpl.eConsoleAppContainer", /*tp_name*/
634         sizeof(eConsolePy), /*tp_basicsize*/
635         0, /*tp_itemsize*/
636         (destructor)eConsolePy_dealloc, /*tp_dealloc*/
637         0, /*tp_print*/
638         0, /*tp_getattr*/
639         0, /*tp_setattr*/
640         0, /*tp_compare*/
641         0, /*tp_repr*/
642         0, /*tp_as_number*/
643         0, /*tp_as_sequence*/
644         0, /*tp_as_mapping*/
645         0, /*tp_hash */
646         0, /*tp_call*/
647         0, /*tp_str*/
648         0, /*tp_getattro*/
649         0, /*tp_setattro*/
650         0, /*tp_as_buffer*/
651         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
652         "eConsoleAppContainer objects", /* tp_doc */
653         (traverseproc)eConsolePy_traverse, /* tp_traverse */
654         (inquiry)eConsolePy_clear, /* tp_clear */
655         0, /* tp_richcompare */
656         offsetof(eConsolePy, in_weakreflist), /* tp_weaklistoffset */
657         0, /* tp_iter */
658         0, /* tp_iternext */
659         eConsolePy_methods, /* tp_methods */
660         0, /* tp_members */
661         eConsolePy_getseters, /* tp_getset */
662         0, /* tp_base */
663         0, /* tp_dict */
664         0, /* tp_descr_get */
665         0, /* tp_descr_set */
666         0, /* tp_dictoffset */
667         0, /* tp_init */
668         0, /* tp_alloc */
669         eConsolePy_new, /* tp_new */
670 };
671
672 static PyMethodDef module_methods[] = {
673         {NULL}  /* Sentinel */
674 };
675
676 void eConsoleInit(void)
677 {
678         PyObject* m = Py_InitModule3("eConsoleImpl", module_methods,
679                 "Module that implements eConsoleAppContainer with working cyclic garbage collection.");
680
681         if (m == NULL)
682                 return;
683
684         if (!PyType_Ready(&eConsolePyType))
685         {
686                 Org_Py_INCREF((PyObject*)&eConsolePyType);
687                 PyModule_AddObject(m, "eConsoleAppContainer", (PyObject*)&eConsolePyType);
688         }
689 }
690 }