1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
4 * Copyright 2002-2004 Wilmer van der Gaast and others *
5 \********************************************************************/
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License with
21 the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22 if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23 Suite 330, Boston, MA 02111-1307 USA
29 #include "protocols/nogaim.h"
36 static gboolean
bitlbee_io_new_client( gpointer data
, gint fd
, b_input_condition condition
);
38 static gboolean
try_listen( struct addrinfo
*res
)
42 global
.listen_socket
= socket( res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
43 if( global
.listen_socket
< 0 )
45 log_error( "socket" );
50 if( res
->ai_family
== AF_INET6
)
53 setsockopt( global
.listen_socket
, IPPROTO_IPV6
, IPV6_V6ONLY
,
54 (char *) &i
, sizeof( i
) );
58 /* TIME_WAIT (?) sucks.. */
60 setsockopt( global
.listen_socket
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof( i
) );
62 i
= bind( global
.listen_socket
, res
->ai_addr
, res
->ai_addrlen
);
65 closesocket( global
.listen_socket
);
66 global
.listen_socket
= -1;
75 int bitlbee_daemon_init()
77 struct addrinfo
*res
, hints
, *addrinfo_bind
;
81 log_link( LOGLVL_ERROR
, LOGOUTPUT_CONSOLE
);
82 log_link( LOGLVL_WARNING
, LOGOUTPUT_CONSOLE
);
84 memset( &hints
, 0, sizeof( hints
) );
85 hints
.ai_family
= PF_UNSPEC
;
86 hints
.ai_socktype
= SOCK_STREAM
;
87 hints
.ai_flags
= AI_PASSIVE
89 /* Disabled as it may be doing more harm than good: this flag
90 ignores IPv6 addresses on lo (which seems reasonable), but
91 the result is that some clients (including irssi) try to
92 connect to ::1 and fail.
97 i
= getaddrinfo( global
.conf
->iface_in
, global
.conf
->port
, &hints
, &addrinfo_bind
);
100 log_message( LOGLVL_ERROR
, "Couldn't parse address `%s': %s",
101 global
.conf
->iface_in
, gai_strerror(i
) );
105 global
.listen_socket
= -1;
107 /* Try IPv6 first (which will become an IPv6+IPv4 socket). */
108 for( res
= addrinfo_bind
; res
; res
= res
->ai_next
)
109 if( res
->ai_family
== AF_INET6
&& try_listen( res
) )
112 /* The rest (so IPv4, I guess). */
114 for( res
= addrinfo_bind
; res
; res
= res
->ai_next
)
115 if( res
->ai_family
!= AF_INET6
&& try_listen( res
) )
118 freeaddrinfo( addrinfo_bind
);
120 i
= listen( global
.listen_socket
, 10 );
123 log_error( "listen" );
127 global
.listen_watch_source_id
= b_input_add( global
.listen_socket
, B_EV_IO_READ
, bitlbee_io_new_client
, NULL
);
130 if( !global
.conf
->nofork
)
143 /* Don't use i, just make gcc happy. :-/ */
145 if( getenv( "_BITLBEE_RESTART_STATE" ) == NULL
)
146 for( i
= 0; i
< 3; i
++ )
147 if( close( i
) == 0 )
149 /* Keep something bogus on those fd's just in case. */
150 open( "/dev/null", O_WRONLY
);
155 if( global
.conf
->runmode
== RUNMODE_FORKDAEMON
)
156 ipc_master_load_state( getenv( "_BITLBEE_RESTART_STATE" ) );
158 if( global
.conf
->runmode
== RUNMODE_DAEMON
|| global
.conf
->runmode
== RUNMODE_FORKDAEMON
)
159 ipc_master_listen_socket();
162 if( ( fp
= fopen( global
.conf
->pidfile
, "w" ) ) )
164 fprintf( fp
, "%d\n", (int) getpid() );
169 log_message( LOGLVL_WARNING
, "Warning: Couldn't write PID to `%s'", global
.conf
->pidfile
);
173 if( !global
.conf
->nofork
)
175 log_link( LOGLVL_ERROR
, LOGOUTPUT_SYSLOG
);
176 log_link( LOGLVL_WARNING
, LOGOUTPUT_SYSLOG
);
182 int bitlbee_inetd_init()
190 gboolean
bitlbee_io_current_client_read( gpointer data
, gint fd
, b_input_condition cond
)
196 st
= read( irc
->fd
, line
, sizeof( line
) - 1 );
199 irc_abort( irc
, 1, "Connection reset by peer" );
204 if( sockerr_again() )
210 irc_abort( irc
, 1, "Read error: %s", strerror( errno
) );
216 if( irc
->readbuffer
== NULL
)
218 irc
->readbuffer
= g_strdup( line
);
222 irc
->readbuffer
= g_renew( char, irc
->readbuffer
, strlen( irc
->readbuffer
) + strlen ( line
) + 1 );
223 strcpy( ( irc
->readbuffer
+ strlen( irc
->readbuffer
) ), line
);
228 /* Normally, irc_process() shouldn't call irc_free() but irc_abort(). Just in case: */
229 if( !g_slist_find( irc_connection_list
, irc
) )
231 log_message( LOGLVL_WARNING
, "Abnormal termination of connection with fd %d.", fd
);
235 /* Very naughty, go read the RFCs! >:) */
236 if( irc
->readbuffer
&& ( strlen( irc
->readbuffer
) > 1024 ) )
238 irc_abort( irc
, 0, "Maximum line length exceeded" );
245 gboolean
bitlbee_io_current_client_write( gpointer data
, gint fd
, b_input_condition cond
)
251 if( irc
->sendbuffer
== NULL
)
254 size
= strlen( irc
->sendbuffer
);
255 st
= write( irc
->fd
, irc
->sendbuffer
, size
);
257 if( st
== 0 || ( st
< 0 && !sockerr_again() ) )
259 irc_abort( irc
, 1, "Write error: %s", strerror( errno
) );
262 else if( st
< 0 ) /* && sockerr_again() */
269 if( irc
->status
& USTATUS_SHUTDOWN
)
275 g_free( irc
->sendbuffer
);
276 irc
->sendbuffer
= NULL
;
277 irc
->w_watch_source_id
= 0;
284 temp
= g_strdup( irc
->sendbuffer
+ st
);
285 g_free( irc
->sendbuffer
);
286 irc
->sendbuffer
= temp
;
292 static gboolean
bitlbee_io_new_client( gpointer data
, gint fd
, b_input_condition condition
)
294 socklen_t size
= sizeof( struct sockaddr_in
);
295 struct sockaddr_in conn_info
;
296 int new_socket
= accept( global
.listen_socket
, (struct sockaddr
*) &conn_info
, &size
);
298 if( new_socket
== -1 )
300 log_message( LOGLVL_WARNING
, "Could not accept new connection: %s", strerror( errno
) );
305 if( global
.conf
->runmode
== RUNMODE_FORKDAEMON
)
307 pid_t client_pid
= 0;
310 if( socketpair( AF_UNIX
, SOCK_STREAM
, 0, fds
) == -1 )
312 log_message( LOGLVL_WARNING
, "Could not create IPC socket for client: %s", strerror( errno
) );
313 fds
[0] = fds
[1] = -1;
316 sock_make_nonblocking( fds
[0] );
317 sock_make_nonblocking( fds
[1] );
321 if( client_pid
> 0 && fds
[0] != -1 )
323 struct bitlbee_child
*child
;
325 /* TODO: Stuff like this belongs in ipc.c. */
326 child
= g_new0( struct bitlbee_child
, 1 );
327 child
->pid
= client_pid
;
328 child
->ipc_fd
= fds
[0];
329 child
->ipc_inpa
= b_input_add( child
->ipc_fd
, B_EV_IO_READ
, ipc_master_read
, child
);
331 child_list
= g_slist_append( child_list
, child
);
333 log_message( LOGLVL_INFO
, "Creating new subprocess with pid %d.", (int) client_pid
);
335 /* Close some things we don't need in the parent process. */
339 else if( client_pid
== 0 )
343 /* Since we're fork()ing here, let's make sure we won't
344 get the same random numbers as the parent/siblings. */
345 srand( time( NULL
) ^ getpid() );
349 /* Close the listening socket, we're a client. */
350 close( global
.listen_socket
);
351 b_event_remove( global
.listen_watch_source_id
);
353 /* Make the connection. */
354 irc
= irc_new( new_socket
);
356 /* We can store the IPC fd there now. */
357 global
.listen_socket
= fds
[1];
358 global
.listen_watch_source_id
= b_input_add( fds
[1], B_EV_IO_READ
, ipc_child_read
, irc
);
362 ipc_master_free_all();
368 log_message( LOGLVL_INFO
, "Creating new connection with fd %d.", new_socket
);
369 irc_new( new_socket
);
375 gboolean
bitlbee_shutdown( gpointer data
, gint fd
, b_input_condition cond
)
377 /* Try to save data for all active connections (if desired). */
378 while( irc_connection_list
!= NULL
)
379 irc_abort( irc_connection_list
->data
, TRUE
,
380 "BitlBee server shutting down" );
382 /* We'll only reach this point when not running in inetd mode: */