00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "syncml.h"
00023 #include "syncml_internals.h"
00024
00025 #include "sml_support.h"
00026 #include "sml_error_internals.h"
00027
00028 #include "config.h"
00029
00030 #include <glib.h>
00031 #include <pthread.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #include <stdio.h>
00035
00036 #include <libxml/parser.h>
00037
00038 GPrivate* current_tabs = NULL;
00039 GPrivate* thread_id = NULL;
00040
00041 #define G_ERRORCHECK_MUTEXES
00042
00043 static const char* smlCheckDebugDirectory(const char *env)
00044 {
00045 const char *dirname = g_getenv(env);
00046 if (!dirname)
00047 return NULL;
00048
00049 if (!g_file_test(dirname, G_FILE_TEST_EXISTS)) {
00050 if (g_mkdir_with_parents(dirname, 0700) != 0) {
00051 g_warning("The debug directory %s cannot be created.", dirname);
00052 return NULL;
00053 }
00054 }
00055
00056 if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) {
00057 g_warning("The debug directory %s is not a directory.", dirname);
00058 return NULL;
00059 }
00060
00061 return dirname;
00062 }
00063
00071
00072
00073 void smlLog(const char *logname, const char *data, unsigned int size)
00074 {
00075 #if defined ENABLE_TRACE
00076
00077 const char *trace;
00078 if (!(trace = smlCheckDebugDirectory("SYNCML_LOG")))
00079 return;
00080
00081 int i = 0;
00082 char *logfile = NULL;
00083 for (;;i = i + 1) {
00084 char *logfile_tmp = g_strdup_printf("%s/%s", trace, logname);
00085 logfile = g_strdup_printf(logfile_tmp, i);
00086 smlSafeCFree(&logfile_tmp);
00087 if (!g_file_test(logfile, G_FILE_TEST_EXISTS))
00088 break;
00089 smlSafeCFree(&logfile);
00090 }
00091
00092 GError *error = NULL;
00093 GIOChannel *chan = g_io_channel_new_file(logfile, "w", &error);
00094 if (!chan) {
00095 printf("unable to open %s for writing: %s\n", logfile, error->message);
00096 return;
00097 }
00098
00099 gsize writen;
00100 g_io_channel_set_encoding(chan, NULL, NULL);
00101 if (g_io_channel_write_chars(chan, data, size, &writen, NULL) != G_IO_STATUS_NORMAL) {
00102 printf("unable to write trace to %s\n", logfile);
00103 } else
00104 g_io_channel_flush(chan, NULL);
00105
00106 g_io_channel_shutdown(chan, TRUE, NULL);
00107 g_io_channel_unref(chan);
00108 #endif
00109 }
00110
00120 void smlTrace(SmlTraceType type, const char *message, ...)
00121 {
00122 #if defined ENABLE_TRACE
00123
00124 const char *trace;
00125 if (!(trace = smlCheckDebugDirectory("SYNCML_TRACE")))
00126 return;
00127
00128 if (!g_thread_supported ()) g_thread_init (NULL);
00129 int tabs = 0;
00130 if (!current_tabs)
00131 current_tabs = g_private_new (NULL);
00132 else
00133 tabs = GPOINTER_TO_INT(g_private_get(current_tabs));
00134
00135 unsigned long int id;
00136 pid_t pid;
00137 const char *endline;
00138 #ifdef _WIN32
00139 #ifdef PTW32_VERSION
00140 id = (unsigned long int)pthread_self().p;
00141 #else
00142 if (!thread_id)
00143 thread_id = g_private_new (NULL);
00144 id = GPOINTER_TO_INT(thread_id);
00145 #endif
00146 pid = _getpid();
00147 endline = "\r\n";
00148 #else
00149 id = (unsigned long int)pthread_self();
00150 pid = getpid();
00151 endline = "\n";
00152 #endif
00153 char *logfile = g_strdup_printf("%s/Thread%lu-%d.log", trace, id, pid);
00154
00155
00156 va_list arglist;
00157 char *buffer = NULL;
00158 va_start(arglist, message);
00159 buffer = g_strdup_vprintf(message, arglist);
00160 va_end(arglist);
00161
00162
00163 const char *filter = g_getenv("SYNCML_TRACE_FILTER");
00164 if (filter)
00165 {
00166 gchar** filters = g_strsplit(filter, ",", 0);
00167 gint i;
00168 SmlBool matched = FALSE;
00169 for (i=0; filters[i] != NULL && !matched ; i++)
00170 {
00171 if (strstr(buffer, filters[i]))
00172 matched = TRUE;
00173 }
00174 g_strfreev(filters);
00175 if (!matched)
00176 {
00177 smlSafeCFree(&buffer);
00178 return;
00179 }
00180 }
00181
00182 GString *tabstr = g_string_new("");
00183 long i = 0;
00184 for (i = 0; i < tabs; i++) {
00185 tabstr = g_string_append(tabstr, "\t");
00186 }
00187
00188 GTimeVal curtime;
00189 g_get_current_time(&curtime);
00190 char *logmessage = NULL;
00191 switch (type) {
00192 case TRACE_ENTRY:
00193 logmessage = g_strdup_printf("[%li.%06li]\t%s>>>>>>> %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
00194 tabs++;
00195 break;
00196 case TRACE_INTERNAL:
00197 logmessage = g_strdup_printf("[%li.%06li]\t%s%s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
00198 break;
00199 case TRACE_ERROR:
00200 logmessage = g_strdup_printf("[%li.%06li]\t%sERROR: %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
00201 break;
00202 case TRACE_EXIT:
00203 logmessage = g_strdup_printf("[%li.%06li]%s<<<<<<< %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
00204 tabs--;
00205 if (tabs < 0)
00206 tabs = 0;
00207 break;
00208 case TRACE_EXIT_ERROR:
00209 logmessage = g_strdup_printf("[%li.%06li]%s<--- ERROR --- %s%s", curtime.tv_sec, curtime.tv_usec, tabstr->str, buffer, endline);
00210 tabs--;
00211 if (tabs < 0)
00212 tabs = 0;
00213 break;
00214 }
00215 smlSafeCFree(&buffer);
00216
00217 g_private_set(current_tabs, GINT_TO_POINTER(tabs));
00218
00219 g_string_free(tabstr, TRUE);
00220
00221 GError *error = NULL;
00222 GIOChannel *chan = g_io_channel_new_file(logfile, "a", &error);
00223 if (!chan) {
00224 printf("unable to open %s for writing: %s%s", logfile, error->message, endline);
00225 smlSafeCFree(&logfile);
00226 smlSafeCFree(&logmessage);
00227 return;
00228 }
00229
00230 gsize writen;
00231 g_io_channel_set_encoding(chan, NULL, NULL);
00232 if (g_io_channel_write_chars(chan, logmessage, strlen(logmessage), &writen, NULL) != G_IO_STATUS_NORMAL) {
00233 printf("unable to write trace to %s%s", logfile, endline);
00234 } else
00235 g_io_channel_flush(chan, NULL);
00236
00237 g_io_channel_shutdown(chan, TRUE, NULL);
00238 g_io_channel_unref(chan);
00239 smlSafeCFree(&logmessage);
00240 smlSafeCFree(&logfile);
00241 #endif
00242 }
00243
00252 char *smlPrintBinary(const char *data, int len)
00253 {
00254 int t;
00255 GString *str = g_string_new("");
00256 for (t = 0; t < len; t++) {
00257 if (data[t] >= ' ' && data[t] <= 'z')
00258 g_string_append_c(str, data[t]);
00259 else
00260 g_string_append_printf(str, " %02x ", (unsigned char) data[t]);
00261 }
00262 return g_string_free(str, FALSE);
00263 }
00264
00271 char *smlPrintHex(const char *data, int len)
00272 {
00273 int t;
00274 GString *str = g_string_new("");
00275 for (t = 0; t < len; t++) {
00276 g_string_append_printf(str, " %02x", (unsigned char) data[t]);
00277 if (data[t] >= ' ' && data[t] <= 'z')
00278 g_string_append_printf(str, "(%c)", data[t]);
00279 g_string_append_c(str, ' ');
00280 }
00281 return g_string_free(str, FALSE);
00282 }
00283
00285
00293
00302 char *smlRandStr(int maxlength, SmlBool exact)
00303 {
00304 const char *randchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIKLMNOPQRSTUVWXYZ1234567890";
00305
00306 int length;
00307 char *retchar;
00308 int i = 0;
00309
00310 if (exact)
00311 length = maxlength;
00312 else
00313 length = g_random_int_range(1, maxlength + 1);
00314
00315 retchar = malloc(length * sizeof(char) + 1);
00316 retchar[0] = 0;
00317
00318 for (i = 0; i < length; i++) {
00319 retchar[i] = randchars[g_random_int_range(0, strlen(randchars))];
00320 retchar[i + 1] = 0;
00321 }
00322
00323 return retchar;
00324 }
00325
00335 void *smlTryMalloc0(long n_bytes, SmlError **error)
00336 {
00337 CHECK_ERROR_REF
00338
00339 void *result = g_try_malloc(n_bytes);
00340 if (!result) {
00341
00342 char *msg = NULL;
00343 if (n_bytes > 0)
00344 msg = g_strdup_printf("No memory left (needed %ld).", n_bytes);
00345 else
00346 msg = g_strdup_printf("Malloc of zero bytes requested.");
00347
00348
00349 if (error == NULL)
00350 g_error("%s", msg);
00351 else
00352 smlErrorSet(error, SML_ERROR_INTERNAL_NO_MEMORY, "%s", msg);
00353 return NULL;
00354 }
00355 memset(result, 0, n_bytes);
00356 return result;
00357 }
00358
00364 const char *smlGetVersion(void)
00365 {
00369 return "$Id$";
00370 }
00371
00372 SmlThread *smlThreadNew(GMainContext *context, SmlError **error)
00373 {
00374 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, context, error);
00375 CHECK_ERROR_REF
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385 smlAssert(context);
00386
00387 SmlThread *thread = smlTryMalloc0(sizeof(SmlThread), error);
00388 if (!thread)
00389 goto error;
00390
00391 if (!g_thread_supported ()) g_thread_init (NULL);
00392 if (!g_thread_supported ())
00393 {
00394 smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
00395 "Threads are not supported.");
00396 goto error;
00397 }
00398
00399 thread->started = FALSE;
00400 thread->started_mutex = g_mutex_new();
00401 thread->started_cond = g_cond_new();
00402 thread->context = context;
00403 g_main_context_ref(thread->context);
00404 thread->loop = g_main_loop_new(thread->context, FALSE);
00405
00406 smlTrace(TRACE_EXIT, "%s: %p", __func__, thread);
00407 return thread;
00408
00409 error:
00410 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
00411 return NULL;
00412 }
00413
00414 void smlThreadFree(SmlThread *thread)
00415 {
00416 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, thread);
00417 smlAssert(thread);
00418
00419 if (thread->started_mutex)
00420 g_mutex_free(thread->started_mutex);
00421
00422 if (thread->started_cond)
00423 g_cond_free(thread->started_cond);
00424
00425 if (thread->loop)
00426 g_main_loop_unref(thread->loop);
00427
00428 if (thread->context)
00429 g_main_context_unref(thread->context);
00430
00431 smlSafeFree((gpointer *)&thread);
00432 smlTrace(TRACE_EXIT, "%s", __func__);
00433 }
00434
00435 static gpointer smlThreadStartCallback(gpointer data)
00436 {
00437 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
00438 SmlThread *thread = data;
00439 smlTrace(TRACE_INTERNAL, "%s: +++++++++ This is the worker thread of thread %p for context %p +++++++++", __func__, thread, thread->context);
00440
00441 smlTrace(TRACE_INTERNAL, "%s: locking", __func__);
00442 g_mutex_lock(thread->started_mutex);
00443 thread->started = TRUE;
00444 smlTrace(TRACE_INTERNAL, "%s: sending condition", __func__);
00445 g_cond_signal(thread->started_cond);
00446 smlTrace(TRACE_INTERNAL, "%s: unlocking", __func__);
00447 g_mutex_unlock(thread->started_mutex);
00448
00449
00450 if (!g_main_context_acquire(thread->context)) {
00451 smlAssertMsg(FALSE, "This thread is not the owner of the GMainContext.");
00452 } else {
00453 smlTrace(TRACE_INTERNAL, "%s: Thread is owner of the GMainContext.", __func__);
00454 }
00455
00456 g_main_loop_run(thread->loop);
00457
00458
00459 g_main_context_release(thread->context);
00460
00461 smlTrace(TRACE_EXIT, "%s", __func__);
00462 return FALSE;
00463 }
00464
00465 static gboolean smlThreadStopCallback(gpointer data)
00466 {
00467 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
00468 SmlThread *thread = data;
00469 smlTrace(TRACE_INTERNAL, "%s: +++++++++ Quitting worker thread +++++++++", __func__);
00470
00471 g_main_loop_quit(thread->loop);
00472
00473 smlTrace(TRACE_EXIT, "%s", __func__);
00474 return FALSE;
00475 }
00476
00477 void smlThreadStart(SmlThread *thread)
00478 {
00479 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, thread);
00480 smlAssert(thread);
00481
00482
00483 smlTrace(TRACE_INTERNAL, "%s: locking", __func__);
00484 g_mutex_lock(thread->started_mutex);
00485 smlTrace(TRACE_INTERNAL, "%s: creating thread", __func__);
00486 thread->thread = g_thread_create (smlThreadStartCallback, thread, TRUE, NULL);
00487 smlAssert(thread->thread);
00488 smlTrace(TRACE_INTERNAL, "%s: waiting for start", __func__);
00489 while(!thread->started) {
00490 smlTrace(TRACE_INTERNAL, "%s: checking condition", __func__);
00491 g_cond_wait(thread->started_cond, thread->started_mutex);
00492 }
00493 smlTrace(TRACE_INTERNAL, "%s: condition received", __func__);
00494 g_mutex_unlock(thread->started_mutex);
00495
00496 smlTrace(TRACE_EXIT, "%s", __func__);
00497 }
00498
00499 void smlThreadStop(SmlThread *thread)
00500 {
00501 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, thread);
00502 smlAssert(thread);
00503
00504 GSource *source = g_idle_source_new();
00505 g_source_set_callback(source, smlThreadStopCallback, thread, NULL);
00506 g_source_attach(source, thread->context);
00507
00508 g_thread_join(thread->thread);
00509 thread->thread = NULL;
00510
00511 g_source_unref(source);
00512
00513 smlTrace(TRACE_EXIT, "%s", __func__);
00514 }
00515
00521 typedef struct SmlThreadFunctionContext {
00522 GMutex *mutex;
00523 GCond *cond;
00524 SmlThreadCallFunctionType func;
00525 gpointer data;
00526 SmlBool result;
00527 SmlError **error;
00528 } SmlThreadFunctionContext;
00529
00534 gboolean smlThreadCallFunctionCallback(gpointer data)
00535 {
00536 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
00537 smlAssert(data);
00538
00539 SmlThreadFunctionContext *ctx = data;
00540 ctx->result = ctx->func(ctx->data, ctx->error);
00541 g_mutex_lock(ctx->mutex);
00542 g_cond_signal(ctx->cond);
00543 g_mutex_unlock(ctx->mutex);
00544
00545
00546 smlTrace(TRACE_EXIT, "%s", __func__);
00547 return FALSE;
00548 }
00549
00561 SmlBool smlThreadCallFunction(
00562 SmlThread *thread,
00563 SmlThreadCallFunctionType func,
00564 gpointer data,
00565 SmlError **error)
00566 {
00567 smlTrace(TRACE_ENTRY, "%s(%p => %p, %p, %p, %p)", __func__, thread, thread?thread->context:NULL, func, data, error);
00568 CHECK_ERROR_REF
00569 smlAssert(func);
00570
00571
00572 smlTrace(TRACE_INTERNAL, "%s: preparing context", __func__);
00573 SmlThreadFunctionContext *ctx;
00574 ctx = smlTryMalloc0(sizeof(SmlThreadFunctionContext), error);
00575 if (!ctx)
00576 goto error;
00577 ctx->mutex = g_mutex_new();
00578 if (!ctx->mutex) {
00579 smlErrorSet(error, SML_ERROR_GENERIC, "Cannot create new mutex.");
00580 goto error;
00581 }
00582 ctx->cond = g_cond_new();
00583 if (!ctx->cond) {
00584 smlErrorSet(error, SML_ERROR_GENERIC, "Cannot create new condition.");
00585 goto error;
00586 }
00587 ctx->func = func;
00588 ctx->data = data;
00589 ctx->error = error;
00590
00591
00592 smlTrace(TRACE_INTERNAL, "%s: preparing source", __func__);
00593 GSource *source = g_idle_source_new();
00594 g_source_set_callback(source, smlThreadCallFunctionCallback, ctx, NULL);
00595
00596
00597 g_mutex_lock(ctx->mutex);
00598 smlTrace(TRACE_INTERNAL, "%s: attach source", __func__);
00599 g_source_attach(source, thread->context);
00600 smlTrace(TRACE_INTERNAL, "%s: wait for condition", __func__);
00601 g_cond_wait(ctx->cond, ctx->mutex);
00602 smlTrace(TRACE_INTERNAL, "%s: get condition", __func__);
00603 g_mutex_unlock(ctx->mutex);
00604
00605
00606 smlTrace(TRACE_INTERNAL, "%s: cleanup", __func__);
00607 SmlBool result = ctx->result;
00608 g_source_unref(source);
00609 g_mutex_free(ctx->mutex);
00610 g_cond_free(ctx->cond);
00611 smlSafeFree((gpointer *) &ctx);
00612
00613
00614 smlTrace(TRACE_EXIT, "%s - %i", __func__, result);
00615 return result;
00616 error:
00617 if (ctx->cond)
00618 g_cond_free(ctx->cond);
00619 if (ctx->mutex)
00620 g_mutex_free(ctx->mutex);
00621 if (ctx)
00622 smlSafeFree((gpointer *) &ctx);
00623 smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
00624 return FALSE;
00625 }
00626
00627 void smlSafeFree(gpointer *address)
00628 {
00629 smlAssert(address);
00630 smlAssert(*address);
00631 g_free(*address);
00632 *address = NULL;
00633 }
00634
00635 void smlSafeCFree(char **address)
00636 {
00637 smlSafeFree((gpointer *)address);
00638 }
00639
00640 const char *smlGetLibraryVersion()
00641 {
00642 return PACKAGE_VERSION;
00643 }
00644
00645 const char *smlGetLibrarySoName()
00646 {
00647 return PACKAGE_SONAME;
00648 }
00649