libsyncml  0.5.4
sml_support.c
1 /*
2  * libsyncml - A syncml protocol implementation
3  * Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
4  * Copyright (C) 2007-2008 Michael Bell <michael.bell@opensync.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include "syncml.h"
23 #include "syncml_internals.h"
24 
25 #include "sml_support.h"
26 #include "sml_error_internals.h"
27 
28 #include "config.h"
29 
30 #include <glib.h>
31 #include <pthread.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdio.h>
35 
36 #include <libxml/parser.h>
37 
38 GPrivate* current_tabs = NULL;
39 GPrivate* thread_id = NULL;
40 
41 #define G_ERRORCHECK_MUTEXES
42 
43 static const char* smlCheckDebugDirectory(const char *env)
44 {
45  const char *dirname = g_getenv(env);
46  if (!dirname)
47  return NULL;
48 
49  if (!g_file_test(dirname, G_FILE_TEST_EXISTS)) {
50  if (g_mkdir_with_parents(dirname, 0700) != 0) {
51  g_warning("The debug directory %s cannot be created.", dirname);
52  return NULL;
53  }
54  }
55 
56  if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
57  g_warning("The debug directory %s is not a directory.", dirname);
58  return NULL;
59  }
60 
61  return dirname;
62 }
63 
71 
72 
73 void smlLog(const char *logname, const char *data, unsigned int size)
74 {
75 #if defined ENABLE_TRACE
76 
77  const char *trace;
78  if (!(trace = smlCheckDebugDirectory("SYNCML_LOG")))
79  return;
80 
81  int i = 0;
82  char *logfile = NULL;
83  for (;;i = i + 1) {
84  char *logfile_tmp = g_strdup_printf("%s/%s", trace, logname);
85  logfile = g_strdup_printf(logfile_tmp, i);
86  smlSafeCFree(&logfile_tmp);
87  if (!g_file_test(logfile, G_FILE_TEST_EXISTS))
88  break;
89  smlSafeCFree(&logfile);
90  }
91 
92  GError *error = NULL;
93  GIOChannel *chan = g_io_channel_new_file(logfile, "w", &error);
94  if (!chan) {
95  printf("unable to open %s for writing: %s\n", logfile, error->message);
96  return;
97  }
98 
99  gsize writen;
100  g_io_channel_set_encoding(chan, NULL, NULL);
101  if (g_io_channel_write_chars(chan, data, size, &writen, NULL) != G_IO_STATUS_NORMAL) {
102  printf("unable to write trace to %s\n", logfile);
103  } else
104  g_io_channel_flush(chan, NULL);
105 
106  g_io_channel_shutdown(chan, TRUE, NULL);
107  g_io_channel_unref(chan);
108 #endif
109 }
110 
120 void smlTrace(SmlTraceType type, const char *message, ...)
121 {
122 #if defined ENABLE_TRACE
123 
124  const char *trace;
125  if (!(trace = smlCheckDebugDirectory("SYNCML_TRACE")))
126  return;
127 
128  if (!g_thread_supported ()) g_thread_init (NULL);
129  int tabs = 0;
130  if (!current_tabs)
131  current_tabs = g_private_new (NULL);
132  else
133  tabs = GPOINTER_TO_INT(g_private_get(current_tabs));
134 
135  unsigned long int id;
136  pid_t pid;
137  const char *endline;
138 #ifdef _WIN32
139 #ifdef PTW32_VERSION
140  id = (unsigned long int)pthread_self().p;
141 #else
142  if (!thread_id)
143  thread_id = g_private_new (NULL);
144  id = GPOINTER_TO_INT(thread_id);
145 #endif
146  pid = _getpid();
147  endline = "\r\n";
148 #else
149  id = (unsigned long int)pthread_self();
150  pid = getpid();
151  endline = "\n";
152 #endif
153  char *logfile = g_strdup_printf("%s/Thread%lu-%d.log", trace, id, pid);
154 
155  // create message
156  va_list arglist;
157  char *buffer = NULL;
158  va_start(arglist, message);
159  buffer = g_strdup_vprintf(message, arglist);
160  va_end(arglist);
161 
162  // filter message if necessary
163  const char *filter = g_getenv("SYNCML_TRACE_FILTER");
164  if (filter)
165  {
166  gchar** filters = g_strsplit(filter, ",", 0);
167  gint i;
168  SmlBool matched = FALSE;
169  for (i=0; filters[i] != NULL && !matched ; i++)
170  {
171  if (strstr(buffer, filters[i]))
172  matched = TRUE;
173  }
174  g_strfreev(filters);
175  if (!matched)
176  {
177  smlSafeCFree(&buffer);
178  return;
179  }
180  }
181 
182  GString *tabstr = g_string_new("");
183  long i = 0;
184  for (i = 0; i < tabs; i++) {
185  tabstr = g_string_append(tabstr, "\t");
186  }
187 
188  GTimeVal curtime;
189  g_get_current_time(&curtime);
190  char *logmessage = NULL;
191  switch (type) {
192  case TRACE_ENTRY:
193  logmessage = g_strdup_printf("[%li.%06li]\t%s>>>>>>> %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
194  tabs++;
195  break;
196  case TRACE_INTERNAL:
197  logmessage = g_strdup_printf("[%li.%06li]\t%s%s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
198  break;
199  case TRACE_ERROR:
200  logmessage = g_strdup_printf("[%li.%06li]\t%sERROR: %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
201  break;
202  case TRACE_EXIT:
203  logmessage = g_strdup_printf("[%li.%06li]%s<<<<<<< %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
204  tabs--;
205  if (tabs < 0)
206  tabs = 0;
207  break;
208  case TRACE_EXIT_ERROR:
209  logmessage = g_strdup_printf("[%li.%06li]%s<--- ERROR --- %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
210  tabs--;
211  if (tabs < 0)
212  tabs = 0;
213  break;
214  }
215  smlSafeCFree(&buffer);
216 
217  g_private_set(current_tabs, GINT_TO_POINTER(tabs));
218 
219  g_string_free(tabstr, TRUE);
220 
221  GError *error = NULL;
222  GIOChannel *chan = g_io_channel_new_file(logfile, "a", &error);
223  if (!chan) {
224  printf("unable to open %s for writing: %s%s", logfile, error->message, endline);
225  smlSafeCFree(&logfile);
226  smlSafeCFree(&logmessage);
227  return;
228  }
229 
230  gsize writen;
231  g_io_channel_set_encoding(chan, NULL, NULL);
232  if (g_io_channel_write_chars(chan, logmessage, strlen(logmessage), &writen, NULL) != G_IO_STATUS_NORMAL) {
233  printf("unable to write trace to %s%s", logfile, endline);
234  } else
235  g_io_channel_flush(chan, NULL);
236 
237  g_io_channel_shutdown(chan, TRUE, NULL);
238  g_io_channel_unref(chan);
239  smlSafeCFree(&logmessage);
240  smlSafeCFree(&logfile);
241 #endif
242 }
243 
252 char *smlPrintBinary(const char *data, int len)
253 {
254  int t;
255  GString *str = g_string_new("");
256  for (t = 0; t < len; t++) {
257  if (data[t] >= ' ' && data[t] <= 'z')
258  g_string_append_c(str, data[t]);
259  else
260  g_string_append_printf(str, " %02x ", (unsigned char) data[t]);
261  }
262  return g_string_free(str, FALSE);
263 }
264 
271 char *smlPrintHex(const char *data, int len)
272 {
273  int t;
274  GString *str = g_string_new("");
275  for (t = 0; t < len; t++) {
276  g_string_append_printf(str, " %02x", (unsigned char) data[t]);
277  if (data[t] >= ' ' && data[t] <= 'z')
278  g_string_append_printf(str, "(%c)", data[t]);
279  g_string_append_c(str, ' ');
280  }
281  return g_string_free(str, FALSE);
282 }
283  /* End of debug API */
285 
293 
302 char *smlRandStr(int maxlength, SmlBool exact)
303 {
304  const char *randchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIKLMNOPQRSTUVWXYZ1234567890";
305 
306  int length;
307  char *retchar;
308  int i = 0;
309 
310  if (exact)
311  length = maxlength;
312  else
313  length = g_random_int_range(1, maxlength + 1);
314 
315  retchar = malloc(length * sizeof(char) + 1);
316  retchar[0] = 0;
317 
318  for (i = 0; i < length; i++) {
319  retchar[i] = randchars[g_random_int_range(0, strlen(randchars))];
320  retchar[i + 1] = 0;
321  }
322 
323  return retchar;
324 }
325 
335 void *smlTryMalloc0(long n_bytes, SmlError **error)
336 {
337  CHECK_ERROR_REF
338 
339  void *result = g_try_malloc(n_bytes);
340  if (!result) {
341  /* set error */
342  char *msg = NULL;
343  if (n_bytes > 0)
344  msg = g_strdup_printf("No memory left (needed %ld).", n_bytes);
345  else
346  msg = g_strdup_printf("Malloc of zero bytes requested.");
347 
348  /* publish error */
349  if (error == NULL)
350  g_error("%s", msg);
351  else
352  smlErrorSet(error, SML_ERROR_INTERNAL_NO_MEMORY, "%s", msg);
353  return NULL;
354  }
355  memset(result, 0, n_bytes);
356  return result;
357 }
358 
364 const char *smlGetVersion(void)
365 {
369  return "$Id$";
370 }
371 
372 SmlThread *smlThreadNew(GMainContext *context, SmlError **error)
373 {
374  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, context, error);
375  CHECK_ERROR_REF
376  /* Never ever run on the default context because a library
377  * thread should never influence the behavior of an user
378  * application and should never be influence by an user
379  * application.
380  *
381  * This is highly critical especially if the user application
382  * user the default context to loop on smlDsSessionDispatch
383  * and smlManagerDispatch.
384  */
385  smlAssert(context);
386 
387  SmlThread *thread = smlTryMalloc0(sizeof(SmlThread), error);
388  if (!thread)
389  goto error;
390 
391  if (!g_thread_supported ()) g_thread_init (NULL);
392  if (!g_thread_supported ())
393  {
394  smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
395  "Threads are not supported.");
396  goto error;
397  }
398 
399  thread->started = FALSE;
400  thread->started_mutex = g_mutex_new();
401  thread->started_cond = g_cond_new();
402  thread->context = context;
403  g_main_context_ref(thread->context);
404  thread->loop = g_main_loop_new(thread->context, FALSE);
405 
406  smlTrace(TRACE_EXIT, "%s: %p", __func__, thread);
407  return thread;
408 
409 error:
410  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
411  return NULL;
412 }
413 
414 void smlThreadFree(SmlThread *thread)
415 {
416  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, thread);
417  smlAssert(thread);
418 
419  if (thread->started_mutex)
420  g_mutex_free(thread->started_mutex);
421 
422  if (thread->started_cond)
423  g_cond_free(thread->started_cond);
424 
425  if (thread->loop)
426  g_main_loop_unref(thread->loop);
427 
428  if (thread->context)
429  g_main_context_unref(thread->context);
430 
431  smlSafeFree((gpointer *)&thread);
432  smlTrace(TRACE_EXIT, "%s", __func__);
433 }
434 
435 static gpointer smlThreadStartCallback(gpointer data)
436 {
437  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
438  SmlThread *thread = data;
439  smlTrace(TRACE_INTERNAL, "%s: +++++++++ This is the worker thread of thread %p for context %p +++++++++", __func__, thread, thread->context);
440 
441  smlTrace(TRACE_INTERNAL, "%s: locking", __func__);
442  g_mutex_lock(thread->started_mutex);
443  thread->started = TRUE;
444  smlTrace(TRACE_INTERNAL, "%s: sending condition", __func__);
445  g_cond_signal(thread->started_cond);
446  smlTrace(TRACE_INTERNAL, "%s: unlocking", __func__);
447  g_mutex_unlock(thread->started_mutex);
448 
449  /* check main context ownership */
450  if (!g_main_context_acquire(thread->context)) {
451  smlAssertMsg(FALSE, "This thread is not the owner of the GMainContext.");
452  } else {
453  smlTrace(TRACE_INTERNAL, "%s: Thread is owner of the GMainContext.", __func__);
454  }
455 
456  g_main_loop_run(thread->loop);
457 
458  /* release context ownership / decrement reference counter */
459  g_main_context_release(thread->context);
460 
461  smlTrace(TRACE_EXIT, "%s", __func__);
462  return FALSE;
463 }
464 
465 static gboolean smlThreadStopCallback(gpointer data)
466 {
467  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
468  SmlThread *thread = data;
469  smlTrace(TRACE_INTERNAL, "%s: +++++++++ Quitting worker thread +++++++++", __func__);
470 
471  g_main_loop_quit(thread->loop);
472 
473  smlTrace(TRACE_EXIT, "%s", __func__);
474  return FALSE;
475 }
476 
477 void smlThreadStart(SmlThread *thread)
478 {
479  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, thread);
480  smlAssert(thread);
481 
482  //Start the thread
483  smlTrace(TRACE_INTERNAL, "%s: locking", __func__);
484  g_mutex_lock(thread->started_mutex);
485  smlTrace(TRACE_INTERNAL, "%s: creating thread", __func__);
486  thread->thread = g_thread_create (smlThreadStartCallback, thread, TRUE, NULL);
487  smlAssert(thread->thread);
488  smlTrace(TRACE_INTERNAL, "%s: waiting for start", __func__);
489  while(!thread->started) {
490  smlTrace(TRACE_INTERNAL, "%s: checking condition", __func__);
491  g_cond_wait(thread->started_cond, thread->started_mutex);
492  }
493  smlTrace(TRACE_INTERNAL, "%s: condition received", __func__);
494  g_mutex_unlock(thread->started_mutex);
495 
496  smlTrace(TRACE_EXIT, "%s", __func__);
497 }
498 
499 void smlThreadStop(SmlThread *thread)
500 {
501  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, thread);
502  smlAssert(thread);
503 
504  GSource *source = g_idle_source_new();
505  g_source_set_callback(source, smlThreadStopCallback, thread, NULL);
506  g_source_attach(source, thread->context);
507 
508  g_thread_join(thread->thread);
509  thread->thread = NULL;
510 
511  g_source_unref(source);
512 
513  smlTrace(TRACE_EXIT, "%s", __func__);
514 }
515 
521 typedef struct SmlThreadFunctionContext {
522  GMutex *mutex;
523  GCond *cond;
524  SmlThreadCallFunctionType func;
525  gpointer data;
526  SmlBool result;
527  SmlError **error;
529 
534 gboolean smlThreadCallFunctionCallback(gpointer data)
535 {
536  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
537  smlAssert(data);
538 
539  SmlThreadFunctionContext *ctx = data;
540  ctx->result = ctx->func(ctx->data, ctx->error);
541  g_mutex_lock(ctx->mutex);
542  g_cond_signal(ctx->cond);
543  g_mutex_unlock(ctx->mutex);
544 
545  /* This function should only run once. */
546  smlTrace(TRACE_EXIT, "%s", __func__);
547  return FALSE;
548 }
549 
562  SmlThread *thread,
563  SmlThreadCallFunctionType func,
564  gpointer data,
565  SmlError **error)
566 {
567  smlTrace(TRACE_ENTRY, "%s(%p => %p, %p, %p, %p)", __func__, thread, thread?thread->context:NULL, func, data, error);
568  CHECK_ERROR_REF
569  smlAssert(func);
570 
571  /* initialize function context */
572  smlTrace(TRACE_INTERNAL, "%s: preparing context", __func__);
574  ctx = smlTryMalloc0(sizeof(SmlThreadFunctionContext), error);
575  if (!ctx)
576  goto error;
577  ctx->mutex = g_mutex_new();
578  if (!ctx->mutex) {
579  smlErrorSet(error, SML_ERROR_GENERIC, "Cannot create new mutex.");
580  goto error;
581  }
582  ctx->cond = g_cond_new();
583  if (!ctx->cond) {
584  smlErrorSet(error, SML_ERROR_GENERIC, "Cannot create new condition.");
585  goto error;
586  }
587  ctx->func = func;
588  ctx->data = data;
589  ctx->error = error;
590 
591  /* prepare glib source */
592  smlTrace(TRACE_INTERNAL, "%s: preparing source", __func__);
593  GSource *source = g_idle_source_new();
594  g_source_set_callback(source, smlThreadCallFunctionCallback, ctx, NULL);
595 
596  /* call function "synchronously" */
597  g_mutex_lock(ctx->mutex);
598  smlTrace(TRACE_INTERNAL, "%s: attach source", __func__);
599  g_source_attach(source, thread->context);
600  smlTrace(TRACE_INTERNAL, "%s: wait for condition", __func__);
601  g_cond_wait(ctx->cond, ctx->mutex);
602  smlTrace(TRACE_INTERNAL, "%s: get condition", __func__);
603  g_mutex_unlock(ctx->mutex);
604 
605  /* cleanup */
606  smlTrace(TRACE_INTERNAL, "%s: cleanup", __func__);
607  SmlBool result = ctx->result;
608  g_source_unref(source);
609  g_mutex_free(ctx->mutex);
610  g_cond_free(ctx->cond);
611  smlSafeFree((gpointer *) &ctx);
612 
613  /* return result */
614  smlTrace(TRACE_EXIT, "%s - %i", __func__, result);
615  return result;
616 error:
617  if (ctx->cond)
618  g_cond_free(ctx->cond);
619  if (ctx->mutex)
620  g_mutex_free(ctx->mutex);
621  if (ctx)
622  smlSafeFree((gpointer *) &ctx);
623  smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
624  return FALSE;
625 }
626 
627 void smlSafeFree(gpointer *address)
628 {
629  smlAssert(address);
630  smlAssert(*address);
631  g_free(*address);
632  *address = NULL;
633 }
634 
635 void smlSafeCFree(char **address)
636 {
637  smlSafeFree((gpointer *)address);
638 }
639 
640 const char *smlGetLibraryVersion()
641 {
642  return PACKAGE_VERSION;
643 }
644 
645 const char *smlGetLibrarySoName()
646 {
647  return PACKAGE_SONAME;
648 }
649