source: lib/events_libevent.c @ 9ff5737

Last change on this file since 9ff5737 was 9ff5737, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-11-29T21:55:14Z

printf() in daemons considered harmful.

  • Property mode set to 100644
File size: 7.2 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2006 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/*
8 * Event handling (using libevent)
9 */
10
11/*
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  GNU General Public License for more details.
21
22  You should have received a copy of the GNU General Public License with
23  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
24  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
25  Suite 330, Boston, MA  02111-1307  USA
26*/
27
28#define BITLBEE_CORE
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#include <sys/types.h>
34#include <sys/time.h>
35#include <event.h>
36#include "proxy.h"
37
38static void b_main_restart();
39static guint id_next = 1;
40static GHashTable *id_hash;
41static int quitting = 0;
42
43/* Since libevent doesn't handle two event handlers for one fd-condition
44   very well (which happens sometimes when BitlBee changes event handlers
45   for a combination), let's buid some indexes so we can delete them here
46   already, just in time. */
47static GHashTable *read_hash;
48static GHashTable *write_hash;
49
50struct event_base *leh;
51struct event_base *old_leh;
52
53struct b_event_data
54{
55        guint id;
56        struct event evinfo;
57        gint timeout;
58        b_event_handler function;
59        void *data;
60};
61
62void b_main_init()
63{
64        if( leh != NULL )
65        {
66                /* Clean up the hash tables? */
67               
68                b_main_restart();
69                old_leh = leh;
70        }
71       
72        leh = event_init();
73       
74        id_hash = g_hash_table_new( g_int_hash, g_int_equal );
75        read_hash = g_hash_table_new( g_int_hash, g_int_equal );
76        write_hash = g_hash_table_new( g_int_hash, g_int_equal );
77}
78
79void b_main_run()
80{
81        /* This while loop is necessary to exit the event loop and start a
82           different one (necessary for ForkDaemon mode). */
83        while( event_base_dispatch( leh ) == 0 && !quitting )
84        {
85                if( old_leh != NULL )
86                {
87                        /* For some reason this just isn't allowed...
88                           Possibly a bug in older versions, will see later.
89                        event_base_free( old_leh ); */
90                        old_leh = NULL;
91                }
92               
93                event_debug( "New event loop.\n" );
94        }
95}
96
97static void b_main_restart()
98{
99        struct timeval tv;
100       
101        memset( &tv, 0, sizeof( struct timeval ) );
102        event_base_loopexit( leh, &tv );
103       
104        event_debug( "b_main_restart()\n" );
105}
106
107void b_main_quit()
108{
109        /* Tell b_main_run() that it shouldn't restart the loop. Also,
110           libevent sometimes generates events before really quitting,
111           we want to stop them. */
112        quitting = 1;
113       
114        b_main_restart();
115}
116
117static void b_event_passthrough( int fd, short event, void *data )
118{
119        struct b_event_data *b_ev = data;
120        b_input_condition cond = 0;
121        int id;
122       
123        if( fd >= 0 )
124        {
125                if( event & EV_READ )
126                        cond |= GAIM_INPUT_READ;
127                if( event & EV_WRITE )
128                        cond |= GAIM_INPUT_WRITE;
129        }
130       
131        event_debug( "b_event_passthrough( %d, %d, 0x%x ) (%d)\n", fd, event, (int) data, b_ev->id );
132       
133        /* Since the called function might cancel this handler already
134           (which free()s b_ev), we have to remember the ID here. */
135        id = b_ev->id;
136       
137        if( quitting )
138        {
139                b_event_remove( id );
140                return;
141        }
142       
143        if( !b_ev->function( b_ev->data, fd, cond ) )
144        {
145                event_debug( "Handler returned FALSE: " );
146                b_event_remove( id );
147        }
148        else if( fd == -1 )
149        {
150                struct timeval tv;
151               
152                tv.tv_sec = b_ev->timeout / 1000;
153                tv.tv_usec = ( b_ev->timeout % 1000 ) * 1000;
154               
155                evtimer_add( &b_ev->evinfo, &tv );
156        }
157}
158
159gint b_input_add( gint fd, b_input_condition condition, b_event_handler function, gpointer data )
160{
161        struct b_event_data *b_ev;
162       
163        event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data );
164       
165        if( ( condition & GAIM_INPUT_READ  && ( b_ev = g_hash_table_lookup( read_hash,  &fd ) ) ) ||
166            ( condition & GAIM_INPUT_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) )
167        {
168                /* We'll stick with this libevent entry, but give it a new BitlBee id. */
169                g_hash_table_remove( id_hash, &b_ev->id );
170               
171                event_debug( "(replacing old handler (id = %d)) = %d\n", b_ev->id, id_next );
172               
173                b_ev->id = id_next++;
174                b_ev->function = function;
175                b_ev->data = data;
176        }
177        else
178        {
179                GIOCondition out_cond;
180               
181                event_debug( "(new) = %d\n", id_next );
182               
183                b_ev = g_new0( struct b_event_data, 1 );
184                b_ev->id = id_next++;
185                b_ev->function = function;
186                b_ev->data = data;
187               
188                out_cond = EV_PERSIST;
189                if( condition & GAIM_INPUT_READ )
190                        out_cond |= EV_READ;
191                if( condition & GAIM_INPUT_WRITE )
192                        out_cond |= EV_WRITE;
193               
194                event_set( &b_ev->evinfo, fd, out_cond, b_event_passthrough, b_ev );
195                event_add( &b_ev->evinfo, NULL );
196               
197                if( out_cond & EV_READ )
198                        g_hash_table_insert( read_hash, &b_ev->evinfo.ev_fd, b_ev );
199                if( out_cond & EV_WRITE )
200                        g_hash_table_insert( write_hash, &b_ev->evinfo.ev_fd, b_ev );
201        }
202       
203        g_hash_table_insert( id_hash, &b_ev->id, b_ev );
204        return b_ev->id;
205}
206
207/* TODO: Persistence for timers! */
208gint b_timeout_add( gint timeout, b_event_handler function, gpointer data )
209{
210        struct b_event_data *b_ev = g_new0( struct b_event_data, 1 );
211        struct timeval tv;
212       
213        b_ev->id = id_next++;
214        b_ev->timeout = timeout;
215        b_ev->function = function;
216        b_ev->data = data;
217       
218        tv.tv_sec = timeout / 1000;
219        tv.tv_usec = ( timeout % 1000 ) * 1000;
220       
221        evtimer_set( &b_ev->evinfo, b_event_passthrough, b_ev );
222        evtimer_add( &b_ev->evinfo, &tv );
223       
224        event_debug( "b_timeout_add( %d, 0x%x, 0x%x ) = %d\n", timeout, function, data, b_ev->id );
225       
226        g_hash_table_insert( id_hash, &b_ev->id, b_ev );
227       
228        return b_ev->id;
229}
230
231void b_event_remove( gint id )
232{
233        struct b_event_data *b_ev = g_hash_table_lookup( id_hash, &id );
234       
235        event_debug( "b_event_remove( %d )\n", id );
236        if( b_ev )
237        {
238                g_hash_table_remove( id_hash, &b_ev->id );
239                if( b_ev->evinfo.ev_fd >= 0 )
240                {
241                        if( b_ev->evinfo.ev_events & EV_READ )
242                                g_hash_table_remove( read_hash, &b_ev->evinfo.ev_fd );
243                        if( b_ev->evinfo.ev_events & EV_WRITE )
244                                g_hash_table_remove( write_hash, &b_ev->evinfo.ev_fd );
245                }
246               
247                event_del( &b_ev->evinfo );
248                g_free( b_ev );
249        }
250        else
251        {
252                event_debug( "Already removed?\n" );
253        }
254}
255
256void closesocket( int fd )
257{
258        struct b_event_data *b_ev;
259       
260        /* Since epoll() (the main reason we use libevent) automatically removes sockets from
261           the epoll() list when a socket gets closed and some modules have a habit of
262           closing sockets before removing event handlers, our and libevent's administration
263           get a little bit messed up. So this little function will remove the handlers
264           properly before closing a socket. */
265       
266        if( ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) )
267        {
268                event_debug( "Warning: fd %d still had a read event handler when shutting down.\n", fd );
269                b_event_remove( b_ev->id );
270        }
271        if( ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) )
272        {
273                event_debug( "Warning: fd %d still had a write event handler when shutting down.\n", fd );
274                b_event_remove( b_ev->id );
275        }
276       
277        close( fd );
278}
Note: See TracBrowser for help on using the repository browser.