]> git.vomp.tv Git - vompserver.git/blob - medialauncher.c
15 years that line of code has been waiting to crash
[vompserver.git] / medialauncher.c
1 /*
2     Copyright 2004-2005 Chris Tallon, Andreas Vogel
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "medialauncher.h"
22 #include "media.h"
23 #include <iostream>
24 #include "log.h"
25 #include <sys/types.h>
26 #include <signal.h>
27 #include <sys/wait.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <fcntl.h>
33
34
35 #define MAXCMD 50
36
37 MediaLauncher::MediaLauncher(Config *c) {
38   cfg=c;
39   numcommands=0;
40   commands=NULL;
41   pnum=-1;
42   child=-1;
43 }
44
45 MediaLauncher::~MediaLauncher(){
46   if (commands) {
47     for (int i=0;i<numcommands;i++)
48       delete commands[i];
49     delete commands;
50   }
51   };
52
53 MediaLauncher::MCommand::MCommand(const char *n,ULONG t,const char *ext) {
54   command=new char[strlen(n)+1];
55   strcpy(command,n);
56   mediaType=t;
57   extension=new char[strlen(ext)+1];
58   strcpy(extension,ext);
59 }
60 MediaLauncher::MCommand::~MCommand() {
61   delete command;
62   delete extension;
63 }
64
65 #define NUMTYPES 4
66
67 static struct {
68   const char* mtypename;
69   ULONG mtypeid;
70 } mediatypes[]= {
71   {"PICTURE",MEDIA_TYPE_PICTURE},
72   {"AUDIO",MEDIA_TYPE_AUDIO},
73   {"VIDEO",MEDIA_TYPE_VIDEO},
74   {"LIST",MEDIA_TYPE_DIR}
75 };
76
77 static ULONG typeIdFromName(const char *name) {
78   for(int i=0;i<NUMTYPES;i++){
79     if (strcasecmp(mediatypes[i].mtypename,name) == 0) return mediatypes[i].mtypeid;
80   }
81   return MEDIA_TYPE_UNKNOWN;
82 }
83
84 int MediaLauncher::init() {
85   Log::getInstance()->log("MediaLauncher",Log::DEBUG,"init");
86
87   commands=new Pcmd[MAXCMD];
88   char buf[100];
89   for(int i=1;i<=MAXCMD;i++){
90     sprintf(buf,"Command.Name.%d",i);
91     const char *cmname=cfg->getValueString("Media",buf);
92     if (!cmname) continue;
93     sprintf(buf,"Command.Extension.%d",i);
94     const char *cmext=cfg->getValueString("Media",buf);
95     if (!cmext) continue;
96     sprintf(buf,"Command.Type.%d",i);
97     const char *cmtype=cfg->getValueString("Media",buf);
98     if (! cmtype) continue;
99     ULONG cmtypeid=typeIdFromName(cmtype);
100     if (cmtypeid == MEDIA_TYPE_UNKNOWN) {
101       Log::getInstance()->log("MediaLauncher",Log::ERR,"unknown media type %s",cmtype);
102       continue;
103     }
104     commands[numcommands]=new MCommand(cmname,cmtypeid,cmext);
105     Log::getInstance()->log("MediaLauncher",Log::DEBUG,"found command %s for ext %s, type %s",cmname,cmext,cmtype);
106     //check the command
107     char cbuf[strlen(cmname)+40];
108     sprintf(cbuf,"%s check",cmname);
109     int rt=system(cbuf);
110     if (rt != 0) {
111       Log::getInstance()->log("MediaLauncher",Log::ERR,"testting command %s failed, ignore",cmname);
112       continue;
113     }
114     numcommands++;
115   }
116
117   Log::getInstance()->log("MediaLauncher",Log::DEBUG,"found %d commands",numcommands);
118   return 0;
119 }
120
121 int MediaLauncher::init(MediaLauncher *cp) {
122   commands=new Pcmd[MAXCMD];
123   for (int i=0;i<cp->numcommands;i++) {
124     commands[i]=new MCommand(cp->commands[i]->command,cp->commands[i]->mediaType,cp->commands[i]->extension);
125   }
126   numcommands=cp->numcommands;
127   return 0;
128 }
129
130   
131
132 int MediaLauncher::findCommand(const char *name){
133   const char *ext=name+strlen(name);
134   while (*ext != '.' && ext > name) ext--;
135   if (*ext == '.') ext++;
136   //Log::getInstance()->log("MediaLauncher",Log::DEBUG,"found extension %s for name %s",ext,name);
137   for (int i=0;i<numcommands;i++) {
138     if (strcasecmp(ext,commands[i]->extension) == 0) {
139       Log::getInstance()->log("MediaLauncher",Log::DEBUG,"found command %s to handle name %s",commands[i]->command,name);
140       return i;
141     }
142   }
143   return -1;
144 }
145
146 ULONG MediaLauncher::getTypeForName(const char *name) {
147   int rt=findCommand(name);
148   Log::getInstance()->log("MediaLauncher",Log::DEBUG,"getTypeForName %s entry %d",name,rt);
149   if (rt>=0) return commands[rt]->mediaType;
150   return MEDIA_TYPE_UNKNOWN;
151 }
152
153 int MediaLauncher::openStream(const char *fname,ULONG xsize,ULONG ysize,const char * command) {
154   if (command == NULL) command="play";
155   Log::getInstance()->log("MediaLauncher",Log::DEBUG,"open stream for %s command %s",fname,command);
156   int cmnum=findCommand(fname);
157   if (cmnum < 0) {
158     Log::getInstance()->log("MediaLauncher",Log::ERR,"unable to find command for %s",fname);
159     return -1;
160   }
161   if (pnum >= 0) closeStream();
162   int pfd[2];
163   if (pipe(pfd) == -1) {
164     Log::getInstance()->log("MediaLauncher",Log::ERR,"unable to create pipe");
165     return -1;
166   }
167   pnum=pfd[0];
168   child=fork();
169   if (child == -1) {
170     Log::getInstance()->log("MediaLauncher",Log::ERR,"unable to fork");
171     return -1;
172   }
173   if (child == 0) {
174     //we are the child
175     close(pfd[0]);
176     dup2(pfd[1],fileno(stdout));
177     close(fileno(stdin));
178     dup2(pfd[1],fileno(stderr));
179     //try to close all open FDs
180     for (int i=0;i<=sysconf(_SC_OPEN_MAX);i++) {
181        if (i != fileno(stderr) && i != fileno(stdout)) close(i);
182     }
183     char buf1[30];
184     char buf2[30];
185     sprintf(buf1,"%u",xsize);
186     sprintf(buf2,"%u",ysize);
187     execlp(commands[cmnum]->command,commands[cmnum]->command,command,fname,buf1,buf2,NULL);
188     //no chance for logging here after close... Log::getInstance()->log("MediaLauncher",Log::ERR,"unable to execlp %s",commands[cmnum]->command);
189     exit(-1);
190   }
191   if (fcntl(pnum,F_SETFL,fcntl(pnum,F_GETFL)|O_NONBLOCK) != 0) {
192     Log::getInstance()->log("MediaLauncher",Log::ERR,"unable to set to nonblocking");
193     closeStream();
194     return -1;
195   }
196   //the parent
197   return child;
198 }
199
200
201 int MediaLauncher::closeStream() {
202   if (child <= 0) return -1;
203   Log::getInstance()->log("MediaLauncher",Log::DEBUG,"close stream for child %d",child);
204   close(pnum);
205   if (kill(child,0) == 0) {
206       Log::getInstance()->log("MediaLauncher",Log::DEBUG,"trying to kill child %d",child);
207       kill(child,SIGINT);
208   }
209   waitpid(child,NULL,WNOHANG);
210   for (int i=0;i< 150;i++) {
211     if (kill(child,0) == 0) {
212       usleep(10000);
213       waitpid(child,NULL,WNOHANG);
214     }
215   }
216   if (kill(child,0) == 0) {
217     Log::getInstance()->log("MediaLauncher",Log::DEBUG,"child %d aktive after wait, kill -9",child);
218     kill(child,SIGKILL);
219   }
220   for (int i=0;i< 100;i++) {
221     if (kill(child,0) == 0) {
222       usleep(10000);
223     }
224   }
225   waitpid(child,NULL,WNOHANG);
226   child=-1;
227   pnum=-1;
228   return 0;
229 }
230
231 int MediaLauncher::getNextBlock(ULONG size,unsigned char **buffer,ULONG *readLen) {
232   Log::getInstance()->log("MediaLauncher",Log::DEBUG,"get Block buf %p, len %lu",*buffer,size);
233   if (pnum <= 0) {
234     Log::getInstance()->log("MediaLauncher",Log::ERR,"stream not open in getnextBlock");
235     return -1;
236   }
237   *readLen=0;
238   struct timeval to;
239   to.tv_sec=0;
240   to.tv_usec=100000; //100ms
241   fd_set readfds;
242   FD_ZERO(&readfds);
243   FD_SET(pnum,&readfds);
244   int rt=select(pnum+1,&readfds,NULL,NULL,&to);
245   if (rt < 0) {
246     Log::getInstance()->log("MediaLauncher",Log::ERR,"error in select");
247     return -1;
248   }
249   if (rt == 0) {
250     Log::getInstance()->log("MediaLauncher",Log::DEBUG,"read 0 bytes (no data within 100ms)");
251     waitpid(child,NULL,WNOHANG);
252     if (kill(child,0) != 0) {
253       Log::getInstance()->log("MediaLauncher",Log::DEBUG,"child is dead, returning EOF");
254       return 1;
255     }
256     return 0;
257   }
258   if (! FD_ISSET(pnum,&readfds)) {
259     Log::getInstance()->log("MediaLauncher",Log::ERR,"error in select - nothing read");
260     return -1;
261   }
262   if (! *buffer) {
263     *buffer=(UCHAR *)malloc(size);
264   }
265   if (! *buffer) {
266     Log::getInstance()->log("MediaLauncher",Log::ERR,"unable to allocate buffer");
267     return -1;
268   }
269   ssize_t rdsz=read(pnum,*buffer,size);
270   *readLen=(ULONG)rdsz;
271   Log::getInstance()->log("MediaLauncher",Log::DEBUG,"read %lu bytes",*readLen);
272   if (rdsz == 0) return 1; //EOF
273   return 0;
274 }
275
276 bool MediaLauncher::isOpen() {
277   return pnum > 0;
278 }
279
280
281
282
283
284
285
286
287     
288