1 /*
2 (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org)
3 (c) Copyright 2000-2004 Convergence (integrated media) GmbH
4
5 All rights reserved.
6
7 Written by Denis Oliver Kropp <dok@directfb.org>,
8 Andreas Hundt <andi@fischlustig.de>,
9 Sven Neumann <neo@directfb.org>,
10 Ville Syrjälä <syrjala@sci.fi> and
11 Claudio Ciccani <klan@users.sf.net>.
12
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2 of the License, or (at your option) any later version.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
22
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the
25 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 Boston, MA 02111-1307, USA.
27 */
28
29 #include <config.h>
30
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <netdb.h>
38
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42
43 #include <netinet/in.h>
44 #include <netdb.h>
45
46 #include <direct/debug.h>
47 #include <direct/mem.h>
48 #include <direct/log.h>
49 #include <direct/util.h>
50
51
52 struct __D_DirectLog {
53 int magic;
54
55 DirectLogType type;
56
57 int fd;
58
59 pthread_mutex_t lock;
60 };
61
62 /**********************************************************************************************************************/
63
64 /* Statically allocated to avoid endless loops between D_CALLOC() and D_DEBUG(), while the latter would only
65 * call the allocation once, if there wouldn't be the loopback...
66 */
67 static DirectLog fallback_log;
68
69 static DirectLog *default_log = NULL;
70 static pthread_once_t init_fallback = PTHREAD_ONCE_INIT;
71
72 /**********************************************************************************************************************/
73
74 static DirectResult init_stderr( DirectLog *log );
75
76 static DirectResult init_file ( DirectLog *log,
77 const char *filename );
78
79 static DirectResult init_udp ( DirectLog *log,
80 const char *hostport );
81
82 /**********************************************************************************************************************/
83
84 DirectResult
direct_log_create(DirectLogType type,const char * param,DirectLog ** ret_log)85 direct_log_create( DirectLogType type,
86 const char *param,
87 DirectLog **ret_log )
88 {
89 DirectResult ret = DR_INVARG;
90 DirectLog *log;
91
92 log = D_CALLOC( 1, sizeof(DirectLog) );
93 if (!log)
94 return D_OOM();
95
96 log->type = type;
97
98 switch (type) {
99 case DLT_STDERR:
100 ret = init_stderr( log );
101 break;
102
103 case DLT_FILE:
104 ret = init_file( log, param );
105 break;
106
107 case DLT_UDP:
108 ret = init_udp( log, param );
109 break;
110 }
111
112 if (ret)
113 D_FREE( log );
114 else {
115 direct_util_recursive_pthread_mutex_init( &log->lock );
116
117 D_MAGIC_SET( log, DirectLog );
118
119 *ret_log = log;
120 }
121
122 return ret;
123 }
124
125 DirectResult
direct_log_destroy(DirectLog * log)126 direct_log_destroy( DirectLog *log )
127 {
128 D_MAGIC_ASSERT( log, DirectLog );
129
130 D_ASSERT( &fallback_log != log );
131
132 if (log == default_log)
133 default_log = NULL;
134
135 close( log->fd );
136
137 D_MAGIC_CLEAR( log );
138
139 D_FREE( log );
140
141 return DR_OK;
142 }
143
144 __attribute__((no_instrument_function))
145 DirectResult
direct_log_printf(DirectLog * log,const char * format,...)146 direct_log_printf( DirectLog *log,
147 const char *format, ... )
148 {
149 va_list args;
150
151 /*
152 * Don't use D_MAGIC_ASSERT or any other
153 * macros/functions that might cause an endless loop.
154 */
155
156 va_start( args, format );
157
158 /* Use the default log if passed log is invalid. */
159 if (!log || log->magic != D_MAGIC("DirectLog"))
160 log = direct_log_default();
161
162 /* Write to stderr as a fallback if default is invalid, too. */
163 if (!log || log->magic != D_MAGIC("DirectLog")) {
164 vfprintf( stderr, format, args );
165 fflush( stderr );
166 }
167 else {
168 int len;
169 char buf[512];
170
171 len = vsnprintf( buf, sizeof(buf), format, args );
172
173 pthread_mutex_lock( &log->lock );
174
175 write( log->fd, buf, len );
176
177 pthread_mutex_unlock( &log->lock );
178 }
179
180 va_end( args );
181
182 return DR_OK;
183 }
184
185 DirectResult
direct_log_set_default(DirectLog * log)186 direct_log_set_default( DirectLog *log )
187 {
188 D_MAGIC_ASSERT( log, DirectLog );
189
190 default_log = log;
191
192 return DR_OK;
193 }
194
195 __attribute__((no_instrument_function))
196 void
direct_log_lock(DirectLog * log)197 direct_log_lock( DirectLog *log )
198 {
199 D_MAGIC_ASSERT_IF( log, DirectLog );
200
201 if (!log)
202 log = direct_log_default();
203
204 D_MAGIC_ASSERT( log, DirectLog );
205
206 pthread_mutex_lock( &log->lock );
207 }
208
209 __attribute__((no_instrument_function))
210 void
direct_log_unlock(DirectLog * log)211 direct_log_unlock( DirectLog *log )
212 {
213 D_MAGIC_ASSERT_IF( log, DirectLog );
214
215 if (!log)
216 log = direct_log_default();
217
218 D_MAGIC_ASSERT( log, DirectLog );
219
220 pthread_mutex_unlock( &log->lock );
221 }
222
223 __attribute__((no_instrument_function))
224 static void
init_fallback_log(void)225 init_fallback_log( void )
226 {
227 fallback_log.type = DLT_STDERR;
228 fallback_log.fd = fileno( stderr );
229
230 direct_util_recursive_pthread_mutex_init( &fallback_log.lock );
231
232 D_MAGIC_SET( &fallback_log, DirectLog );
233 }
234
235 __attribute__((no_instrument_function))
236 DirectLog *
direct_log_default(void)237 direct_log_default( void )
238 {
239 pthread_once( &init_fallback, init_fallback_log );
240
241 if (!default_log)
242 default_log = &fallback_log;
243
244 D_MAGIC_ASSERT( default_log, DirectLog );
245
246 return default_log;
247 }
248
249 /**********************************************************************************************************************/
250
251 static DirectResult
init_stderr(DirectLog * log)252 init_stderr( DirectLog *log )
253 {
254 log->fd = dup( fileno( stderr ) );
255
256 return DR_OK;
257 }
258
259 static DirectResult
init_file(DirectLog * log,const char * filename)260 init_file( DirectLog *log,
261 const char *filename )
262 {
263 DirectResult ret;
264 int fd;
265
266 fd = open( filename, O_WRONLY | O_CREAT | O_APPEND, 0664 );
267 if (fd < 0) {
268 ret = errno2result( errno );
269 D_PERROR( "Direct/Log: Could not open '%s' for writing!\n", filename );
270 return ret;
271 }
272
273 log->fd = fd;
274
275 return DR_OK;
276 }
277
278 static DirectResult
parse_host_addr(const char * hostport,struct addrinfo ** ret_addr)279 parse_host_addr( const char *hostport,
280 struct addrinfo **ret_addr )
281 {
282 int i, ret;
283
284 int size = strlen( hostport ) + 1;
285 char buf[size];
286
287 char *hoststr = buf;
288 char *portstr = NULL;
289 char *end;
290
291 struct addrinfo hints;
292
293 memcpy( buf, hostport, size );
294
295 for (i=0; i<size; i++) {
296 if (buf[i] == ':') {
297 buf[i] = 0;
298 portstr = &buf[i+1];
299
300 break;
301 }
302 }
303
304 if (!portstr) {
305 D_ERROR( "Direct/Log: Parse error in '%s' that should be '<host>:<port>'!\n", hostport );
306 return DR_INVARG;
307 }
308
309 strtoul( portstr, &end, 10 );
310 if (end && *end) {
311 D_ERROR( "Direct/Log: Parse error in port number '%s'!\n", portstr );
312 return DR_INVARG;
313 }
314
315 memset( &hints, 0, sizeof(hints) );
316 hints.ai_socktype = SOCK_DGRAM;
317 hints.ai_family = PF_UNSPEC;
318
319 ret = getaddrinfo( hoststr, portstr, &hints, ret_addr );
320 if (ret) {
321 switch (ret) {
322 case EAI_FAMILY:
323 D_ERROR( "Direct/Log: Unsupported address family!\n" );
324 return DR_UNSUPPORTED;
325
326 case EAI_SOCKTYPE:
327 D_ERROR( "Direct/Log: Unsupported socket type!\n" );
328 return DR_UNSUPPORTED;
329
330 case EAI_NONAME:
331 D_ERROR( "Direct/Log: Host not found!\n" );
332 return DR_FAILURE;
333
334 case EAI_SERVICE:
335 D_ERROR( "Direct/Log: Port %s is unreachable!\n", portstr );
336 return DR_FAILURE;
337
338 /*
339 #ifdef EAI_ADDRFAMILY
340 case EAI_ADDRFAMILY:
341 #endif
342 case EAI_NODATA:
343 D_ERROR( "Direct/Log: Host found, but has no address!\n" );
344 return DR_FAILURE;
345 */
346
347 case EAI_MEMORY:
348 return D_OOM();
349
350 case EAI_FAIL:
351 D_ERROR( "Direct/Log: A non-recoverable name server error occurred!\n" );
352 return DR_FAILURE;
353
354 case EAI_AGAIN:
355 D_ERROR( "Direct/Log: Temporary error, try again!\n" );
356 return DR_TEMPUNAVAIL;
357
358 default:
359 D_ERROR( "Direct/Log: Unknown error occured!?\n" );
360 return DR_FAILURE;
361 }
362 }
363
364 return DR_OK;
365 }
366
367 static DirectResult
init_udp(DirectLog * log,const char * hostport)368 init_udp( DirectLog *log,
369 const char *hostport )
370 {
371 DirectResult ret;
372 int fd;
373 struct addrinfo *addr;
374
375 ret = parse_host_addr( hostport, &addr );
376 if (ret)
377 return ret;
378
379 fd = socket( addr->ai_family, SOCK_DGRAM, 0 );
380 if (fd < 0) {
381 ret = errno2result( errno );
382 D_PERROR( "Direct/Log: Could not create a UDP socket!\n" );
383 freeaddrinfo( addr );
384 return ret;
385 }
386
387 ret = connect( fd, addr->ai_addr, addr->ai_addrlen );
388 freeaddrinfo( addr );
389
390 if (ret) {
391 ret = errno2result( errno );
392 D_PERROR( "Direct/Log: Could not connect UDP socket to '%s'!\n", hostport );
393 close( fd );
394 return ret;
395 }
396
397 log->fd = fd;
398
399 return DR_OK;
400 }
401