Coverage Report

Created: 2025-07-23 08:13

/src/pango/subprojects/glib/gio/gunixinputstream.c
Line
Count
Source (jump to first uncovered line)
1
/* GIO - GLib Input, Output and Streaming Library
2
 * 
3
 * Copyright (C) 2006-2007 Red Hat, Inc.
4
 *
5
 * SPDX-License-Identifier: LGPL-2.1-or-later
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General
18
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19
 *
20
 * Author: Alexander Larsson <alexl@redhat.com>
21
 */
22
23
#include "config.h"
24
25
#include <unistd.h>
26
#include <errno.h>
27
#include <stdio.h>
28
29
#include <glib.h>
30
#include <glib/gstdio.h>
31
#include <glib/glib-unix.h>
32
#include "gioerror.h"
33
#include "gunixinputstream.h"
34
#include "gcancellable.h"
35
#include "gasynchelper.h"
36
#include "gfiledescriptorbased.h"
37
#include "glibintl.h"
38
#include "giounix-private.h"
39
40
41
/**
42
 * GUnixInputStream:
43
 *
44
 * `GUnixInputStream` implements [class@Gio.InputStream] for reading from a UNIX
45
 * file descriptor, including asynchronous operations. (If the file
46
 * descriptor refers to a socket or pipe, this will use `poll()` to do
47
 * asynchronous I/O. If it refers to a regular file, it will fall back
48
 * to doing asynchronous I/O in another thread.)
49
 *
50
 * Note that `<gio/gunixinputstream.h>` belongs to the UNIX-specific GIO
51
 * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
52
 * file or the `GioUnix-2.0` GIR namespace when using it.
53
 */
54
55
enum {
56
  PROP_0,
57
  PROP_FD,
58
  PROP_CLOSE_FD
59
};
60
61
struct _GUnixInputStreamPrivate {
62
  int fd;
63
  guint close_fd : 1;
64
  guint can_poll : 1;
65
};
66
67
static void g_unix_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface);
68
static void g_unix_input_stream_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface);
69
70
G_DEFINE_TYPE_WITH_CODE (GUnixInputStream, g_unix_input_stream, G_TYPE_INPUT_STREAM,
71
                         G_ADD_PRIVATE (GUnixInputStream)
72
       G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
73
            g_unix_input_stream_pollable_iface_init)
74
       G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED,
75
            g_unix_input_stream_file_descriptor_based_iface_init)
76
       )
77
78
static void     g_unix_input_stream_set_property (GObject              *object,
79
              guint                 prop_id,
80
              const GValue         *value,
81
              GParamSpec           *pspec);
82
static void     g_unix_input_stream_get_property (GObject              *object,
83
              guint                 prop_id,
84
              GValue               *value,
85
              GParamSpec           *pspec);
86
static gssize   g_unix_input_stream_read         (GInputStream         *stream,
87
              void                 *buffer,
88
              gsize                 count,
89
              GCancellable         *cancellable,
90
              GError              **error);
91
static gboolean g_unix_input_stream_close        (GInputStream         *stream,
92
              GCancellable         *cancellable,
93
              GError              **error);
94
static void     g_unix_input_stream_skip_async   (GInputStream         *stream,
95
              gsize                 count,
96
              int                   io_priority,
97
              GCancellable         *cancellable,
98
              GAsyncReadyCallback   callback,
99
              gpointer              data);
100
static gssize   g_unix_input_stream_skip_finish  (GInputStream         *stream,
101
              GAsyncResult         *result,
102
              GError              **error);
103
104
static gboolean g_unix_input_stream_pollable_can_poll      (GPollableInputStream *stream);
105
static gboolean g_unix_input_stream_pollable_is_readable   (GPollableInputStream *stream);
106
static GSource *g_unix_input_stream_pollable_create_source (GPollableInputStream *stream,
107
                  GCancellable         *cancellable);
108
109
static void
110
g_unix_input_stream_class_init (GUnixInputStreamClass *klass)
111
0
{
112
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
113
0
  GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
114
115
0
  gobject_class->get_property = g_unix_input_stream_get_property;
116
0
  gobject_class->set_property = g_unix_input_stream_set_property;
117
118
0
  stream_class->read_fn = g_unix_input_stream_read;
119
0
  stream_class->close_fn = g_unix_input_stream_close;
120
0
  if (0)
121
0
    {
122
      /* TODO: Implement instead of using fallbacks */
123
0
      stream_class->skip_async = g_unix_input_stream_skip_async;
124
0
      stream_class->skip_finish = g_unix_input_stream_skip_finish;
125
0
    }
126
127
  /**
128
   * GUnixInputStream:fd:
129
   *
130
   * The file descriptor that the stream reads from.
131
   *
132
   * Since: 2.20
133
   */
134
0
  g_object_class_install_property (gobject_class,
135
0
           PROP_FD,
136
0
           g_param_spec_int ("fd", NULL, NULL,
137
0
                 G_MININT, G_MAXINT, -1,
138
0
                 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
139
140
  /**
141
   * GUnixInputStream:close-fd:
142
   *
143
   * Whether to close the file descriptor when the stream is closed.
144
   *
145
   * Since: 2.20
146
   */
147
0
  g_object_class_install_property (gobject_class,
148
0
           PROP_CLOSE_FD,
149
0
           g_param_spec_boolean ("close-fd", NULL, NULL,
150
0
               TRUE,
151
0
               G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
152
0
}
153
154
static void
155
g_unix_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
156
0
{
157
0
  iface->can_poll = g_unix_input_stream_pollable_can_poll;
158
0
  iface->is_readable = g_unix_input_stream_pollable_is_readable;
159
0
  iface->create_source = g_unix_input_stream_pollable_create_source;
160
0
}
161
162
static void
163
g_unix_input_stream_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface)
164
0
{
165
0
  iface->get_fd = (int (*) (GFileDescriptorBased *))g_unix_input_stream_get_fd;
166
0
}
167
168
static void
169
g_unix_input_stream_set_property (GObject         *object,
170
          guint            prop_id,
171
          const GValue    *value,
172
          GParamSpec      *pspec)
173
0
{
174
0
  GUnixInputStream *unix_stream;
175
  
176
0
  unix_stream = G_UNIX_INPUT_STREAM (object);
177
178
0
  switch (prop_id)
179
0
    {
180
0
    case PROP_FD:
181
0
      unix_stream->priv->fd = g_value_get_int (value);
182
0
      unix_stream->priv->can_poll = _g_fd_is_pollable (unix_stream->priv->fd);
183
0
      break;
184
0
    case PROP_CLOSE_FD:
185
0
      unix_stream->priv->close_fd = g_value_get_boolean (value);
186
0
      break;
187
0
    default:
188
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
189
0
      break;
190
0
    }
191
0
}
192
193
static void
194
g_unix_input_stream_get_property (GObject    *object,
195
          guint       prop_id,
196
          GValue     *value,
197
          GParamSpec *pspec)
198
0
{
199
0
  GUnixInputStream *unix_stream;
200
201
0
  unix_stream = G_UNIX_INPUT_STREAM (object);
202
203
0
  switch (prop_id)
204
0
    {
205
0
    case PROP_FD:
206
0
      g_value_set_int (value, unix_stream->priv->fd);
207
0
      break;
208
0
    case PROP_CLOSE_FD:
209
0
      g_value_set_boolean (value, unix_stream->priv->close_fd);
210
0
      break;
211
0
    default:
212
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
213
0
    }
214
0
}
215
216
static void
217
g_unix_input_stream_init (GUnixInputStream *unix_stream)
218
0
{
219
0
  unix_stream->priv = g_unix_input_stream_get_instance_private (unix_stream);
220
0
  unix_stream->priv->fd = -1;
221
0
  unix_stream->priv->close_fd = TRUE;
222
0
}
223
224
/**
225
 * g_unix_input_stream_new:
226
 * @fd: a UNIX file descriptor
227
 * @close_fd: %TRUE to close the file descriptor when done
228
 * 
229
 * Creates a new #GUnixInputStream for the given @fd. 
230
 *
231
 * If @close_fd is %TRUE, the file descriptor will be closed 
232
 * when the stream is closed.
233
 * 
234
 * Returns: a new #GUnixInputStream
235
 **/
236
GInputStream *
237
g_unix_input_stream_new (gint     fd,
238
       gboolean close_fd)
239
0
{
240
0
  GUnixInputStream *stream;
241
242
0
  g_return_val_if_fail (fd != -1, NULL);
243
244
0
  stream = g_object_new (G_TYPE_UNIX_INPUT_STREAM,
245
0
       "fd", fd,
246
0
       "close-fd", close_fd,
247
0
       NULL);
248
249
0
  return G_INPUT_STREAM (stream);
250
0
}
251
252
/**
253
 * g_unix_input_stream_set_close_fd:
254
 * @stream: a #GUnixInputStream
255
 * @close_fd: %TRUE to close the file descriptor when done
256
 *
257
 * Sets whether the file descriptor of @stream shall be closed
258
 * when the stream is closed.
259
 *
260
 * Since: 2.20
261
 */
262
void
263
g_unix_input_stream_set_close_fd (GUnixInputStream *stream,
264
          gboolean          close_fd)
265
0
{
266
0
  g_return_if_fail (G_IS_UNIX_INPUT_STREAM (stream));
267
268
0
  close_fd = close_fd != FALSE;
269
0
  if (stream->priv->close_fd != close_fd)
270
0
    {
271
0
      stream->priv->close_fd = close_fd;
272
0
      g_object_notify (G_OBJECT (stream), "close-fd");
273
0
    }
274
0
}
275
276
/**
277
 * g_unix_input_stream_get_close_fd:
278
 * @stream: a #GUnixInputStream
279
 *
280
 * Returns whether the file descriptor of @stream will be
281
 * closed when the stream is closed.
282
 *
283
 * Returns: %TRUE if the file descriptor is closed when done
284
 *
285
 * Since: 2.20
286
 */
287
gboolean
288
g_unix_input_stream_get_close_fd (GUnixInputStream *stream)
289
0
{
290
0
  g_return_val_if_fail (G_IS_UNIX_INPUT_STREAM (stream), FALSE);
291
292
0
  return stream->priv->close_fd;
293
0
}
294
295
/**
296
 * g_unix_input_stream_get_fd:
297
 * @stream: a #GUnixInputStream
298
 *
299
 * Return the UNIX file descriptor that the stream reads from.
300
 *
301
 * Returns: The file descriptor of @stream
302
 *
303
 * Since: 2.20
304
 */
305
gint
306
g_unix_input_stream_get_fd (GUnixInputStream *stream)
307
0
{
308
0
  g_return_val_if_fail (G_IS_UNIX_INPUT_STREAM (stream), -1);
309
  
310
0
  return stream->priv->fd;
311
0
}
312
313
static gssize
314
g_unix_input_stream_read (GInputStream  *stream,
315
        void          *buffer,
316
        gsize          count,
317
        GCancellable  *cancellable,
318
        GError       **error)
319
0
{
320
0
  GUnixInputStream *unix_stream;
321
0
  gssize res = -1;
322
0
  GPollFD poll_fds[2];
323
0
  int nfds;
324
0
  int poll_ret;
325
326
0
  unix_stream = G_UNIX_INPUT_STREAM (stream);
327
328
0
  poll_fds[0].fd = unix_stream->priv->fd;
329
0
  poll_fds[0].events = G_IO_IN;
330
0
  if (unix_stream->priv->can_poll &&
331
0
      g_cancellable_make_pollfd (cancellable, &poll_fds[1]))
332
0
    nfds = 2;
333
0
  else
334
0
    nfds = 1;
335
336
0
  while (1)
337
0
    {
338
0
      int errsv;
339
340
0
      poll_fds[0].revents = poll_fds[1].revents = 0;
341
0
      do
342
0
        {
343
0
          poll_ret = g_poll (poll_fds, nfds, -1);
344
0
          errsv = errno;
345
0
        }
346
0
      while (poll_ret == -1 && errsv == EINTR);
347
348
0
      if (poll_ret == -1)
349
0
  {
350
0
    g_set_error (error, G_IO_ERROR,
351
0
           g_io_error_from_errno (errsv),
352
0
           _("Error reading from file descriptor: %s"),
353
0
           g_strerror (errsv));
354
0
    break;
355
0
  }
356
357
0
      if (g_cancellable_set_error_if_cancelled (cancellable, error))
358
0
  break;
359
360
0
      if (!poll_fds[0].revents)
361
0
  continue;
362
363
0
      res = read (unix_stream->priv->fd, buffer, count);
364
0
      if (res == -1)
365
0
  {
366
0
          int errsv = errno;
367
368
0
    if (errsv == EINTR || errsv == EAGAIN)
369
0
      continue;
370
371
0
    g_set_error (error, G_IO_ERROR,
372
0
           g_io_error_from_errno (errsv),
373
0
           _("Error reading from file descriptor: %s"),
374
0
           g_strerror (errsv));
375
0
  }
376
377
0
      break;
378
0
    }
379
380
0
  if (nfds == 2)
381
0
    g_cancellable_release_fd (cancellable);
382
0
  return res;
383
0
}
384
385
static gboolean
386
g_unix_input_stream_close (GInputStream  *stream,
387
         GCancellable  *cancellable,
388
         GError       **error)
389
0
{
390
0
  GUnixInputStream *unix_stream;
391
0
  int res;
392
393
0
  unix_stream = G_UNIX_INPUT_STREAM (stream);
394
395
0
  if (!unix_stream->priv->close_fd)
396
0
    return TRUE;
397
  
398
  /* This might block during the close. Doesn't seem to be a way to avoid it though. */
399
0
  res = close (unix_stream->priv->fd);
400
0
  if (res == -1)
401
0
    {
402
0
      int errsv = errno;
403
404
0
      g_set_error (error, G_IO_ERROR,
405
0
       g_io_error_from_errno (errsv),
406
0
       _("Error closing file descriptor: %s"),
407
0
       g_strerror (errsv));
408
0
    }
409
  
410
0
  return res != -1;
411
0
}
412
413
static void
414
g_unix_input_stream_skip_async (GInputStream        *stream,
415
        gsize                count,
416
        int                  io_priority,
417
        GCancellable        *cancellable,
418
        GAsyncReadyCallback  callback,
419
        gpointer             data)
420
0
{
421
0
  g_warn_if_reached ();
422
0
  /* TODO: Not implemented */
423
0
}
424
425
static gssize
426
g_unix_input_stream_skip_finish  (GInputStream  *stream,
427
          GAsyncResult  *result,
428
          GError       **error)
429
0
{
430
0
  g_warn_if_reached ();
431
0
  return 0;
432
0
  /* TODO: Not implemented */
433
0
}
434
435
static gboolean
436
g_unix_input_stream_pollable_can_poll (GPollableInputStream *stream)
437
0
{
438
0
  return G_UNIX_INPUT_STREAM (stream)->priv->can_poll;
439
0
}
440
441
static gboolean
442
g_unix_input_stream_pollable_is_readable (GPollableInputStream *stream)
443
0
{
444
0
  GUnixInputStream *unix_stream = G_UNIX_INPUT_STREAM (stream);
445
0
  GPollFD poll_fd;
446
0
  gint result;
447
448
0
  poll_fd.fd = unix_stream->priv->fd;
449
0
  poll_fd.events = G_IO_IN;
450
0
  poll_fd.revents = 0;
451
452
0
  do
453
0
    result = g_poll (&poll_fd, 1, 0);
454
0
  while (result == -1 && errno == EINTR);
455
456
0
  return poll_fd.revents != 0;
457
0
}
458
459
static GSource *
460
g_unix_input_stream_pollable_create_source (GPollableInputStream *stream,
461
              GCancellable         *cancellable)
462
0
{
463
0
  GUnixInputStream *unix_stream = G_UNIX_INPUT_STREAM (stream);
464
0
  GSource *inner_source, *cancellable_source, *pollable_source;
465
466
0
  pollable_source = g_pollable_source_new (G_OBJECT (stream));
467
468
0
  inner_source = g_unix_fd_source_new (unix_stream->priv->fd, G_IO_IN);
469
0
  g_source_set_dummy_callback (inner_source);
470
0
  g_source_add_child_source (pollable_source, inner_source);
471
0
  g_source_unref (inner_source);
472
473
0
  if (cancellable)
474
0
    {
475
0
      cancellable_source = g_cancellable_source_new (cancellable);
476
0
      g_source_set_dummy_callback (cancellable_source);
477
0
      g_source_add_child_source (pollable_source, cancellable_source);
478
0
      g_source_unref (cancellable_source);
479
0
    }
480
481
0
  return pollable_source;
482
0
}