not block so long
[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 //      eDebug("%d args", cnt);
128         argv[0] = new char[ plen+1 ];
129 //      eDebug("new argv[0] %d bytes (%s)", plen+1, path.c_str());
130         strcpy( argv[0], path.c_str() );
131         argv[cnt-1] = 0;               // set terminating null
132
133         if ( cnt > 2 )  // more then default args?
134         {
135                 cnt=1;  // do not overwrite path in argv[0]
136
137                 it = brackets.find(cmds[0]);
138                 idx=0;
139                 while ( (idx = cmds.find(' ',idx)) != std::string::npos )  // parse all args..
140                 {
141                         bool bracketClosed=false;
142                         if ( it != brackets.end() )
143                         {
144                                 if (cmds[idx-1]==it->second)
145                                 {
146                                         it = brackets.end();
147                                         bracketClosed=true;
148                                 }
149                         }
150                         if ( it == brackets.end() )
151                         {
152                                 std::string tmp = cmds.substr(0, idx);
153                                 if (bracketClosed)
154                                 {
155                                         tmp.erase(0,1);
156                                         tmp.erase(tmp.length()-1, 1);
157                                         bracketClosed=false;
158                                 }
159 //                              eDebug("new argv[%d] %d bytes (%s)", cnt, tmp.length()+1, tmp.c_str());
160                                 argv[cnt] = new char[ tmp.length()+1 ];
161 //                              eDebug("idx=%d, arg = %s", idx, tmp.c_str() );
162                                 strcpy( argv[cnt++], tmp.c_str() );
163                                 cmds.erase(0, idx+1);
164 //                              eDebug("str = %s", cmds.c_str() );
165                                 it = brackets.find(cmds[0]);
166                                 idx=0;
167                         }
168                         else
169                                 idx++;
170                 }
171                 if ( it != brackets.end() )
172                 {
173                         cmds.erase(0,1);
174                         cmds.erase(cmds.length()-1, 1);
175                 }
176                 // store the last arg
177 //              eDebug("new argv[%d] %d bytes (%s)", cnt, cmds.length()+1, cmds.c_str());
178                 argv[cnt] = new char[ cmds.length()+1 ];
179                 strcpy( argv[cnt], cmds.c_str() );
180         }
181         else
182                 cnt=1;
183
184   // get one read ,one write and the err pipe to the prog..
185
186 //      int tmp=0;
187 //      while(argv[tmp])
188 //              eDebug("%d is %s", tmp, argv[tmp++]);
189   
190         pid = bidirpipe(fd, argv[0], argv);
191
192         while ( cnt >= 0 )  // release heap memory
193         {
194 //              eDebug("delete argv[%d]", cnt);
195                 delete [] argv[cnt--];
196         }
197 //      eDebug("delete argv");
198         delete [] argv;
199         
200         if ( pid == -1 )
201                 return -3;
202
203 //      eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
204
205         in = new eSocketNotifier(eApp, fd[0], eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Hungup );
206         out = new eSocketNotifier(eApp, fd[1], eSocketNotifier::Write, false);  
207         err = new eSocketNotifier(eApp, fd[2], eSocketNotifier::Read|eSocketNotifier::Priority );
208         CONNECT(in->activated, eConsoleAppContainer::readyRead);
209         CONNECT(out->activated, eConsoleAppContainer::readyWrite);
210         CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
211         return 0;
212 }
213
214 eConsoleAppContainer::~eConsoleAppContainer()
215 {
216         kill();
217 }
218
219 void eConsoleAppContainer::kill()
220 {
221         if ( killstate != -1 && pid != -1 )
222         {
223                 eDebug("user kill(SIGKILL) console App");
224                 killstate=-1;
225                 ::kill(pid, SIGKILL);
226                 closePipes();
227         }
228         while( outbuf.size() ) // cleanup out buffer
229         {
230                 queue_data d = outbuf.front();
231                 outbuf.pop();
232                 delete [] d.data;
233         }
234         delete in;
235         delete out;
236         delete err;
237         in=out=err=0;
238 }
239
240 void eConsoleAppContainer::sendCtrlC()
241 {
242         if ( killstate != -1 && pid != -1 )
243         {
244                 eDebug("user send SIGINT(Ctrl-C) to console App");
245                 ::kill(pid, SIGINT);
246         }
247 }
248
249 void eConsoleAppContainer::closePipes()
250 {
251         if (in)
252                 in->stop();
253         if (out)
254                 out->stop();
255         if (err)
256                 err->stop();
257         if (fd[0] != -1)
258         {
259                 ::close(fd[0]);
260                 fd[0]=-1;
261         }
262         if (fd[1] != -1)
263         {
264                 ::close(fd[1]);
265                 fd[1]=-1;
266         }
267         if (fd[2] != -1)
268         {
269                 ::close(fd[2]);
270                 fd[2]=-1;
271         }
272         eDebug("pipes closed");
273         while( outbuf.size() ) // cleanup out buffer
274         {
275                 queue_data d = outbuf.front();
276                 outbuf.pop();
277                 delete [] d.data;
278         }
279         pid = -1;
280 }
281
282 void eConsoleAppContainer::readyRead(int what)
283 {
284         bool hungup = what & eSocketNotifier::Hungup;
285         if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
286         {
287 //              eDebug("what = %d");
288                 char buf[2048];
289                 int rd;
290                 while((rd = read(fd[0], buf, 2047)) > 0)
291                 {
292 /*                      for ( int i = 0; i < rd; i++ )
293                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
294                         buf[rd]=0;
295                         /*emit*/ dataAvail(buf);
296                         if (!hungup)
297                                 break;
298                 }
299         }
300         if (hungup)
301         {
302                 eDebug("child has terminated");
303                 closePipes();
304                 /*emit*/ appClosed(killstate);
305         }
306 }
307
308 void eConsoleAppContainer::readyErrRead(int what)
309 {
310         if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
311         {
312 //              eDebug("what = %d");
313                 char buf[2048];
314                 int rd;
315                 while((rd = read(fd[2], buf, 2047)) > 0)
316                 {
317 /*                      for ( int i = 0; i < rd; i++ )
318                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
319                         buf[rd]=0;
320                         /*emit*/ dataAvail(buf);
321                 }
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&eSocketNotifier::Write && 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 }