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
93 i
= getaddrinfo( global
.conf
->iface_in
, global
.conf
->port
, &hints
, &addrinfo_bind
);
96 log_message( LOGLVL_ERROR
, "Couldn't parse address `%s': %s",
97 global
.conf
->iface_in
, gai_strerror(i
) );
101 global
.listen_socket
= -1;
103 /* Try IPv6 first (which will become an IPv6+IPv4 socket). */
104 for( res
= addrinfo_bind
; res
; res
= res
->ai_next
)
105 if( res
->ai_family
== AF_INET6
&& try_listen( res
) )
108 /* The rest (so IPv4, I guess). */
110 for( res
= addrinfo_bind
; res
; res
= res
->ai_next
)
111 if( res
->ai_family
!= AF_INET6
&& try_listen( res
) )
114 freeaddrinfo( addrinfo_bind
);
116 i
= listen( global
.listen_socket
, 10 );
119 log_error( "listen" );
123 global
.listen_watch_source_id
= b_input_add( global
.listen_socket
, GAIM_INPUT_READ
, bitlbee_io_new_client
, NULL
);
126 if( !global
.conf
->nofork
)
140 if( getenv( "_BITLBEE_RESTART_STATE" ) == NULL
)
141 for( i
= 0; i
< 3; i
++ )
142 if( close( i
) == 0 )
144 /* Keep something bogus on those fd's just in case. */
145 open( "/dev/null", O_WRONLY
);
150 if( global
.conf
->runmode
== RUNMODE_FORKDAEMON
)
151 ipc_master_load_state( getenv( "_BITLBEE_RESTART_STATE" ) );
153 if( global
.conf
->runmode
== RUNMODE_DAEMON
|| global
.conf
->runmode
== RUNMODE_FORKDAEMON
)
154 ipc_master_listen_socket();
157 if( ( fp
= fopen( global
.conf
->pidfile
, "w" ) ) )
159 fprintf( fp
, "%d\n", (int) getpid() );
164 log_message( LOGLVL_WARNING
, "Warning: Couldn't write PID to `%s'", global
.conf
->pidfile
);
168 if( !global
.conf
->nofork
)
170 log_link( LOGLVL_ERROR
, LOGOUTPUT_SYSLOG
);
171 log_link( LOGLVL_WARNING
, LOGOUTPUT_SYSLOG
);
177 int bitlbee_inetd_init()
185 gboolean
bitlbee_io_current_client_read( gpointer data
, gint fd
, b_input_condition cond
)
191 st
= read( irc
->fd
, line
, sizeof( line
) - 1 );
194 irc_abort( irc
, 1, "Connection reset by peer" );
199 if( sockerr_again() )
205 irc_abort( irc
, 1, "Read error: %s", strerror( errno
) );
211 if( irc
->readbuffer
== NULL
)
213 irc
->readbuffer
= g_strdup( line
);
217 irc
->readbuffer
= g_renew( char, irc
->readbuffer
, strlen( irc
->readbuffer
) + strlen ( line
) + 1 );
218 strcpy( ( irc
->readbuffer
+ strlen( irc
->readbuffer
) ), line
);
223 /* Normally, irc_process() shouldn't call irc_free() but irc_abort(). Just in case: */
224 if( !g_slist_find( irc_connection_list
, irc
) )
226 log_message( LOGLVL_WARNING
, "Abnormal termination of connection with fd %d.", fd
);
230 /* Very naughty, go read the RFCs! >:) */
231 if( irc
->readbuffer
&& ( strlen( irc
->readbuffer
) > 1024 ) )
233 irc_abort( irc
, 0, "Maximum line length exceeded" );
240 gboolean
bitlbee_io_current_client_write( gpointer data
, gint fd
, b_input_condition cond
)
246 if( irc
->sendbuffer
== NULL
)
249 size
= strlen( irc
->sendbuffer
);
250 st
= write( irc
->fd
, irc
->sendbuffer
, size
);
252 if( st
== 0 || ( st
< 0 && !sockerr_again() ) )
254 irc_abort( irc
, 1, "Write error: %s", strerror( errno
) );
257 else if( st
< 0 ) /* && sockerr_again() */
264 if( irc
->status
& USTATUS_SHUTDOWN
)
270 g_free( irc
->sendbuffer
);
271 irc
->sendbuffer
= NULL
;
272 irc
->w_watch_source_id
= 0;
279 temp
= g_strdup( irc
->sendbuffer
+ st
);
280 g_free( irc
->sendbuffer
);
281 irc
->sendbuffer
= temp
;
287 static gboolean
bitlbee_io_new_client( gpointer data
, gint fd
, b_input_condition condition
)
289 socklen_t size
= sizeof( struct sockaddr_in
);
290 struct sockaddr_in conn_info
;
291 int new_socket
= accept( global
.listen_socket
, (struct sockaddr
*) &conn_info
, &size
);
293 if( new_socket
== -1 )
295 log_message( LOGLVL_WARNING
, "Could not accept new connection: %s", strerror( errno
) );
300 if( global
.conf
->runmode
== RUNMODE_FORKDAEMON
)
302 pid_t client_pid
= 0;
305 if( socketpair( AF_UNIX
, SOCK_STREAM
, 0, fds
) == -1 )
307 log_message( LOGLVL_WARNING
, "Could not create IPC socket for client: %s", strerror( errno
) );
308 fds
[0] = fds
[1] = -1;
311 sock_make_nonblocking( fds
[0] );
312 sock_make_nonblocking( fds
[1] );
316 if( client_pid
> 0 && fds
[0] != -1 )
318 struct bitlbee_child
*child
;
320 child
= g_new0( struct bitlbee_child
, 1 );
321 child
->pid
= client_pid
;
322 child
->ipc_fd
= fds
[0];
323 child
->ipc_inpa
= b_input_add( child
->ipc_fd
, GAIM_INPUT_READ
, ipc_master_read
, child
);
324 child_list
= g_slist_append( child_list
, child
);
326 log_message( LOGLVL_INFO
, "Creating new subprocess with pid %d.", (int) client_pid
);
328 /* Close some things we don't need in the parent process. */
332 else if( client_pid
== 0 )
336 /* Since we're fork()ing here, let's make sure we won't
337 get the same random numbers as the parent/siblings. */
338 srand( time( NULL
) ^ getpid() );
342 /* Close the listening socket, we're a client. */
343 close( global
.listen_socket
);
344 b_event_remove( global
.listen_watch_source_id
);
346 /* Make the connection. */
347 irc
= irc_new( new_socket
);
349 /* We can store the IPC fd there now. */
350 global
.listen_socket
= fds
[1];
351 global
.listen_watch_source_id
= b_input_add( fds
[1], GAIM_INPUT_READ
, ipc_child_read
, irc
);
355 ipc_master_free_all();
361 log_message( LOGLVL_INFO
, "Creating new connection with fd %d.", new_socket
);
362 irc_new( new_socket
);
368 gboolean
bitlbee_shutdown( gpointer data
, gint fd
, b_input_condition cond
)
370 /* Try to save data for all active connections (if desired). */
371 while( irc_connection_list
!= NULL
)
372 irc_free( irc_connection_list
->data
);
374 /* We'll only reach this point when not running in inetd mode: */