]> git.vomp.tv Git - vompserver.git/blob - mvpserver.c
15 years that line of code has been waiting to crash
[vompserver.git] / mvpserver.c
1 /*
2     Copyright 2004-2019 Chris Tallon
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, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "mvpserver.h"
21 #ifdef VOMPSTANDALONE
22 #include <iostream>
23 #endif
24
25 extern pthread_mutex_t threadClientMutex;
26
27 #define UDP6PORT 51056
28
29 MVPServer::MVPServer()
30 {
31   // MH in case anbody has a better position :-)
32   pthread_mutex_init(&threadClientMutex, NULL);
33   tcpServerPort = 0;
34   logoDir = NULL;
35   resourceDir = NULL;
36   imageDir = NULL;
37   cacheDir = NULL;
38 }
39
40 MVPServer::~MVPServer()
41 {
42   if (logoDir) delete[] logoDir;
43   if (resourceDir) delete[] resourceDir;
44   if (imageDir) delete[] imageDir;
45   if (cacheDir) delete[] cacheDir;
46   stop();
47 }
48
49 int MVPServer::stop()
50 {
51   if (threadIsActive()) threadCancel();
52   close(listeningSocket);
53
54   udpr.shutdown();
55   udpr6.shutdown();
56   bootpd.shutdown();
57   tftpd.shutdown();
58   mvprelay.shutdown();
59
60   log.log("Main", Log::INFO, "Stopped main server thread");
61   log.shutdown();
62
63   config.shutdown();
64
65   return 1;
66 }
67
68 int MVPServer::run(char* tconfigDir)
69 {
70   if (threadIsActive()) return 1;
71
72   configDir = tconfigDir;
73
74   // Start config
75 #ifdef VOMPSTANDALONE
76 #define dsyslog(x) std::cout << x << std::endl;
77 #endif
78
79   char configFileName[PATH_MAX];
80   snprintf(configFileName, PATH_MAX, "%s/vomp.conf", configDir);
81
82   if (config.init(configFileName))
83   {
84     dsyslog("VOMP: Config file found");
85   }
86   else
87   {
88     dsyslog("VOMP: Config file not found");
89   }
90
91   // Start logging
92
93   char* cfgLogFilename = config.getValueString("General", "Log file");
94   if (cfgLogFilename)
95   {
96     log.init(Log::DEBUG, cfgLogFilename);
97     delete[] cfgLogFilename;
98     log.log("Main", Log::INFO, "Logging started");
99   }
100   else
101   {
102     dsyslog("VOMP: Logging disabled");
103   }
104
105   const char *bigresdir = cPlugin::ResourceDirectory();  
106   const char *bigcachedir = cPlugin::CacheDirectory();  
107   // get logo directory
108   logoDir =  config.getValueString("General", "Channel logo directory");
109   
110   if (logoDir) 
111   {
112     log.log("Main", Log::INFO, "LogoDir set %s", logoDir);
113   } else {
114     if (bigresdir) {
115         logoDir = new char[strlen(bigresdir)+1+7];
116         sprintf(logoDir,"%s/logos/",bigresdir);    
117         log.log("Main", Log::INFO, "No LogoDir set, default %s",logoDir);
118      } else {
119         log.log("Main", Log::INFO, "No LogoDir set, no res dir");
120      }
121         
122   }
123
124   // get epg Image directory
125   imageDir =  config.getValueString("General", "Epg image directory");
126   
127   if (imageDir) 
128   {
129     log.log("Main", Log::INFO, "ImageDir set %s", imageDir);
130   } else {
131     if (bigcachedir) {
132         imageDir = new char[strlen(bigcachedir)+1+11+3];
133         sprintf(imageDir,"%s/../epgimages/",bigcachedir);    
134         log.log("Main", Log::INFO, "No ImageDir set, default %s",imageDir);
135     } else {
136         log.log("Main", Log::INFO, "No ImageDir set, no cache dir");
137     }
138   }
139
140   if (bigresdir) {
141     resourceDir = new char[strlen(bigresdir)+1];
142     strcpy(resourceDir,bigresdir);
143     log.log("Main", Log::INFO, "Resource directory is  %s",bigresdir);
144   } else {
145     log.log("Main", Log::INFO, "Resource directory is  not set");
146   }
147   
148   if (bigcachedir) {
149     cacheDir = new char[strlen(bigcachedir)+1];
150     strcpy(cacheDir,bigcachedir);
151     log.log("Main", Log::INFO, "Cache directory is  %s",bigcachedir);
152   } else {
153     log.log("Main", Log::INFO, "Cache directory is  not set");
154   }
155   // Get UDP port number for discovery service
156
157   int fail = 1;
158   int udpport = config.getValueLong("General", "UDP port", &fail);
159   if (fail) udpport = 51051;  
160   
161   // Work out a name for this server
162
163   char* serverName;
164
165   // Try to get from vomp.conf
166   serverName = config.getValueString("General", "Server name");
167   if (!serverName) // If not, get the hostname
168   {
169     serverName = new char[1024];
170     if (gethostname(serverName, 1024)) // if not, set default
171     {
172       strcpy(serverName, "VOMP Server");
173     }
174   }
175
176   // Get VOMP server TCP port to give to UDP replier to put in packets
177   fail = 1;
178   tcpServerPort = config.getValueLong("General", "TCP port", &fail);
179   if (fail) tcpServerPort = 3024;
180   
181   int udpSuccess = udpr.run(udpport, serverName, tcpServerPort);
182   int udp6Success = udpr6.run(UDP6PORT, serverName, tcpServerPort);
183
184   delete[] serverName;
185
186   if (!udpSuccess)
187   {
188     log.log("Main", Log::CRIT, "Could not start UDP replier");
189     stop();
190     return 0;
191   }
192
193   if (!udp6Success)
194   {
195     log.log("Main", Log::CRIT, "Could not start UDP6 replier");
196     stop();
197     return 0;
198   }
199
200   // Read config and start bootp and tftp as appropriate
201
202   char* configString;
203   int bootpEnabled = 0;
204   int tftpEnabled = 0;
205   int mvprelayEnabled = 1;
206
207   configString = config.getValueString("General", "Bootp server enabled");
208   if (configString && (!strcasecmp(configString, "yes"))) bootpEnabled = 1;
209   if (configString) delete[] configString;
210
211   configString = config.getValueString("General", "TFTP server enabled");
212   if (configString && (!strcasecmp(configString, "yes"))) tftpEnabled = 1;
213   if (configString) delete[] configString;
214
215   configString = config.getValueString("General", "MVPRelay enabled");
216   if (configString && (strcasecmp(configString, "yes"))) mvprelayEnabled = 0;
217   if (configString) delete[] configString;
218
219
220   if (bootpEnabled)
221   {
222     if (!bootpd.run(configDir))
223     {
224       log.log("Main", Log::CRIT, "Could not start Bootpd");
225       stop();
226       return 0;
227     }
228   }
229   else
230   {
231     log.log("Main", Log::INFO, "Not starting Bootpd");
232   }
233
234   if (tftpEnabled)
235   {
236     char tftpPath[PATH_MAX];
237
238     configString = config.getValueString("General", "TFTP directory");
239     if (configString)
240     {
241       snprintf(tftpPath, PATH_MAX, "%s", configString);
242
243       // this will never happen.. surely.
244       if ((strlen(tftpPath) + 2) >= PATH_MAX)
245       {
246         delete[] configString;
247         log.log("Main", Log::CRIT, "Could not understand TFTP directory from config");
248         stop();
249         return 0;
250       }
251
252       // if there isn't a / at the end of the dir, add one
253       if (tftpPath[strlen(tftpPath) - 1] != '/') strcat(tftpPath, "/");
254
255       delete[] configString;
256     }
257     else
258     {
259       snprintf(tftpPath, PATH_MAX, "%s/", configDir);
260     }
261
262     log.log("Main", Log::INFO, "TFTP path '%s'", tftpPath);
263
264     if (!tftpd.run(tftpPath))
265     {
266       log.log("Main", Log::CRIT, "Could not start TFTPd");
267       stop();
268       return 0;
269     }
270   }
271   else
272   {
273     log.log("Main", Log::INFO, "Not starting TFTPd");
274   }
275
276   // Start mvprelay thread
277   if (mvprelayEnabled)
278   {
279     if (!mvprelay.run())
280     {
281       log.log("Main", Log::CRIT, "Could not start MVPRelay");
282       stop();
283       return 0;
284     }
285     else
286     {
287       log.log("Main", Log::INFO, "MVPRelay started");
288     }
289   }
290   else
291   {
292     log.log("Main", Log::INFO, "Not starting MVPRelay");
293   }
294   
295   // start thread here
296   if (!threadStart())
297   {
298     log.log("Main", Log::CRIT, "Could not start MVPServer thread");
299     stop();
300     return 0;
301   }
302
303   log.log("Main", Log::DEBUG, "MVPServer run success");
304   return 1;
305 }
306
307 void MVPServer::threadMethod()
308 {
309   // I want to die as soon as I am cancelled because I'll be in accept()
310   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
311   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
312
313   /*
314   struct sockaddr_in address;
315   address.sin_family = AF_INET;
316   address.sin_port = htons(tcpServerPort);
317   address.sin_addr.s_addr = INADDR_ANY;
318   socklen_t length = sizeof(address);
319
320   listeningSocket = socket(AF_INET, SOCK_STREAM, 0);
321   */
322
323   struct sockaddr_in6 address;
324   address.sin6_family = AF_INET6;
325   address.sin6_port = htons(tcpServerPort);
326   address.sin6_addr = in6addr_any;
327   socklen_t length = sizeof(address);
328
329   listeningSocket = socket(AF_INET6, SOCK_STREAM, 0);
330
331
332   if (listeningSocket < 0)
333   {
334     log.log("MVPServer", Log::CRIT, "Could not get TCP socket in vompserver");
335     return;
336   }
337
338   int value=1;
339   setsockopt(listeningSocket,SOL_SOCKET,SO_REUSEADDR,&value,sizeof(value));
340
341   if (bind(listeningSocket,(struct sockaddr *)&address,sizeof(address)) < 0)
342   {
343     log.log("MVPServer", Log::CRIT, "Could not bind to socket in vompserver");
344     close(listeningSocket);
345     return;
346   }
347
348   listen(listeningSocket, 5);
349
350   int clientSocket;
351
352   while(1)
353   {
354     clientSocket = accept(listeningSocket,(struct sockaddr *)&address, &length);
355     VompClient* m = new VompClient(&config, configDir, logoDir, resourceDir, imageDir, cacheDir, clientSocket);
356     m->run();
357   }
358 }
359