2 * libopensync - A synchronization framework
3 * Copyright (C) 2004-2005 Armin Bauer <armin.bauer@opensync.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <libxml/xmlmemory.h>
22 #include <libxml/parser.h>
25 #include "opensync_internals.h"
27 #include "opensync-support.h"
28 #include "opensync_support_internals.h"
30 GPrivate
* current_tabs
= NULL
;
31 GPrivate
* thread_id
= NULL
;
32 GPrivate
* trace_disabled
= NULL
;
33 GPrivate
* trace_sensitive
= NULL
;
34 GPrivate
* print_stderr
= NULL
;
35 const char *trace
= NULL
;
42 * @defgroup OSyncDebugAPI OpenSync Debug
43 * @ingroup OSyncPublic
44 * @brief Debug functions used by opensync
49 /*! @brief Reset the indentation of the trace function.
51 * Use this function after when process got forked. It's up to the forked
52 * process (child) to reset the indent.
55 void osync_trace_reset_indent(void)
57 g_private_set(current_tabs
, GINT_TO_POINTER(0));
60 /*! @brief Disable tracing
63 void osync_trace_disable(void)
66 trace_disabled
= g_private_new (NULL
);
68 g_private_set(trace_disabled
, GINT_TO_POINTER(1));
71 /*! @brief Enable tracing
74 void osync_trace_enable(void)
77 trace_disabled
= g_private_new (NULL
);
80 g_private_set(trace_disabled
, GINT_TO_POINTER(1));
82 g_private_set(trace_disabled
, GINT_TO_POINTER(0));
86 /*! @brief Initailize tracing
89 static void _osync_trace_init()
91 const char *noprivacy
;
93 trace
= g_getenv("OSYNC_TRACE");
97 noprivacy
= g_getenv("OSYNC_NOPRIVACY");
99 trace_sensitive
= g_private_new(NULL
);
102 g_private_set(trace_sensitive
, GINT_TO_POINTER(1));
104 g_private_set(trace_sensitive
, GINT_TO_POINTER(0));
106 error
= g_getenv("OSYNC_PRINTERROR");
108 print_stderr
= g_private_new(NULL
);
111 g_private_set(print_stderr
, GINT_TO_POINTER(1));
113 g_private_set(print_stderr
, GINT_TO_POINTER(0));
115 if (!g_file_test(trace
, G_FILE_TEST_IS_DIR
)) {
116 printf("OSYNC_TRACE argument is no directory\n");
122 /*! @brief Used for tracing the application
124 * use this function to trace calls. The call graph will be saved into
125 * the file that is given in the OSYNC_TRACE environment variable
127 * @param type The type of the trace
128 * @param message The message to save
132 void osync_trace(OSyncTraceType type
, const char *message
, ...)
134 #ifdef OPENSYNC_TRACE
138 unsigned long int id
= 0;
145 char *logfile
= NULL
;
146 GString
*tabstr
= NULL
;
149 char *logmessage
= NULL
;
150 GError
*error
= NULL
;
151 GIOChannel
*chan
= NULL
;
153 const char *endline
= NULL
;
155 if (!g_thread_supported ()) g_thread_init (NULL
);
157 if (!trace_disabled
|| !g_private_get(trace_disabled
)) {
159 osync_trace_enable();
162 if (GPOINTER_TO_INT(g_private_get(trace_disabled
)))
166 current_tabs
= g_private_new (NULL
);
168 tabs
= GPOINTER_TO_INT(g_private_get(current_tabs
));
172 thread_id
= g_private_new (NULL
);
173 id
= GPOINTER_TO_INT(thread_id
);
177 id
= (unsigned long int)pthread_self();
181 logfile
= g_strdup_printf("%s%cThread%lu-%i.log", trace
, G_DIR_SEPARATOR
, id
, pid
);
183 va_start(arglist
, message
);
186 vsnprintf(tmp_buf
, 1024, message
, arglist
);
187 buffer
= g_strdup(tmp_buf
);
189 buffer
= g_strdup_vprintf(message
, arglist
);
192 tabstr
= g_string_new("");
193 for (i
= 0; i
< tabs
; i
++) {
194 tabstr
= g_string_append(tabstr
, "\t");
197 g_get_current_time(&curtime
);
200 logmessage
= g_strdup_printf("[%li.%06li]\t%s>>>>>>> %s%s", curtime
.tv_sec
, curtime
.tv_usec
, tabstr
->str
, buffer
, endline
);
204 logmessage
= g_strdup_printf("[%li.%06li]\t%s%s%s", curtime
.tv_sec
, curtime
.tv_usec
, tabstr
->str
, buffer
, endline
);
206 case TRACE_SENSITIVE
:
207 if (GPOINTER_TO_INT(g_private_get(trace_sensitive
)))
208 logmessage
= g_strdup_printf("[%li.%06li]\t%s[SENSITIVE] %s%s", curtime
.tv_sec
, curtime
.tv_usec
, tabstr
->str
, buffer
, endline
);
210 logmessage
= g_strdup_printf("[%li.%06li]\t%s[SENSITIVE CONTENT HIDDEN]%s", curtime
.tv_sec
, curtime
.tv_usec
, tabstr
->str
, endline
);
213 logmessage
= g_strdup_printf("[%li.%06li]%s<<<<<<< %s%s", curtime
.tv_sec
, curtime
.tv_usec
, tabstr
->str
, buffer
, endline
);
218 case TRACE_EXIT_ERROR
:
219 logmessage
= g_strdup_printf("[%li.%06li]%s<--- ERROR --- %s%s", curtime
.tv_sec
, curtime
.tv_usec
, tabstr
->str
, buffer
, endline
);
225 fprintf(stderr
, "EXIT_ERROR: %s\n", buffer
);
228 logmessage
= g_strdup_printf("[%li.%06li]%sERROR: %s%s", curtime
.tv_sec
, curtime
.tv_usec
, tabstr
->str
, buffer
, endline
);
231 fprintf(stderr
, "ERROR: %s\n", buffer
);
236 g_private_set(current_tabs
, GINT_TO_POINTER(tabs
));
239 g_string_free(tabstr
, TRUE
);
241 chan
= g_io_channel_new_file(logfile
, "a", &error
);
243 printf("unable to open %s for writing: %s\n", logfile
, error
->message
);
247 g_io_channel_set_encoding(chan
, NULL
, NULL
);
248 if (g_io_channel_write_chars(chan
, logmessage
, strlen(logmessage
), &writen
, NULL
) != G_IO_STATUS_NORMAL
) {
249 printf("unable to write trace to %s\n", logfile
);
251 g_io_channel_flush(chan
, NULL
);
253 g_io_channel_shutdown(chan
, TRUE
, NULL
);
254 g_io_channel_unref(chan
);
258 #endif /* OPENSYNC_TRACE */
261 /*! @brief Used for printing binary data
263 * Unprintable character will be printed in hex, printable are just printed
265 * @param data The data to print
266 * @param len The length to print
269 char *osync_print_binary(const unsigned char *data
, int len
)
272 GString
*str
= g_string_new("");
273 for (t
= 0; t
< len
; t
++) {
274 if (data
[t
] >= ' ' && data
[t
] <= 'z')
275 g_string_append_c(str
, data
[t
]);
277 g_string_append_printf(str
, " %02x ", data
[t
]);
279 return g_string_free(str
, FALSE
);
285 * @defgroup OSyncEnvAPIMisc OpenSync Misc
286 * @ingroup OSyncPrivate
287 * @brief Some helper functions
292 /*! @brief Stop callback function to stop thread mainloop
294 * @param data Pointer to passed callback data
295 * @returns Always FALSE
297 static gboolean
osyncThreadStopCallback(gpointer data
)
299 OSyncThread
*thread
= data
;
301 g_main_loop_quit(thread
->loop
);
306 /*! @brief Start callback function to emit signal when thread's mainloop got started
308 * @param data Pointer to passed callback data
309 * @returns Always FALSE
311 static gboolean
osyncThreadStartCallback(gpointer data
)
313 OSyncThread
*thread
= data
;
315 g_mutex_lock(thread
->started_mutex
);
316 g_cond_signal(thread
->started
);
317 g_mutex_unlock(thread
->started_mutex
);
325 * @defgroup OSyncEnvAPIMisc OpenSync Misc
326 * @ingroup OSyncPublic
327 * @brief Some helper functions
332 /*! @brief Writes data to a file
334 * Writes data to a file
336 * @param filename Where to save the data
337 * @param data Pointer to the data
338 * @param size Size of the data
339 * @param mode The mode to set on the file
340 * @param oserror Pointer to a error struct
341 * @returns TRUE if successful, FALSE otherwise
344 osync_bool
osync_file_write(const char *filename
, const char *data
, unsigned int size
, int mode
, OSyncError
**oserror
)
346 osync_bool ret
= FALSE
;
347 GError
*error
= NULL
;
350 GIOChannel
*chan
= g_io_channel_new_file(filename
, "w", &error
);
352 osync_trace(TRACE_INTERNAL
, "Unable to open file %s for writing: %s", filename
, error
->message
);
353 osync_error_set(oserror
, OSYNC_ERROR_IO_ERROR
, "Unable to open file %s for writing: %s", filename
, error
->message
);
357 if (g_chmod(filename
, mode
)) {
358 osync_trace(TRACE_INTERNAL
, "Unable to set file permissions %i for file %s", mode
, filename
);
359 osync_error_set(oserror
, OSYNC_ERROR_IO_ERROR
, "Unable to set file permissions %i for file %s", mode
, filename
);
364 g_io_channel_set_encoding(chan
, NULL
, NULL
);
365 if (g_io_channel_write_chars(chan
, data
, size
, &writen
, &error
) != G_IO_STATUS_NORMAL
) {
366 osync_trace(TRACE_INTERNAL
, "Unable to write contents of file %s: %s", filename
, error
->message
);
367 osync_error_set(oserror
, OSYNC_ERROR_IO_ERROR
, "Unable to write contents of file %s: %s", filename
, error
->message
);
369 g_io_channel_flush(chan
, NULL
);
372 g_io_channel_shutdown(chan
, FALSE
, NULL
);
373 g_io_channel_unref(chan
);
377 /*! @brief Reads a file
381 * @param filename Where to read the data from
382 * @param data Pointer to the data
383 * @param size Size of the data
384 * @param oserror Pointer to a error struct
385 * @returns TRUE if successful, FALSE otherwise
388 osync_bool
osync_file_read(const char *filename
, char **data
, unsigned int *size
, OSyncError
**oserror
)
390 osync_bool ret
= FALSE
;
391 GError
*error
= NULL
;
393 GIOChannel
*chan
= NULL
;
396 osync_trace(TRACE_INTERNAL
, "No file open specified");
397 osync_error_set(oserror
, OSYNC_ERROR_IO_ERROR
, "No file to open specified");
401 chan
= g_io_channel_new_file(filename
, "r", &error
);
403 osync_trace(TRACE_INTERNAL
, "Unable to read file %s: %s", filename
, error
->message
);
404 osync_error_set(oserror
, OSYNC_ERROR_IO_ERROR
, "Unable to open file %s for reading: %s", filename
, error
->message
);
408 g_io_channel_set_encoding(chan
, NULL
, NULL
);
409 if (g_io_channel_read_to_end(chan
, data
, &sz
, &error
) != G_IO_STATUS_NORMAL
) {
410 osync_trace(TRACE_INTERNAL
, "Unable to read contents of file %s: %s", filename
, error
->message
);
411 osync_error_set(oserror
, OSYNC_ERROR_IO_ERROR
, "Unable to read contents of file %s: %s", filename
, error
->message
);
417 g_io_channel_shutdown(chan
, FALSE
, NULL
);
418 g_io_channel_unref(chan
);
422 /*! @brief Returns the version of opensync
424 * Returns a string identifying the major and minor version
425 * of opensync (something like "0.11")
427 * @returns String with version
430 const char *osync_get_version(void)
432 return OPENSYNC_VERSION
;
435 /*! @brief Safely tries to malloc memory
437 * Tries to malloc memory but returns an error in an OOM situation instead
440 * @param size The size in bytes to malloc
441 * @param error The error which will hold the info in case of an error
442 * @returns A pointer to the new memory or NULL in case of error
445 void *osync_try_malloc0(unsigned int size
, OSyncError
**error
)
449 #ifdef OPENSYNC_UNITTESTS
450 if (!g_getenv("OSYNC_NOMEMORY"))
451 result
= g_try_malloc(size
);
453 result
= g_try_malloc(size
);
454 #endif /*OPENSYNC_UNITTESTS*/
457 osync_error_set(error
, OSYNC_ERROR_GENERIC
, "No memory left");
460 memset(result
, 0, size
);
464 /*! @brief Frees memory
466 * Frees memory allocated by osync_try_malloc0().
468 * @param ptr Pointer to allocated memory which should get freed
471 void osync_free(void *ptr
)
479 /*! @brief Allocates a new thread with a g_mainloop
481 * @param context Pointer to GMainContext
482 * @param error The error which will hold the info in case of an error
483 * @returns A pointer to the new allocated OSyncThread with inactive thread and mainloop
486 OSyncThread
*osync_thread_new(GMainContext
*context
, OSyncError
**error
)
488 OSyncThread
*thread
= NULL
;
489 osync_trace(TRACE_ENTRY
, "%s(%p, %p)", __func__
, context
, error
);
491 thread
= osync_try_malloc0(sizeof(OSyncThread
), error
);
495 if (!g_thread_supported ()) g_thread_init (NULL
);
497 thread
->started_mutex
= g_mutex_new();
498 thread
->started
= g_cond_new();
499 thread
->context
= context
;
501 g_main_context_ref(thread
->context
);
502 thread
->loop
= g_main_loop_new(thread
->context
, FALSE
);
504 osync_trace(TRACE_EXIT
, "%s: %p", __func__
, thread
);
508 osync_trace(TRACE_EXIT_ERROR
, "%s: %s", __func__
, osync_error_print(error
));
512 /*! @brief Frees thread object
514 * @param thread Pointer to OSyncThread
517 void osync_thread_free(OSyncThread
*thread
)
519 osync_trace(TRACE_ENTRY
, "%s(%p)", __func__
, thread
);
520 osync_assert(thread
);
522 if (thread
->started_mutex
)
523 g_mutex_free(thread
->started_mutex
);
526 g_cond_free(thread
->started
);
529 g_main_loop_unref(thread
->loop
);
532 g_main_context_unref(thread
->context
);
535 osync_trace(TRACE_EXIT
, "%s", __func__
);
538 /*static gpointer osyncThreadStartCallback(gpointer data)
540 OSyncThread *thread = data;
542 g_mutex_lock(thread->started_mutex);
543 g_cond_signal(thread->started);
544 g_mutex_unlock(thread->started_mutex);
546 g_main_loop_run(thread->loop);
551 /*void osync_thread_start(OSyncThread *thread)
553 osync_trace(TRACE_ENTRY, "%s(%p)", __func__, thread);
554 osync_assert(thread);
557 g_mutex_lock(thread->started_mutex);
558 thread->thread = g_thread_create (osyncThreadStartCallback, thread, TRUE, NULL);
559 g_cond_wait(thread->started, thread->started_mutex);
560 g_mutex_unlock(thread->started_mutex);
562 osync_trace(TRACE_EXIT, "%s", __func__);
566 /*! @brief Start thread and it's mainloop
568 * @param func GThreadFunc Pointer
569 * @param userdata Custom data poitner which get supplied to thread function
570 * @param error Pointer to error struct
571 * @return Newly allocate OSyncThread object with inactive mainloop
574 OSyncThread
*osync_thread_create(GThreadFunc func
, void *userdata
, OSyncError
**error
)
580 osync_trace(TRACE_ENTRY
, "%s(%p, %p, %p)", __func__
, func
, userdata
, error
);
582 thread
= osync_try_malloc0(sizeof(OSyncThread
), error
);
586 if (!g_thread_supported ())
587 g_thread_init (NULL
);
589 thread
->thread
= g_thread_create(func
, userdata
, TRUE
, &gerror
);
591 if (!thread
->thread
) {
592 osync_error_set(error
, OSYNC_ERROR_GENERIC
, "%s", gerror
->message
);
593 g_error_free(gerror
);
597 osync_trace(TRACE_EXIT
, "%s", __func__
);
601 osync_trace(TRACE_EXIT_ERROR
, "%s", __func__
, osync_error_print(error
));
606 /*! @brief Start thread and it's mainloop
608 * @param thread Thread object
611 void osync_thread_start(OSyncThread
*thread
)
613 GSource
*idle
= NULL
;
614 osync_trace(TRACE_ENTRY
, "%s(%p)", __func__
, thread
);
616 g_mutex_lock(thread
->started_mutex
);
617 idle
= g_idle_source_new();
618 g_source_set_callback(idle
, osyncThreadStartCallback
, thread
, NULL
);
619 g_source_attach(idle
, thread
->context
);
620 thread
->thread
= g_thread_create ((GThreadFunc
)g_main_loop_run
, thread
->loop
, TRUE
, NULL
);
621 g_cond_wait(thread
->started
, thread
->started_mutex
);
622 g_mutex_unlock(thread
->started_mutex
);
624 osync_trace(TRACE_EXIT
, "%s", __func__
);
627 /*! @brief Stops thread's mainloop and joins the thread
629 * @param thread Thread object
632 void osync_thread_stop(OSyncThread
*thread
)
634 GSource
*source
= NULL
;
635 osync_trace(TRACE_ENTRY
, "%s(%p)", __func__
, thread
);
636 osync_assert(thread
);
638 source
= g_idle_source_new();
639 g_source_set_callback(source
, osyncThreadStopCallback
, thread
, NULL
);
640 g_source_attach(source
, thread
->context
);
642 g_thread_join(thread
->thread
);
643 thread
->thread
= NULL
;
645 g_source_unref(source
);
647 osync_trace(TRACE_EXIT
, "%s", __func__
);
650 /*! @brief Exit thread
652 * @param thread Thread object
653 * @param retval Return value of thread while exiting
656 void osync_thread_exit(OSyncThread
*thread
, int retval
)
658 GSource
*source
= NULL
;
659 osync_trace(TRACE_ENTRY
, "%s(%p, %i)", __func__
, thread
, retval
);
660 osync_assert(thread
);
662 source
= g_idle_source_new();
663 g_source_set_callback(source
, osyncThreadStopCallback
, thread
, NULL
);
664 g_source_attach(source
, thread
->context
);
665 g_source_unref(source
);
666 thread
->thread
= NULL
;
668 g_thread_exit(GINT_TO_POINTER(retval
));
670 osync_trace(TRACE_EXIT
, "%s", __func__
);
674 /*! @brief String replace
676 * @param input Input string to work on
677 * @param delimiter Delimiter
678 * @param replacement Replacement
679 * @returns Replaced/Modified string result
682 char *osync_strreplace(const char *input
, const char *delimiter
, const char *replacement
)
686 osync_return_val_if_fail(input
!= NULL
, NULL
);
687 osync_return_val_if_fail(delimiter
!= NULL
, NULL
);
688 osync_return_val_if_fail(replacement
!= NULL
, NULL
);
690 array
= g_strsplit(input
, delimiter
, 0);
691 ret
= g_strjoinv(replacement
, array
);
697 /*! @brief Bit counting
699 * MIT HAKMEM Count, Bit counting in constant time and memory.
701 * @param u unsigned integer value to count bits
702 * @returns The bit counting result
705 int osync_bitcount(unsigned int u
)
707 unsigned int uCount
= u
- ((u
>> 1) & 033333333333) - ((u
>> 2) & 011111111111);
708 return ((uCount
+ (uCount
>> 3)) & 030707070707) % 63;
711 /*! @brief Creates a random string
713 * Creates a random string of given length or less
715 * @param maxlength The maximum length of the string
716 * @returns The random string
719 char *osync_rand_str(int maxlength
)
721 char *randchars
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIKLMNOPQRSTUVWXYZ1234567890";
727 length
= g_random_int_range(1, maxlength
+ 1);
728 retchar
= malloc(length
* sizeof(char) + 1);
731 for (i
= 0; i
< length
; i
++) {
732 retchar
[i
] = randchars
[g_random_int_range(0, strlen(randchars
))];
739 /*! @brief Duplicates a string
741 * Duplicates a string, ending with terminating-zero: \0
743 * @param str The pointer of the string to duplicate
744 * @returns The duplicate string, caller is responsible for freeing.
747 char *osync_strdup(const char *str
)
749 return g_strdup(str
);
752 /*! @brief Duplicates a formated string
754 * Duplicates a formated string, ending with terminating-zero: \0
756 * @param str The pointer of the string to duplicate
758 * @returns The duplicate string, caller is responsible for freeing.
761 char *osync_strdup_printf(const char *format
, ...)
766 va_start(ap
, format
);
767 str
= g_strdup_vprintf(format
, ap
);