Coverage Report

Created: 2018-09-25 13:52

/src/glib/gio/gunixconnection.c
Line
Count
Source (jump to first uncovered line)
1
/* GIO - GLib Input, Output and Streaming Library
2
 *
3
 * Copyright © 2009 Codethink Limited
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.1 of the License, or (at your option) any later version.
9
 *
10
 * See the included COPYING file for more information.
11
 *
12
 * Authors: Ryan Lortie <desrt@desrt.ca>
13
 */
14
15
#include "config.h"
16
17
#include "gunixconnection.h"
18
#include "gnetworking.h"
19
#include "gsocket.h"
20
#include "gsocketcontrolmessage.h"
21
#include "gunixcredentialsmessage.h"
22
#include "gunixfdmessage.h"
23
#include "glibintl.h"
24
25
#include <errno.h>
26
#include <string.h>
27
#include <unistd.h>
28
29
/**
30
 * SECTION:gunixconnection
31
 * @title: GUnixConnection
32
 * @short_description: A UNIX domain GSocketConnection
33
 * @include: gio/gunixconnection.h
34
 * @see_also: #GSocketConnection.
35
 *
36
 * This is the subclass of #GSocketConnection that is created
37
 * for UNIX domain sockets.
38
 *
39
 * It contains functions to do some of the UNIX socket specific
40
 * functionality like passing file descriptors.
41
 *
42
 * Note that `<gio/gunixconnection.h>` belongs to the UNIX-specific
43
 * GIO interfaces, thus you have to use the `gio-unix-2.0.pc`
44
 * pkg-config file when using it.
45
 *
46
 * Since: 2.22
47
 */
48
49
/**
50
 * GUnixConnection:
51
 *
52
 * #GUnixConnection is an opaque data structure and can only be accessed
53
 * using the following functions.
54
 **/
55
56
G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection,
57
       G_TYPE_SOCKET_CONNECTION,
58
  g_socket_connection_factory_register_type (g_define_type_id,
59
               G_SOCKET_FAMILY_UNIX,
60
               G_SOCKET_TYPE_STREAM,
61
               G_SOCKET_PROTOCOL_DEFAULT);
62
       );
63
64
/**
65
 * g_unix_connection_send_fd:
66
 * @connection: a #GUnixConnection
67
 * @fd: a file descriptor
68
 * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
69
 * @error: (nullable): #GError for error reporting, or %NULL to ignore.
70
 *
71
 * Passes a file descriptor to the receiving side of the
72
 * connection. The receiving end has to call g_unix_connection_receive_fd()
73
 * to accept the file descriptor.
74
 *
75
 * As well as sending the fd this also writes a single byte to the
76
 * stream, as this is required for fd passing to work on some
77
 * implementations.
78
 *
79
 * Returns: a %TRUE on success, %NULL on error.
80
 *
81
 * Since: 2.22
82
 */
83
gboolean
84
g_unix_connection_send_fd (GUnixConnection  *connection,
85
                           gint              fd,
86
                           GCancellable     *cancellable,
87
                           GError          **error)
88
0
{
89
0
  GSocketControlMessage *scm;
90
0
  GSocket *socket;
91
0
92
0
  g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE);
93
0
  g_return_val_if_fail (fd >= 0, FALSE);
94
0
95
0
  scm = g_unix_fd_message_new ();
96
0
97
0
  if (!g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (scm), fd, error))
98
0
    {
99
0
      g_object_unref (scm);
100
0
      return FALSE;
101
0
    }
102
0
103
0
  g_object_get (connection, "socket", &socket, NULL);
104
0
  if (g_socket_send_message (socket, NULL, NULL, 0, &scm, 1, 0, cancellable, error) != 1)
105
0
    /* XXX could it 'fail' with zero? */
106
0
    {
107
0
      g_object_unref (socket);
108
0
      g_object_unref (scm);
109
0
110
0
      return FALSE;
111
0
    }
112
0
113
0
  g_object_unref (socket);
114
0
  g_object_unref (scm);
115
0
116
0
  return TRUE;
117
0
}
118
119
/**
120
 * g_unix_connection_receive_fd:
121
 * @connection: a #GUnixConnection
122
 * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
123
 * @error: (nullable): #GError for error reporting, or %NULL to ignore
124
 *
125
 * Receives a file descriptor from the sending end of the connection.
126
 * The sending end has to call g_unix_connection_send_fd() for this
127
 * to work.
128
 *
129
 * As well as reading the fd this also reads a single byte from the
130
 * stream, as this is required for fd passing to work on some
131
 * implementations.
132
 *
133
 * Returns: a file descriptor on success, -1 on error.
134
 *
135
 * Since: 2.22
136
 **/
137
gint
138
g_unix_connection_receive_fd (GUnixConnection  *connection,
139
                              GCancellable     *cancellable,
140
                              GError          **error)
141
0
{
142
0
  GSocketControlMessage **scms;
143
0
  gint *fds, nfd, fd, nscm;
144
0
  GUnixFDMessage *fdmsg;
145
0
  GSocket *socket;
146
0
147
0
  g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), -1);
148
0
149
0
  g_object_get (connection, "socket", &socket, NULL);
150
0
  if (g_socket_receive_message (socket, NULL, NULL, 0,
151
0
                                &scms, &nscm, NULL, cancellable, error) != 1)
152
0
    /* XXX it _could_ 'fail' with zero. */
153
0
    {
154
0
      g_object_unref (socket);
155
0
156
0
      return -1;
157
0
    }
158
0
159
0
  g_object_unref (socket);
160
0
161
0
  if (nscm != 1)
162
0
    {
163
0
      gint i;
164
0
165
0
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
166
0
        ngettext("Expecting 1 control message, got %d",
167
0
                 "Expecting 1 control message, got %d",
168
0
                 nscm),
169
0
        nscm);
170
0
171
0
      for (i = 0; i < nscm; i++)
172
0
        g_object_unref (scms[i]);
173
0
174
0
      g_free (scms);
175
0
176
0
      return -1;
177
0
    }
178
0
179
0
  if (!G_IS_UNIX_FD_MESSAGE (scms[0]))
180
0
    {
181
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
182
0
         _("Unexpected type of ancillary data"));
183
0
      g_object_unref (scms[0]);
184
0
      g_free (scms);
185
0
186
0
      return -1;
187
0
    }
188
0
189
0
  fdmsg = G_UNIX_FD_MESSAGE (scms[0]);
190
0
  g_free (scms);
191
0
192
0
  fds = g_unix_fd_message_steal_fds (fdmsg, &nfd);
193
0
  g_object_unref (fdmsg);
194
0
195
0
  if (nfd != 1)
196
0
    {
197
0
      gint i;
198
0
199
0
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
200
0
                   ngettext("Expecting one fd, but got %d\n",
201
0
                            "Expecting one fd, but got %d\n",
202
0
                            nfd),
203
0
                   nfd);
204
0
205
0
      for (i = 0; i < nfd; i++)
206
0
        close (fds[i]);
207
0
208
0
      g_free (fds);
209
0
210
0
      return -1;
211
0
    }
212
0
213
0
  fd = *fds;
214
0
  g_free (fds);
215
0
216
0
  if (fd < 0)
217
0
    {
218
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
219
0
                           _("Received invalid fd"));
220
0
      fd = -1;
221
0
    }
222
0
223
0
  return fd;
224
0
}
225
226
static void
227
g_unix_connection_init (GUnixConnection *connection)
228
0
{
229
0
}
230
231
static void
232
g_unix_connection_class_init (GUnixConnectionClass *class)
233
0
{
234
0
}
235
236
/* TODO: Other stuff we might want to add are:
237
void                    g_unix_connection_send_fd_async                 (GUnixConnection      *connection,
238
                                                                         gint                  fd,
239
                                                                         gboolean              close,
240
                                                                         gint                  io_priority,
241
                                                                         GAsyncReadyCallback   callback,
242
                                                                         gpointer              user_data);
243
gboolean                g_unix_connection_send_fd_finish                (GUnixConnection      *connection,
244
                                                                         GError              **error);
245
246
gboolean                g_unix_connection_send_fds                      (GUnixConnection      *connection,
247
                                                                         gint                 *fds,
248
                                                                         gint                  nfds,
249
                                                                         GError              **error);
250
void                    g_unix_connection_send_fds_async                (GUnixConnection      *connection,
251
                                                                         gint                 *fds,
252
                                                                         gint                  nfds,
253
                                                                         gint                  io_priority,
254
                                                                         GAsyncReadyCallback   callback,
255
                                                                         gpointer              user_data);
256
gboolean                g_unix_connection_send_fds_finish               (GUnixConnection      *connection,
257
                                                                         GError              **error);
258
259
void                    g_unix_connection_receive_fd_async              (GUnixConnection      *connection,
260
                                                                         gint                  io_priority,
261
                                                                         GAsyncReadyCallback   callback,
262
                                                                         gpointer              user_data);
263
gint                    g_unix_connection_receive_fd_finish             (GUnixConnection      *connection,
264
                                                                         GError              **error);
265
266
267
gboolean                g_unix_connection_send_fake_credentials         (GUnixConnection      *connection,
268
                                                                         guint64               pid,
269
                                                                         guint64               uid,
270
                                                                         guint64               gid,
271
                                                                         GError              **error);
272
void                    g_unix_connection_send_fake_credentials_async   (GUnixConnection      *connection,
273
                                                                         guint64               pid,
274
                                                                         guint64               uid,
275
                                                                         guint64               gid,
276
                                                                         gint                  io_priority,
277
                                                                         GAsyncReadyCallback   callback,
278
                                                                         gpointer              user_data);
279
gboolean                g_unix_connection_send_fake_credentials_finish  (GUnixConnection      *connection,
280
                                                                         GError              **error);
281
282
gboolean                g_unix_connection_create_pair                   (GUnixConnection     **one,
283
                                                                         GUnixConnection     **two,
284
                                                                         GError              **error);
285
*/
286
287
288
/**
289
 * g_unix_connection_send_credentials:
290
 * @connection: A #GUnixConnection.
291
 * @cancellable: (nullable): A #GCancellable or %NULL.
292
 * @error: Return location for error or %NULL.
293
 *
294
 * Passes the credentials of the current user the receiving side
295
 * of the connection. The receiving end has to call
296
 * g_unix_connection_receive_credentials() (or similar) to accept the
297
 * credentials.
298
 *
299
 * As well as sending the credentials this also writes a single NUL
300
 * byte to the stream, as this is required for credentials passing to
301
 * work on some implementations.
302
 *
303
 * Other ways to exchange credentials with a foreign peer includes the
304
 * #GUnixCredentialsMessage type and g_socket_get_credentials() function.
305
 *
306
 * Returns: %TRUE on success, %FALSE if @error is set.
307
 *
308
 * Since: 2.26
309
 */
310
gboolean
311
g_unix_connection_send_credentials (GUnixConnection      *connection,
312
                                    GCancellable         *cancellable,
313
                                    GError              **error)
314
0
{
315
0
  GCredentials *credentials;
316
0
  GSocketControlMessage *scm;
317
0
  GSocket *socket;
318
0
  gboolean ret;
319
0
  GOutputVector vector;
320
0
  guchar nul_byte[1] = {'\0'};
321
0
  gint num_messages;
322
0
323
0
  g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE);
324
0
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
325
0
326
0
  ret = FALSE;
327
0
328
0
  credentials = g_credentials_new ();
329
0
330
0
  vector.buffer = &nul_byte;
331
0
  vector.size = 1;
332
0
333
0
  if (g_unix_credentials_message_is_supported ())
334
0
    {
335
0
      scm = g_unix_credentials_message_new_with_credentials (credentials);
336
0
      num_messages = 1;
337
0
    }
338
0
  else
339
0
    {
340
0
      scm = NULL;
341
0
      num_messages = 0;
342
0
    }
343
0
344
0
  g_object_get (connection, "socket", &socket, NULL);
345
0
  if (g_socket_send_message (socket,
346
0
                             NULL, /* address */
347
0
                             &vector,
348
0
                             1,
349
0
                             &scm,
350
0
                             num_messages,
351
0
                             G_SOCKET_MSG_NONE,
352
0
                             cancellable,
353
0
                             error) != 1)
354
0
    {
355
0
      g_prefix_error (error, _("Error sending credentials: "));
356
0
      goto out;
357
0
    }
358
0
359
0
  ret = TRUE;
360
0
361
0
 out:
362
0
  g_object_unref (socket);
363
0
  if (scm != NULL)
364
0
    g_object_unref (scm);
365
0
  g_object_unref (credentials);
366
0
  return ret;
367
0
}
368
369
static void
370
send_credentials_async_thread (GTask         *task,
371
             gpointer       source_object,
372
             gpointer       task_data,
373
             GCancellable  *cancellable)
374
0
{
375
0
  GError *error = NULL;
376
0
377
0
  if (g_unix_connection_send_credentials (G_UNIX_CONNECTION (source_object),
378
0
            cancellable,
379
0
            &error))
380
0
    g_task_return_boolean (task, TRUE);
381
0
  else
382
0
    g_task_return_error (task, error);
383
0
  g_object_unref (task);
384
0
}
385
386
/**
387
 * g_unix_connection_send_credentials_async:
388
 * @connection: A #GUnixConnection.
389
 * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
390
 * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
391
 * @user_data: (closure): the data to pass to callback function
392
 *
393
 * Asynchronously send credentials.
394
 *
395
 * For more details, see g_unix_connection_send_credentials() which is
396
 * the synchronous version of this call.
397
 *
398
 * When the operation is finished, @callback will be called. You can then call
399
 * g_unix_connection_send_credentials_finish() to get the result of the operation.
400
 *
401
 * Since: 2.32
402
 **/
403
void
404
g_unix_connection_send_credentials_async (GUnixConnection      *connection,
405
                                          GCancellable         *cancellable,
406
                                          GAsyncReadyCallback   callback,
407
                                          gpointer              user_data)
408
0
{
409
0
  GTask *task;
410
0
411
0
  task = g_task_new (connection, cancellable, callback, user_data);
412
0
  g_task_set_source_tag (task, g_unix_connection_send_credentials_async);
413
0
  g_task_run_in_thread (task, send_credentials_async_thread);
414
0
}
415
416
/**
417
 * g_unix_connection_send_credentials_finish:
418
 * @connection: A #GUnixConnection.
419
 * @result: a #GAsyncResult.
420
 * @error: a #GError, or %NULL
421
 *
422
 * Finishes an asynchronous send credentials operation started with
423
 * g_unix_connection_send_credentials_async().
424
 *
425
 * Returns: %TRUE if the operation was successful, otherwise %FALSE.
426
 *
427
 * Since: 2.32
428
 **/
429
gboolean
430
g_unix_connection_send_credentials_finish (GUnixConnection *connection,
431
                                           GAsyncResult    *result,
432
                                           GError         **error)
433
0
{
434
0
  g_return_val_if_fail (g_task_is_valid (result, connection), FALSE);
435
0
436
0
  return g_task_propagate_boolean (G_TASK (result), error);
437
0
}
438
439
/**
440
 * g_unix_connection_receive_credentials:
441
 * @connection: A #GUnixConnection.
442
 * @cancellable: (nullable): A #GCancellable or %NULL.
443
 * @error: Return location for error or %NULL.
444
 *
445
 * Receives credentials from the sending end of the connection.  The
446
 * sending end has to call g_unix_connection_send_credentials() (or
447
 * similar) for this to work.
448
 *
449
 * As well as reading the credentials this also reads (and discards) a
450
 * single byte from the stream, as this is required for credentials
451
 * passing to work on some implementations.
452
 *
453
 * Other ways to exchange credentials with a foreign peer includes the
454
 * #GUnixCredentialsMessage type and g_socket_get_credentials() function.
455
 *
456
 * Returns: (transfer full): Received credentials on success (free with
457
 * g_object_unref()), %NULL if @error is set.
458
 *
459
 * Since: 2.26
460
 */
461
GCredentials *
462
g_unix_connection_receive_credentials (GUnixConnection      *connection,
463
                                       GCancellable         *cancellable,
464
                                       GError              **error)
465
0
{
466
0
  GCredentials *ret;
467
0
  GSocketControlMessage **scms;
468
0
  gint nscm;
469
0
  GSocket *socket;
470
0
  gint n;
471
0
  gssize num_bytes_read;
472
0
#ifdef __linux__
473
0
  gboolean turn_off_so_passcreds;
474
0
#endif
475
0
476
0
  g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), NULL);
477
0
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
478
0
479
0
  ret = NULL;
480
0
  scms = NULL;
481
0
482
0
  g_object_get (connection, "socket", &socket, NULL);
483
0
484
0
  /* On Linux, we need to turn on SO_PASSCRED if it isn't enabled
485
0
   * already. We also need to turn it off when we're done.  See
486
0
   * #617483 for more discussion.
487
0
   */
488
0
#ifdef __linux__
489
0
  {
490
0
    gint opt_val;
491
0
492
0
    turn_off_so_passcreds = FALSE;
493
0
    opt_val = 0;
494
0
    if (!g_socket_get_option (socket,
495
0
            SOL_SOCKET,
496
0
            SO_PASSCRED,
497
0
            &opt_val,
498
0
            NULL))
499
0
      {
500
0
        int errsv = errno;
501
0
        g_set_error (error,
502
0
                     G_IO_ERROR,
503
0
                     g_io_error_from_errno (errsv),
504
0
                     _("Error checking if SO_PASSCRED is enabled for socket: %s"),
505
0
                     g_strerror (errsv));
506
0
        goto out;
507
0
      }
508
0
    if (opt_val == 0)
509
0
      {
510
0
        if (!g_socket_set_option (socket,
511
0
          SOL_SOCKET,
512
0
          SO_PASSCRED,
513
0
          TRUE,
514
0
          NULL))
515
0
          {
516
0
            int errsv = errno;
517
0
            g_set_error (error,
518
0
                         G_IO_ERROR,
519
0
                         g_io_error_from_errno (errsv),
520
0
                         _("Error enabling SO_PASSCRED: %s"),
521
0
                         g_strerror (errsv));
522
0
            goto out;
523
0
          }
524
0
        turn_off_so_passcreds = TRUE;
525
0
      }
526
0
  }
527
0
#endif
528
0
529
0
  g_type_ensure (G_TYPE_UNIX_CREDENTIALS_MESSAGE);
530
0
  num_bytes_read = g_socket_receive_message (socket,
531
0
                                             NULL, /* GSocketAddress **address */
532
0
                                             NULL,
533
0
                                             0,
534
0
                                             &scms,
535
0
                                             &nscm,
536
0
                                             NULL,
537
0
                                             cancellable,
538
0
                                             error);
539
0
  if (num_bytes_read != 1)
540
0
    {
541
0
      /* Handle situation where g_socket_receive_message() returns
542
0
       * 0 bytes and not setting @error
543
0
       */
544
0
      if (num_bytes_read == 0 && error != NULL && *error == NULL)
545
0
        {
546
0
          g_set_error_literal (error,
547
0
                               G_IO_ERROR,
548
0
                               G_IO_ERROR_FAILED,
549
0
                               _("Expecting to read a single byte for receiving credentials but read zero bytes"));
550
0
        }
551
0
      goto out;
552
0
    }
553
0
554
0
  if (g_unix_credentials_message_is_supported () &&
555
0
      /* Fall back on get_credentials if the other side didn't send the credentials */
556
0
      nscm > 0)
557
0
    {
558
0
      if (nscm != 1)
559
0
        {
560
0
          g_set_error (error,
561
0
                       G_IO_ERROR,
562
0
                       G_IO_ERROR_FAILED,
563
0
                       ngettext("Expecting 1 control message, got %d",
564
0
                                "Expecting 1 control message, got %d",
565
0
                                nscm),
566
0
                       nscm);
567
0
          goto out;
568
0
        }
569
0
570
0
      if (!G_IS_UNIX_CREDENTIALS_MESSAGE (scms[0]))
571
0
        {
572
0
          g_set_error_literal (error,
573
0
                               G_IO_ERROR,
574
0
                               G_IO_ERROR_FAILED,
575
0
                               _("Unexpected type of ancillary data"));
576
0
          goto out;
577
0
        }
578
0
579
0
      ret = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (scms[0]));
580
0
      g_object_ref (ret);
581
0
    }
582
0
  else
583
0
    {
584
0
      if (nscm != 0)
585
0
        {
586
0
          g_set_error (error,
587
0
                       G_IO_ERROR,
588
0
                       G_IO_ERROR_FAILED,
589
0
                       _("Not expecting control message, but got %d"),
590
0
                       nscm);
591
0
          goto out;
592
0
        }
593
0
      else
594
0
        {
595
0
          ret = g_socket_get_credentials (socket, error);
596
0
        }
597
0
    }
598
0
599
0
 out:
600
0
601
0
#ifdef __linux__
602
0
  if (turn_off_so_passcreds)
603
0
    {
604
0
      if (!g_socket_set_option (socket,
605
0
        SOL_SOCKET,
606
0
        SO_PASSCRED,
607
0
        FALSE,
608
0
        NULL))
609
0
        {
610
0
          int errsv = errno;
611
0
          g_set_error (error,
612
0
                       G_IO_ERROR,
613
0
                       g_io_error_from_errno (errsv),
614
0
                       _("Error while disabling SO_PASSCRED: %s"),
615
0
                       g_strerror (errsv));
616
0
          goto out;
617
0
        }
618
0
    }
619
0
#endif
620
0
621
0
  if (scms != NULL)
622
0
    {
623
0
      for (n = 0; n < nscm; n++)
624
0
        g_object_unref (scms[n]);
625
0
      g_free (scms);
626
0
    }
627
0
  g_object_unref (socket);
628
0
  return ret;
629
0
}
630
631
static void
632
receive_credentials_async_thread (GTask         *task,
633
          gpointer       source_object,
634
          gpointer       task_data,
635
          GCancellable  *cancellable)
636
0
{
637
0
  GCredentials *creds;
638
0
  GError *error = NULL;
639
0
640
0
  creds = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (source_object),
641
0
                                                 cancellable,
642
0
                                                 &error);
643
0
  if (creds)
644
0
    g_task_return_pointer (task, creds, g_object_unref);
645
0
  else
646
0
    g_task_return_error (task, error);
647
0
  g_object_unref (task);
648
0
}
649
650
/**
651
 * g_unix_connection_receive_credentials_async:
652
 * @connection: A #GUnixConnection.
653
 * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
654
 * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
655
 * @user_data: (closure): the data to pass to callback function
656
 *
657
 * Asynchronously receive credentials.
658
 *
659
 * For more details, see g_unix_connection_receive_credentials() which is
660
 * the synchronous version of this call.
661
 *
662
 * When the operation is finished, @callback will be called. You can then call
663
 * g_unix_connection_receive_credentials_finish() to get the result of the operation.
664
 *
665
 * Since: 2.32
666
 **/
667
void
668
g_unix_connection_receive_credentials_async (GUnixConnection      *connection,
669
                                              GCancellable         *cancellable,
670
                                              GAsyncReadyCallback   callback,
671
                                              gpointer              user_data)
672
0
{
673
0
  GTask *task;
674
0
675
0
  task = g_task_new (connection, cancellable, callback, user_data);
676
0
  g_task_set_source_tag (task, g_unix_connection_receive_credentials_async);
677
0
  g_task_run_in_thread (task, receive_credentials_async_thread);
678
0
}
679
680
/**
681
 * g_unix_connection_receive_credentials_finish:
682
 * @connection: A #GUnixConnection.
683
 * @result: a #GAsyncResult.
684
 * @error: a #GError, or %NULL
685
 *
686
 * Finishes an asynchronous receive credentials operation started with
687
 * g_unix_connection_receive_credentials_async().
688
 *
689
 * Returns: (transfer full): a #GCredentials, or %NULL on error.
690
 *     Free the returned object with g_object_unref().
691
 *
692
 * Since: 2.32
693
 **/
694
GCredentials *
695
g_unix_connection_receive_credentials_finish (GUnixConnection *connection,
696
                                              GAsyncResult    *result,
697
                                              GError         **error)
698
0
{
699
0
  g_return_val_if_fail (g_task_is_valid (result, connection), NULL);
700
0
701
0
  return g_task_propagate_pointer (G_TASK (result), error);
702
0
}