Coverage Report

Created: 2025-06-13 06:55

/src/glib/gio/gunixoutputstream.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
#include <sys/uio.h>
29
30
#include <glib.h>
31
#include <glib/gstdio.h>
32
#include <glib/glib-unix.h>
33
#include "gioerror.h"
34
#include "gunixoutputstream.h"
35
#include "gcancellable.h"
36
#include "gasynchelper.h"
37
#include "gfiledescriptorbased.h"
38
#include "glibintl.h"
39
#include "gioprivate.h"
40
#include "giounix-private.h"
41
42
43
/**
44
 * SECTION:gunixoutputstream
45
 * @short_description: Streaming output operations for UNIX file descriptors
46
 * @include: gio/gunixoutputstream.h
47
 * @see_also: #GOutputStream
48
 *
49
 * #GUnixOutputStream implements #GOutputStream for writing to a UNIX
50
 * file descriptor, including asynchronous operations. (If the file
51
 * descriptor refers to a socket or pipe, this will use poll() to do
52
 * asynchronous I/O. If it refers to a regular file, it will fall back
53
 * to doing asynchronous I/O in another thread.)
54
 *
55
 * Note that `<gio/gunixoutputstream.h>` belongs to the UNIX-specific GIO
56
 * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config file
57
 * when using it.
58
 */
59
60
enum {
61
  PROP_0,
62
  PROP_FD,
63
  PROP_CLOSE_FD
64
};
65
66
struct _GUnixOutputStreamPrivate {
67
  int fd;
68
  guint close_fd : 1;
69
  guint can_poll : 1;
70
};
71
72
static void g_unix_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
73
static void g_unix_output_stream_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface);
74
75
G_DEFINE_TYPE_WITH_CODE (GUnixOutputStream, g_unix_output_stream, G_TYPE_OUTPUT_STREAM,
76
                         G_ADD_PRIVATE (GUnixOutputStream)
77
       G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
78
            g_unix_output_stream_pollable_iface_init)
79
       G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED,
80
            g_unix_output_stream_file_descriptor_based_iface_init)
81
       )
82
83
static void     g_unix_output_stream_set_property (GObject              *object,
84
               guint                 prop_id,
85
               const GValue         *value,
86
               GParamSpec           *pspec);
87
static void     g_unix_output_stream_get_property (GObject              *object,
88
               guint                 prop_id,
89
               GValue               *value,
90
               GParamSpec           *pspec);
91
static gssize   g_unix_output_stream_write        (GOutputStream        *stream,
92
               const void           *buffer,
93
               gsize                 count,
94
               GCancellable         *cancellable,
95
               GError              **error);
96
static gboolean g_unix_output_stream_writev       (GOutputStream        *stream,
97
               const GOutputVector  *vectors,
98
               gsize                 n_vectors,
99
               gsize                *bytes_written,
100
               GCancellable         *cancellable,
101
               GError              **error);
102
static gboolean g_unix_output_stream_close        (GOutputStream        *stream,
103
               GCancellable         *cancellable,
104
               GError              **error);
105
106
static gboolean g_unix_output_stream_pollable_can_poll      (GPollableOutputStream *stream);
107
static gboolean g_unix_output_stream_pollable_is_writable   (GPollableOutputStream *stream);
108
static GSource *g_unix_output_stream_pollable_create_source (GPollableOutputStream *stream,
109
                   GCancellable         *cancellable);
110
static GPollableReturn g_unix_output_stream_pollable_writev_nonblocking (GPollableOutputStream  *stream,
111
                   const GOutputVector    *vectors,
112
                   gsize                   n_vectors,
113
                   gsize                  *bytes_written,
114
                   GError                **error);
115
116
static void
117
g_unix_output_stream_class_init (GUnixOutputStreamClass *klass)
118
0
{
119
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
120
0
  GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
121
122
0
  gobject_class->get_property = g_unix_output_stream_get_property;
123
0
  gobject_class->set_property = g_unix_output_stream_set_property;
124
125
0
  stream_class->write_fn = g_unix_output_stream_write;
126
0
  stream_class->writev_fn = g_unix_output_stream_writev;
127
0
  stream_class->close_fn = g_unix_output_stream_close;
128
129
   /**
130
   * GUnixOutputStream:fd:
131
   *
132
   * The file descriptor that the stream writes to.
133
   *
134
   * Since: 2.20
135
   */
136
0
  g_object_class_install_property (gobject_class,
137
0
           PROP_FD,
138
0
           g_param_spec_int ("fd",
139
0
                 P_("File descriptor"),
140
0
                 P_("The file descriptor to write to"),
141
0
                 G_MININT, G_MAXINT, -1,
142
0
                 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
143
144
  /**
145
   * GUnixOutputStream:close-fd:
146
   *
147
   * Whether to close the file descriptor when the stream is closed.
148
   *
149
   * Since: 2.20
150
   */
151
0
  g_object_class_install_property (gobject_class,
152
0
           PROP_CLOSE_FD,
153
0
           g_param_spec_boolean ("close-fd",
154
0
               P_("Close file descriptor"),
155
0
               P_("Whether to close the file descriptor when the stream is closed"),
156
0
               TRUE,
157
0
               G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
158
0
}
159
160
static void
161
g_unix_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
162
0
{
163
0
  iface->can_poll = g_unix_output_stream_pollable_can_poll;
164
0
  iface->is_writable = g_unix_output_stream_pollable_is_writable;
165
0
  iface->create_source = g_unix_output_stream_pollable_create_source;
166
0
  iface->writev_nonblocking = g_unix_output_stream_pollable_writev_nonblocking;
167
0
}
168
169
static void
170
g_unix_output_stream_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface)
171
0
{
172
0
  iface->get_fd = (int (*) (GFileDescriptorBased *))g_unix_output_stream_get_fd;
173
0
}
174
175
static void
176
g_unix_output_stream_set_property (GObject         *object,
177
           guint            prop_id,
178
           const GValue    *value,
179
           GParamSpec      *pspec)
180
0
{
181
0
  GUnixOutputStream *unix_stream;
182
183
0
  unix_stream = G_UNIX_OUTPUT_STREAM (object);
184
185
0
  switch (prop_id)
186
0
    {
187
0
    case PROP_FD:
188
0
      unix_stream->priv->fd = g_value_get_int (value);
189
0
      unix_stream->priv->can_poll = _g_fd_is_pollable (unix_stream->priv->fd);
190
0
      break;
191
0
    case PROP_CLOSE_FD:
192
0
      unix_stream->priv->close_fd = g_value_get_boolean (value);
193
0
      break;
194
0
    default:
195
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
196
0
      break;
197
0
    }
198
0
}
199
200
static void
201
g_unix_output_stream_get_property (GObject    *object,
202
           guint       prop_id,
203
           GValue     *value,
204
           GParamSpec *pspec)
205
0
{
206
0
  GUnixOutputStream *unix_stream;
207
208
0
  unix_stream = G_UNIX_OUTPUT_STREAM (object);
209
210
0
  switch (prop_id)
211
0
    {
212
0
    case PROP_FD:
213
0
      g_value_set_int (value, unix_stream->priv->fd);
214
0
      break;
215
0
    case PROP_CLOSE_FD:
216
0
      g_value_set_boolean (value, unix_stream->priv->close_fd);
217
0
      break;
218
0
    default:
219
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
220
0
    }
221
0
}
222
223
static void
224
g_unix_output_stream_init (GUnixOutputStream *unix_stream)
225
0
{
226
0
  unix_stream->priv = g_unix_output_stream_get_instance_private (unix_stream);
227
0
  unix_stream->priv->fd = -1;
228
0
  unix_stream->priv->close_fd = TRUE;
229
0
}
230
231
/**
232
 * g_unix_output_stream_new:
233
 * @fd: a UNIX file descriptor
234
 * @close_fd: %TRUE to close the file descriptor when done
235
 * 
236
 * Creates a new #GUnixOutputStream for the given @fd. 
237
 * 
238
 * If @close_fd, is %TRUE, the file descriptor will be closed when 
239
 * the output stream is destroyed.
240
 * 
241
 * Returns: a new #GOutputStream
242
 **/
243
GOutputStream *
244
g_unix_output_stream_new (gint     fd,
245
        gboolean close_fd)
246
0
{
247
0
  GUnixOutputStream *stream;
248
249
0
  g_return_val_if_fail (fd != -1, NULL);
250
251
0
  stream = g_object_new (G_TYPE_UNIX_OUTPUT_STREAM,
252
0
       "fd", fd,
253
0
       "close-fd", close_fd,
254
0
       NULL);
255
  
256
0
  return G_OUTPUT_STREAM (stream);
257
0
}
258
259
/**
260
 * g_unix_output_stream_set_close_fd:
261
 * @stream: a #GUnixOutputStream
262
 * @close_fd: %TRUE to close the file descriptor when done
263
 *
264
 * Sets whether the file descriptor of @stream shall be closed
265
 * when the stream is closed.
266
 *
267
 * Since: 2.20
268
 */
269
void
270
g_unix_output_stream_set_close_fd (GUnixOutputStream *stream,
271
                                   gboolean           close_fd)
272
0
{
273
0
  g_return_if_fail (G_IS_UNIX_OUTPUT_STREAM (stream));
274
275
0
  close_fd = close_fd != FALSE;
276
0
  if (stream->priv->close_fd != close_fd)
277
0
    {
278
0
      stream->priv->close_fd = close_fd;
279
0
      g_object_notify (G_OBJECT (stream), "close-fd");
280
0
    }
281
0
}
282
283
/**
284
 * g_unix_output_stream_get_close_fd:
285
 * @stream: a #GUnixOutputStream
286
 *
287
 * Returns whether the file descriptor of @stream will be
288
 * closed when the stream is closed.
289
 *
290
 * Returns: %TRUE if the file descriptor is closed when done
291
 *
292
 * Since: 2.20
293
 */
294
gboolean
295
g_unix_output_stream_get_close_fd (GUnixOutputStream *stream)
296
0
{
297
0
  g_return_val_if_fail (G_IS_UNIX_OUTPUT_STREAM (stream), FALSE);
298
299
0
  return stream->priv->close_fd;
300
0
}
301
302
/**
303
 * g_unix_output_stream_get_fd:
304
 * @stream: a #GUnixOutputStream
305
 *
306
 * Return the UNIX file descriptor that the stream writes to.
307
 *
308
 * Returns: The file descriptor of @stream
309
 *
310
 * Since: 2.20
311
 */
312
gint
313
g_unix_output_stream_get_fd (GUnixOutputStream *stream)
314
0
{
315
0
  g_return_val_if_fail (G_IS_UNIX_OUTPUT_STREAM (stream), -1);
316
317
0
  return stream->priv->fd;
318
0
}
319
320
static gssize
321
g_unix_output_stream_write (GOutputStream  *stream,
322
          const void     *buffer,
323
          gsize           count,
324
          GCancellable   *cancellable,
325
          GError        **error)
326
0
{
327
0
  GUnixOutputStream *unix_stream;
328
0
  gssize res = -1;
329
0
  GPollFD poll_fds[2];
330
0
  int nfds = 0;
331
0
  int poll_ret;
332
333
0
  unix_stream = G_UNIX_OUTPUT_STREAM (stream);
334
335
0
  poll_fds[0].fd = unix_stream->priv->fd;
336
0
  poll_fds[0].events = G_IO_OUT;
337
0
  nfds++;
338
339
0
  if (unix_stream->priv->can_poll &&
340
0
      g_cancellable_make_pollfd (cancellable, &poll_fds[1]))
341
0
    nfds++;
342
343
0
  while (1)
344
0
    {
345
0
      int errsv;
346
347
0
      poll_fds[0].revents = poll_fds[1].revents = 0;
348
0
      do
349
0
        {
350
0
          poll_ret = g_poll (poll_fds, nfds, -1);
351
0
          errsv = errno;
352
0
        }
353
0
      while (poll_ret == -1 && errsv == EINTR);
354
355
0
      if (poll_ret == -1)
356
0
  {
357
0
    g_set_error (error, G_IO_ERROR,
358
0
           g_io_error_from_errno (errsv),
359
0
           _("Error writing to file descriptor: %s"),
360
0
           g_strerror (errsv));
361
0
    break;
362
0
  }
363
364
0
      if (g_cancellable_set_error_if_cancelled (cancellable, error))
365
0
  break;
366
367
0
      if (!poll_fds[0].revents)
368
0
  continue;
369
370
0
      res = write (unix_stream->priv->fd, buffer, count);
371
0
      errsv = errno;
372
0
      if (res == -1)
373
0
  {
374
0
    if (errsv == EINTR || errsv == EAGAIN)
375
0
      continue;
376
377
0
    g_set_error (error, G_IO_ERROR,
378
0
           g_io_error_from_errno (errsv),
379
0
           _("Error writing to file descriptor: %s"),
380
0
           g_strerror (errsv));
381
0
  }
382
383
0
      break;
384
0
    }
385
386
0
  if (nfds == 2)
387
0
    g_cancellable_release_fd (cancellable);
388
0
  return res;
389
0
}
390
391
/* Macro to check if struct iovec and GOutputVector have the same ABI */
392
0
#define G_OUTPUT_VECTOR_IS_IOVEC (sizeof (struct iovec) == sizeof (GOutputVector) && \
393
0
      G_SIZEOF_MEMBER (struct iovec, iov_base) == G_SIZEOF_MEMBER (GOutputVector, buffer) && \
394
0
      G_STRUCT_OFFSET (struct iovec, iov_base) == G_STRUCT_OFFSET (GOutputVector, buffer) && \
395
0
      G_SIZEOF_MEMBER (struct iovec, iov_len) == G_SIZEOF_MEMBER (GOutputVector, size) && \
396
0
      G_STRUCT_OFFSET (struct iovec, iov_len) == G_STRUCT_OFFSET (GOutputVector, size))
397
398
static gboolean
399
g_unix_output_stream_writev (GOutputStream        *stream,
400
           const GOutputVector  *vectors,
401
           gsize                 n_vectors,
402
           gsize                *bytes_written,
403
           GCancellable         *cancellable,
404
           GError              **error)
405
0
{
406
0
  GUnixOutputStream *unix_stream;
407
0
  gssize res = -1;
408
0
  GPollFD poll_fds[2];
409
0
  int nfds = 0;
410
0
  int poll_ret;
411
0
  struct iovec *iov;
412
413
0
  if (bytes_written)
414
0
    *bytes_written = 0;
415
416
  /* Clamp the number of vectors if more given than we can write in one go.
417
   * The caller has to handle short writes anyway.
418
   */
419
0
  if (n_vectors > G_IOV_MAX)
420
0
    n_vectors = G_IOV_MAX;
421
422
0
  unix_stream = G_UNIX_OUTPUT_STREAM (stream);
423
424
0
  if (G_OUTPUT_VECTOR_IS_IOVEC)
425
0
    {
426
      /* ABI is compatible */
427
0
      iov = (struct iovec *) vectors;
428
0
    }
429
0
  else
430
0
    {
431
0
      gsize i;
432
433
      /* ABI is incompatible */
434
0
      iov = g_newa (struct iovec, n_vectors);
435
0
      for (i = 0; i < n_vectors; i++)
436
0
        {
437
0
          iov[i].iov_base = (void *)vectors[i].buffer;
438
0
          iov[i].iov_len = vectors[i].size;
439
0
        }
440
0
    }
441
442
0
  poll_fds[0].fd = unix_stream->priv->fd;
443
0
  poll_fds[0].events = G_IO_OUT;
444
0
  nfds++;
445
446
0
  if (unix_stream->priv->can_poll &&
447
0
      g_cancellable_make_pollfd (cancellable, &poll_fds[1]))
448
0
    nfds++;
449
450
0
  while (1)
451
0
    {
452
0
      int errsv;
453
454
0
      poll_fds[0].revents = poll_fds[1].revents = 0;
455
0
      do
456
0
        {
457
0
          poll_ret = g_poll (poll_fds, nfds, -1);
458
0
          errsv = errno;
459
0
        }
460
0
      while (poll_ret == -1 && errsv == EINTR);
461
462
0
      if (poll_ret == -1)
463
0
  {
464
0
    g_set_error (error, G_IO_ERROR,
465
0
           g_io_error_from_errno (errsv),
466
0
           _("Error writing to file descriptor: %s"),
467
0
           g_strerror (errsv));
468
0
    break;
469
0
  }
470
471
0
      if (g_cancellable_set_error_if_cancelled (cancellable, error))
472
0
  break;
473
474
0
      if (!poll_fds[0].revents)
475
0
  continue;
476
477
0
      res = writev (unix_stream->priv->fd, iov, n_vectors);
478
0
      errsv = errno;
479
0
      if (res == -1)
480
0
  {
481
0
    if (errsv == EINTR || errsv == EAGAIN)
482
0
      continue;
483
484
0
    g_set_error (error, G_IO_ERROR,
485
0
           g_io_error_from_errno (errsv),
486
0
           _("Error writing to file descriptor: %s"),
487
0
           g_strerror (errsv));
488
0
  }
489
490
0
      if (bytes_written)
491
0
        *bytes_written = res;
492
493
0
      break;
494
0
    }
495
496
0
  if (nfds == 2)
497
0
    g_cancellable_release_fd (cancellable);
498
0
  return res != -1;
499
0
}
500
501
static gboolean
502
g_unix_output_stream_close (GOutputStream  *stream,
503
          GCancellable   *cancellable,
504
          GError        **error)
505
0
{
506
0
  GUnixOutputStream *unix_stream;
507
0
  int res;
508
509
0
  unix_stream = G_UNIX_OUTPUT_STREAM (stream);
510
511
0
  if (!unix_stream->priv->close_fd)
512
0
    return TRUE;
513
  
514
  /* This might block during the close. Doesn't seem to be a way to avoid it though. */
515
0
  res = close (unix_stream->priv->fd);
516
0
  if (res == -1)
517
0
    {
518
0
      int errsv = errno;
519
520
0
      g_set_error (error, G_IO_ERROR,
521
0
       g_io_error_from_errno (errsv),
522
0
       _("Error closing file descriptor: %s"),
523
0
       g_strerror (errsv));
524
0
    }
525
526
0
  return res != -1;
527
0
}
528
529
static gboolean
530
g_unix_output_stream_pollable_can_poll (GPollableOutputStream *stream)
531
0
{
532
0
  return G_UNIX_OUTPUT_STREAM (stream)->priv->can_poll;
533
0
}
534
535
static gboolean
536
g_unix_output_stream_pollable_is_writable (GPollableOutputStream *stream)
537
0
{
538
0
  GUnixOutputStream *unix_stream = G_UNIX_OUTPUT_STREAM (stream);
539
0
  GPollFD poll_fd;
540
0
  gint result;
541
542
0
  poll_fd.fd = unix_stream->priv->fd;
543
0
  poll_fd.events = G_IO_OUT;
544
0
  poll_fd.revents = 0;
545
546
0
  do
547
0
    result = g_poll (&poll_fd, 1, 0);
548
0
  while (result == -1 && errno == EINTR);
549
550
0
  return poll_fd.revents != 0;
551
0
}
552
553
static GSource *
554
g_unix_output_stream_pollable_create_source (GPollableOutputStream *stream,
555
               GCancellable          *cancellable)
556
0
{
557
0
  GUnixOutputStream *unix_stream = G_UNIX_OUTPUT_STREAM (stream);
558
0
  GSource *inner_source, *cancellable_source, *pollable_source;
559
560
0
  pollable_source = g_pollable_source_new (G_OBJECT (stream));
561
562
0
  inner_source = g_unix_fd_source_new (unix_stream->priv->fd, G_IO_OUT);
563
0
  g_source_set_dummy_callback (inner_source);
564
0
  g_source_add_child_source (pollable_source, inner_source);
565
0
  g_source_unref (inner_source);
566
567
0
  if (cancellable)
568
0
    {
569
0
      cancellable_source = g_cancellable_source_new (cancellable);
570
0
      g_source_set_dummy_callback (cancellable_source);
571
0
      g_source_add_child_source (pollable_source, cancellable_source);
572
0
      g_source_unref (cancellable_source);
573
0
    }
574
575
0
  return pollable_source;
576
0
}
577
578
static GPollableReturn
579
g_unix_output_stream_pollable_writev_nonblocking (GPollableOutputStream  *stream,
580
              const GOutputVector    *vectors,
581
              gsize                   n_vectors,
582
              gsize                  *bytes_written,
583
              GError                **error)
584
0
{
585
0
  GUnixOutputStream *unix_stream = G_UNIX_OUTPUT_STREAM (stream);
586
0
  struct iovec *iov;
587
0
  gssize res = -1;
588
589
0
  if (!g_pollable_output_stream_is_writable (stream))
590
0
    {
591
0
      *bytes_written = 0;
592
0
      return G_POLLABLE_RETURN_WOULD_BLOCK;
593
0
    }
594
595
  /* Clamp the number of vectors if more given than we can write in one go.
596
   * The caller has to handle short writes anyway.
597
   */
598
0
  if (n_vectors > G_IOV_MAX)
599
0
    n_vectors = G_IOV_MAX;
600
601
0
  if (G_OUTPUT_VECTOR_IS_IOVEC)
602
0
    {
603
      /* ABI is compatible */
604
0
      iov = (struct iovec *) vectors;
605
0
    }
606
0
  else
607
0
    {
608
0
      gsize i;
609
610
      /* ABI is incompatible */
611
0
      iov = g_newa (struct iovec, n_vectors);
612
0
      for (i = 0; i < n_vectors; i++)
613
0
        {
614
0
          iov[i].iov_base = (void *)vectors[i].buffer;
615
0
          iov[i].iov_len = vectors[i].size;
616
0
        }
617
0
    }
618
619
0
  while (1)
620
0
    {
621
0
      int errsv;
622
623
0
      res = writev (unix_stream->priv->fd, iov, n_vectors);
624
0
      errsv = errno;
625
0
      if (res == -1)
626
0
  {
627
0
    if (errsv == EINTR)
628
0
      continue;
629
630
0
    g_set_error (error, G_IO_ERROR,
631
0
           g_io_error_from_errno (errsv),
632
0
           _("Error writing to file descriptor: %s"),
633
0
           g_strerror (errsv));
634
0
  }
635
636
0
      if (bytes_written)
637
0
        *bytes_written = res;
638
639
0
      break;
640
0
    }
641
642
0
  return res != -1 ? G_POLLABLE_RETURN_OK : G_POLLABLE_RETURN_FAILED;
643
0
}