irc_t *irc_new(int fd) { irc_t *irc; struct sockaddr_storage sock; socklen_t socklen = sizeof(sock); char *host = NULL, *myhost = NULL; irc_user_t *iu; GSList *l; set_t *s; bee_t *b; irc = g_new0(irc_t, 1); irc->fd = fd; sock_make_nonblocking(irc->fd); irc->r_watch_source_id = b_input_add(irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc); irc->status = USTATUS_OFFLINE; irc->last_pong = gettime(); irc->nick_user_hash = g_hash_table_new(g_str_hash, g_str_equal); irc->watches = g_hash_table_new(g_str_hash, g_str_equal); irc->iconv = (GIConv) - 1; irc->oconv = (GIConv) - 1; if (global.conf->hostname) { myhost = g_strdup(global.conf->hostname); } else if (getsockname(irc->fd, (struct sockaddr*) &sock, &socklen) == 0) { char buf[NI_MAXHOST + 1]; if (getnameinfo((struct sockaddr *) &sock, socklen, buf, NI_MAXHOST, NULL, 0, 0) == 0) { myhost = g_strdup(ipv6_unwrap(buf)); } } if (getpeername(irc->fd, (struct sockaddr*) &sock, &socklen) == 0) { char buf[NI_MAXHOST + 1]; if (getnameinfo((struct sockaddr *) &sock, socklen, buf, NI_MAXHOST, NULL, 0, 0) == 0) { host = g_strdup(ipv6_unwrap(buf)); } } if (host == NULL) { host = g_strdup("localhost.localdomain"); } if (myhost == NULL) { myhost = g_strdup("localhost.localdomain"); } if (global.conf->ping_interval > 0 && global.conf->ping_timeout > 0) { irc->ping_source_id = b_timeout_add(global.conf->ping_interval * 1000, irc_userping, irc); } irc_connection_list = g_slist_append(irc_connection_list, irc); b = irc->b = bee_new(); b->ui_data = irc; b->ui = &irc_ui_funcs; s = set_add(&b->set, "allow_takeover", "true", set_eval_bool, irc); s = set_add(&b->set, "away_devoice", "true", set_eval_bw_compat, irc); s->flags |= SET_HIDDEN; s = set_add(&b->set, "away_reply_timeout", "3600", set_eval_int, irc); s = set_add(&b->set, "charset", "utf-8", set_eval_charset, irc); s = set_add(&b->set, "default_target", "root", NULL, irc); s = set_add(&b->set, "display_namechanges", "false", set_eval_bool, irc); s = set_add(&b->set, "display_timestamps", "true", set_eval_bool, irc); s = set_add(&b->set, "handle_unknown", "add_channel", NULL, irc); s = set_add(&b->set, "last_version", "0", NULL, irc); s->flags |= SET_HIDDEN; s = set_add(&b->set, "nick_format", "%-@nick", NULL, irc); s = set_add(&b->set, "nick_lowercase", "false", set_eval_bool, irc); s = set_add(&b->set, "nick_underscores", "false", set_eval_bool, irc); s = set_add(&b->set, "offline_user_quits", "true", set_eval_bool, irc); s = set_add(&b->set, "ops", "both", set_eval_irc_channel_ops, irc); s = set_add(&b->set, "paste_buffer", "false", set_eval_bool, irc); s->old_key = g_strdup("buddy_sendbuffer"); s = set_add(&b->set, "paste_buffer_delay", "200", set_eval_int, irc); s->old_key = g_strdup("buddy_sendbuffer_delay"); s = set_add(&b->set, "password", NULL, set_eval_password, irc); s->flags |= SET_NULL_OK | SET_PASSWORD; s = set_add(&b->set, "private", "true", set_eval_bool, irc); s = set_add(&b->set, "query_order", "lifo", NULL, irc); s = set_add(&b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc); s->flags |= SET_HIDDEN; s = set_add(&b->set, "show_offline", "false", set_eval_bw_compat, irc); s->flags |= SET_HIDDEN; s = set_add(&b->set, "self_messages", "true", set_eval_self_messages, irc); s = set_add(&b->set, "simulate_netsplit", "true", set_eval_bool, irc); s = set_add(&b->set, "timezone", "local", set_eval_timezone, irc); s = set_add(&b->set, "to_char", ": ", set_eval_to_char, irc); s = set_add(&b->set, "typing_notice", "false", set_eval_bool, irc); s = set_add(&b->set, "utf8_nicks", "false", set_eval_utf8_nicks, irc); irc->root = iu = irc_user_new(irc, ROOT_NICK); iu->host = g_strdup(myhost); iu->fullname = g_strdup(ROOT_FN); iu->f = &irc_user_root_funcs; iu = irc_user_new(irc, NS_NICK); iu->host = g_strdup(myhost); iu->fullname = g_strdup(ROOT_FN); iu->f = &irc_user_root_funcs; irc->user = g_new0(irc_user_t, 1); irc->user->host = g_strdup(host); conf_loaddefaults(irc); /* Evaluator sets the iconv/oconv structures. */ set_eval_charset(set_find(&b->set, "charset"), set_getstr(&b->set, "charset")); irc_write(irc, ":%s NOTICE * :%s", irc->root->host, "BitlBee-IRCd initialized, please go on"); if (isatty(irc->fd)) { irc_write(irc, ":%s NOTICE * :%s", irc->root->host, "If you read this, you most likely accidentally " "started BitlBee in inetd mode on the command line. " "You probably want to run it in (Fork)Daemon mode. " "See doc/README for more information."); } g_free(myhost); g_free(host); /* libpurple doesn't like fork()s after initializing itself, so this is the right moment to initialize it. */ #ifdef WITH_PURPLE nogaim_init(); #endif /* SSL library initialization also should be done after the fork, to avoid shared CSPRNG state. This is required by NSS, which refuses to work if a fork is detected */ ssl_init(); for (l = irc_plugins; l; l = l->next) { irc_plugin_t *p = l->data; if (p->irc_new) { p->irc_new(irc); } } return irc; }
int main( int argc, char *argv[] ) { int i = 0; char *old_cwd = NULL; struct sigaction sig, old; /* Required to make iconv to ASCII//TRANSLIT work. This makes BitlBee system-locale-sensitive. :-( */ setlocale( LC_CTYPE, "" ); if( argc > 1 && strcmp( argv[1], "-x" ) == 0 ) return crypt_main( argc, argv ); log_init(); global.conf_file = g_strdup( CONF_FILE_DEF ); global.conf = conf_load( argc, argv ); if( global.conf == NULL ) return( 1 ); b_main_init(); /* libpurple doesn't like fork()s after initializing itself, so if we use it, do this init a little later (in case we're running in ForkDaemon mode). */ #ifndef WITH_PURPLE nogaim_init(); #endif /* Ugly Note: libotr and gnutls both use libgcrypt. libgcrypt has a process-global config state whose initialization happpens twice if libotr and gnutls are used together. libotr installs custom memory management functions for libgcrypt while our gnutls module uses the defaults. Therefore we initialize OTR after SSL. *sigh* */ ssl_init(); #ifdef OTR_BI otr_init(); #endif /* And in case OTR is loaded as a plugin, it'll also get loaded after this point. */ srand( time( NULL ) ^ getpid() ); global.helpfile = g_strdup( HELP_FILE ); if( help_init( &global.help, global.helpfile ) == NULL ) log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE ); global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage ); if( global.storage == NULL ) { log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); return( 1 ); } if( global.conf->runmode == RUNMODE_INETD ) { log_link( LOGLVL_ERROR, LOGOUTPUT_IRC ); log_link( LOGLVL_WARNING, LOGOUTPUT_IRC ); i = bitlbee_inetd_init(); log_message( LOGLVL_INFO, "%s %s starting in inetd mode.", PACKAGE, BITLBEE_VERSION ); } else if( global.conf->runmode == RUNMODE_DAEMON ) { log_link( LOGLVL_ERROR, LOGOUTPUT_CONSOLE ); log_link( LOGLVL_WARNING, LOGOUTPUT_CONSOLE ); i = bitlbee_daemon_init(); log_message( LOGLVL_INFO, "%s %s starting in daemon mode.", PACKAGE, BITLBEE_VERSION ); } else if( global.conf->runmode == RUNMODE_FORKDAEMON ) { log_link( LOGLVL_ERROR, LOGOUTPUT_CONSOLE ); log_link( LOGLVL_WARNING, LOGOUTPUT_CONSOLE ); /* In case the operator requests a restart, we need this. */ old_cwd = g_malloc( 256 ); if( getcwd( old_cwd, 255 ) == NULL ) { log_message( LOGLVL_WARNING, "Could not save current directory: %s", strerror( errno ) ); g_free( old_cwd ); old_cwd = NULL; } i = bitlbee_daemon_init(); log_message( LOGLVL_INFO, "%s %s starting in forking daemon mode.", PACKAGE, BITLBEE_VERSION ); } if( i != 0 ) return( i ); if( ( global.conf->user && *global.conf->user ) && ( global.conf->runmode == RUNMODE_DAEMON || global.conf->runmode == RUNMODE_FORKDAEMON ) && ( !getuid() || !geteuid() ) ) { struct passwd *pw = NULL; pw = getpwnam( global.conf->user ); if( pw ) { initgroups( global.conf->user, pw->pw_gid ); setgid( pw->pw_gid ); setuid( pw->pw_uid ); } else { log_message( LOGLVL_WARNING, "Failed to look up user %s.", global.conf->user ); } } /* Catch some signals to tell the user what's happening before quitting */ memset( &sig, 0, sizeof( sig ) ); sig.sa_handler = sighandler; sigaction( SIGCHLD, &sig, &old ); sigaction( SIGPIPE, &sig, &old ); sig.sa_flags = SA_RESETHAND; sigaction( SIGINT, &sig, &old ); sigaction( SIGILL, &sig, &old ); sigaction( SIGBUS, &sig, &old ); sigaction( SIGFPE, &sig, &old ); sigaction( SIGSEGV, &sig, &old ); sigaction( SIGTERM, &sig, &old ); sigaction( SIGQUIT, &sig, &old ); sigaction( SIGXCPU, &sig, &old ); if( !getuid() || !geteuid() ) log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" ); b_main_run(); /* Mainly good for restarting, to make sure we close the help.txt fd. */ help_free( &global.help ); if( global.restart ) { char *fn = ipc_master_save_state(); char *env; env = g_strdup_printf( "_BITLBEE_RESTART_STATE=%s", fn ); putenv( env ); g_free( fn ); /* Looks like env should *not* be freed here as putenv doesn't make a copy. Odd. */ i = chdir( old_cwd ); close( global.listen_socket ); if( execv( argv[0], argv ) == -1 ) /* Apparently the execve() failed, so let's just jump back into our own/current main(). */ /* Need more cleanup code to make this work. */ return 1; /* main( argc, argv ); */ } return( 0 ); }