2002-12-25 Anders Carlsson <andersca@codefactory.se>
[platform/upstream/dbus.git] / dbus / dbus-transport-unix.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-transport-unix.c UNIX socket subclasses of DBusTransport
3  *
4  * Copyright (C) 2002  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dbus-internals.h"
25 #include "dbus-connection-internal.h"
26 #include "dbus-transport-unix.h"
27 #include "dbus-transport-protected.h"
28 #include "dbus-watch.h"
29 #include <sys/types.h>
30 #include <sys/time.h>
31 #include <unistd.h>
32
33
34 /**
35  * @defgroup DBusTransportUnix DBusTransport implementations for UNIX
36  * @ingroup  DBusInternals
37  * @brief Implementation details of DBusTransport on UNIX
38  *
39  * @{
40  */
41
42 /**
43  * Opaque object representing a Unix file descriptor transport.
44  */
45 typedef struct DBusTransportUnix DBusTransportUnix;
46
47 /**
48  * Implementation details of DBusTransportUnix. All members are private.
49  */
50 struct DBusTransportUnix
51 {
52   DBusTransport base;                   /**< Parent instance */
53   int fd;                               /**< File descriptor. */
54   DBusWatch *watch;                     /**< Watch for readability. */
55   DBusWatch *write_watch;               /**< Watch for writability. */
56
57   int max_bytes_read_per_iteration;     /**< To avoid blocking too long. */
58   int max_bytes_written_per_iteration;  /**< To avoid blocking too long. */
59
60   int message_bytes_written;            /**< Number of bytes of current
61                                          *   outgoing message that have
62                                          *   been written.
63                                          */
64 };
65
66 static void
67 unix_finalize (DBusTransport *transport)
68 {
69   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
70   
71   _dbus_transport_finalize_base (transport);
72
73   if (unix_transport->watch)
74     {
75       _dbus_watch_invalidate (unix_transport->watch);
76       _dbus_watch_unref (unix_transport->watch);
77     }
78   
79   dbus_free (transport);
80 }
81
82 static void
83 do_io_error (DBusTransport *transport)
84 {
85   _dbus_transport_disconnect (transport);
86   _dbus_connection_transport_error (transport->connection,
87                                     DBUS_RESULT_DISCONNECTED);
88 }
89
90 static void
91 do_writing (DBusTransport *transport)
92 {
93   int total;
94   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
95   
96   total = 0;
97
98   while (_dbus_connection_have_messages_to_send (transport->connection))
99     {
100       int bytes_written;
101       DBusMessage *message;
102       const DBusString *header;
103       const DBusString *body;
104       int header_len, body_len;
105       
106       if (total > unix_transport->max_bytes_written_per_iteration)
107         {
108           _dbus_verbose ("%d bytes exceeds %d bytes written per iteration, returning\n",
109                          total, unix_transport->max_bytes_written_per_iteration);
110           goto out;
111         }
112       
113       message = _dbus_connection_get_message_to_send (transport->connection);
114       _dbus_assert (message != NULL);
115       _dbus_message_lock (message);
116
117       _dbus_message_get_network_data (message,
118                                       &header, &body);
119
120       header_len = _dbus_string_get_length (header);
121       body_len = _dbus_string_get_length (body);
122       
123       if (unix_transport->message_bytes_written < header_len)
124         {
125           bytes_written =
126             _dbus_write_two (unix_transport->fd,
127                              header,
128                              unix_transport->message_bytes_written,
129                              header_len - unix_transport->message_bytes_written,
130                              body,
131                              0, body_len);
132         }
133       else
134         {
135           bytes_written =
136             _dbus_write (unix_transport->fd,
137                          body,
138                          (unix_transport->message_bytes_written - header_len),
139                          body_len -
140                          (unix_transport->message_bytes_written - header_len));
141         }
142
143       if (bytes_written < 0)
144         {
145           /* EINTR already handled for us */
146           
147           if (errno == EAGAIN ||
148               errno == EWOULDBLOCK)
149             goto out;
150           else
151             {
152               _dbus_verbose ("Error writing to message bus: %s\n",
153                              _dbus_strerror (errno));
154               do_io_error (transport);
155               goto out;
156             }
157         }
158       else
159         {          
160           _dbus_verbose (" wrote %d bytes\n", bytes_written);
161           
162           total += bytes_written;
163           unix_transport->message_bytes_written += bytes_written;
164
165           _dbus_assert (unix_transport->message_bytes_written <=
166                         (header_len + body_len));
167           
168           if (unix_transport->message_bytes_written == (header_len + body_len))
169             {
170               _dbus_connection_message_sent (transport->connection,
171                                              message);
172               unix_transport->message_bytes_written = 0;
173             }
174         }
175     }
176
177  out:
178   return; /* I think some C compilers require a statement after a label */
179 }
180
181 static void
182 do_reading (DBusTransport *transport)
183 {
184   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
185   DBusString *buffer;
186   int buffer_len;
187   int bytes_read;
188   int total;
189   
190   total = 0;
191
192  again:
193   
194   if (total > unix_transport->max_bytes_read_per_iteration)
195     {
196       _dbus_verbose ("%d bytes exceeds %d bytes read per iteration, returning\n",
197                      total, unix_transport->max_bytes_read_per_iteration);
198       goto out;
199     }
200
201   _dbus_message_loader_get_buffer (transport->loader,
202                                    &buffer);
203
204   buffer_len = _dbus_string_get_length (buffer);  
205   
206   bytes_read = _dbus_read (unix_transport->fd,
207                            buffer, unix_transport->max_bytes_read_per_iteration);
208
209   _dbus_message_loader_return_buffer (transport->loader,
210                                       buffer,
211                                       bytes_read < 0 ? 0 : bytes_read);
212   
213   if (bytes_read < 0)
214     {
215       /* EINTR already handled for us */
216       
217       if (errno == EAGAIN ||
218           errno == EWOULDBLOCK)
219         goto out;
220       else
221         {
222           _dbus_verbose ("Error reading from message bus: %s\n",
223                          _dbus_strerror (errno));
224           do_io_error (transport);
225           goto out;
226         }
227     }
228   else if (bytes_read == 0)
229     {
230       _dbus_verbose ("Disconnected from message bus\n");
231       do_io_error (transport);
232       goto out;
233     }
234   else
235     {
236       DBusMessage *message;
237       
238       _dbus_verbose (" read %d bytes\n", bytes_read);
239       
240       total += bytes_read;      
241
242       /* Queue any messages */
243       while ((message = _dbus_message_loader_pop_message (transport->loader)))
244         {
245           _dbus_verbose ("queueing received message %p\n", message);
246           
247           _dbus_connection_queue_received_message (transport->connection,
248                                                    message);
249           dbus_message_unref (message);
250         }
251       
252       /* Try reading more data until we get EAGAIN and return, or
253        * exceed max bytes per iteration.  If in blocking mode of
254        * course we'll block instead of returning.
255        */
256       goto again;
257     }
258
259  out:
260   return; /* I think some C compilers require a statement after a label */
261 }
262
263 static void
264 unix_handle_watch (DBusTransport *transport,
265                    DBusWatch     *watch,
266                    unsigned int   flags)
267 {
268   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
269
270   _dbus_assert (watch == unix_transport->watch ||
271                 watch == unix_transport->write_watch);
272
273   if (flags & (DBUS_WATCH_HANGUP | DBUS_WATCH_ERROR))
274     {
275       _dbus_transport_disconnect (transport);
276       _dbus_connection_transport_error (transport->connection,
277                                         DBUS_RESULT_DISCONNECTED);
278       return;
279     }
280   
281   if (watch == unix_transport->watch &&
282       (flags & DBUS_WATCH_READABLE))
283     do_reading (transport);
284   else if (watch == unix_transport->write_watch &&
285            (flags & DBUS_WATCH_WRITABLE))
286     do_writing (transport); 
287 }
288
289 static void
290 unix_disconnect (DBusTransport *transport)
291 {
292   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
293
294   if (unix_transport->watch)
295     {
296       _dbus_connection_remove_watch (transport->connection,
297                                      unix_transport->watch);
298       _dbus_watch_invalidate (unix_transport->watch);
299       _dbus_watch_unref (unix_transport->watch);
300       unix_transport->watch = NULL;
301     }
302   
303   close (unix_transport->fd);
304   unix_transport->fd = -1;
305 }
306
307 static void
308 unix_connection_set (DBusTransport *transport)
309 {
310   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
311   DBusWatch *watch;
312
313   _dbus_assert (unix_transport->watch == NULL);
314   
315   watch = _dbus_watch_new (unix_transport->fd,
316                            DBUS_WATCH_READABLE);
317   
318   if (watch == NULL)
319     {
320       _dbus_transport_disconnect (transport);
321       return;
322     }
323   
324   if (!_dbus_connection_add_watch (transport->connection,
325                                    watch))
326     {
327       _dbus_transport_disconnect (transport);
328       return;
329     }
330
331   unix_transport->watch = watch;
332 }
333
334 static void
335 unix_messages_pending (DBusTransport *transport,
336                        int            messages_pending)
337 {
338   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
339
340   if (messages_pending > 0 &&
341       unix_transport->write_watch == NULL)
342     {
343       unix_transport->write_watch =
344         _dbus_watch_new (unix_transport->fd,
345                          DBUS_WATCH_WRITABLE);
346
347       /* we can maybe add it some other time, just silently bomb */
348       if (unix_transport->write_watch == NULL)
349         return;
350
351       if (!_dbus_connection_add_watch (transport->connection,
352                                        unix_transport->write_watch))
353         {
354           _dbus_watch_invalidate (unix_transport->write_watch);
355           _dbus_watch_unref (unix_transport->write_watch);
356           unix_transport->write_watch = NULL;
357         }
358     }
359   else if (messages_pending == 0 &&
360            unix_transport->write_watch != NULL)
361     {
362       _dbus_connection_remove_watch (transport->connection,
363                                      unix_transport->write_watch);
364       _dbus_watch_invalidate (unix_transport->write_watch);
365       _dbus_watch_unref (unix_transport->write_watch);
366       unix_transport->write_watch = NULL;
367     }
368 }
369
370 static  void
371 unix_do_iteration (DBusTransport *transport,
372                    unsigned int   flags,
373                    int            timeout_milliseconds)
374 {
375   DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
376   fd_set read_set;
377   fd_set write_set;
378   dbus_bool_t do_select;
379   
380   do_select = FALSE;
381   
382   FD_ZERO (&read_set);
383   if (flags & DBUS_ITERATION_DO_READING)
384     {
385       FD_SET (unix_transport->fd, &read_set);
386       do_select = TRUE;
387     }
388   
389   FD_ZERO (&write_set);
390   if (flags & DBUS_ITERATION_DO_WRITING)
391     {
392       FD_SET (unix_transport->fd, &write_set);
393       do_select = TRUE;
394     }
395
396   if (do_select)
397     {
398       fd_set err_set;
399       struct timeval timeout;
400       dbus_bool_t use_timeout;
401
402     again:
403       
404       FD_ZERO (&err_set);
405       FD_SET (unix_transport->fd, &err_set);
406   
407       if (flags & DBUS_ITERATION_BLOCK)
408         {
409           if (timeout_milliseconds >= 0)
410             {
411               timeout.tv_sec = timeout_milliseconds / 1000;
412               timeout.tv_usec = (timeout_milliseconds % 1000) * 1000;
413               
414               /* Always use timeout if one is passed in. */
415               use_timeout = TRUE;
416             }
417           else
418             {
419               use_timeout = FALSE; /* NULL timeout to block forever */
420             }
421         }
422       else
423         {
424           /* 0 timeout to not block */
425           timeout.tv_sec = 0;
426           timeout.tv_usec = 0;
427           use_timeout = TRUE;
428         }
429       
430       if (select (unix_transport->fd + 1, &read_set, &write_set, &err_set,
431                   use_timeout ? &timeout : NULL) >= 0)
432         {
433           if (FD_ISSET (unix_transport->fd, &err_set))
434             do_io_error (transport);
435           else
436             {
437               if (FD_ISSET (unix_transport->fd, &read_set))
438                 do_reading (transport);
439               if (FD_ISSET (unix_transport->fd, &write_set))
440                 do_writing (transport);
441             }
442         }
443       else if (errno == EINTR)
444         goto again;
445       else
446         {
447           _dbus_verbose ("Error from select(): %s\n",
448                          _dbus_strerror (errno));
449         }
450     }
451 }
452
453 static DBusTransportVTable unix_vtable = {
454   unix_finalize,
455   unix_handle_watch,
456   unix_disconnect,
457   unix_connection_set,
458   unix_messages_pending,
459   unix_do_iteration
460 };
461
462 /**
463  * Creates a new transport for the given file descriptor.  The file
464  * descriptor must be nonblocking (use _dbus_set_fd_nonblocking() to
465  * make it so). This function is shared by various transports that
466  * boil down to a full duplex file descriptor.
467  *
468  * @param fd the file descriptor.
469  * @returns the new transport, or #NULL if no memory.
470  */
471 DBusTransport*
472 _dbus_transport_new_for_fd (int fd)
473 {
474   DBusTransportUnix *unix_transport;
475   
476   unix_transport = dbus_new0 (DBusTransportUnix, 1);
477   if (unix_transport == NULL)
478     return NULL;
479
480   if (!_dbus_transport_init_base (&unix_transport->base,
481                                   &unix_vtable))
482     {
483       dbus_free (unix_transport);
484       return NULL;
485     }
486   
487   unix_transport->fd = fd;
488   unix_transport->message_bytes_written = 0;
489   
490   /* These values should probably be tunable or something. */     
491   unix_transport->max_bytes_read_per_iteration = 2048;
492   unix_transport->max_bytes_written_per_iteration = 2048;
493   
494   return (DBusTransport*) unix_transport;
495 }
496
497 /**
498  * Creates a new transport for the given Unix domain socket
499  * path.
500  *
501  * @param path the path to the domain socket.
502  * @param result location to store reason for failure.
503  * @returns a new transport, or #NULL on failure.
504  */
505 DBusTransport*
506 _dbus_transport_new_for_domain_socket (const char     *path,
507                                        DBusResultCode *result)
508 {
509   int fd;
510   DBusTransport *transport;
511
512   fd = _dbus_connect_unix_socket (path, result);
513   if (fd < 0)
514     return NULL;
515   
516   transport = _dbus_transport_new_for_fd (fd);
517   if (transport == NULL)
518     {
519       dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
520       close (fd);
521       fd = -1;
522     }
523
524   return transport;
525 }
526
527
528 /** @} */
529