make glibc memchecker happy :)
[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), in(0), out(0), err(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 //                              eDebug("new argv[%d] %d bytes", tmp.length()+1);
158                                 argv[cnt] = new char[ tmp.length()+1 ];
159 //                              eDebug("idx=%d, arg = %s", idx, tmp.c_str() );
160                                 strcpy( argv[cnt++], tmp.c_str() );
161                                 cmds.erase(0, idx+1);
162 //                              eDebug("str = %s", cmds.c_str() );
163                                 it = brackets.find(cmds[0]);
164                                 idx=0;
165                         }
166                         else
167                                 idx++;
168                 }
169                 if ( it != brackets.end() )
170                 {
171                         cmds.erase(0,1);
172                         cmds.erase(cmds.length()-1, 1);
173                 }
174                 // store the last arg
175 //              eDebug("new argv[%d] %d bytes", cmds.length());
176                 argv[cnt] = new char[ cmds.length()+1 ];
177                 strcpy( argv[cnt], cmds.c_str() );
178         }
179         else
180                 cnt=1;
181
182   // get one read ,one write and the err pipe to the prog..
183
184 //      int tmp=0;
185 //      while(argv[tmp])
186 //              eDebug("%d is %s", tmp, argv[tmp++]);
187   
188         pid = bidirpipe(fd, argv[0], argv);
189
190         while ( cnt >= 0 )  // release heap memory
191         {
192 //              eDebug("delete argv[%d]", cnt);
193                 delete [] argv[cnt--];
194         }
195 //      eDebug("delete argv");
196         delete [] argv;
197         
198         if ( pid == -1 )
199                 return -3;
200
201         eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
202
203         in = new eSocketNotifier(eApp, fd[0], POLLIN|POLLPRI|POLLHUP );
204         out = new eSocketNotifier(eApp, fd[1], POLLOUT, false);  
205         err = new eSocketNotifier(eApp, fd[2], POLLIN|POLLPRI );
206         CONNECT(in->activated, eConsoleAppContainer::readyRead);
207         CONNECT(out->activated, eConsoleAppContainer::readyWrite);
208         CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
209         return 0;
210 }
211
212 eConsoleAppContainer::~eConsoleAppContainer()
213 {
214         kill();
215 }
216
217 void eConsoleAppContainer::kill()
218 {
219         if ( killstate != -1 )
220         {
221                 eDebug("user kill(SIGKILL) console App");
222                 killstate=-1;
223                 ::kill(pid, SIGKILL);
224                 closePipes();
225         }
226         while( outbuf.size() ) // cleanup out buffer
227         {
228                 queue_data d = outbuf.front();
229                 outbuf.pop();
230                 delete [] d.data;
231         }
232         delete in;
233         delete out;
234         delete err;
235         in=out=err=0;
236 }
237
238 void eConsoleAppContainer::sendCtrlC()
239 {
240         if ( killstate != -1 )
241         {
242                 eDebug("user send SIGINT(Ctrl-C) to console App");
243                 ::kill(pid, SIGINT);
244         }
245 }
246
247 void eConsoleAppContainer::closePipes()
248 {
249         if (in)
250                 in->stop();
251         if (out)
252                 out->stop();
253         if (err)
254                 err->stop();
255         if (fd[0] != -1)
256         {
257                 ::close(fd[0]);
258                 fd[0]=-1;
259         }
260         if (fd[1] != -1)
261         {
262                 ::close(fd[1]);
263                 fd[1]=-1;
264         }
265         if (fd[2] != -1)
266         {
267                 ::close(fd[2]);
268                 fd[2]=-1;
269         }
270         eDebug("pipes closed");
271         while( outbuf.size() ) // cleanup out buffer
272         {
273                 queue_data d = outbuf.front();
274                 outbuf.pop();
275                 delete [] d.data;
276         }
277 }
278
279 void eConsoleAppContainer::readyRead(int what)
280 {
281         if (what & POLLPRI|POLLIN)
282         {
283 //              eDebug("what = %d");
284                 char buf[2048];
285                 int readed = read(fd[0], buf, 2047);
286 //              eDebug("%d bytes read", readed);
287                 if ( readed != -1 && readed )
288                 {
289 /*                      for ( int i = 0; i < readed; i++ )
290                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
291                         buf[readed]=0;
292                         /*emit*/ dataAvail(buf);
293                 }
294                 else if (readed == -1)
295                         eDebug("readerror %d", errno);
296         }
297         if (what & eSocketNotifier::Hungup)
298         {
299                 eDebug("child has terminated");
300                 closePipes();
301                 /*emit*/ appClosed(killstate);
302         }
303 }
304
305 void eConsoleAppContainer::readyErrRead(int what)
306 {
307         if (what & POLLPRI|POLLIN)
308         {
309 //              eDebug("what = %d");
310                 char buf[2048];
311                 int readed = read(fd[2], buf, 2047);
312 //              eDebug("%d bytes read", readed);
313                 if ( readed != -1 && readed )
314                 {
315 /*                      for ( int i = 0; i < readed; i++ )
316                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
317                         buf[readed]=0;
318                         /*emit*/ dataAvail(buf);
319                 }
320                 else if (readed == -1)
321                         eDebug("readerror %d", errno);
322         }
323 }
324
325 void eConsoleAppContainer::write( const char *data, int len )
326 {
327         char *tmp = new char[len];
328         memcpy(tmp, data, len);
329         outbuf.push(queue_data(tmp,len));
330         out->start();
331 }
332
333 void eConsoleAppContainer::readyWrite(int what)
334 {
335         if (what&POLLOUT && outbuf.size() )
336         {
337                 queue_data d = outbuf.front();
338                 outbuf.pop();
339                 if ( ::write( fd[1], d.data, d.len ) != d.len )
340                 {
341                         /* emit */ dataSent(-1);
342 //                      eDebug("writeError");
343                 }
344                 else
345                 {
346                         /* emit */ dataSent(0);
347 //                      eDebug("write ok");
348                 }
349                 delete [] d.data;
350         }
351         if ( !outbuf.size() )
352                 out->stop();
353 }
354