1 /*
2 * main.cpp -- Simple daemon to source an icecast server with direct
3 * mp3/ogg streaming, no resampling.
4 *
5 * Copyright (C) 2004 Tony Sin(x) '76 <administrator@tortugalabs.it>
6 * All rights reserved.
7 *
8 */
9
10 /*
11 * GNU GENERAL PUBLIC LICENSE
12 * Version 2, June 1991
13 *
14 * Copyright (C) 1989, 1991 Free Software Foundation, Inc.
15 * 675 Mass Ave, Cambridge, MA 02139, USA
16 * Everyone is permitted to copy and distribute verbatim copies
17 * of this license document, but changing it is not allowed.
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 *
33 */
34
35 /* Version history:
36 0.1 - First release
37 0.3 - Libshout 2.0 support
38 Strong configuration parses
39 Support for Icecast2.0/Shoutcast
40 Bug fixes
41 0.5 - Libshout 1.3.x no longer supported
42 Full POSIX implementation
43 Multiple playlist directories
44 Support for OGG Vorbis
45 Command line interface
46 Multiple log configuration
47 Threads (no more detached processes)
48 All "external errors" are catched
49 Clean exit, all system resources are released
50 FreeBSD/NetBSD/Linux 2.x/MacOS platform supported
51 0.5.1 - Code cleaner
52 Added SOURCE option
53 Added LOOP option
54 Added SHUFFLE option
55 Added METAUPDATE option
56 FIXED: Serious bug in SERVER option
57 FIXED: try/catch mechanism didn't work properly
58 FIXED: possible error in parsing configuration
59 FIXED: wrong path bug
60 FIXED: opendir possibly crash (thanks to DIeMONd)
61 FIXED: multiple directories existing check
62 Songs are sorted by filename on SHUFFLE = 0
63 Now IceG is a GNU standard package, hope that multiple platforms work now
64 Added MP3/OGG recursive directory search option (thx to DIeMONd again for suggesting me)
65 MP3/OGG file extension search is now case insensitive
66 Update time for metadata information is configurable, even metadata information presence too
67 0.5.2 - Added telnet minimal interface
68 Added MySQL support
69 Code cleaner (again)
70 FIXED: heading '/' in log file path
71 FIXED: massive bugfix in configure script
72 0.5.3 - FIXED: Missing declaration on Mac OS X
73 FIXED: Alpha signal.h compilation error
74 FIXED: removed POSIX definition, cause BSD conflict
75 0.5.4 - FIXED: Incorrect socket definitions on MacOSX
76 FIXED: Segfault occurs if log object is NULL when error
77 FIXED: --with-shout parameter (-lshout -lvorbis missing)
78 Added PostGreSQL support
79 Added .M3U playlist support
80 Added .PLS playlist support
81 Removed basename function cause is not POSIX compliant
82 Log file is now path configurable
83 Added more info into README file
84 0.5.5 - FIXED: Serious bug in configuration file parser module
85 FIXED: Don't try to open non-existing files during stream
86 FIXED: Race condition on meta streamer <-> selector launch
87 FIXED: Memory leaks
88 Added ID3 support
89 Added ICEMETAL language support
90 Default icegenerator.conf path is now SYSCONFDIR-fixed
91 */
92
93 #ifdef HAVE_CONFIG_H
94 #ifndef __main_config_h__
95 #define __main_config_h__
96 #include "../config.h"
97 #endif
98
99 #ifndef __GNU_LIBRARY__
100 #define __GNU_LIBRARY__
101 #endif
102
103 #ifdef HAVE_GETOPT_H
104 #include <getopt.h>
105 #endif
106
107 #ifdef HAVE_STDIO_H
108 #include <stdio.h>
109 #endif
110
111 #ifdef HAVE_STDLIB_H
112 #include <stdlib.h>
113 #endif
114
115 #ifdef HAVE_PTHREAD_H
116 #include <pthread.h>
117 #endif
118
119 #ifdef HAVE_SYS_TYPES_H
120 #include <sys/types.h>
121 #endif
122
123 #ifdef HAVE_SYS_STAT_H
124 #include <sys/stat.h>
125 #endif
126
127 #ifdef HAVE_FCNTL_H
128 #include <fcntl.h>
129 #endif
130
131 #ifdef HAVE_CTYPE_H
132 #include <ctype.h>
133 #endif
134
135 #ifdef HAVE_STRING_H
136 #include <string.h>
137 #endif
138
139 #ifdef HAVE_UNISTD_H
140 #include <unistd.h>
141 #endif
142
143 #define APPNAME PACKAGE_STRING
144 #else
145 #define APPNAME "IceGenerator"
146 #endif
147
148 #include "log.h"
149 #include "config.h"
150 #include "sgnl_handler.h"
151 #include "streamer.h"
152 #include "circular.h"
153 #include "synch.h"
154 #include "main.h"
155
156 #define HELP_MSG_LINES 8
157
158 const char *help_msg[HELP_MSG_LINES] = { APPNAME,
159 NULL,
160 "Usage:",
161 "\ticegenerator [-h] [-d] [-f filename]",
162 NULL,
163 "\t-h\t\tDisplay this help message",
164 "\t-f filename\tSpecify configuration file",
165 "\t-d\t\tRun in foreground - no daemon" };
166 cLog *log_obj = NULL;
167 cSignal *sig_obj = NULL;
168 cConfig *conf_obj = NULL;
169 cStreamer *stream_obj = NULL;
170 cCircularList *ready_obj = NULL, *played_obj = NULL;
171 cSemaphore *sem_obj = NULL;
172 int track_load, end_track, data_mutex, start_meta;
173 volatile bool time_to_quit = false;
174 bool ok_to_end = false, changed = false;
175 char ErrStr[127] = "Unknown error";
176
signal_termination_proc(const int sig)177 void signal_termination_proc(const int sig)
178 {
179 if (!time_to_quit)
180 log_obj->WriteLog("Wait for all child process to terminate...");
181 time_to_quit = true;
182 }
183
clean_objs()184 void clean_objs()
185 {
186 if (sem_obj != NULL)
187 delete sem_obj;
188
189 if (played_obj != NULL)
190 delete played_obj;
191
192 if (ready_obj != NULL)
193 delete ready_obj;
194
195 if (stream_obj != NULL)
196 delete stream_obj;
197
198 if (conf_obj != NULL)
199 delete conf_obj;
200
201 if (sig_obj != NULL)
202 delete sig_obj;
203
204 if (log_obj != NULL)
205 delete log_obj;
206 }
207
main(int argc,char ** argv)208 int main(int argc, char **argv)
209 {
210 int i, c;
211 bool is_daemon = true;
212 pthread_t player_tid, selector_tid, data_streamer_tid, data_server_tid;
213
214 try
215 {
216 /* Parse command line parameters and reads configuration file */
217 if (argc > 1)
218 while ((c = getopt (argc, argv, "hdf:")) != -1)
219 switch (c)
220 {
221 case 'h': for (i = 0; i < HELP_MSG_LINES; i++)
222 {
223 if (help_msg[i] != NULL)
224 printf("%s",help_msg[i]);
225 printf("\n");
226 }
227 printf("\n");
228 exit(0);
229 break;
230 case 'f': conf_obj = new cConfig(optarg);
231 break;
232 case 'd': is_daemon = false;
233 break;
234 case ':': printf("Invalid parameter\n");
235 exit(1);
236 break;
237 }
238
239 if (conf_obj == NULL)
240 conf_obj = new cConfig(DEFAULT_CONF_FILE);
241
242 /* Init log file */
243 log_obj = new cLog(conf_obj);
244
245 /* Select streaming type and scans directories */
246 ready_obj = new cCircularList;
247 ready_obj->ParsePlayList(conf_obj);
248 if (ready_obj->Empty())
249 {
250 strcpy(ErrStr,"No files found");
251 throw 0;
252 }
253 else
254 {
255 played_obj = new cCircularList;
256 ready_obj->SetStart();
257 }
258
259 /* Opens connection to stream server */
260 stream_obj = new cStreamer;
261 stream_obj->SetData(conf_obj);
262 stream_obj->Connect();
263
264 log_obj->WriteLog("Connected to stream server");
265
266 /* Init synchronization module */
267 sem_obj = new cSemaphore;
268 data_mutex = sem_obj->AddSem(1);
269 track_load = sem_obj->AddSem(0);
270 end_track = sem_obj->AddSem(0);
271 start_meta = sem_obj->AddSem(0);
272
273 /* Launches as a daemon */
274 if (is_daemon)
275 {
276 fclose(stdin);
277 fclose(stdout);
278 fclose(stderr);
279 if (fork())
280 exit(0);
281 setsid();
282 if (fork())
283 exit(0);
284 chdir("/");
285 umask(0);
286 log_obj->WriteLog("Going to daemon land...");
287 }
288
289 /* Install signal handler */
290 sig_obj = new cSignal();
291 sig_obj->AddHandler(SIGHUP,signal_termination_proc);
292 sig_obj->AddHandler(SIGTERM,signal_termination_proc);
293 sig_obj->AddHandler(SIGINT,signal_termination_proc);
294 sig_obj->AddHandler(SIGQUIT,signal_termination_proc);
295 sig_obj->AddHandler(SIGABRT,signal_termination_proc);
296
297 /* Generates threads */
298 pthread_create(&player_tid,NULL,player,NULL);
299 pthread_create(&data_streamer_tid,NULL,data_streamer,NULL);
300 pthread_create(&selector_tid,NULL,selector,NULL);
301 if (conf_obj->GetValue(_dataport) != NULL)
302 pthread_create(&data_server_tid,NULL,data_server,NULL);
303
304 /* Doesn't care about SIGS */
305 sig_obj->Block();
306
307 /* Waits for childen to die */
308 pthread_join(player_tid,NULL);
309 pthread_join(data_streamer_tid,NULL);
310 pthread_join(selector_tid,NULL);
311
312 if (conf_obj->GetValue(_dataport) != NULL)
313 {
314 close_data_server();
315 pthread_join(data_server_tid,NULL);
316 }
317
318 log_obj->WriteLog("Have a nice day");
319
320 /* Time to go out */
321 stream_obj->Disconnect();
322 clean_objs();
323 }
324 catch (...)
325 {
326 /* Something went wrong ... */
327 if (is_daemon && (log_obj != NULL))
328 log_obj->WriteLog(ErrStr);
329 else
330 printf("%s\n\n",ErrStr);
331
332 clean_objs();
333 }
334
335 return 0;
336 }
337