• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2006-2007 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.1 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, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: Alexander Larsson <alexl@redhat.com>
19  */
20 
21 #include "config.h"
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <fcntl.h>
29 
30 #include <glib.h>
31 #include <glib/gstdio.h>
32 #include <glib/glib-unix.h>
33 #include "gioerror.h"
34 #include "gunixinputstream.h"
35 #include "gcancellable.h"
36 #include "gasynchelper.h"
37 #include "gfiledescriptorbased.h"
38 #include "glibintl.h"
39 
40 
41 /**
42  * SECTION:gunixinputstream
43  * @short_description: Streaming input operations for UNIX file descriptors
44  * @include: gio/gunixinputstream.h
45  * @see_also: #GInputStream
46  *
47  * #GUnixInputStream implements #GInputStream for reading from a UNIX
48  * file descriptor, including asynchronous operations. (If the file
49  * descriptor refers to a socket or pipe, this will use poll() to do
50  * 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/gunixinputstream.h>` belongs to the UNIX-specific GIO
54  * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
55  * file when using it.
56  */
57 
58 enum {
59   PROP_0,
60   PROP_FD,
61   PROP_CLOSE_FD
62 };
63 
64 struct _GUnixInputStreamPrivate {
65   int fd;
66   guint close_fd : 1;
67   guint is_pipe_or_socket : 1;
68 };
69 
70 static void g_unix_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface);
71 static void g_unix_input_stream_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface);
72 
73 G_DEFINE_TYPE_WITH_CODE (GUnixInputStream, g_unix_input_stream, G_TYPE_INPUT_STREAM,
74                          G_ADD_PRIVATE (GUnixInputStream)
75 			 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
76 						g_unix_input_stream_pollable_iface_init)
77 			 G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED,
78 						g_unix_input_stream_file_descriptor_based_iface_init)
79 			 )
80 
81 static void     g_unix_input_stream_set_property (GObject              *object,
82 						  guint                 prop_id,
83 						  const GValue         *value,
84 						  GParamSpec           *pspec);
85 static void     g_unix_input_stream_get_property (GObject              *object,
86 						  guint                 prop_id,
87 						  GValue               *value,
88 						  GParamSpec           *pspec);
89 static gssize   g_unix_input_stream_read         (GInputStream         *stream,
90 						  void                 *buffer,
91 						  gsize                 count,
92 						  GCancellable         *cancellable,
93 						  GError              **error);
94 static gboolean g_unix_input_stream_close        (GInputStream         *stream,
95 						  GCancellable         *cancellable,
96 						  GError              **error);
97 static void     g_unix_input_stream_skip_async   (GInputStream         *stream,
98 						  gsize                 count,
99 						  int                   io_priority,
100 						  GCancellable         *cancellable,
101 						  GAsyncReadyCallback   callback,
102 						  gpointer              data);
103 static gssize   g_unix_input_stream_skip_finish  (GInputStream         *stream,
104 						  GAsyncResult         *result,
105 						  GError              **error);
106 static void     g_unix_input_stream_close_async  (GInputStream         *stream,
107 						  int                   io_priority,
108 						  GCancellable         *cancellable,
109 						  GAsyncReadyCallback   callback,
110 						  gpointer              data);
111 static gboolean g_unix_input_stream_close_finish (GInputStream         *stream,
112 						  GAsyncResult         *result,
113 						  GError              **error);
114 
115 static gboolean g_unix_input_stream_pollable_can_poll      (GPollableInputStream *stream);
116 static gboolean g_unix_input_stream_pollable_is_readable   (GPollableInputStream *stream);
117 static GSource *g_unix_input_stream_pollable_create_source (GPollableInputStream *stream,
118 							    GCancellable         *cancellable);
119 
120 static void
g_unix_input_stream_class_init(GUnixInputStreamClass * klass)121 g_unix_input_stream_class_init (GUnixInputStreamClass *klass)
122 {
123   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
124   GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
125 
126   gobject_class->get_property = g_unix_input_stream_get_property;
127   gobject_class->set_property = g_unix_input_stream_set_property;
128 
129   stream_class->read_fn = g_unix_input_stream_read;
130   stream_class->close_fn = g_unix_input_stream_close;
131   if (0)
132     {
133       /* TODO: Implement instead of using fallbacks */
134       stream_class->skip_async = g_unix_input_stream_skip_async;
135       stream_class->skip_finish = g_unix_input_stream_skip_finish;
136     }
137   stream_class->close_async = g_unix_input_stream_close_async;
138   stream_class->close_finish = g_unix_input_stream_close_finish;
139 
140   /**
141    * GUnixInputStream:fd:
142    *
143    * The file descriptor that the stream reads from.
144    *
145    * Since: 2.20
146    */
147   g_object_class_install_property (gobject_class,
148 				   PROP_FD,
149 				   g_param_spec_int ("fd",
150 						     P_("File descriptor"),
151 						     P_("The file descriptor to read from"),
152 						     G_MININT, G_MAXINT, -1,
153 						     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
154 
155   /**
156    * GUnixInputStream:close-fd:
157    *
158    * Whether to close the file descriptor when the stream is closed.
159    *
160    * Since: 2.20
161    */
162   g_object_class_install_property (gobject_class,
163 				   PROP_CLOSE_FD,
164 				   g_param_spec_boolean ("close-fd",
165 							 P_("Close file descriptor"),
166 							 P_("Whether to close the file descriptor when the stream is closed"),
167 							 TRUE,
168 							 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
169 }
170 
171 static void
g_unix_input_stream_pollable_iface_init(GPollableInputStreamInterface * iface)172 g_unix_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
173 {
174   iface->can_poll = g_unix_input_stream_pollable_can_poll;
175   iface->is_readable = g_unix_input_stream_pollable_is_readable;
176   iface->create_source = g_unix_input_stream_pollable_create_source;
177 }
178 
179 static void
g_unix_input_stream_file_descriptor_based_iface_init(GFileDescriptorBasedIface * iface)180 g_unix_input_stream_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface)
181 {
182   iface->get_fd = (int (*) (GFileDescriptorBased *))g_unix_input_stream_get_fd;
183 }
184 
185 static void
g_unix_input_stream_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)186 g_unix_input_stream_set_property (GObject         *object,
187 				  guint            prop_id,
188 				  const GValue    *value,
189 				  GParamSpec      *pspec)
190 {
191   GUnixInputStream *unix_stream;
192 
193   unix_stream = G_UNIX_INPUT_STREAM (object);
194 
195   switch (prop_id)
196     {
197     case PROP_FD:
198       unix_stream->priv->fd = g_value_get_int (value);
199       if (lseek (unix_stream->priv->fd, 0, SEEK_CUR) == -1 && errno == ESPIPE)
200 	unix_stream->priv->is_pipe_or_socket = TRUE;
201       else
202 	unix_stream->priv->is_pipe_or_socket = FALSE;
203       break;
204     case PROP_CLOSE_FD:
205       unix_stream->priv->close_fd = g_value_get_boolean (value);
206       break;
207     default:
208       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
209       break;
210     }
211 }
212 
213 static void
g_unix_input_stream_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)214 g_unix_input_stream_get_property (GObject    *object,
215 				  guint       prop_id,
216 				  GValue     *value,
217 				  GParamSpec *pspec)
218 {
219   GUnixInputStream *unix_stream;
220 
221   unix_stream = G_UNIX_INPUT_STREAM (object);
222 
223   switch (prop_id)
224     {
225     case PROP_FD:
226       g_value_set_int (value, unix_stream->priv->fd);
227       break;
228     case PROP_CLOSE_FD:
229       g_value_set_boolean (value, unix_stream->priv->close_fd);
230       break;
231     default:
232       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
233     }
234 }
235 
236 static void
g_unix_input_stream_init(GUnixInputStream * unix_stream)237 g_unix_input_stream_init (GUnixInputStream *unix_stream)
238 {
239   unix_stream->priv = g_unix_input_stream_get_instance_private (unix_stream);
240   unix_stream->priv->fd = -1;
241   unix_stream->priv->close_fd = TRUE;
242 }
243 
244 /**
245  * g_unix_input_stream_new:
246  * @fd: a UNIX file descriptor
247  * @close_fd: %TRUE to close the file descriptor when done
248  *
249  * Creates a new #GUnixInputStream for the given @fd.
250  *
251  * If @close_fd is %TRUE, the file descriptor will be closed
252  * when the stream is closed.
253  *
254  * Returns: a new #GUnixInputStream
255  **/
256 GInputStream *
g_unix_input_stream_new(gint fd,gboolean close_fd)257 g_unix_input_stream_new (gint     fd,
258 			 gboolean close_fd)
259 {
260   GUnixInputStream *stream;
261 
262   g_return_val_if_fail (fd != -1, NULL);
263 
264   stream = g_object_new (G_TYPE_UNIX_INPUT_STREAM,
265 			 "fd", fd,
266 			 "close-fd", close_fd,
267 			 NULL);
268 
269   return G_INPUT_STREAM (stream);
270 }
271 
272 /**
273  * g_unix_input_stream_set_close_fd:
274  * @stream: a #GUnixInputStream
275  * @close_fd: %TRUE to close the file descriptor when done
276  *
277  * Sets whether the file descriptor of @stream shall be closed
278  * when the stream is closed.
279  *
280  * Since: 2.20
281  */
282 void
g_unix_input_stream_set_close_fd(GUnixInputStream * stream,gboolean close_fd)283 g_unix_input_stream_set_close_fd (GUnixInputStream *stream,
284 				  gboolean          close_fd)
285 {
286   g_return_if_fail (G_IS_UNIX_INPUT_STREAM (stream));
287 
288   close_fd = close_fd != FALSE;
289   if (stream->priv->close_fd != close_fd)
290     {
291       stream->priv->close_fd = close_fd;
292       g_object_notify (G_OBJECT (stream), "close-fd");
293     }
294 }
295 
296 /**
297  * g_unix_input_stream_get_close_fd:
298  * @stream: a #GUnixInputStream
299  *
300  * Returns whether the file descriptor of @stream will be
301  * closed when the stream is closed.
302  *
303  * Returns: %TRUE if the file descriptor is closed when done
304  *
305  * Since: 2.20
306  */
307 gboolean
g_unix_input_stream_get_close_fd(GUnixInputStream * stream)308 g_unix_input_stream_get_close_fd (GUnixInputStream *stream)
309 {
310   g_return_val_if_fail (G_IS_UNIX_INPUT_STREAM (stream), FALSE);
311 
312   return stream->priv->close_fd;
313 }
314 
315 /**
316  * g_unix_input_stream_get_fd:
317  * @stream: a #GUnixInputStream
318  *
319  * Return the UNIX file descriptor that the stream reads from.
320  *
321  * Returns: The file descriptor of @stream
322  *
323  * Since: 2.20
324  */
325 gint
g_unix_input_stream_get_fd(GUnixInputStream * stream)326 g_unix_input_stream_get_fd (GUnixInputStream *stream)
327 {
328   g_return_val_if_fail (G_IS_UNIX_INPUT_STREAM (stream), -1);
329 
330   return stream->priv->fd;
331 }
332 
333 static gssize
g_unix_input_stream_read(GInputStream * stream,void * buffer,gsize count,GCancellable * cancellable,GError ** error)334 g_unix_input_stream_read (GInputStream  *stream,
335 			  void          *buffer,
336 			  gsize          count,
337 			  GCancellable  *cancellable,
338 			  GError       **error)
339 {
340   GUnixInputStream *unix_stream;
341   gssize res = -1;
342   GPollFD poll_fds[2];
343   int nfds;
344   int poll_ret;
345 
346   unix_stream = G_UNIX_INPUT_STREAM (stream);
347 
348   poll_fds[0].fd = unix_stream->priv->fd;
349   poll_fds[0].events = G_IO_IN;
350   if (unix_stream->priv->is_pipe_or_socket &&
351       g_cancellable_make_pollfd (cancellable, &poll_fds[1]))
352     nfds = 2;
353   else
354     nfds = 1;
355 
356   while (1)
357     {
358       int errsv;
359 
360       poll_fds[0].revents = poll_fds[1].revents = 0;
361       do
362         {
363           poll_ret = g_poll (poll_fds, nfds, -1);
364           errsv = errno;
365         }
366       while (poll_ret == -1 && errsv == EINTR);
367 
368       if (poll_ret == -1)
369 	{
370 	  g_set_error (error, G_IO_ERROR,
371 		       g_io_error_from_errno (errsv),
372 		       _("Error reading from file descriptor: %s"),
373 		       g_strerror (errsv));
374 	  break;
375 	}
376 
377       if (g_cancellable_set_error_if_cancelled (cancellable, error))
378 	break;
379 
380       if (!poll_fds[0].revents)
381 	continue;
382 
383       res = read (unix_stream->priv->fd, buffer, count);
384       if (res == -1)
385 	{
386           int errsv = errno;
387 
388 	  if (errsv == EINTR || errsv == EAGAIN)
389 	    continue;
390 
391 	  g_set_error (error, G_IO_ERROR,
392 		       g_io_error_from_errno (errsv),
393 		       _("Error reading from file descriptor: %s"),
394 		       g_strerror (errsv));
395 	}
396 
397       break;
398     }
399 
400   if (nfds == 2)
401     g_cancellable_release_fd (cancellable);
402   return res;
403 }
404 
405 static gboolean
g_unix_input_stream_close(GInputStream * stream,GCancellable * cancellable,GError ** error)406 g_unix_input_stream_close (GInputStream  *stream,
407 			   GCancellable  *cancellable,
408 			   GError       **error)
409 {
410   GUnixInputStream *unix_stream;
411   int res;
412 
413   unix_stream = G_UNIX_INPUT_STREAM (stream);
414 
415   if (!unix_stream->priv->close_fd)
416     return TRUE;
417 
418   /* This might block during the close. Doesn't seem to be a way to avoid it though. */
419   res = close (unix_stream->priv->fd);
420   if (res == -1)
421     {
422       int errsv = errno;
423 
424       g_set_error (error, G_IO_ERROR,
425 		   g_io_error_from_errno (errsv),
426 		   _("Error closing file descriptor: %s"),
427 		   g_strerror (errsv));
428     }
429 
430   return res != -1;
431 }
432 
433 static void
g_unix_input_stream_skip_async(GInputStream * stream,gsize count,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer data)434 g_unix_input_stream_skip_async (GInputStream        *stream,
435 				gsize                count,
436 				int                  io_priority,
437 				GCancellable        *cancellable,
438 				GAsyncReadyCallback  callback,
439 				gpointer             data)
440 {
441   g_warn_if_reached ();
442   /* TODO: Not implemented */
443 }
444 
445 static gssize
g_unix_input_stream_skip_finish(GInputStream * stream,GAsyncResult * result,GError ** error)446 g_unix_input_stream_skip_finish  (GInputStream  *stream,
447 				  GAsyncResult  *result,
448 				  GError       **error)
449 {
450   g_warn_if_reached ();
451   return 0;
452   /* TODO: Not implemented */
453 }
454 
455 static void
g_unix_input_stream_close_async(GInputStream * stream,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)456 g_unix_input_stream_close_async (GInputStream        *stream,
457 				 int                  io_priority,
458 				 GCancellable        *cancellable,
459 				 GAsyncReadyCallback  callback,
460 				 gpointer             user_data)
461 {
462   GTask *task;
463   GError *error = NULL;
464 
465   task = g_task_new (stream, cancellable, callback, user_data);
466   g_task_set_source_tag (task, g_unix_input_stream_close_async);
467   g_task_set_priority (task, io_priority);
468 
469   if (g_unix_input_stream_close (stream, cancellable, &error))
470     g_task_return_boolean (task, TRUE);
471   else
472     g_task_return_error (task, error);
473   g_object_unref (task);
474 }
475 
476 static gboolean
g_unix_input_stream_close_finish(GInputStream * stream,GAsyncResult * result,GError ** error)477 g_unix_input_stream_close_finish (GInputStream  *stream,
478 				  GAsyncResult  *result,
479 				  GError       **error)
480 {
481   g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
482 
483   return g_task_propagate_boolean (G_TASK (result), error);
484 }
485 
486 static gboolean
g_unix_input_stream_pollable_can_poll(GPollableInputStream * stream)487 g_unix_input_stream_pollable_can_poll (GPollableInputStream *stream)
488 {
489   return G_UNIX_INPUT_STREAM (stream)->priv->is_pipe_or_socket;
490 }
491 
492 static gboolean
g_unix_input_stream_pollable_is_readable(GPollableInputStream * stream)493 g_unix_input_stream_pollable_is_readable (GPollableInputStream *stream)
494 {
495   GUnixInputStream *unix_stream = G_UNIX_INPUT_STREAM (stream);
496   GPollFD poll_fd;
497   gint result;
498 
499   poll_fd.fd = unix_stream->priv->fd;
500   poll_fd.events = G_IO_IN;
501   poll_fd.revents = 0;
502 
503   do
504     result = g_poll (&poll_fd, 1, 0);
505   while (result == -1 && errno == EINTR);
506 
507   return poll_fd.revents != 0;
508 }
509 
510 static GSource *
g_unix_input_stream_pollable_create_source(GPollableInputStream * stream,GCancellable * cancellable)511 g_unix_input_stream_pollable_create_source (GPollableInputStream *stream,
512 					    GCancellable         *cancellable)
513 {
514   GUnixInputStream *unix_stream = G_UNIX_INPUT_STREAM (stream);
515   GSource *inner_source, *cancellable_source, *pollable_source;
516 
517   pollable_source = g_pollable_source_new (G_OBJECT (stream));
518 
519   inner_source = g_unix_fd_source_new (unix_stream->priv->fd, G_IO_IN);
520   g_source_set_dummy_callback (inner_source);
521   g_source_add_child_source (pollable_source, inner_source);
522   g_source_unref (inner_source);
523 
524   if (cancellable)
525     {
526       cancellable_source = g_cancellable_source_new (cancellable);
527       g_source_set_dummy_callback (cancellable_source);
528       g_source_add_child_source (pollable_source, cancellable_source);
529       g_source_unref (cancellable_source);
530     }
531 
532   return pollable_source;
533 }
534