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