libsyncml 0.5.4

- Main Page
- Modules
- Data Structures
- Files

- File List

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;
528 } SmlThreadFunctionContext;
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
561 SmlBool smlThreadCallFunction(
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__);
573 SmlThreadFunctionContext *ctx;
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