source: lib/events_libevent.c @ f1e7407

Last change on this file since f1e7407 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
RevLine 
[13cc96c]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>
[3f199fc]32#include <unistd.h>
[13cc96c]33#include <sys/types.h>
34#include <sys/time.h>
35#include <event.h>
[9ff5737]36#include "proxy.h"
[13cc96c]37
[b6a2373]38static void b_main_restart();
39static guint id_next = 1;
[13cc96c]40static GHashTable *id_hash;
[cdca30b]41static int quitting = 0;
[13cc96c]42
[782d988]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
[b6a2373]50struct event_base *leh;
51struct event_base *old_leh;
52
[13cc96c]53struct b_event_data
54{
55        guint id;
56        struct event evinfo;
[19ac9c5]57        gint timeout;
[13cc96c]58        b_event_handler function;
59        void *data;
60};
61
62void b_main_init()
63{
[b6a2373]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();
[13cc96c]73       
74        id_hash = g_hash_table_new( g_int_hash, g_int_equal );
[782d988]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 );
[13cc96c]77}
78
79void b_main_run()
80{
[b6a2373]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               
[9ff5737]93                event_debug( "New event loop.\n" );
[b6a2373]94        }
[13cc96c]95}
96
[b6a2373]97static void b_main_restart()
[13cc96c]98{
99        struct timeval tv;
100       
[b6a2373]101        memset( &tv, 0, sizeof( struct timeval ) );
102        event_base_loopexit( leh, &tv );
103       
[9ff5737]104        event_debug( "b_main_restart()\n" );
[b6a2373]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,
[cdca30b]111           we want to stop them. */
112        quitting = 1;
113       
[b6a2373]114        b_main_restart();
[13cc96c]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;
[fc2ee0f]121        int id;
[09f8cd1]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       
[b642f381]131        event_debug( "b_event_passthrough( %d, %d, 0x%x ) (%d)\n", fd, event, (int) data, b_ev->id );
132       
[fc2ee0f]133        /* Since the called function might cancel this handler already
[04026d4]134           (which free()s b_ev), we have to remember the ID here. */
[fc2ee0f]135        id = b_ev->id;
136       
[cdca30b]137        if( quitting )
138        {
139                b_event_remove( id );
140                return;
141        }
142       
[13cc96c]143        if( !b_ev->function( b_ev->data, fd, cond ) )
[b642f381]144        {
145                event_debug( "Handler returned FALSE: " );
[fc2ee0f]146                b_event_remove( id );
[b642f381]147        }
[19ac9c5]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        }
[13cc96c]157}
158
[b642f381]159gint b_input_add( gint fd, b_input_condition condition, b_event_handler function, gpointer data )
[13cc96c]160{
[782d988]161        struct b_event_data *b_ev;
[13cc96c]162       
[782d988]163        event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data );
[13cc96c]164       
[782d988]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        }
[b642f381]202       
[13cc96c]203        g_hash_table_insert( id_hash, &b_ev->id, b_ev );
204        return b_ev->id;
205}
206
[09f8cd1]207/* TODO: Persistence for timers! */
[13cc96c]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       
[09f8cd1]213        b_ev->id = id_next++;
[19ac9c5]214        b_ev->timeout = timeout;
[13cc96c]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 );
[09f8cd1]222        evtimer_add( &b_ev->evinfo, &tv );
[13cc96c]223       
[782d988]224        event_debug( "b_timeout_add( %d, 0x%x, 0x%x ) = %d\n", timeout, function, data, b_ev->id );
[b642f381]225       
[13cc96c]226        g_hash_table_insert( id_hash, &b_ev->id, b_ev );
227       
228        return b_ev->id;
229}
230
[b642f381]231void b_event_remove( gint id )
[13cc96c]232{
[b642f381]233        struct b_event_data *b_ev = g_hash_table_lookup( id_hash, &id );
[13cc96c]234       
[b642f381]235        event_debug( "b_event_remove( %d )\n", id );
[13cc96c]236        if( b_ev )
237        {
[09f8cd1]238                g_hash_table_remove( id_hash, &b_ev->id );
[782d988]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 );
[13cc96c]248                g_free( b_ev );
249        }
[b642f381]250        else
251        {
[782d988]252                event_debug( "Already removed?\n" );
[b642f381]253        }
[13cc96c]254}
255
[3f199fc]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       
[b8b8c6c]266        if( ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) )
[df70eafa]267        {
[5330e3d]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        }
[b8b8c6c]271        if( ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) )
[5330e3d]272        {
273                event_debug( "Warning: fd %d still had a write event handler when shutting down.\n", fd );
[3f199fc]274                b_event_remove( b_ev->id );
[df70eafa]275        }
[3f199fc]276       
277        close( fd );
278}
Note: See TracBrowser for help on using the repository browser.