patch by Pieter Grimmerink: use ext3 largefile option only for disks > 4G
[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 #include <sys/types.h>
9 #include <sys/wait.h>
10
11 int bidirpipe(int pfd[], char *cmd , char *argv[])
12 {
13         int pfdin[2];  /* from child to parent */
14         int pfdout[2]; /* from parent to child */
15         int pfderr[2]; /* stderr from child to parent */
16         int pid;       /* child's pid */
17
18         if ( pipe(pfdin) == -1 || pipe(pfdout) == -1 || pipe(pfderr) == -1)
19                 return(-1);
20
21         if ( ( pid = vfork() ) == -1 )
22                 return(-1);
23         else if (pid == 0) /* child process */
24         {
25                 setsid();
26                 if ( close(0) == -1 || close(1) == -1 || close(2) == -1 )
27                         _exit(0);
28
29                 if (dup(pfdout[0]) != 0 || dup(pfdin[1]) != 1 || dup(pfderr[1]) != 2 )
30                         _exit(0);
31
32                 if (close(pfdout[0]) == -1 || close(pfdout[1]) == -1 ||
33                                 close(pfdin[0]) == -1 || close(pfdin[1]) == -1 ||
34                                 close(pfderr[0]) == -1 || close(pfderr[1]) == -1 )
35                         _exit(0);
36
37                 for (unsigned int i=3; i < 90; ++i )
38                         close(i);
39
40                 execvp(cmd,argv);
41                 _exit(0);
42         }
43         if (close(pfdout[0]) == -1 || close(pfdin[1]) == -1 || close(pfderr[1]) == -1)
44                         return(-1);
45
46         pfd[0] = pfdin[0];
47         pfd[1] = pfdout[1];
48         pfd[2] = pfderr[0];
49
50         return(pid);
51 }
52
53 eConsoleAppContainer::eConsoleAppContainer()
54 :pid(-1), killstate(0), in(0), out(0), err(0)
55 {
56         for (int i=0; i < 3; ++i)
57                 fd[i]=-1;
58 }
59
60 static char brakets[][2] = {
61         { '\'','\'' },
62         {'"','"'},
63         {'`','`'},
64         {'(',')'},
65         {'{','}'},
66         {'[',']'},
67         {'<','>'}
68 };
69
70 static char *find_bracket(char ch)
71 {
72         size_t idx=0;
73         while (idx < sizeof(brakets)/2) {
74                 if (brakets[idx][0] == ch)
75                         return &brakets[idx][0];
76                 ++idx;
77         }
78         return NULL;
79 }
80
81 int eConsoleAppContainer::execute( const char *cmd )
82 {
83         if (running())
84                 return -1;
85         pid=-1;
86         killstate=0;
87
88         int cnt=0, slen=strlen(cmd);
89         char buf[slen+1];
90         char *tmp=0, *argv[64], *path=buf, *cmds = buf;
91         memcpy(buf, cmd, slen+1);
92
93 //      printf("cmd = %s, len %d\n", cmd, slen);
94
95         // kill spaces at beginning
96         while(path[0] == ' ') {
97                 ++path;
98                 ++cmds;
99                 --slen;
100         }
101
102         // kill spaces at the end
103         while(slen && path[slen-1] == ' ') {
104                 path[slen-1] = 0;
105                 --slen;
106         }
107
108         if (!slen)
109                 return -2;
110
111         tmp = strchr(path, ' ');
112         if (tmp) {
113                 *tmp = 0;
114                 cmds = tmp+1;
115                 while(*cmds && *cmds == ' ')
116                         ++cmds;
117         }
118         else
119                 cmds = path+slen;
120
121         memset(argv, 0, sizeof(argv));
122         argv[cnt++] = path;
123
124         if (*cmds) {
125                 char *argb=NULL, *it=NULL;
126                 while ( (tmp = strchr(cmds, ' ')) ) {
127                         if (!it && *cmds && (it = find_bracket(*cmds)) )
128                                 *cmds = 'X'; // replace open braket...
129                         if (!argb) // not arg begin
130                                 argb = cmds;
131                         if (it && *(tmp-1) == it[1]) {
132                                 *argb = it[0]; // set old char for open braket
133                                 it = 0;
134                         }
135                         if (!it) { // end of arg
136                                 *tmp = 0;
137                                 argv[cnt++] = argb;
138                                 argb=0; // reset arg begin
139                         }
140                         cmds = tmp+1;
141                         while (*cmds && *cmds == ' ')
142                                 ++cmds;
143                 }
144                 argv[cnt++] = argb ? argb : cmds;
145                 if (it)
146                     *argv[cnt-1] = it[0]; // set old char for open braket
147         }
148
149 //      int tmp=0;
150 //      while(argv[tmp])
151 //              eDebug("%d is %s", tmp, argv[tmp++]);
152
153         // get one read ,one write and the err pipe to the prog..
154         pid = bidirpipe(fd, argv[0], argv);
155
156         if ( pid == -1 )
157                 return -3;
158
159 //      eDebug("pipe in = %d, out = %d, err = %d", fd[0], fd[1], fd[2]);
160
161         in = new eSocketNotifier(eApp, fd[0], eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Hungup );
162         out = new eSocketNotifier(eApp, fd[1], eSocketNotifier::Write, false);  
163         err = new eSocketNotifier(eApp, fd[2], eSocketNotifier::Read|eSocketNotifier::Priority );
164         CONNECT(in->activated, eConsoleAppContainer::readyRead);
165         CONNECT(out->activated, eConsoleAppContainer::readyWrite);
166         CONNECT(err->activated, eConsoleAppContainer::readyErrRead);
167
168         return 0;
169 }
170
171 eConsoleAppContainer::~eConsoleAppContainer()
172 {
173         kill();
174 }
175
176 void eConsoleAppContainer::kill()
177 {
178         if ( killstate != -1 && pid != -1 )
179         {
180                 eDebug("user kill(SIGKILL) console App");
181                 killstate=-1;
182                 /*
183                  * Use a negative pid value, to signal the whole process group
184                  * ('pid' might not even be running anymore at this point)
185                  */
186                 ::kill(-pid, SIGKILL);
187                 closePipes();
188         }
189         while( outbuf.size() ) // cleanup out buffer
190         {
191                 queue_data d = outbuf.front();
192                 outbuf.pop();
193                 delete [] d.data;
194         }
195         delete in;
196         delete out;
197         delete err;
198         in=out=err=0;
199 }
200
201 void eConsoleAppContainer::sendCtrlC()
202 {
203         if ( killstate != -1 && pid != -1 )
204         {
205                 eDebug("user send SIGINT(Ctrl-C) to console App");
206                 /*
207                  * Use a negative pid value, to signal the whole process group
208                  * ('pid' might not even be running anymore at this point)
209                  */
210                 ::kill(-pid, SIGINT);
211         }
212 }
213
214 void eConsoleAppContainer::closePipes()
215 {
216         if (in)
217                 in->stop();
218         if (out)
219                 out->stop();
220         if (err)
221                 err->stop();
222         if (fd[0] != -1)
223         {
224                 ::close(fd[0]);
225                 fd[0]=-1;
226         }
227         if (fd[1] != -1)
228         {
229                 ::close(fd[1]);
230                 fd[1]=-1;
231         }
232         if (fd[2] != -1)
233         {
234                 ::close(fd[2]);
235                 fd[2]=-1;
236         }
237         eDebug("pipes closed");
238         while( outbuf.size() ) // cleanup out buffer
239         {
240                 queue_data d = outbuf.front();
241                 outbuf.pop();
242                 delete [] d.data;
243         }
244         pid = -1;
245 }
246
247 void eConsoleAppContainer::readyRead(int what)
248 {
249         bool hungup = what & eSocketNotifier::Hungup;
250         if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
251         {
252 //              eDebug("what = %d");
253                 char buf[2048];
254                 int rd;
255                 while((rd = read(fd[0], buf, 2047)) > 0)
256                 {
257 /*                      for ( int i = 0; i < rd; i++ )
258                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
259                         buf[rd]=0;
260                         /*emit*/ dataAvail(buf);
261                         if (!hungup)
262                                 break;
263                 }
264         }
265         if (hungup)
266         {
267                 eDebug("child has terminated");
268                 closePipes();
269                 int childstatus;
270                 int retval = killstate;
271                 /*
272                  * We have to call 'wait' on the child process, in order to avoid zombies.
273                  * Also, this gives us the chance to provide better exit status info to appClosed.
274                  */
275                 if (::waitpid(pid, &childstatus, 0) > 0)
276                 {
277                         if (WIFEXITED(childstatus))
278                         {
279                                 retval = WEXITSTATUS(childstatus);
280                         }
281                 }
282                 /*emit*/ appClosed(retval);
283         }
284 }
285
286 void eConsoleAppContainer::readyErrRead(int what)
287 {
288         if (what & (eSocketNotifier::Priority|eSocketNotifier::Read))
289         {
290 //              eDebug("what = %d");
291                 char buf[2048];
292                 int rd;
293                 while((rd = read(fd[2], buf, 2047)) > 0)
294                 {
295 /*                      for ( int i = 0; i < rd; i++ )
296                                 eDebug("%d = %c (%02x)", i, buf[i], buf[i] );*/
297                         buf[rd]=0;
298                         /*emit*/ dataAvail(buf);
299                 }
300         }
301 }
302
303 void eConsoleAppContainer::write( const char *data, int len )
304 {
305         char *tmp = new char[len];
306         memcpy(tmp, data, len);
307         outbuf.push(queue_data(tmp,len));
308         out->start();
309 }
310
311 void eConsoleAppContainer::readyWrite(int what)
312 {
313         if (what&eSocketNotifier::Write && outbuf.size() )
314         {
315                 queue_data d = outbuf.front();
316                 outbuf.pop();
317                 if ( ::write( fd[1], d.data, d.len ) != d.len )
318                 {
319                         /* emit */ dataSent(-1);
320 //                      eDebug("writeError");
321                 }
322                 else
323                 {
324                         /* emit */ dataSent(0);
325 //                      eDebug("write ok");
326                 }
327                 delete [] d.data;
328         }
329         if ( !outbuf.size() )
330                 out->stop();
331 }