add eConsoleAppContainer to execute shell scripts or applications from python without...
[enigma2.git] / 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
9 int bidirpipe(int pfd[], char *cmd , char *argv[])
10 {
11         int pfdin[2];  /* from child to parent */
12         int pfdout[2]; /* from parent to child */
13         int pfderr[2]; /* stderr from child to parent */
14         int pid;       /* child's pid */
15
16         if ( pipe(pfdin) == -1 || pipe(pfdout) == -1 || pipe(pfderr) == -1)
17                 return(-1);
18
19         if ( ( pid = fork() ) == -1 )
20                 return(-1);
21         else if (pid == 0) /* child process */
22         {
23                 setsid();
24                 if ( close(0) == -1 || close(1) == -1 || close(2) == -1 )
25                         _exit(0);
26
27                 if (dup(pfdout[0]) != 0 || dup(pfdin[1]) != 1 || dup(pfderr[1]) != 2 )
28                         _exit(0);
29
30                 if (close(pfdout[0]) == -1 || close(pfdout[1]) == -1 ||
31                                 close(pfdin[0]) == -1 || close(pfdin[1]) == -1 ||
32                                 close(pfderr[0]) == -1 || close(pfderr[1]) == -1 )
33                         _exit(0);
34
35                 for (unsigned int i=3; i < 90; ++i )
36                         close(i);
37
38                 execvp(cmd,argv);
39                 _exit(0);
40         }
41         if (close(pfdout[0]) == -1 || close(pfdin[1]) == -1 || close(pfderr[1]) == -1)
42                         return(-1);
43
44         pfd[0] = pfdin[0];
45         pfd[1] = pfdout[1];
46         pfd[2] = pfderr[0];
47
48         return(pid);
49 }
50
51 eConsoleAppContainer::eConsoleAppContainer()
52 :pid(-1), killstate(0)
53 {
54         for (int i=0; i < 3; ++i)
55                 fd[i]=-1;
56 }
57
58 int eConsoleAppContainer::execute( const std::string &cmd )
59 {
60         if (running())
61                 return -1;
62         pid=-1;
63         killstate=0;
64 //      eDebug("cmd = %s", cmd.c_str() );
65         int cnt=2; // path to app + terminated 0
66         std::string str(cmd.length()?cmd:"");
67
68         // kill spaces at beginning
69         unsigned int pos = str.find_first_not_of(' ');
70         if (pos != std::string::npos && pos)
71                 str = str.substr(pos);
72
73         // kill spaces at the end
74         pos = str.find_last_not_of(' ');
75         if (pos != std::string::npos && (pos+1) < str.length())
76                 str = str.erase(pos+1);
77
78         unsigned int slen=str.length();
79         if (!slen)
80                 return -2;
81
82         std::map<char,char> brackets;
83         brackets.insert(std::pair<char,char>('\'','\''));
84         brackets.insert(std::pair<char,char>('"','"'));
85         brackets.insert(std::pair<char,char>('`','`'));
86         brackets.insert(std::pair<char,char>('(',')'));
87         brackets.insert(std::pair<char,char>('{','}'));
88         brackets.insert(std::pair<char,char>('[',']'));
89         brackets.insert(std::pair<char,char>('<','>'));
90
91         unsigned int idx=str.find(' ');
92         std::string path = str.substr(0, idx != std::string::npos ? idx : slen );
93 //      eDebug("path = %s", path.c_str() );
94         unsigned int plen = path.length();
95
96         std::string cmds = slen > plen ? str.substr( plen+1 ) : "";
97         unsigned int clen = cmds.length();
98 //      eDebug("cmds = %s", cmds.c_str() );
99
100         idx = 0;
101         std::map<char,char>::iterator it = brackets.find(cmds[idx]);
102         while ( (idx = cmds.find(' ',idx) ) != std::string::npos )  // count args
103         {
104                 if (it != brackets.end())
105                 {
106                         if (cmds[idx-1] == it->second)
107                                 it = brackets.end();
108                 }
109                 if (it == brackets.end())
110                 {
111                         cnt++;
112                         it = brackets.find(cmds[idx+1]);
113                 }
114                 idx++;
115         }
116
117 //      eDebug("idx = %d, %d counted spaces", idx, cnt-2);
118
119         if ( clen )
120         {
121                 cnt++;
122 //              eDebug("increase cnt");
123         }
124
125 //      eDebug("%d args", cnt-2);
126         char **argv = new char*[cnt];  // min two args... path and terminating 0
127         argv[0] = new char[ plen ];
128         strcpy( argv[0], path.c_str() );
129         argv[cnt-1] = 0;               // set terminating null
130
131         if ( cnt > 2 )  // more then default args?
132         {
133                 cnt=1;  // do not overwrite path in argv[0]
134
135                 it = brackets.find(cmds[0]);
136                 idx=0;
137                 while ( (idx = cmds.find(' ',idx)) != std::string::npos )  // parse all args..
138                 {
139                         bool bracketClosed=false;
140                         if ( it != brackets.end() )
141                         {
142                                 if (cmds[idx-1]==it->second)
143                                 {
144                                         it = brackets.end();
145                                         bracketClosed=true;
146                                 }
147                         }
148                         if ( it == brackets.end() )
149                         {
150                                 std::string tmp = cmds.substr(0, idx);
151                                 if (bracketClosed)
152                                 {
153                                         tmp.erase(0,1);
154                                         tmp.erase(tmp.length()-1, 1);
155                                         bracketClosed=false;
156                                 }
157                                 argv[cnt] = new char[ tmp.length()+1 ];
158 //                              eDebug("idx=%d, arg = %s", idx, tmp.c_str() );
159                                 strcpy( argv[cnt++], tmp.c_str() );
160                                 cmds.erase(0, idx+1);
161 //                              eDebug("str = %s", cmds.c_str() );
162                                 it = brackets.find(cmds[0]);
163                                 idx=0;
164                         }
165                         else
166                                 idx++;
167                 }
168                 if ( it != brackets.end() )
169                 {
170                         cmds.erase(0,1);
171                         cmds.erase(cmds.length()-1, 1);
172                 }
173                 // store the last arg
174                 argv[cnt] = new char[ cmds.length() ];
175                 strcpy( argv[cnt], cmds.c_str() );
176         }
177         else
178                 cnt=1;
179
180   // get one read ,one write and the err pipe to the prog..
181
182 //      int tmp=0;
183 //      while(argv[tmp])
184 //              eDebug("%d is %s", tmp, argv[tmp++]);
185   
186         pid = bidirpipe(fd, argv[0], argv);
187
188         while ( cnt >= 0 )  // release heap memory
189         {
190 //              eDebug("delete argv[%d]", cnt);
191                 delete [] argv[cnt--];
192         }
193 //      eDebug("delete argv");
194         delete [] argv;
195         
196         if ( pid == -1 )
197                 return -3;
198
199         eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
200
201         in = new eSocketNotifier(eApp, fd[0], POLLIN|POLLPRI|POLLHUP );
202         out = new eSocketNotifier(eApp, fd[1], POLLOUT, false);  
203         err = new eSocketNotifier(eApp, fd[2], POLLIN|POLLPRI );
204         CONNECT(in->activated, eConsoleAppContainer::readyRead);
205         CONNECT(out->activated, eConsoleAppContainer::readyWrite);
206         CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
207         return 0;
208 }
209
210 eConsoleAppContainer::~eConsoleAppContainer()
211 {
212         kill();
213 }
214
215 void eConsoleAppContainer::kill()
216 {
217         if ( killstate != -1 )
218         {
219                 eDebug("user kill(SIGKILL) console App");
220                 killstate=-1;
221                 ::kill(pid, SIGKILL);
222                 closePipes();
223         }
224         while( outbuf.size() ) // cleanup out buffer
225         {
226                 queue_data d = outbuf.front();
227                 outbuf.pop();
228                 delete [] d.data;
229         }
230         delete in;
231         delete out;
232         delete err;
233         in=out=err=0;
234 }
235
236 void eConsoleAppContainer::sendCtrlC()
237 {
238         if ( killstate != -1 )
239         {
240                 eDebug("user send SIGINT(Ctrl-C) to console App");
241                 ::kill(pid, SIGINT);
242         }
243 }
244
245 void eConsoleAppContainer::closePipes()
246 {
247         in->stop();
248         out->stop();
249         err->stop();
250         ::close(fd[0]);
251         fd[0]=-1;
252         ::close(fd[1]);
253         fd[1]=-1;
254         ::close(fd[2]);
255         fd[2]=-1;
256         eDebug("pipes closed");
257         while( outbuf.size() ) // cleanup out buffer
258         {
259                 queue_data d = outbuf.front();
260                 outbuf.pop();
261                 delete [] d.data;
262         }
263 }
264
265 void eConsoleAppContainer::readyRead(int what)
266 {
267         if (what & POLLPRI|POLLIN)
268         {
269 //              eDebug("what = %d");
270                 char buf[2048];
271                 int readed = read(fd[0], buf, 2047);
272 //              eDebug("%d bytes read", readed);
273                 if ( readed != -1 && readed )
274                 {
275 /*                      for ( int i = 0; i < readed; i++ )
276                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
277                         buf[readed]=0;
278                         /*emit*/ dataAvail(buf);
279                 }
280                 else if (readed == -1)
281                         eDebug("readerror %d", errno);
282         }
283         if (what & eSocketNotifier::Hungup)
284         {
285                 eDebug("child has terminated");
286                 closePipes();
287                 /*emit*/ appClosed(killstate);
288         }
289 }
290
291 void eConsoleAppContainer::readyErrRead(int what)
292 {
293         if (what & POLLPRI|POLLIN)
294         {
295 //              eDebug("what = %d");
296                 char buf[2048];
297                 int readed = read(fd[2], buf, 2047);
298 //              eDebug("%d bytes read", readed);
299                 if ( readed != -1 && readed )
300                 {
301 /*                      for ( int i = 0; i < readed; i++ )
302                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
303                         buf[readed]=0;
304                         /*emit*/ dataAvail(buf);
305                 }
306                 else if (readed == -1)
307                         eDebug("readerror %d", errno);
308         }
309 }
310
311 void eConsoleAppContainer::write( const char *data, int len )
312 {
313         char *tmp = new char[len];
314         memcpy(tmp, data, len);
315         outbuf.push(queue_data(tmp,len));
316         out->start();
317 }
318
319 void eConsoleAppContainer::readyWrite(int what)
320 {
321         if (what&POLLOUT && outbuf.size() )
322         {
323                 queue_data d = outbuf.front();
324                 outbuf.pop();
325                 if ( ::write( fd[1], d.data, d.len ) != d.len )
326                 {
327                         /* emit */ dataSent(-1);
328 //                      eDebug("writeError");
329                 }
330                 else
331                 {
332                         /* emit */ dataSent(0);
333 //                      eDebug("write ok");
334                 }
335                 delete [] d.data;
336         }
337         if ( !outbuf.size() )
338                 out->stop();
339 }