aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2010-07-06 22:44:52 +0100
committerWilmer van der Gaast <wilmer@gaast.net>2010-07-06 22:44:52 +0100
commit6c2404e051cb6a235f985797c149af0791f44bbd (patch)
tree7f690ac9089925e345df5d4922ae29a4c1207d62
parent006a84f999248d1bc1c1e36fa3437765d4bd1142 (diff)
First part of the handshake, including sending a file descriptor to the
IPC master.
-rw-r--r--bitlbee.c2
-rw-r--r--bitlbee.h1
-rw-r--r--ipc.c119
-rw-r--r--ipc.h6
-rw-r--r--irc.h1
-rw-r--r--root_commands.c25
6 files changed, 140 insertions, 14 deletions
diff --git a/bitlbee.c b/bitlbee.c
index d0d95e67..78f4b4e5 100644
--- a/bitlbee.c
+++ b/bitlbee.c
@@ -317,10 +317,12 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition
{
struct bitlbee_child *child;
+ /* TODO: Stuff like this belongs in ipc.c. */
child = g_new0( struct bitlbee_child, 1 );
child->pid = client_pid;
child->ipc_fd = fds[0];
child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
+ child->to_fd = -1;
child_list = g_slist_append( child_list, child );
log_message( LOGLVL_INFO, "Creating new subprocess with pid %d.", (int) client_pid );
diff --git a/bitlbee.h b/bitlbee.h
index 5610d95b..9b35810f 100644
--- a/bitlbee.h
+++ b/bitlbee.h
@@ -162,6 +162,7 @@ gboolean bitlbee_io_current_client_write( gpointer data, gint source, b_input_co
void root_command_string( irc_t *irc, char *command );
void root_command( irc_t *irc, char *command[] );
+gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond );
gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond );
char *set_eval_root_nick( set_t *set, char *new_nick );
diff --git a/ipc.c b/ipc.c
index 81c5b8b4..cdf6539d 100644
--- a/ipc.c
+++ b/ipc.c
@@ -111,6 +111,31 @@ void ipc_master_cmd_restart( irc_t *data, char **cmd )
bitlbee_shutdown( NULL, -1, 0 );
}
+void ipc_master_cmd_identify( irc_t *data, char **cmd )
+{
+ struct bitlbee_child *child = (void*) data, *old = NULL;
+ GSList *l;
+
+ if( strcmp( child->nick, cmd[1] ) != 0 )
+ return;
+
+ g_free( child->password );
+ child->password = g_strdup( cmd[2] );
+
+ for( l = child_list; l; l = l->next )
+ {
+ old = l->data;
+ if( nick_cmp( old->nick, child->nick ) == 0 && child != old &&
+ old->password && strcmp( old->password, child->password ) )
+ break;
+ }
+
+ if( old == NULL )
+ return;
+
+ child->to_child = old;
+}
+
static const command_t ipc_master_commands[] = {
{ "client", 3, ipc_master_cmd_client, 0 },
{ "hello", 0, ipc_master_cmd_client, 0 },
@@ -122,6 +147,7 @@ static const command_t ipc_master_commands[] = {
{ "rehash", 0, ipc_master_cmd_rehash, 0 },
{ "kill", 2, NULL, IPC_CMD_TO_CHILDREN },
{ "restart", 0, ipc_master_cmd_restart, 0 },
+ { "identify", 2, ipc_master_cmd_identify, 0 },
{ NULL }
};
@@ -201,6 +227,22 @@ static const command_t ipc_child_commands[] = {
{ NULL }
};
+static gboolean ipc_send_fd( int fd, int send_fd );
+
+gboolean ipc_child_identify( irc_t *irc )
+{
+ if( global.conf->runmode == RUNMODE_FORKDAEMON )
+ {
+ if( !ipc_send_fd( global.listen_socket, irc->fd ) )
+ ipc_child_disable();
+
+ ipc_to_master_str( "IDENTIFY %s :%s\r\n", irc->user->nick, irc->password );
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
static void ipc_command_exec( void *data, char **cmd, const command_t *commands )
{
@@ -229,8 +271,12 @@ static void ipc_command_exec( void *data, char **cmd, const command_t *commands
/* Return just one line. Returns NULL if something broke, an empty string
on temporary "errors" (EAGAIN and friends). */
-static char *ipc_readline( int fd )
+static char *ipc_readline( int fd, int *recv_fd )
{
+ struct msghdr msg;
+ struct iovec iov;
+ char ccmsg[CMSG_SPACE(sizeof(recv_fd))];
+ struct cmsghdr *cmsg;
char buf[513], *eol;
int size;
@@ -252,22 +298,44 @@ static char *ipc_readline( int fd )
else
size = eol - buf + 2;
- if( recv( fd, buf, size, 0 ) != size )
+ iov.iov_base = buf;
+ iov.iov_len = size;
+
+ memset( &msg, 0, sizeof( msg ) );
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = ccmsg;
+ msg.msg_controllen = sizeof( ccmsg );
+
+ if( recvmsg( fd, &msg, 0 ) != size )
return NULL;
- else
- return g_strndup( buf, size - 2 );
+
+ if( recv_fd )
+ for( cmsg = CMSG_FIRSTHDR( &msg ); cmsg; cmsg = CMSG_NXTHDR( &msg, cmsg ) )
+ if( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS )
+ {
+ /* Getting more than one shouldn't happen but if it does,
+ make sure we don't leave them around. */
+ if( *recv_fd != -1 )
+ close( *recv_fd );
+
+ *recv_fd = *(int*) CMSG_DATA( cmsg );
+ }
+
+ return g_strndup( buf, size - 2 );
}
gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond )
{
+ struct bitlbee_child *child = data;
char *buf, **cmd;
- if( ( buf = ipc_readline( source ) ) )
+ if( ( buf = ipc_readline( source, &child->to_fd ) ) )
{
cmd = irc_parse_line( buf );
if( cmd )
{
- ipc_command_exec( data, cmd, ipc_master_commands );
+ ipc_command_exec( child, cmd, ipc_master_commands );
g_free( cmd );
}
g_free( buf );
@@ -283,8 +351,9 @@ gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond )
gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond )
{
char *buf, **cmd;
+ int recv_fd = -1;
- if( ( buf = ipc_readline( source ) ) )
+ if( ( buf = ipc_readline( source, &recv_fd ) ) )
{
cmd = irc_parse_line( buf );
if( cmd )
@@ -412,14 +481,43 @@ void ipc_to_children_str( char *format, ... )
g_free( msg_buf );
}
+static gboolean ipc_send_fd( int fd, int send_fd )
+{
+ struct msghdr msg;
+ struct iovec iov;
+ char ccmsg[CMSG_SPACE(sizeof(fd))];
+ struct cmsghdr *cmsg;
+
+ memset( &msg, 0, sizeof( msg ) );
+ iov.iov_base = "0x90\r\n";
+ iov.iov_len = 6;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = ccmsg;
+ msg.msg_controllen = sizeof( ccmsg );
+ cmsg = CMSG_FIRSTHDR( &msg );
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN( sizeof( send_fd ) );
+ *(int*)CMSG_DATA( cmsg ) = send_fd;
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ return sendmsg( fd, &msg, 0 ) == 6;
+}
+
void ipc_master_free_one( struct bitlbee_child *c )
{
b_event_remove( c->ipc_inpa );
closesocket( c->ipc_fd );
+ if( c->to_fd != -1 )
+ close( c->to_fd );
+
g_free( c->host );
g_free( c->nick );
g_free( c->realname );
+ g_free( c->password );
g_free( c );
}
@@ -505,8 +603,8 @@ static gboolean new_ipc_client( gpointer data, gint serversock, b_input_conditio
{
struct bitlbee_child *child = g_new0( struct bitlbee_child, 1 );
+ child->to_fd = -1;
child->ipc_fd = accept( serversock, NULL, 0 );
-
if( child->ipc_fd == -1 )
{
log_message( LOGLVL_WARNING, "Unable to accept connection on UNIX domain socket: %s", strerror(errno) );
@@ -515,7 +613,7 @@ static gboolean new_ipc_client( gpointer data, gint serversock, b_input_conditio
child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
- child_list = g_slist_append( child_list, child );
+ child_list = g_slist_prepend( child_list, child );
return TRUE;
}
@@ -597,8 +695,9 @@ int ipc_master_load_state( char *statefile )
return 0;
}
child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
+ child->to_fd = -1;
- child_list = g_slist_append( child_list, child );
+ child_list = g_slist_prepend( child_list, child );
}
ipc_to_children_str( "HELLO\r\n" );
diff --git a/ipc.h b/ipc.h
index 0e71c520..bf5f0454 100644
--- a/ipc.h
+++ b/ipc.h
@@ -36,6 +36,12 @@ struct bitlbee_child
char *host;
char *nick;
char *realname;
+
+ char *password;
+
+ /* For takeovers: */
+ struct bitlbee_child *to_child;
+ int to_fd;
};
diff --git a/irc.h b/irc.h
index 94738832..3b5e244f 100644
--- a/irc.h
+++ b/irc.h
@@ -86,6 +86,7 @@ typedef struct irc
gint r_watch_source_id;
gint w_watch_source_id;
gint ping_source_id;
+ gint login_source_id; /* To slightly delay some events at login time. */
struct bee *b;
} irc_t;
diff --git a/root_commands.c b/root_commands.c
index 62fe1e79..5d84c6d4 100644
--- a/root_commands.c
+++ b/root_commands.c
@@ -104,7 +104,6 @@ static void cmd_account( irc_t *irc, char **cmd );
static void cmd_identify( irc_t *irc, char **cmd )
{
storage_status_t status;
- char *account_on[] = { "account", "on", NULL };
gboolean load = TRUE;
char *password = cmd[1];
@@ -157,8 +156,16 @@ static void cmd_identify( irc_t *irc, char **cmd )
irc->status |= USTATUS_IDENTIFIED;
irc_umode_set( irc, "+R", 1 );
irc_channel_auto_joins( irc, NULL );
- if( load && set_getbool( &irc->b->set, "auto_connect" ) )
- cmd_account( irc, account_on );
+
+ if( ipc_child_identify( irc ) )
+ {
+ if( load && set_getbool( &irc->b->set, "auto_connect" ) )
+ irc->login_source_id = b_timeout_add( 200,
+ cmd_identify_finish, irc );
+ }
+ else if( load && set_getbool( &irc->b->set, "auto_connect" ) )
+ cmd_identify_finish( irc, 0, 0 );
+
break;
case STORAGE_OTHER_ERROR:
default:
@@ -167,6 +174,16 @@ static void cmd_identify( irc_t *irc, char **cmd )
}
}
+gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond )
+{
+ char *account_on[] = { "account", "on", NULL };
+ irc_t *irc = data;
+
+ cmd_account( irc, account_on );
+
+ return FALSE;
+}
+
static void cmd_register( irc_t *irc, char **cmd )
{
if( global.conf->authmode == AUTHMODE_REGISTERED )
@@ -671,7 +688,7 @@ static void cmd_rename( irc_t *irc, char **cmd )
}
else if( iu == irc->user )
{
- irc_usermsg( irc, "Nick `%s' can't be changed", cmd[1] );
+ irc_usermsg( irc, "Use /nick to change your own nickname" );
}
else if( !nick_ok( cmd[2] ) )
{