GDBus: Add `return' debug option
[platform/upstream/glib.git] / gio / gdbusprivate.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2010 Red Hat, Inc.
4  *
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 of the License, or (at your option) any later version.
9  *
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.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
31 #include "giotypes.h"
32 #include "gsocket.h"
33 #include "gdbusprivate.h"
34 #include "gdbusmessage.h"
35 #include "gdbuserror.h"
36 #include "gdbusintrospection.h"
37 #include "gasyncresult.h"
38 #include "gsimpleasyncresult.h"
39 #include "ginputstream.h"
40 #include "giostream.h"
41 #include "gsocketcontrolmessage.h"
42 #include "gsocketconnection.h"
43
44 #ifdef G_OS_UNIX
45 #include "gunixfdmessage.h"
46 #include "gunixconnection.h"
47 #include "gunixcredentialsmessage.h"
48 #endif
49
50 #ifdef G_OS_WIN32
51 #include <windows.h>
52 #endif
53
54 #include "glibintl.h"
55
56 /* ---------------------------------------------------------------------------------------------------- */
57
58 gchar *
59 _g_dbus_hexdump (const gchar *data, gsize len, guint indent)
60 {
61  guint n, m;
62  GString *ret;
63
64  ret = g_string_new (NULL);
65
66  for (n = 0; n < len; n += 16)
67    {
68      g_string_append_printf (ret, "%*s%04x: ", indent, "", n);
69
70      for (m = n; m < n + 16; m++)
71        {
72          if (m > n && (m%4) == 0)
73            g_string_append_c (ret, ' ');
74          if (m < len)
75            g_string_append_printf (ret, "%02x ", (guchar) data[m]);
76          else
77            g_string_append (ret, "   ");
78        }
79
80      g_string_append (ret, "   ");
81
82      for (m = n; m < len && m < n + 16; m++)
83        g_string_append_c (ret, g_ascii_isprint (data[m]) ? data[m] : '.');
84
85      g_string_append_c (ret, '\n');
86    }
87
88  return g_string_free (ret, FALSE);
89 }
90
91 /* ---------------------------------------------------------------------------------------------------- */
92
93 /* Unfortunately ancillary messages are discarded when reading from a
94  * socket using the GSocketInputStream abstraction. So we provide a
95  * very GInputStream-ish API that uses GSocket in this case (very
96  * similar to GSocketInputStream).
97  */
98
99 typedef struct
100 {
101   GSocket *socket;
102   GCancellable *cancellable;
103
104   void *buffer;
105   gsize count;
106
107   GSocketControlMessage ***messages;
108   gint *num_messages;
109
110   GSimpleAsyncResult *simple;
111
112   gboolean from_mainloop;
113 } ReadWithControlData;
114
115 static void
116 read_with_control_data_free (ReadWithControlData *data)
117 {
118   g_object_unref (data->socket);
119   if (data->cancellable != NULL)
120     g_object_unref (data->cancellable);
121   g_object_unref (data->simple);
122   g_free (data);
123 }
124
125 static gboolean
126 _g_socket_read_with_control_messages_ready (GSocket      *socket,
127                                             GIOCondition  condition,
128                                             gpointer      user_data)
129 {
130   ReadWithControlData *data = user_data;
131   GError *error;
132   gssize result;
133   GInputVector vector;
134
135   error = NULL;
136   vector.buffer = data->buffer;
137   vector.size = data->count;
138   result = g_socket_receive_message (data->socket,
139                                      NULL, /* address */
140                                      &vector,
141                                      1,
142                                      data->messages,
143                                      data->num_messages,
144                                      NULL,
145                                      data->cancellable,
146                                      &error);
147   if (result >= 0)
148     {
149       g_simple_async_result_set_op_res_gssize (data->simple, result);
150     }
151   else
152     {
153       g_assert (error != NULL);
154       g_simple_async_result_set_from_error (data->simple, error);
155       g_error_free (error);
156     }
157
158   if (data->from_mainloop)
159     g_simple_async_result_complete (data->simple);
160   else
161     g_simple_async_result_complete_in_idle (data->simple);
162
163   return FALSE;
164 }
165
166 static void
167 _g_socket_read_with_control_messages (GSocket                 *socket,
168                                       void                    *buffer,
169                                       gsize                    count,
170                                       GSocketControlMessage ***messages,
171                                       gint                    *num_messages,
172                                       gint                     io_priority,
173                                       GCancellable            *cancellable,
174                                       GAsyncReadyCallback      callback,
175                                       gpointer                 user_data)
176 {
177   ReadWithControlData *data;
178
179   data = g_new0 (ReadWithControlData, 1);
180   data->socket = g_object_ref (socket);
181   data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
182   data->buffer = buffer;
183   data->count = count;
184   data->messages = messages;
185   data->num_messages = num_messages;
186
187   data->simple = g_simple_async_result_new (G_OBJECT (socket),
188                                             callback,
189                                             user_data,
190                                             _g_socket_read_with_control_messages);
191
192   if (!g_socket_condition_check (socket, G_IO_IN))
193     {
194       GSource *source;
195       data->from_mainloop = TRUE;
196       source = g_socket_create_source (data->socket,
197                                        G_IO_IN | G_IO_HUP | G_IO_ERR,
198                                        cancellable);
199       g_source_set_callback (source,
200                              (GSourceFunc) _g_socket_read_with_control_messages_ready,
201                              data,
202                              (GDestroyNotify) read_with_control_data_free);
203       g_source_attach (source, g_main_context_get_thread_default ());
204       g_source_unref (source);
205     }
206   else
207     {
208       _g_socket_read_with_control_messages_ready (data->socket, G_IO_IN, data);
209       read_with_control_data_free (data);
210     }
211 }
212
213 static gssize
214 _g_socket_read_with_control_messages_finish (GSocket       *socket,
215                                              GAsyncResult  *result,
216                                              GError       **error)
217 {
218   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
219
220   g_return_val_if_fail (G_IS_SOCKET (socket), -1);
221   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == _g_socket_read_with_control_messages);
222
223   if (g_simple_async_result_propagate_error (simple, error))
224       return -1;
225   else
226     return g_simple_async_result_get_op_res_gssize (simple);
227 }
228
229 /* ---------------------------------------------------------------------------------------------------- */
230
231 G_LOCK_DEFINE_STATIC (shared_thread_lock);
232
233 typedef struct
234 {
235   gint num_users;
236   GThread *thread;
237   GMainContext *context;
238   GMainLoop *loop;
239 } SharedThreadData;
240
241 static SharedThreadData *shared_thread_data = NULL;
242
243 static gpointer
244 shared_thread_func (gpointer data)
245 {
246   g_main_context_push_thread_default (shared_thread_data->context);
247   g_main_loop_run (shared_thread_data->loop);
248   g_main_context_pop_thread_default (shared_thread_data->context);
249   return NULL;
250 }
251
252 typedef void (*GDBusSharedThreadFunc) (gpointer user_data);
253
254 typedef struct
255 {
256   GDBusSharedThreadFunc func;
257   gpointer              user_data;
258   gboolean              done;
259 } CallerData;
260
261 static gboolean
262 invoke_caller (gpointer user_data)
263 {
264   CallerData *data = user_data;
265   data->func (data->user_data);
266   data->done = TRUE;
267   return FALSE;
268 }
269
270 static void
271 _g_dbus_shared_thread_ref (GDBusSharedThreadFunc func,
272                            gpointer              user_data)
273 {
274   GError *error;
275   GSource *idle_source;
276   CallerData *data;
277
278   G_LOCK (shared_thread_lock);
279
280   if (shared_thread_data != NULL)
281     {
282       shared_thread_data->num_users += 1;
283       goto have_thread;
284     }
285
286   shared_thread_data = g_new0 (SharedThreadData, 1);
287   shared_thread_data->num_users = 1;
288
289   error = NULL;
290   shared_thread_data->context = g_main_context_new ();
291   shared_thread_data->loop = g_main_loop_new (shared_thread_data->context, FALSE);
292   shared_thread_data->thread = g_thread_create (shared_thread_func,
293                                                 NULL,
294                                                 TRUE,
295                                                 &error);
296   g_assert_no_error (error);
297
298  have_thread:
299
300   data = g_new0 (CallerData, 1);
301   data->func = func;
302   data->user_data = user_data;
303   data->done = FALSE;
304
305   idle_source = g_idle_source_new ();
306   g_source_set_priority (idle_source, G_PRIORITY_DEFAULT);
307   g_source_set_callback (idle_source,
308                          invoke_caller,
309                          data,
310                          NULL);
311   g_source_attach (idle_source, shared_thread_data->context);
312   g_source_unref (idle_source);
313
314   /* wait for the user code to run.. hmm.. probably use a condition variable instead */
315   while (!data->done)
316     g_thread_yield ();
317
318   g_free (data);
319
320   G_UNLOCK (shared_thread_lock);
321 }
322
323 static void
324 _g_dbus_shared_thread_unref (void)
325 {
326   /* TODO: actually destroy the shared thread here */
327 #if 0
328   G_LOCK (shared_thread_lock);
329   g_assert (shared_thread_data != NULL);
330   shared_thread_data->num_users -= 1;
331   if (shared_thread_data->num_users == 0)
332     {
333       g_main_loop_quit (shared_thread_data->loop);
334       //g_thread_join (shared_thread_data->thread);
335       g_main_loop_unref (shared_thread_data->loop);
336       g_main_context_unref (shared_thread_data->context);
337       g_free (shared_thread_data);
338       shared_thread_data = NULL;
339       G_UNLOCK (shared_thread_lock);
340     }
341   else
342     {
343       G_UNLOCK (shared_thread_lock);
344     }
345 #endif
346 }
347
348 /* ---------------------------------------------------------------------------------------------------- */
349
350 struct GDBusWorker
351 {
352   volatile gint                       ref_count;
353
354   gboolean                            stopped;
355
356   /* TODO: frozen (e.g. G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING) currently
357    * only affects messages received from the other peer (since GDBusServer is the
358    * only user) - we might want it to affect messages sent to the other peer too?
359    */
360   gboolean                            frozen;
361   GQueue                             *received_messages_while_frozen;
362
363   GIOStream                          *stream;
364   GDBusCapabilityFlags                capabilities;
365   GCancellable                       *cancellable;
366   GDBusWorkerMessageReceivedCallback  message_received_callback;
367   GDBusWorkerMessageAboutToBeSentCallback message_about_to_be_sent_callback;
368   GDBusWorkerDisconnectedCallback     disconnected_callback;
369   gpointer                            user_data;
370
371   GThread                            *thread;
372
373   /* if not NULL, stream is GSocketConnection */
374   GSocket *socket;
375
376   /* used for reading */
377   GMutex                             *read_lock;
378   gchar                              *read_buffer;
379   gsize                               read_buffer_allocated_size;
380   gsize                               read_buffer_cur_size;
381   gsize                               read_buffer_bytes_wanted;
382   GUnixFDList                        *read_fd_list;
383   GSocketControlMessage             **read_ancillary_messages;
384   gint                                read_num_ancillary_messages;
385
386   /* used for writing */
387   GMutex                             *write_lock;
388   GQueue                             *write_queue;
389   gboolean                            write_is_pending;
390   guint64                             write_num_messages_written;
391   GList                              *write_pending_flushes;
392 };
393
394 typedef struct
395 {
396   GMutex *mutex;
397   GCond *cond;
398   guint64 number_to_wait_for;
399 } FlushData;
400
401 struct _MessageToWriteData ;
402 typedef struct _MessageToWriteData MessageToWriteData;
403
404 static void message_to_write_data_free (MessageToWriteData *data);
405
406 static GDBusWorker *
407 _g_dbus_worker_ref (GDBusWorker *worker)
408 {
409   g_atomic_int_inc (&worker->ref_count);
410   return worker;
411 }
412
413 static void
414 _g_dbus_worker_unref (GDBusWorker *worker)
415 {
416   if (g_atomic_int_dec_and_test (&worker->ref_count))
417     {
418       g_assert (worker->write_pending_flushes == NULL);
419
420       _g_dbus_shared_thread_unref ();
421
422       g_object_unref (worker->stream);
423
424       g_mutex_free (worker->read_lock);
425       g_object_unref (worker->cancellable);
426       if (worker->read_fd_list != NULL)
427         g_object_unref (worker->read_fd_list);
428
429       g_queue_foreach (worker->received_messages_while_frozen, (GFunc) g_object_unref, NULL);
430       g_queue_free (worker->received_messages_while_frozen);
431
432       g_mutex_free (worker->write_lock);
433       g_queue_foreach (worker->write_queue, (GFunc) message_to_write_data_free, NULL);
434       g_queue_free (worker->write_queue);
435
436       g_free (worker);
437     }
438 }
439
440 static void
441 _g_dbus_worker_emit_disconnected (GDBusWorker  *worker,
442                                   gboolean      remote_peer_vanished,
443                                   GError       *error)
444 {
445   if (!worker->stopped)
446     worker->disconnected_callback (worker, remote_peer_vanished, error, worker->user_data);
447 }
448
449 static void
450 _g_dbus_worker_emit_message_received (GDBusWorker  *worker,
451                                       GDBusMessage *message)
452 {
453   if (!worker->stopped)
454     worker->message_received_callback (worker, message, worker->user_data);
455 }
456
457 static gboolean
458 _g_dbus_worker_emit_message_about_to_be_sent (GDBusWorker  *worker,
459                                               GDBusMessage *message)
460 {
461   gboolean ret;
462   ret = FALSE;
463   if (!worker->stopped)
464     ret = worker->message_about_to_be_sent_callback (worker, message, worker->user_data);
465   return ret;
466 }
467
468 /* can only be called from private thread with read-lock held - takes ownership of @message */
469 static void
470 _g_dbus_worker_queue_or_deliver_received_message (GDBusWorker  *worker,
471                                                   GDBusMessage *message)
472 {
473   if (worker->frozen || g_queue_get_length (worker->received_messages_while_frozen) > 0)
474     {
475       /* queue up */
476       g_queue_push_tail (worker->received_messages_while_frozen, message);
477     }
478   else
479     {
480       /* not frozen, nor anything in queue */
481       _g_dbus_worker_emit_message_received (worker, message);
482       g_object_unref (message);
483     }
484 }
485
486 /* called in private thread shared by all GDBusConnection instances (without read-lock held) */
487 static gboolean
488 unfreeze_in_idle_cb (gpointer user_data)
489 {
490   GDBusWorker *worker = user_data;
491   GDBusMessage *message;
492
493   g_mutex_lock (worker->read_lock);
494   if (worker->frozen)
495     {
496       while ((message = g_queue_pop_head (worker->received_messages_while_frozen)) != NULL)
497         {
498           _g_dbus_worker_emit_message_received (worker, message);
499           g_object_unref (message);
500         }
501       worker->frozen = FALSE;
502     }
503   else
504     {
505       g_assert (g_queue_get_length (worker->received_messages_while_frozen) == 0);
506     }
507   g_mutex_unlock (worker->read_lock);
508   return FALSE;
509 }
510
511 /* can be called from any thread */
512 void
513 _g_dbus_worker_unfreeze (GDBusWorker *worker)
514 {
515   GSource *idle_source;
516   idle_source = g_idle_source_new ();
517   g_source_set_priority (idle_source, G_PRIORITY_DEFAULT);
518   g_source_set_callback (idle_source,
519                          unfreeze_in_idle_cb,
520                          _g_dbus_worker_ref (worker),
521                          (GDestroyNotify) _g_dbus_worker_unref);
522   g_source_attach (idle_source, shared_thread_data->context);
523   g_source_unref (idle_source);
524 }
525
526 /* ---------------------------------------------------------------------------------------------------- */
527
528 static void _g_dbus_worker_do_read_unlocked (GDBusWorker *worker);
529
530 /* called in private thread shared by all GDBusConnection instances (without read-lock held) */
531 static void
532 _g_dbus_worker_do_read_cb (GInputStream  *input_stream,
533                            GAsyncResult  *res,
534                            gpointer       user_data)
535 {
536   GDBusWorker *worker = user_data;
537   GError *error;
538   gssize bytes_read;
539
540   g_mutex_lock (worker->read_lock);
541
542   /* If already stopped, don't even process the reply */
543   if (worker->stopped)
544     goto out;
545
546   error = NULL;
547   if (worker->socket == NULL)
548     bytes_read = g_input_stream_read_finish (g_io_stream_get_input_stream (worker->stream),
549                                              res,
550                                              &error);
551   else
552     bytes_read = _g_socket_read_with_control_messages_finish (worker->socket,
553                                                               res,
554                                                               &error);
555   if (worker->read_num_ancillary_messages > 0)
556     {
557       gint n;
558       for (n = 0; n < worker->read_num_ancillary_messages; n++)
559         {
560           GSocketControlMessage *control_message = G_SOCKET_CONTROL_MESSAGE (worker->read_ancillary_messages[n]);
561
562           if (FALSE)
563             {
564             }
565 #ifdef G_OS_UNIX
566           else if (G_IS_UNIX_FD_MESSAGE (control_message))
567             {
568               GUnixFDMessage *fd_message;
569               gint *fds;
570               gint num_fds;
571
572               fd_message = G_UNIX_FD_MESSAGE (control_message);
573               fds = g_unix_fd_message_steal_fds (fd_message, &num_fds);
574               if (worker->read_fd_list == NULL)
575                 {
576                   worker->read_fd_list = g_unix_fd_list_new_from_array (fds, num_fds);
577                 }
578               else
579                 {
580                   gint n;
581                   for (n = 0; n < num_fds; n++)
582                     {
583                       /* TODO: really want a append_steal() */
584                       g_unix_fd_list_append (worker->read_fd_list, fds[n], NULL);
585                       close (fds[n]);
586                     }
587                 }
588               g_free (fds);
589             }
590           else if (G_IS_UNIX_CREDENTIALS_MESSAGE (control_message))
591             {
592               /* do nothing */
593             }
594 #endif
595           else
596             {
597               if (error == NULL)
598                 {
599                   g_set_error (&error,
600                                G_IO_ERROR,
601                                G_IO_ERROR_FAILED,
602                                "Unexpected ancillary message of type %s received from peer",
603                                g_type_name (G_TYPE_FROM_INSTANCE (control_message)));
604                   _g_dbus_worker_emit_disconnected (worker, TRUE, error);
605                   g_error_free (error);
606                   g_object_unref (control_message);
607                   n++;
608                   while (n < worker->read_num_ancillary_messages)
609                     g_object_unref (worker->read_ancillary_messages[n++]);
610                   g_free (worker->read_ancillary_messages);
611                   goto out;
612                 }
613             }
614           g_object_unref (control_message);
615         }
616       g_free (worker->read_ancillary_messages);
617     }
618
619   if (bytes_read == -1)
620     {
621       _g_dbus_worker_emit_disconnected (worker, TRUE, error);
622       g_error_free (error);
623       goto out;
624     }
625
626 #if 0
627   g_debug ("read %d bytes (is_closed=%d blocking=%d condition=0x%02x) stream %p, %p",
628            (gint) bytes_read,
629            g_socket_is_closed (g_socket_connection_get_socket (G_SOCKET_CONNECTION (worker->stream))),
630            g_socket_get_blocking (g_socket_connection_get_socket (G_SOCKET_CONNECTION (worker->stream))),
631            g_socket_condition_check (g_socket_connection_get_socket (G_SOCKET_CONNECTION (worker->stream)),
632                                      G_IO_IN | G_IO_OUT | G_IO_HUP),
633            worker->stream,
634            worker);
635 #endif
636
637   /* TODO: hmm, hmm... */
638   if (bytes_read == 0)
639     {
640       g_set_error (&error,
641                    G_IO_ERROR,
642                    G_IO_ERROR_FAILED,
643                    "Underlying GIOStream returned 0 bytes on an async read");
644       _g_dbus_worker_emit_disconnected (worker, TRUE, error);
645       g_error_free (error);
646       goto out;
647     }
648
649   worker->read_buffer_cur_size += bytes_read;
650   if (worker->read_buffer_bytes_wanted == worker->read_buffer_cur_size)
651     {
652       /* OK, got what we asked for! */
653       if (worker->read_buffer_bytes_wanted == 16)
654         {
655           gssize message_len;
656           /* OK, got the header - determine how many more bytes are needed */
657           error = NULL;
658           message_len = g_dbus_message_bytes_needed ((guchar *) worker->read_buffer,
659                                                      16,
660                                                      &error);
661           if (message_len == -1)
662             {
663               g_warning ("_g_dbus_worker_do_read_cb: error determing bytes needed: %s", error->message);
664               _g_dbus_worker_emit_disconnected (worker, FALSE, error);
665               g_error_free (error);
666               goto out;
667             }
668
669           worker->read_buffer_bytes_wanted = message_len;
670           _g_dbus_worker_do_read_unlocked (worker);
671         }
672       else
673         {
674           GDBusMessage *message;
675           error = NULL;
676
677           /* TODO: use connection->priv->auth to decode the message */
678
679           message = g_dbus_message_new_from_blob ((guchar *) worker->read_buffer,
680                                                   worker->read_buffer_cur_size,
681                                                   worker->capabilities,
682                                                   &error);
683           if (message == NULL)
684             {
685               gchar *s;
686               s = _g_dbus_hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2);
687               g_warning ("Error decoding D-Bus message of %" G_GSIZE_FORMAT " bytes\n"
688                          "The error is: %s\n"
689                          "The payload is as follows:\n"
690                          "%s\n",
691                          worker->read_buffer_cur_size,
692                          error->message,
693                          s);
694               g_free (s);
695               _g_dbus_worker_emit_disconnected (worker, FALSE, error);
696               g_error_free (error);
697               goto out;
698             }
699
700 #ifdef G_OS_UNIX
701           if (worker->read_fd_list != NULL)
702             {
703               g_dbus_message_set_unix_fd_list (message, worker->read_fd_list);
704               worker->read_fd_list = NULL;
705             }
706 #endif
707
708           if (G_UNLIKELY (_g_dbus_debug_message ()))
709             {
710               gchar *s;
711               _g_dbus_debug_print_lock ();
712               g_print ("========================================================================\n"
713                        "GDBus-debug:Message:\n"
714                        "  <<<< RECEIVED D-Bus message (%" G_GSIZE_FORMAT " bytes)\n",
715                        worker->read_buffer_cur_size);
716               s = g_dbus_message_print (message, 2);
717               g_print ("%s", s);
718               g_free (s);
719               if (G_UNLIKELY (_g_dbus_debug_payload ()))
720                 {
721                   s = _g_dbus_hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2);
722                   g_print ("%s\n", s);
723                   g_free (s);
724                 }
725               _g_dbus_debug_print_unlock ();
726             }
727
728           /* yay, got a message, go deliver it */
729           _g_dbus_worker_queue_or_deliver_received_message (worker, message);
730
731           /* start reading another message! */
732           worker->read_buffer_bytes_wanted = 0;
733           worker->read_buffer_cur_size = 0;
734           _g_dbus_worker_do_read_unlocked (worker);
735         }
736     }
737   else
738     {
739       /* didn't get all the bytes we requested - so repeat the request... */
740       _g_dbus_worker_do_read_unlocked (worker);
741     }
742
743  out:
744   g_mutex_unlock (worker->read_lock);
745
746   /* gives up the reference acquired when calling g_input_stream_read_async() */
747   _g_dbus_worker_unref (worker);
748 }
749
750 /* called in private thread shared by all GDBusConnection instances (with read-lock held) */
751 static void
752 _g_dbus_worker_do_read_unlocked (GDBusWorker *worker)
753 {
754   /* if bytes_wanted is zero, it means start reading a message */
755   if (worker->read_buffer_bytes_wanted == 0)
756     {
757       worker->read_buffer_cur_size = 0;
758       worker->read_buffer_bytes_wanted = 16;
759     }
760
761   /* ensure we have a (big enough) buffer */
762   if (worker->read_buffer == NULL || worker->read_buffer_bytes_wanted > worker->read_buffer_allocated_size)
763     {
764       /* TODO: 4096 is randomly chosen; might want a better chosen default minimum */
765       worker->read_buffer_allocated_size = MAX (worker->read_buffer_bytes_wanted, 4096);
766       worker->read_buffer = g_realloc (worker->read_buffer, worker->read_buffer_allocated_size);
767     }
768
769   if (worker->socket == NULL)
770     g_input_stream_read_async (g_io_stream_get_input_stream (worker->stream),
771                                worker->read_buffer + worker->read_buffer_cur_size,
772                                worker->read_buffer_bytes_wanted - worker->read_buffer_cur_size,
773                                G_PRIORITY_DEFAULT,
774                                worker->cancellable,
775                                (GAsyncReadyCallback) _g_dbus_worker_do_read_cb,
776                                _g_dbus_worker_ref (worker));
777   else
778     {
779       worker->read_ancillary_messages = NULL;
780       worker->read_num_ancillary_messages = 0;
781       _g_socket_read_with_control_messages (worker->socket,
782                                             worker->read_buffer + worker->read_buffer_cur_size,
783                                             worker->read_buffer_bytes_wanted - worker->read_buffer_cur_size,
784                                             &worker->read_ancillary_messages,
785                                             &worker->read_num_ancillary_messages,
786                                             G_PRIORITY_DEFAULT,
787                                             worker->cancellable,
788                                             (GAsyncReadyCallback) _g_dbus_worker_do_read_cb,
789                                             _g_dbus_worker_ref (worker));
790     }
791 }
792
793 /* called in private thread shared by all GDBusConnection instances (without read-lock held) */
794 static void
795 _g_dbus_worker_do_read (GDBusWorker *worker)
796 {
797   g_mutex_lock (worker->read_lock);
798   _g_dbus_worker_do_read_unlocked (worker);
799   g_mutex_unlock (worker->read_lock);
800 }
801
802 /* ---------------------------------------------------------------------------------------------------- */
803
804 struct _MessageToWriteData
805 {
806   GDBusMessage *message;
807   gchar        *blob;
808   gsize         blob_size;
809 };
810
811 static void
812 message_to_write_data_free (MessageToWriteData *data)
813 {
814   g_object_unref (data->message);
815   g_free (data->blob);
816   g_free (data);
817 }
818
819 /* ---------------------------------------------------------------------------------------------------- */
820
821 /* called in private thread shared by all GDBusConnection instances (without write-lock held) */
822 static gboolean
823 write_message (GDBusWorker         *worker,
824                MessageToWriteData  *data,
825                GError             **error)
826 {
827   gboolean ret;
828   GList *l;
829   GList *ll;
830
831   g_return_val_if_fail (data->blob_size > 16, FALSE);
832
833   ret = FALSE;
834
835   /* First, the initial 16 bytes - special case UNIX sockets here
836    * since it may involve writing an ancillary message with file
837    * descriptors
838    */
839 #ifdef G_OS_UNIX
840   {
841     GOutputVector vector;
842     GSocketControlMessage *message;
843     GUnixFDList *fd_list;
844     gssize bytes_written;
845
846     fd_list = g_dbus_message_get_unix_fd_list (data->message);
847
848     message = NULL;
849     if (fd_list != NULL)
850       {
851         if (!G_IS_UNIX_CONNECTION (worker->stream))
852           {
853             g_set_error (error,
854                          G_IO_ERROR,
855                          G_IO_ERROR_INVALID_ARGUMENT,
856                          "Tried sending a file descriptor on unsupported stream of type %s",
857                          g_type_name (G_TYPE_FROM_INSTANCE (worker->stream)));
858             goto out;
859           }
860         else if (!(worker->capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING))
861           {
862             g_set_error_literal (error,
863                                  G_IO_ERROR,
864                                  G_IO_ERROR_INVALID_ARGUMENT,
865                                  "Tried sending a file descriptor but remote peer does not support this capability");
866             goto out;
867           }
868         message = g_unix_fd_message_new_with_fd_list (fd_list);
869       }
870
871     vector.buffer = data->blob;
872     vector.size = 16;
873
874     bytes_written = g_socket_send_message (worker->socket,
875                                            NULL, /* address */
876                                            &vector,
877                                            1,
878                                            message != NULL ? &message : NULL,
879                                            message != NULL ? 1 : 0,
880                                            G_SOCKET_MSG_NONE,
881                                            worker->cancellable,
882                                            error);
883     if (bytes_written == -1)
884       {
885         g_prefix_error (error, _("Error writing first 16 bytes of message to socket: "));
886         if (message != NULL)
887           g_object_unref (message);
888         goto out;
889       }
890     if (message != NULL)
891       g_object_unref (message);
892
893     if (bytes_written < 16)
894       {
895         /* TODO: I think this needs to be handled ... are we guaranteed that the ancillary
896          * messages are sent?
897          */
898         g_assert_not_reached ();
899       }
900   }
901 #else
902   /* write the first 16 bytes (guaranteed to return an error if everything can't be written) */
903   if (!g_output_stream_write_all (g_io_stream_get_output_stream (worker->stream),
904                                   (const gchar *) data->blob,
905                                   16,
906                                   NULL, /* bytes_written */
907                                   worker->cancellable, /* cancellable */
908                                   error))
909     goto out;
910 #endif
911
912   /* Then write the rest of the message (guaranteed to return an error if everything can't be written) */
913   if (!g_output_stream_write_all (g_io_stream_get_output_stream (worker->stream),
914                                   (const gchar *) data->blob + 16,
915                                   data->blob_size - 16,
916                                   NULL, /* bytes_written */
917                                   worker->cancellable, /* cancellable */
918                                   error))
919     goto out;
920
921   ret = TRUE;
922
923   /* wake up pending flushes */
924   g_mutex_lock (worker->write_lock);
925   worker->write_num_messages_written += 1;
926   for (l = worker->write_pending_flushes; l != NULL; l = ll)
927     {
928       FlushData *f = l->data;
929       ll = l->next;
930
931       if (f->number_to_wait_for == worker->write_num_messages_written)
932         {
933           g_mutex_lock (f->mutex);
934           g_cond_signal (f->cond);
935           g_mutex_unlock (f->mutex);
936           worker->write_pending_flushes = g_list_delete_link (worker->write_pending_flushes, l);
937         }
938     }
939   g_mutex_unlock (worker->write_lock);
940
941   if (G_UNLIKELY (_g_dbus_debug_message ()))
942     {
943       gchar *s;
944       _g_dbus_debug_print_lock ();
945       g_print ("========================================================================\n"
946                "GDBus-debug:Message:\n"
947                "  >>>> SENT D-Bus message (%" G_GSIZE_FORMAT " bytes)\n",
948                data->blob_size);
949       s = g_dbus_message_print (data->message, 2);
950       g_print ("%s", s);
951       g_free (s);
952       if (G_UNLIKELY (_g_dbus_debug_payload ()))
953         {
954           s = _g_dbus_hexdump (data->blob, data->blob_size, 2);
955           g_print ("%s\n", s);
956           g_free (s);
957         }
958       _g_dbus_debug_print_unlock ();
959     }
960
961  out:
962   return ret;
963 }
964
965 /* ---------------------------------------------------------------------------------------------------- */
966
967 /* called in private thread shared by all GDBusConnection instances (without write-lock held) */
968 static gboolean
969 write_message_in_idle_cb (gpointer user_data)
970 {
971   GDBusWorker *worker = user_data;
972   gboolean more_writes_are_pending;
973   MessageToWriteData *data;
974   gboolean message_was_dropped;
975   GError *error;
976
977   g_mutex_lock (worker->write_lock);
978   data = g_queue_pop_head (worker->write_queue);
979   g_assert (data != NULL);
980   more_writes_are_pending = (g_queue_get_length (worker->write_queue) > 0);
981   worker->write_is_pending = more_writes_are_pending;
982   g_mutex_unlock (worker->write_lock);
983
984   /* Note that write_lock is only used for protecting the @write_queue
985    * and @write_is_pending fields of the GDBusWorker struct ... which we
986    * need to modify from arbitrary threads in _g_dbus_worker_send_message().
987    *
988    * Therefore, it's fine to drop it here when calling back into user
989    * code and then writing the message out onto the GIOStream since this
990    * function only runs on the worker thread.
991    */
992   message_was_dropped = _g_dbus_worker_emit_message_about_to_be_sent (worker, data->message);
993   if (G_LIKELY (!message_was_dropped))
994     {
995       error = NULL;
996       if (!write_message (worker,
997                           data,
998                           &error))
999         {
1000           /* TODO: handle */
1001           _g_dbus_worker_emit_disconnected (worker, TRUE, error);
1002           g_error_free (error);
1003         }
1004     }
1005   message_to_write_data_free (data);
1006
1007   return more_writes_are_pending;
1008 }
1009
1010 /* ---------------------------------------------------------------------------------------------------- */
1011
1012 /* can be called from any thread - steals blob */
1013 void
1014 _g_dbus_worker_send_message (GDBusWorker    *worker,
1015                              GDBusMessage   *message,
1016                              gchar          *blob,
1017                              gsize           blob_len)
1018 {
1019   MessageToWriteData *data;
1020
1021   g_return_if_fail (G_IS_DBUS_MESSAGE (message));
1022   g_return_if_fail (blob != NULL);
1023   g_return_if_fail (blob_len > 16);
1024
1025   data = g_new0 (MessageToWriteData, 1);
1026   data->message = g_object_ref (message);
1027   data->blob = blob; /* steal! */
1028   data->blob_size = blob_len;
1029
1030   g_mutex_lock (worker->write_lock);
1031   g_queue_push_tail (worker->write_queue, data);
1032   if (!worker->write_is_pending)
1033     {
1034       GSource *idle_source;
1035
1036       worker->write_is_pending = TRUE;
1037
1038       idle_source = g_idle_source_new ();
1039       g_source_set_priority (idle_source, G_PRIORITY_DEFAULT);
1040       g_source_set_callback (idle_source,
1041                              write_message_in_idle_cb,
1042                              _g_dbus_worker_ref (worker),
1043                              (GDestroyNotify) _g_dbus_worker_unref);
1044       g_source_attach (idle_source, shared_thread_data->context);
1045       g_source_unref (idle_source);
1046     }
1047   g_mutex_unlock (worker->write_lock);
1048 }
1049
1050 /* ---------------------------------------------------------------------------------------------------- */
1051
1052 static void
1053 _g_dbus_worker_thread_begin_func (gpointer user_data)
1054 {
1055   GDBusWorker *worker = user_data;
1056
1057   worker->thread = g_thread_self ();
1058
1059   /* begin reading */
1060   _g_dbus_worker_do_read (worker);
1061 }
1062
1063 GDBusWorker *
1064 _g_dbus_worker_new (GIOStream                              *stream,
1065                     GDBusCapabilityFlags                    capabilities,
1066                     gboolean                                initially_frozen,
1067                     GDBusWorkerMessageReceivedCallback      message_received_callback,
1068                     GDBusWorkerMessageAboutToBeSentCallback message_about_to_be_sent_callback,
1069                     GDBusWorkerDisconnectedCallback         disconnected_callback,
1070                     gpointer                                user_data)
1071 {
1072   GDBusWorker *worker;
1073
1074   g_return_val_if_fail (G_IS_IO_STREAM (stream), NULL);
1075   g_return_val_if_fail (message_received_callback != NULL, NULL);
1076   g_return_val_if_fail (message_about_to_be_sent_callback != NULL, NULL);
1077   g_return_val_if_fail (disconnected_callback != NULL, NULL);
1078
1079   worker = g_new0 (GDBusWorker, 1);
1080   worker->ref_count = 1;
1081
1082   worker->read_lock = g_mutex_new ();
1083   worker->message_received_callback = message_received_callback;
1084   worker->message_about_to_be_sent_callback = message_about_to_be_sent_callback;
1085   worker->disconnected_callback = disconnected_callback;
1086   worker->user_data = user_data;
1087   worker->stream = g_object_ref (stream);
1088   worker->capabilities = capabilities;
1089   worker->cancellable = g_cancellable_new ();
1090
1091   worker->frozen = initially_frozen;
1092   worker->received_messages_while_frozen = g_queue_new ();
1093
1094   worker->write_lock = g_mutex_new ();
1095   worker->write_queue = g_queue_new ();
1096
1097   if (G_IS_SOCKET_CONNECTION (worker->stream))
1098     worker->socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (worker->stream));
1099
1100   _g_dbus_shared_thread_ref (_g_dbus_worker_thread_begin_func, worker);
1101
1102   return worker;
1103 }
1104
1105 /* ---------------------------------------------------------------------------------------------------- */
1106
1107 /* This can be called from any thread - frees worker - guarantees no callbacks
1108  * will ever be issued again
1109  */
1110 void
1111 _g_dbus_worker_stop (GDBusWorker *worker)
1112 {
1113   /* If we're called in the worker thread it means we are called from
1114    * a worker callback. This is fine, we just can't lock in that case since
1115    * we're already holding the lock...
1116    */
1117   if (g_thread_self () != worker->thread)
1118     g_mutex_lock (worker->read_lock);
1119   worker->stopped = TRUE;
1120   if (g_thread_self () != worker->thread)
1121     g_mutex_unlock (worker->read_lock);
1122
1123   g_cancellable_cancel (worker->cancellable);
1124   _g_dbus_worker_unref (worker);
1125 }
1126
1127 /* ---------------------------------------------------------------------------------------------------- */
1128
1129 /* can be called from any thread (except the worker thread) - blocks
1130  * calling thread until all queued outgoing messages are written and
1131  * the transport has been flushed
1132  */
1133 gboolean
1134 _g_dbus_worker_flush_sync (GDBusWorker    *worker,
1135                            GCancellable   *cancellable,
1136                            GError        **error)
1137 {
1138   gboolean ret;
1139   FlushData *data;
1140
1141   data = NULL;
1142
1143   /* if the queue is empty, there's nothing to wait for */
1144   g_mutex_lock (worker->write_lock);
1145   if (g_queue_get_length (worker->write_queue) > 0)
1146     {
1147       data = g_new0 (FlushData, 1);
1148       data->mutex = g_mutex_new ();
1149       data->cond = g_cond_new ();
1150       data->number_to_wait_for = worker->write_num_messages_written + g_queue_get_length (worker->write_queue);
1151       g_mutex_lock (data->mutex);
1152       worker->write_pending_flushes = g_list_prepend (worker->write_pending_flushes, data);
1153     }
1154   g_mutex_unlock (worker->write_lock);
1155
1156   if (data != NULL)
1157     {
1158       g_cond_wait (data->cond, data->mutex);
1159       g_mutex_unlock (data->mutex);
1160
1161       /* note:the element is removed from worker->write_pending_flushes in write_message() */
1162       g_cond_free (data->cond);
1163       g_mutex_free (data->mutex);
1164       g_free (data);
1165     }
1166
1167   ret = g_output_stream_flush (g_io_stream_get_output_stream (worker->stream),
1168                                cancellable,
1169                                error);
1170   return ret;
1171 }
1172
1173 /* ---------------------------------------------------------------------------------------------------- */
1174
1175 #define G_DBUS_DEBUG_AUTHENTICATION (1<<0)
1176 #define G_DBUS_DEBUG_MESSAGE        (1<<1)
1177 #define G_DBUS_DEBUG_PAYLOAD        (1<<2)
1178 #define G_DBUS_DEBUG_CALL           (1<<3)
1179 #define G_DBUS_DEBUG_SIGNAL         (1<<4)
1180 #define G_DBUS_DEBUG_INCOMING       (1<<5)
1181 #define G_DBUS_DEBUG_RETURN         (1<<6)
1182 #define G_DBUS_DEBUG_EMISSION       (1<<7)
1183 #define G_DBUS_DEBUG_ADDRESS        (1<<8)
1184
1185 static gint _gdbus_debug_flags = 0;
1186
1187 gboolean
1188 _g_dbus_debug_authentication (void)
1189 {
1190   _g_dbus_initialize ();
1191   return (_gdbus_debug_flags & G_DBUS_DEBUG_AUTHENTICATION) != 0;
1192 }
1193
1194 gboolean
1195 _g_dbus_debug_message (void)
1196 {
1197   _g_dbus_initialize ();
1198   return (_gdbus_debug_flags & G_DBUS_DEBUG_MESSAGE) != 0;
1199 }
1200
1201 gboolean
1202 _g_dbus_debug_payload (void)
1203 {
1204   _g_dbus_initialize ();
1205   return (_gdbus_debug_flags & G_DBUS_DEBUG_PAYLOAD) != 0;
1206 }
1207
1208 gboolean
1209 _g_dbus_debug_call (void)
1210 {
1211   _g_dbus_initialize ();
1212   return (_gdbus_debug_flags & G_DBUS_DEBUG_CALL) != 0;
1213 }
1214
1215 gboolean
1216 _g_dbus_debug_signal (void)
1217 {
1218   _g_dbus_initialize ();
1219   return (_gdbus_debug_flags & G_DBUS_DEBUG_SIGNAL) != 0;
1220 }
1221
1222 gboolean
1223 _g_dbus_debug_incoming (void)
1224 {
1225   _g_dbus_initialize ();
1226   return (_gdbus_debug_flags & G_DBUS_DEBUG_INCOMING) != 0;
1227 }
1228
1229 gboolean
1230 _g_dbus_debug_return (void)
1231 {
1232   _g_dbus_initialize ();
1233   return (_gdbus_debug_flags & G_DBUS_DEBUG_RETURN) != 0;
1234 }
1235
1236 gboolean
1237 _g_dbus_debug_emission (void)
1238 {
1239   _g_dbus_initialize ();
1240   return (_gdbus_debug_flags & G_DBUS_DEBUG_EMISSION) != 0;
1241 }
1242
1243 gboolean
1244 _g_dbus_debug_address (void)
1245 {
1246   _g_dbus_initialize ();
1247   return (_gdbus_debug_flags & G_DBUS_DEBUG_ADDRESS) != 0;
1248 }
1249
1250 G_LOCK_DEFINE_STATIC (print_lock);
1251
1252 void
1253 _g_dbus_debug_print_lock (void)
1254 {
1255   G_LOCK (print_lock);
1256 }
1257
1258 void
1259 _g_dbus_debug_print_unlock (void)
1260 {
1261   G_UNLOCK (print_lock);
1262 }
1263
1264 /*
1265  * _g_dbus_initialize:
1266  *
1267  * Does various one-time init things such as
1268  *
1269  *  - registering the G_DBUS_ERROR error domain
1270  *  - parses the G_DBUS_DEBUG environment variable
1271  */
1272 void
1273 _g_dbus_initialize (void)
1274 {
1275   static volatile gsize initialized = 0;
1276
1277   if (g_once_init_enter (&initialized))
1278     {
1279       volatile GQuark g_dbus_error_domain;
1280       const gchar *debug;
1281
1282       g_dbus_error_domain = G_DBUS_ERROR;
1283
1284       debug = g_getenv ("G_DBUS_DEBUG");
1285       if (debug != NULL)
1286         {
1287           const GDebugKey keys[] = {
1288             { "authentication", G_DBUS_DEBUG_AUTHENTICATION },
1289             { "message",        G_DBUS_DEBUG_MESSAGE        },
1290             { "payload",        G_DBUS_DEBUG_PAYLOAD        },
1291             { "call",           G_DBUS_DEBUG_CALL           },
1292             { "signal",         G_DBUS_DEBUG_SIGNAL         },
1293             { "incoming",       G_DBUS_DEBUG_INCOMING       },
1294             { "return",         G_DBUS_DEBUG_RETURN         },
1295             { "emission",       G_DBUS_DEBUG_EMISSION       },
1296             { "address",        G_DBUS_DEBUG_ADDRESS        }
1297           };
1298
1299           _gdbus_debug_flags = g_parse_debug_string (debug, keys, G_N_ELEMENTS (keys));
1300           if (_gdbus_debug_flags & G_DBUS_DEBUG_PAYLOAD)
1301             _gdbus_debug_flags |= G_DBUS_DEBUG_MESSAGE;
1302         }
1303
1304       g_once_init_leave (&initialized, 1);
1305     }
1306 }
1307
1308 /* ---------------------------------------------------------------------------------------------------- */
1309
1310 GVariantType *
1311 _g_dbus_compute_complete_signature (GDBusArgInfo **args)
1312 {
1313   const GVariantType *arg_types[256];
1314   guint n;
1315
1316   if (args)
1317     for (n = 0; args[n] != NULL; n++)
1318       {
1319         /* DBus places a hard limit of 255 on signature length.
1320          * therefore number of args must be less than 256.
1321          */
1322         g_assert (n < 256);
1323
1324         arg_types[n] = G_VARIANT_TYPE (args[n]->signature);
1325
1326         if G_UNLIKELY (arg_types[n] == NULL)
1327           return NULL;
1328       }
1329   else
1330     n = 0;
1331
1332   return g_variant_type_new_tuple (arg_types, n);
1333 }
1334
1335 /* ---------------------------------------------------------------------------------------------------- */
1336
1337 #ifdef G_OS_WIN32
1338
1339 extern BOOL WINAPI ConvertSidToStringSidA (PSID Sid, LPSTR *StringSid);
1340
1341 gchar *
1342 _g_dbus_win32_get_user_sid (void)
1343 {
1344   HANDLE h;
1345   TOKEN_USER *user;
1346   DWORD token_information_len;
1347   PSID psid;
1348   gchar *sid;
1349   gchar *ret;
1350
1351   ret = NULL;
1352   user = NULL;
1353   h = INVALID_HANDLE_VALUE;
1354
1355   if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &h))
1356     {
1357       g_warning ("OpenProcessToken failed with error code %d", (gint) GetLastError ());
1358       goto out;
1359     }
1360
1361   /* Get length of buffer */
1362   token_information_len = 0;
1363   if (!GetTokenInformation (h, TokenUser, NULL, 0, &token_information_len))
1364     {
1365       if (GetLastError () != ERROR_INSUFFICIENT_BUFFER)
1366         {
1367           g_warning ("GetTokenInformation() failed with error code %d", (gint) GetLastError ());
1368           goto out;
1369         }
1370     }
1371   user = g_malloc (token_information_len);
1372   if (!GetTokenInformation (h, TokenUser, user, token_information_len, &token_information_len))
1373     {
1374       g_warning ("GetTokenInformation() failed with error code %d", (gint) GetLastError ());
1375       goto out;
1376     }
1377
1378   psid = user->User.Sid;
1379   if (!IsValidSid (psid))
1380     {
1381       g_warning ("Invalid SID");
1382       goto out;
1383     }
1384
1385   if (!ConvertSidToStringSidA (psid, &sid))
1386     {
1387       g_warning ("Invalid SID");
1388       goto out;
1389     }
1390
1391   ret = g_strdup (sid);
1392   LocalFree (sid);
1393
1394 out:
1395   g_free (user);
1396   if (h != INVALID_HANDLE_VALUE)
1397     CloseHandle (h);
1398   return ret;
1399 }
1400 #endif
1401
1402 /* ---------------------------------------------------------------------------------------------------- */
1403
1404 gchar *
1405 _g_dbus_get_machine_id (GError **error)
1406 {
1407   gchar *ret;
1408   /* TODO: use PACKAGE_LOCALSTATEDIR ? */
1409   ret = NULL;
1410   if (!g_file_get_contents ("/var/lib/dbus/machine-id",
1411                             &ret,
1412                             NULL,
1413                             error))
1414     {
1415       g_prefix_error (error, _("Unable to load /var/lib/dbus/machine-id: "));
1416     }
1417   else
1418     {
1419       /* TODO: validate value */
1420       g_strstrip (ret);
1421     }
1422   return ret;
1423 }
1424
1425 /* ---------------------------------------------------------------------------------------------------- */
1426
1427 gchar *
1428 _g_dbus_enum_to_string (GType enum_type, gint value)
1429 {
1430   gchar *ret;
1431   GEnumClass *klass;
1432   GEnumValue *enum_value;
1433
1434   klass = g_type_class_ref (enum_type);
1435   enum_value = g_enum_get_value (klass, value);
1436   if (enum_value != NULL)
1437     ret = g_strdup (enum_value->value_nick);
1438   else
1439     ret = g_strdup_printf ("unknown (value %d)", value);
1440   g_type_class_unref (klass);
1441   return ret;
1442 }
1443
1444 /* ---------------------------------------------------------------------------------------------------- */