http://mail.gnome.org/archives/gtk-devel-list/2007-October/msg00089.html
[platform/upstream/glib.git] / gio / gunixoutputstream.c
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 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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include <config.h>
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <poll.h>
32
33 #include <glib.h>
34 #include <glib/gstdio.h>
35 #include "gioerror.h"
36 #include "gunixoutputstream.h"
37 #include "gcancellable.h"
38 #include "gsimpleasyncresult.h"
39 #include "gasynchelper.h"
40 #include "glibintl.h"
41
42 #include "gioalias.h"
43
44 /**
45  * SECTION:gunixoutputstream
46  * @short_description: Streaming output operations for Unix file descriptors
47  * @see_also: #GOutputStream
48  *
49  * #GUnixOutputStream implements #GOutputStream for writing to a a
50  * unix file descriptor, including asynchronous operations. The file
51  * descriptor much be selectable, so it doesn't work with opened files.
52  **/
53
54 G_DEFINE_TYPE (GUnixOutputStream, g_unix_output_stream, G_TYPE_OUTPUT_STREAM);
55
56
57 struct _GUnixOutputStreamPrivate {
58   int fd;
59   gboolean close_fd_at_close;
60 };
61
62 static gssize   g_unix_output_stream_write        (GOutputStream        *stream,
63                                                    const void           *buffer,
64                                                    gsize                 count,
65                                                    GCancellable         *cancellable,
66                                                    GError              **error);
67 static gboolean g_unix_output_stream_close        (GOutputStream        *stream,
68                                                    GCancellable         *cancellable,
69                                                    GError              **error);
70 static void     g_unix_output_stream_write_async  (GOutputStream        *stream,
71                                                    const void           *buffer,
72                                                    gsize                 count,
73                                                    int                   io_priority,
74                                                    GCancellable         *cancellable,
75                                                    GAsyncReadyCallback   callback,
76                                                    gpointer              data);
77 static gssize   g_unix_output_stream_write_finish (GOutputStream        *stream,
78                                                    GAsyncResult         *result,
79                                                    GError              **error);
80 static void     g_unix_output_stream_close_async  (GOutputStream        *stream,
81                                                    int                   io_priority,
82                                                    GCancellable         *cancellable,
83                                                    GAsyncReadyCallback   callback,
84                                                    gpointer              data);
85 static gboolean g_unix_output_stream_close_finish (GOutputStream        *stream,
86                                                    GAsyncResult         *result,
87                                                    GError              **error);
88
89
90 static void
91 g_unix_output_stream_finalize (GObject *object)
92 {
93   GUnixOutputStream *stream;
94   
95   stream = G_UNIX_OUTPUT_STREAM (object);
96
97   if (G_OBJECT_CLASS (g_unix_output_stream_parent_class)->finalize)
98     (*G_OBJECT_CLASS (g_unix_output_stream_parent_class)->finalize) (object);
99 }
100
101 static void
102 g_unix_output_stream_class_init (GUnixOutputStreamClass *klass)
103 {
104   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
105   GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
106   
107   g_type_class_add_private (klass, sizeof (GUnixOutputStreamPrivate));
108   
109   gobject_class->finalize = g_unix_output_stream_finalize;
110
111   stream_class->write_fn = g_unix_output_stream_write;
112   stream_class->close_fn = g_unix_output_stream_close;
113   stream_class->write_async = g_unix_output_stream_write_async;
114   stream_class->write_finish = g_unix_output_stream_write_finish;
115   stream_class->close_async = g_unix_output_stream_close_async;
116   stream_class->close_finish = g_unix_output_stream_close_finish;
117 }
118
119 static void
120 g_unix_output_stream_init (GUnixOutputStream *unix_stream)
121 {
122   unix_stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (unix_stream,
123                                                    G_TYPE_UNIX_OUTPUT_STREAM,
124                                                    GUnixOutputStreamPrivate);
125 }
126
127
128 /**
129  * g_unix_output_stream_new:
130  * @fd: unix's file descriptor.
131  * @close_fd_at_close: a #gboolean.
132  * 
133  * Creates a new unix output stream for @fd. If @close_fd_at_close
134  * is %TRUE, the fd will be closed when the output stream is destroyed.
135  * 
136  * Returns: #GOutputStream. If @close_fd_at_close is %TRUE, then
137  * @fd will be closed when the #GOutputStream is closed.
138  **/
139 GOutputStream *
140 g_unix_output_stream_new (int      fd,
141                           gboolean close_fd_at_close)
142 {
143   GUnixOutputStream *stream;
144
145   g_return_val_if_fail (fd != -1, NULL);
146
147   stream = g_object_new (G_TYPE_UNIX_OUTPUT_STREAM, NULL);
148
149   stream->priv->fd = fd;
150   stream->priv->close_fd_at_close = close_fd_at_close;
151   
152   return G_OUTPUT_STREAM (stream);
153 }
154
155 static gssize
156 g_unix_output_stream_write (GOutputStream  *stream,
157                             const void     *buffer,
158                             gsize           count,
159                             GCancellable   *cancellable,
160                             GError        **error)
161 {
162   GUnixOutputStream *unix_stream;
163   gssize res;
164   struct pollfd poll_fds[2];
165   int poll_ret;
166   int cancel_fd;
167
168   unix_stream = G_UNIX_OUTPUT_STREAM (stream);
169
170   cancel_fd = g_cancellable_get_fd (cancellable);
171   if (cancel_fd != -1)
172     {
173       do
174         {
175           poll_fds[0].events = POLLOUT;
176           poll_fds[0].fd = unix_stream->priv->fd;
177           poll_fds[1].events = POLLIN;
178           poll_fds[1].fd = cancel_fd;
179           poll_ret = poll (poll_fds, 2, -1);
180         }
181       while (poll_ret == -1 && errno == EINTR);
182       
183       if (poll_ret == -1)
184         {
185           g_set_error (error, G_IO_ERROR,
186                        g_io_error_from_errno (errno),
187                        _("Error writing to unix: %s"),
188                        g_strerror (errno));
189           return -1;
190         }
191     }
192       
193   while (1)
194     {
195       if (g_cancellable_set_error_if_cancelled (cancellable, error))
196         return -1;
197
198       res = write (unix_stream->priv->fd, buffer, count);
199       if (res == -1)
200         {
201           if (errno == EINTR)
202             continue;
203           
204           g_set_error (error, G_IO_ERROR,
205                        g_io_error_from_errno (errno),
206                        _("Error writing to unix: %s"),
207                        g_strerror (errno));
208         }
209       
210       break;
211     }
212   
213   return res;
214 }
215
216 static gboolean
217 g_unix_output_stream_close (GOutputStream  *stream,
218                             GCancellable   *cancellable,
219                             GError        **error)
220 {
221   GUnixOutputStream *unix_stream;
222   int res;
223
224   unix_stream = G_UNIX_OUTPUT_STREAM (stream);
225
226   if (!unix_stream->priv->close_fd_at_close)
227     return TRUE;
228   
229   while (1)
230     {
231       /* This might block during the close. Doesn't seem to be a way to avoid it though. */
232       res = close (unix_stream->priv->fd);
233       if (res == -1)
234         {
235           g_set_error (error, G_IO_ERROR,
236                        g_io_error_from_errno (errno),
237                        _("Error closing unix: %s"),
238                        g_strerror (errno));
239         }
240       break;
241     }
242
243   return res != -1;
244 }
245
246 typedef struct {
247   gsize count;
248   const void *buffer;
249   GAsyncReadyCallback callback;
250   gpointer user_data;
251   GCancellable *cancellable;
252   GUnixOutputStream *stream;
253 } WriteAsyncData;
254
255 static gboolean
256 write_async_cb (WriteAsyncData *data,
257                 GIOCondition    condition,
258                 int             fd)
259 {
260   GSimpleAsyncResult *simple;
261   GError *error = NULL;
262   gssize count_written;
263
264   while (1)
265     {
266       if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
267         {
268           count_written = -1;
269           break;
270         }
271       
272       count_written = write (data->stream->priv->fd, data->buffer, data->count);
273       if (count_written == -1)
274         {
275           if (errno == EINTR)
276             continue;
277           
278           g_set_error (&error, G_IO_ERROR,
279                        g_io_error_from_errno (errno),
280                        _("Error reading from unix: %s"),
281                        g_strerror (errno));
282         }
283       break;
284     }
285
286   simple = g_simple_async_result_new (G_OBJECT (data->stream),
287                                       data->callback,
288                                       data->user_data,
289                                       g_unix_output_stream_write_async);
290   
291   g_simple_async_result_set_op_res_gssize (simple, count_written);
292
293   if (count_written == -1)
294     {
295       g_simple_async_result_set_from_error (simple, error);
296       g_error_free (error);
297     }
298
299   /* Complete immediately, not in idle, since we're already in a mainloop callout */
300   g_simple_async_result_complete (simple);
301   g_object_unref (simple);
302
303   return FALSE;
304 }
305
306 static void
307 g_unix_output_stream_write_async (GOutputStream       *stream,
308                                   const void          *buffer,
309                                   gsize                count,
310                                   int                  io_priority,
311                                   GCancellable        *cancellable,
312                                   GAsyncReadyCallback  callback,
313                                   gpointer             user_data)
314 {
315   GSource *source;
316   GUnixOutputStream *unix_stream;
317   WriteAsyncData *data;
318
319   unix_stream = G_UNIX_OUTPUT_STREAM (stream);
320
321   data = g_new0 (WriteAsyncData, 1);
322   data->count = count;
323   data->buffer = buffer;
324   data->callback = callback;
325   data->user_data = user_data;
326   data->cancellable = cancellable;
327   data->stream = unix_stream;
328
329   source = _g_fd_source_new (unix_stream->priv->fd,
330                              POLLOUT,
331                              cancellable);
332   
333   g_source_set_callback (source, (GSourceFunc)write_async_cb, data, g_free);
334   g_source_attach (source, NULL);
335   
336   g_source_unref (source);
337 }
338
339 static gssize
340 g_unix_output_stream_write_finish (GOutputStream  *stream,
341                                    GAsyncResult   *result,
342                                    GError        **error)
343 {
344   GSimpleAsyncResult *simple;
345   gssize nwritten;
346
347   simple = G_SIMPLE_ASYNC_RESULT (result);
348   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_unix_output_stream_write_async);
349   
350   nwritten = g_simple_async_result_get_op_res_gssize (simple);
351   return nwritten;
352 }
353
354 typedef struct {
355   GOutputStream *stream;
356   GAsyncReadyCallback callback;
357   gpointer user_data;
358 } CloseAsyncData;
359
360 static gboolean
361 close_async_cb (CloseAsyncData *data)
362 {
363   GUnixOutputStream *unix_stream;
364   GSimpleAsyncResult *simple;
365   GError *error = NULL;
366   gboolean result;
367   int res;
368
369   unix_stream = G_UNIX_OUTPUT_STREAM (data->stream);
370
371   if (!unix_stream->priv->close_fd_at_close)
372     {
373       result = TRUE;
374       goto out;
375     }
376   
377   while (1)
378     {
379       res = close (unix_stream->priv->fd);
380       if (res == -1)
381         {
382           g_set_error (&error, G_IO_ERROR,
383                        g_io_error_from_errno (errno),
384                        _("Error closing unix: %s"),
385                        g_strerror (errno));
386         }
387       break;
388     }
389   
390   result = res != -1;
391   
392  out:
393   simple = g_simple_async_result_new (G_OBJECT (data->stream),
394                                       data->callback,
395                                       data->user_data,
396                                       g_unix_output_stream_close_async);
397
398   if (!result)
399     {
400       g_simple_async_result_set_from_error (simple, error);
401       g_error_free (error);
402     }
403
404   /* Complete immediately, not in idle, since we're already in a mainloop callout */
405   g_simple_async_result_complete (simple);
406   g_object_unref (simple);
407   
408   return FALSE;
409 }
410
411 static void
412 g_unix_output_stream_close_async (GOutputStream        *stream,
413                                   int                  io_priority,
414                                   GCancellable        *cancellable,
415                                   GAsyncReadyCallback  callback,
416                                   gpointer             user_data)
417 {
418   GSource *idle;
419   CloseAsyncData *data;
420
421   data = g_new0 (CloseAsyncData, 1);
422
423   data->stream = stream;
424   data->callback = callback;
425   data->user_data = user_data;
426   
427   idle = g_idle_source_new ();
428   g_source_set_callback (idle, (GSourceFunc)close_async_cb, data, g_free);
429   g_source_attach (idle, NULL);
430   g_source_unref (idle);
431 }
432
433 static gboolean
434 g_unix_output_stream_close_finish (GOutputStream  *stream,
435                                    GAsyncResult   *result,
436                                    GError        **error)
437 {
438   /* Failures handled in generic close_finish code */
439   return TRUE;
440 }
441
442 #define __G_UNIX_OUTPUT_STREAM_C__
443 #include "gioaliasdef.c"