gunixoutputstream.c 13.9 KB
Newer Older
1 2 3 4 5 6 7
/* GIO - GLib Input, Output and Streaming Library
 * 
 * Copyright (C) 2006-2007 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * 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 11 12 13 14 15
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
16
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 18 19 20
 *
 * Author: Alexander Larsson <alexl@redhat.com>
 */

21
#include "config.h"
22 23 24 25 26 27 28 29 30 31

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>

#include <glib.h>
#include <glib/gstdio.h>
32
#include <glib/glib-unix.h>
33
#include "gioerror.h"
34
#include "gunixoutputstream.h"
35 36
#include "gcancellable.h"
#include "gasynchelper.h"
37
#include "gfiledescriptorbased.h"
38 39
#include "glibintl.h"

40

41
/**
42
 * SECTION:gunixoutputstream
Matthias Clasen's avatar
Matthias Clasen committed
43
 * @short_description: Streaming output operations for UNIX file descriptors
44
 * @include: gio/gunixoutputstream.h
45
 * @see_also: #GOutputStream
46
 *
47 48 49 50
 * #GUnixOutputStream implements #GOutputStream for writing to a UNIX
 * file descriptor, including asynchronous operations. (If the file
 * descriptor refers to a socket or pipe, this will use poll() to do
 * asynchronous I/O. If it refers to a regular file, it will fall back
51
 * to doing asynchronous I/O in another thread.)
52
 *
53
 * Note that `<gio/gunixoutputstream.h>` belongs to the UNIX-specific GIO
54 55
 * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config file
 * when using it.
56
 */
57

58 59 60 61 62 63
enum {
  PROP_0,
  PROP_FD,
  PROP_CLOSE_FD
};

64 65 66 67 68 69
struct _GUnixOutputStreamPrivate {
  int fd;
  guint close_fd : 1;
  guint is_pipe_or_socket : 1;
};

70
static void g_unix_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
71
static void g_unix_output_stream_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface);
72

73
G_DEFINE_TYPE_WITH_CODE (GUnixOutputStream, g_unix_output_stream, G_TYPE_OUTPUT_STREAM,
74
                         G_ADD_PRIVATE (GUnixOutputStream)
75 76
			 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
						g_unix_output_stream_pollable_iface_init)
77 78 79
			 G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED,
						g_unix_output_stream_file_descriptor_based_iface_init)
			 )
80

81 82 83 84 85 86 87 88
static void     g_unix_output_stream_set_property (GObject              *object,
						   guint                 prop_id,
						   const GValue         *value,
						   GParamSpec           *pspec);
static void     g_unix_output_stream_get_property (GObject              *object,
						   guint                 prop_id,
						   GValue               *value,
						   GParamSpec           *pspec);
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
static gssize   g_unix_output_stream_write        (GOutputStream        *stream,
						   const void           *buffer,
						   gsize                 count,
						   GCancellable         *cancellable,
						   GError              **error);
static gboolean g_unix_output_stream_close        (GOutputStream        *stream,
						   GCancellable         *cancellable,
						   GError              **error);
static void     g_unix_output_stream_close_async  (GOutputStream        *stream,
						   int                   io_priority,
						   GCancellable         *cancellable,
						   GAsyncReadyCallback   callback,
						   gpointer              data);
static gboolean g_unix_output_stream_close_finish (GOutputStream        *stream,
						   GAsyncResult         *result,
						   GError              **error);
105

106
static gboolean g_unix_output_stream_pollable_can_poll      (GPollableOutputStream *stream);
107 108 109
static gboolean g_unix_output_stream_pollable_is_writable   (GPollableOutputStream *stream);
static GSource *g_unix_output_stream_pollable_create_source (GPollableOutputStream *stream,
							     GCancellable         *cancellable);
110 111

static void
112
g_unix_output_stream_class_init (GUnixOutputStreamClass *klass)
113 114 115
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
116 117 118

  gobject_class->get_property = g_unix_output_stream_get_property;
  gobject_class->set_property = g_unix_output_stream_set_property;
119

120 121
  stream_class->write_fn = g_unix_output_stream_write;
  stream_class->close_fn = g_unix_output_stream_close;
122 123
  stream_class->close_async = g_unix_output_stream_close_async;
  stream_class->close_finish = g_unix_output_stream_close_finish;
124 125 126 127 128 129 130 131 132 133 134

   /**
   * GUnixOutputStream:fd:
   *
   * The file descriptor that the stream writes to.
   *
   * Since: 2.20
   */
  g_object_class_install_property (gobject_class,
				   PROP_FD,
				   g_param_spec_int ("fd",
135 136
						     P_("File descriptor"),
						     P_("The file descriptor to write to"),
137 138 139 140 141 142 143 144 145 146 147 148 149
						     G_MININT, G_MAXINT, -1,
						     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));

  /**
   * GUnixOutputStream:close-fd:
   *
   * Whether to close the file descriptor when the stream is closed.
   *
   * Since: 2.20
   */
  g_object_class_install_property (gobject_class,
				   PROP_CLOSE_FD,
				   g_param_spec_boolean ("close-fd",
150 151
							 P_("Close file descriptor"),
							 P_("Whether to close the file descriptor when the stream is closed"),
152 153 154 155
							 TRUE,
							 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
}

156 157 158
static void
g_unix_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
{
159
  iface->can_poll = g_unix_output_stream_pollable_can_poll;
160 161 162 163
  iface->is_writable = g_unix_output_stream_pollable_is_writable;
  iface->create_source = g_unix_output_stream_pollable_create_source;
}

164 165 166 167 168 169
static void
g_unix_output_stream_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface)
{
  iface->get_fd = (int (*) (GFileDescriptorBased *))g_unix_output_stream_get_fd;
}

170 171 172 173 174 175 176 177 178 179 180 181 182 183
static void
g_unix_output_stream_set_property (GObject         *object,
				   guint            prop_id,
				   const GValue    *value,
				   GParamSpec      *pspec)
{
  GUnixOutputStream *unix_stream;

  unix_stream = G_UNIX_OUTPUT_STREAM (object);

  switch (prop_id)
    {
    case PROP_FD:
      unix_stream->priv->fd = g_value_get_int (value);
184 185 186 187
      if (lseek (unix_stream->priv->fd, 0, SEEK_CUR) == -1 && errno == ESPIPE)
	unix_stream->priv->is_pipe_or_socket = TRUE;
      else
	unix_stream->priv->is_pipe_or_socket = FALSE;
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
      break;
    case PROP_CLOSE_FD:
      unix_stream->priv->close_fd = g_value_get_boolean (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
g_unix_output_stream_get_property (GObject    *object,
				   guint       prop_id,
				   GValue     *value,
				   GParamSpec *pspec)
{
  GUnixOutputStream *unix_stream;

  unix_stream = G_UNIX_OUTPUT_STREAM (object);

  switch (prop_id)
    {
    case PROP_FD:
      g_value_set_int (value, unix_stream->priv->fd);
      break;
    case PROP_CLOSE_FD:
      g_value_set_boolean (value, unix_stream->priv->close_fd);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
219 220 221
}

static void
222
g_unix_output_stream_init (GUnixOutputStream *unix_stream)
223
{
224
  unix_stream->priv = g_unix_output_stream_get_instance_private (unix_stream);
225 226 227
  unix_stream->priv->fd = -1;
  unix_stream->priv->close_fd = TRUE;
}
228 229

/**
230
 * g_unix_output_stream_new:
231 232
 * @fd: a UNIX file descriptor
 * @close_fd: %TRUE to close the file descriptor when done
233
 * 
234
 * Creates a new #GUnixOutputStream for the given @fd. 
235
 * 
236 237 238 239
 * If @close_fd, is %TRUE, the file descriptor will be closed when 
 * the output stream is destroyed.
 * 
 * Returns: a new #GOutputStream
240 241
 **/
GOutputStream *
242 243
g_unix_output_stream_new (gint     fd,
			  gboolean close_fd)
244
{
245
  GUnixOutputStream *stream;
246 247 248

  g_return_val_if_fail (fd != -1, NULL);

249 250 251 252
  stream = g_object_new (G_TYPE_UNIX_OUTPUT_STREAM,
			 "fd", fd,
			 "close-fd", close_fd,
			 NULL);
253 254 255 256
  
  return G_OUTPUT_STREAM (stream);
}

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
/**
 * g_unix_output_stream_set_close_fd:
 * @stream: a #GUnixOutputStream
 * @close_fd: %TRUE to close the file descriptor when done
 *
 * Sets whether the file descriptor of @stream shall be closed
 * when the stream is closed.
 *
 * Since: 2.20
 */
void
g_unix_output_stream_set_close_fd (GUnixOutputStream *stream,
                                   gboolean           close_fd)
{
  g_return_if_fail (G_IS_UNIX_OUTPUT_STREAM (stream));

  close_fd = close_fd != FALSE;
  if (stream->priv->close_fd != close_fd)
    {
      stream->priv->close_fd = close_fd;
      g_object_notify (G_OBJECT (stream), "close-fd");
    }
}

/**
 * g_unix_output_stream_get_close_fd:
 * @stream: a #GUnixOutputStream
 *
 * Returns whether the file descriptor of @stream will be
 * closed when the stream is closed.
 *
288
 * Returns: %TRUE if the file descriptor is closed when done
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
 *
 * Since: 2.20
 */
gboolean
g_unix_output_stream_get_close_fd (GUnixOutputStream *stream)
{
  g_return_val_if_fail (G_IS_UNIX_OUTPUT_STREAM (stream), FALSE);

  return stream->priv->close_fd;
}

/**
 * g_unix_output_stream_get_fd:
 * @stream: a #GUnixOutputStream
 *
 * Return the UNIX file descriptor that the stream writes to.
 *
306
 * Returns: The file descriptor of @stream
307 308 309 310 311 312 313 314 315 316 317
 *
 * Since: 2.20
 */
gint
g_unix_output_stream_get_fd (GUnixOutputStream *stream)
{
  g_return_val_if_fail (G_IS_UNIX_OUTPUT_STREAM (stream), -1);

  return stream->priv->fd;
}

318
static gssize
319 320 321 322 323
g_unix_output_stream_write (GOutputStream  *stream,
			    const void     *buffer,
			    gsize           count,
			    GCancellable   *cancellable,
			    GError        **error)
324
{
325
  GUnixOutputStream *unix_stream;
326
  gssize res = -1;
327
  GPollFD poll_fds[2];
328
  int nfds;
329 330
  int poll_ret;

331
  unix_stream = G_UNIX_OUTPUT_STREAM (stream);
332

333 334 335
  poll_fds[0].fd = unix_stream->priv->fd;
  poll_fds[0].events = G_IO_OUT;

336 337
  if (unix_stream->priv->is_pipe_or_socket &&
      g_cancellable_make_pollfd (cancellable, &poll_fds[1]))
338 339 340 341 342
    nfds = 2;
  else
    nfds = 1;

  while (1)
343
    {
344 345
      int errsv;

346
      poll_fds[0].revents = poll_fds[1].revents = 0;
347
      do
348 349 350 351 352
        {
          poll_ret = g_poll (poll_fds, nfds, -1);
          errsv = errno;
        }
      while (poll_ret == -1 && errsv == EINTR);
353

354 355 356
      if (poll_ret == -1)
	{
	  g_set_error (error, G_IO_ERROR,
357
		       g_io_error_from_errno (errsv),
358
		       _("Error writing to file descriptor: %s"),
359
		       g_strerror (errsv));
360
	  break;
361
	}
362

363
      if (g_cancellable_set_error_if_cancelled (cancellable, error))
364 365 366 367
	break;

      if (!poll_fds[0].revents)
	continue;
368

369
      res = write (unix_stream->priv->fd, buffer, count);
370
      errsv = errno;
371 372
      if (res == -1)
	{
373
	  if (errsv == EINTR || errsv == EAGAIN)
374
	    continue;
375

376
	  g_set_error (error, G_IO_ERROR,
377
		       g_io_error_from_errno (errsv),
378
		       _("Error writing to file descriptor: %s"),
379
		       g_strerror (errsv));
380
	}
381

382 383
      break;
    }
384

385 386
  if (nfds == 2)
    g_cancellable_release_fd (cancellable);
387 388 389 390
  return res;
}

static gboolean
391 392 393
g_unix_output_stream_close (GOutputStream  *stream,
			    GCancellable   *cancellable,
			    GError        **error)
394
{
395
  GUnixOutputStream *unix_stream;
396 397
  int res;

398
  unix_stream = G_UNIX_OUTPUT_STREAM (stream);
399

400
  if (!unix_stream->priv->close_fd)
401 402
    return TRUE;
  
403 404 405
  /* This might block during the close. Doesn't seem to be a way to avoid it though. */
  res = close (unix_stream->priv->fd);
  if (res == -1)
406
    {
407
      int errsv = errno;
408

409 410 411 412
      g_set_error (error, G_IO_ERROR,
		   g_io_error_from_errno (errsv),
		   _("Error closing file descriptor: %s"),
		   g_strerror (errsv));
413 414 415 416 417 418
    }

  return res != -1;
}

static void
419
g_unix_output_stream_close_async (GOutputStream       *stream,
420 421 422 423
				  int                  io_priority,
				  GCancellable        *cancellable,
				  GAsyncReadyCallback  callback,
				  gpointer             user_data)
424
{
425 426
  GTask *task;
  GError *error = NULL;
427

428
  task = g_task_new (stream, cancellable, callback, user_data);
429
  g_task_set_source_tag (task, g_unix_output_stream_close_async);
430
  g_task_set_priority (task, io_priority);
431

432 433 434 435 436
  if (g_unix_output_stream_close (stream, cancellable, &error))
    g_task_return_boolean (task, TRUE);
  else
    g_task_return_error (task, error);
  g_object_unref (task);
437 438 439
}

static gboolean
440 441 442
g_unix_output_stream_close_finish (GOutputStream  *stream,
				   GAsyncResult   *result,
				   GError        **error)
443
{
444 445 446
  g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);

  return g_task_propagate_boolean (G_TASK (result), error);
447
}
448

449 450 451 452 453 454
static gboolean
g_unix_output_stream_pollable_can_poll (GPollableOutputStream *stream)
{
  return G_UNIX_OUTPUT_STREAM (stream)->priv->is_pipe_or_socket;
}

455 456 457 458 459 460 461 462 463
static gboolean
g_unix_output_stream_pollable_is_writable (GPollableOutputStream *stream)
{
  GUnixOutputStream *unix_stream = G_UNIX_OUTPUT_STREAM (stream);
  GPollFD poll_fd;
  gint result;

  poll_fd.fd = unix_stream->priv->fd;
  poll_fd.events = G_IO_OUT;
464
  poll_fd.revents = 0;
465 466 467 468 469 470 471 472 473 474 475 476 477

  do
    result = g_poll (&poll_fd, 1, 0);
  while (result == -1 && errno == EINTR);

  return poll_fd.revents != 0;
}

static GSource *
g_unix_output_stream_pollable_create_source (GPollableOutputStream *stream,
					     GCancellable          *cancellable)
{
  GUnixOutputStream *unix_stream = G_UNIX_OUTPUT_STREAM (stream);
478
  GSource *inner_source, *cancellable_source, *pollable_source;
479 480 481

  pollable_source = g_pollable_source_new (G_OBJECT (stream));

482
  inner_source = g_unix_fd_source_new (unix_stream->priv->fd, G_IO_OUT);
483 484 485 486
  g_source_set_dummy_callback (inner_source);
  g_source_add_child_source (pollable_source, inner_source);
  g_source_unref (inner_source);

487 488 489 490 491 492 493 494
  if (cancellable)
    {
      cancellable_source = g_cancellable_source_new (cancellable);
      g_source_set_dummy_callback (cancellable_source);
      g_source_add_child_source (pollable_source, cancellable_source);
      g_source_unref (cancellable_source);
    }

495 496
  return pollable_source;
}