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