do some initializations
[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
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                                 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         if (in)
248                 in->stop();
249         if (out)
250                 out->stop();
251         if (err)
252                 err->stop();
253         if (fd[0] != -1)
254         {
255                 ::close(fd[0]);
256                 fd[0]=-1;
257         }
258         if (fd[1] != -1)
259         {
260                 ::close(fd[1]);
261                 fd[1]=-1;
262         }
263         if (fd[2] != -1)
264         {
265                 ::close(fd[2]);
266                 fd[2]=-1;
267         }
268         eDebug("pipes closed");
269         while( outbuf.size() ) // cleanup out buffer
270         {
271                 queue_data d = outbuf.front();
272                 outbuf.pop();
273                 delete [] d.data;
274         }
275 }
276
277 void eConsoleAppContainer::readyRead(int what)
278 {
279         if (what & POLLPRI|POLLIN)
280         {
281 //              eDebug("what = %d");
282                 char buf[2048];
283                 int readed = read(fd[0], buf, 2047);
284 //              eDebug("%d bytes read", readed);
285                 if ( readed != -1 && readed )
286                 {
287 /*                      for ( int i = 0; i < readed; i++ )
288                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
289                         buf[readed]=0;
290                         /*emit*/ dataAvail(buf);
291                 }
292                 else if (readed == -1)
293                         eDebug("readerror %d", errno);
294         }
295         if (what & eSocketNotifier::Hungup)
296         {
297                 eDebug("child has terminated");
298                 closePipes();
299                 /*emit*/ appClosed(killstate);
300         }
301 }
302
303 void eConsoleAppContainer::readyErrRead(int what)
304 {
305         if (what & POLLPRI|POLLIN)
306         {
307 //              eDebug("what = %d");
308                 char buf[2048];
309                 int readed = read(fd[2], buf, 2047);
310 //              eDebug("%d bytes read", readed);
311                 if ( readed != -1 && readed )
312                 {
313 /*                      for ( int i = 0; i < readed; i++ )
314                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
315                         buf[readed]=0;
316                         /*emit*/ dataAvail(buf);
317                 }
318                 else if (readed == -1)
319                         eDebug("readerror %d", errno);
320         }
321 }
322
323 void eConsoleAppContainer::write( const char *data, int len )
324 {
325         char *tmp = new char[len];
326         memcpy(tmp, data, len);
327         outbuf.push(queue_data(tmp,len));
328         out->start();
329 }
330
331 void eConsoleAppContainer::readyWrite(int what)
332 {
333         if (what&POLLOUT && outbuf.size() )
334         {
335                 queue_data d = outbuf.front();
336                 outbuf.pop();
337                 if ( ::write( fd[1], d.data, d.len ) != d.len )
338                 {
339                         /* emit */ dataSent(-1);
340 //                      eDebug("writeError");
341                 }
342                 else
343                 {
344                         /* emit */ dataSent(0);
345 //                      eDebug("write ok");
346                 }
347                 delete [] d.data;
348         }
349         if ( !outbuf.size() )
350                 out->stop();
351 }