terom@53: #include "nexus.h" terom@48: #include "signals.h" terom@48: #include "log.h" terom@0: terom@0: #include terom@15: #include terom@0: #include terom@15: #include terom@48: #include terom@63: #include terom@0: terom@18: #define DEFAULT_HOST "irc.fixme.fi" terom@18: #define DEFAULT_PORT "6667" terom@18: #define DEFAULT_PORT_SSL "6697" terom@0: terom@63: /** terom@63: * Command-line option codes terom@63: */ terom@23: enum option_code { terom@63: OPT_HELP = 'h', terom@63: OPT_NETWORK = 'n', terom@63: OPT_CHANNEL = 'c', terom@63: OPT_MODULE = 'm', terom@63: OPT_CONFIG = 'C', terom@79: OPT_DEBUG = 'd', terom@63: terom@63: /** Options without short names */ terom@63: _OPT_EXT_BEGIN = 0x00ff, terom@23: terom@23: }; terom@23: terom@63: /** terom@63: * Command-line option definitions terom@63: */ terom@15: static struct option options[] = { terom@63: {"help", 0, NULL, OPT_HELP }, terom@63: {"network", 1, NULL, OPT_NETWORK }, terom@63: {"channel", 1, NULL, OPT_CHANNEL }, terom@63: {"module", 1, NULL, OPT_MODULE }, terom@63: {"config", 1, NULL, OPT_CONFIG }, terom@79: {"debug", 0, NULL, OPT_DEBUG }, terom@63: {0, 0, 0, 0 }, terom@15: }; terom@15: terom@63: /** terom@83: * Display --help output on stdout. terom@83: * terom@83: * If nexus is given, --config options for loaded modules are also listed terom@63: */ terom@83: static void usage (struct nexus *nexus, const char *exe) terom@15: { terom@15: printf("Usage: %s [OPTIONS]\n", exe); terom@15: printf("\n"); terom@15: printf(" --help / -h display this message\n"); terom@63: printf(" --network / -n add an IRC network using ':[:[:ssl]]' format\n"); terom@63: printf(" --channel / -c add an IRC channel using ':' format\n"); terom@63: printf(" --module / -m add a module using ':' format\n"); terom@63: printf(" --config / -C add a module configuration option using ':[:]' format\n"); terom@79: printf(" --debug / -d set logging level to DEBUG\n"); terom@83: terom@83: if (nexus && !TAILQ_EMPTY(&nexus->modules->list)) { terom@83: struct module *module; terom@83: terom@83: printf("\n"); terom@83: printf("Module configuration options\n"); terom@83: terom@83: TAILQ_FOREACH(module, &nexus->modules->list, modules_list) { terom@83: printf("\n"); terom@83: printf("%s:\n", module->info.name); terom@83: terom@83: if (module->funcs->config_options) { terom@83: const struct config_option *opt; terom@83: terom@83: for (opt = module->funcs->config_options; opt->name && opt->type; opt++) { terom@83: printf(" --config %s:%s:%s\t\t%s\n", module->info.name, opt->name, opt->description, opt->help); terom@83: } terom@83: terom@83: } else { terom@83: printf("\t???\n"); terom@83: } terom@83: } terom@83: } terom@15: } terom@15: terom@63: /** terom@65: * Parse and apply a --network option, adding the given network to our irc_client. terom@63: */ terom@63: static err_t apply_network (struct nexus *nexus, char *opt, struct error_info *err) terom@63: { terom@63: struct irc_net_info info = { terom@63: .network = NULL, terom@63: .hostname = NULL, terom@63: .service = DEFAULT_PORT, terom@63: .use_ssl = false, terom@63: .register_info = { terom@63: .nickname = "SpBotDev", terom@63: .username = "spbot-dev", terom@63: .realname = "SpBot (development version)" terom@63: } terom@63: }; terom@63: terom@63: // parse the required fields terom@63: if ((info.network = strsep(&opt, ":")) == NULL) terom@63: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing field for --network"); terom@63: terom@63: if ((info.hostname = strsep(&opt, ":")) == NULL) terom@63: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing field for --network"); terom@63: terom@63: // parse the optional fields terom@63: if (opt) terom@63: info.service = strsep(&opt, ":"); terom@63: terom@63: // parse any remaining flags terom@63: while (opt) { terom@63: char *flag = strsep(&opt, ":"); terom@63: terom@63: if (strcmp(flag, "ssl")) terom@63: info.use_ssl = true; terom@63: terom@63: else terom@63: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "unrecognized flag for --network"); terom@63: } terom@63: terom@66: // create the net terom@65: log_info("add network '%s' at '%s:%s'", info.network, info.hostname, info.service); terom@65: terom@63: if (irc_client_add_net(nexus->client, NULL, &info, err)) terom@63: return ERROR_CODE(err); terom@63: terom@63: // ok terom@63: return SET_ERROR(err, SUCCESS); terom@63: } terom@63: terom@63: /** terom@65: * Parse and apply a --channel option, adding the given channel to the given irc_net. terom@63: */ terom@63: static err_t apply_channel (struct nexus *nexus, char *opt, struct error_info *err) terom@63: { terom@63: const char *network = NULL; terom@63: struct irc_net *net; terom@63: struct irc_chan_info info = { terom@63: .channel = NULL, terom@63: }; terom@63: terom@63: // parse the required fields terom@63: if ((network = strsep(&opt, ":")) == NULL) terom@63: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing field for --channel"); terom@63: terom@63: if ((info.channel = strsep(&opt, ":")) == NULL) terom@63: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing field for --channel"); terom@63: terom@63: // trailing garbage? terom@63: if (opt) terom@63: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "trailing values for --channel"); terom@63: terom@63: // look up the net terom@63: if ((net = irc_client_get_net(nexus->client, network)) == NULL) terom@63: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "unknown network for --channel"); terom@65: terom@66: // add the channel terom@65: log_info("add channel '%s' on network '%s'", info.channel, net->info.network); terom@63: terom@63: if (irc_net_add_chan(net, NULL, &info, err)) terom@63: return ERROR_CODE(err); terom@63: terom@63: // ok terom@63: return SUCCESS; terom@63: } terom@63: terom@63: /** terom@65: * Parse and apply a --module option, loading the given module. terom@65: */ terom@65: static err_t apply_module (struct nexus *nexus, char *opt, struct error_info *err) terom@65: { terom@65: struct module_info info = { terom@65: .name = NULL, terom@65: .path = NULL, terom@65: }; terom@65: struct module *module; terom@65: terom@65: // parse the required fields terom@65: if ((info.name = strsep(&opt, ":")) == NULL) terom@65: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing field for --module"); terom@65: terom@65: if ((info.path = strsep(&opt, ":")) == NULL) terom@65: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing field for --module"); terom@65: terom@65: // trailing garbage? terom@65: if (opt) terom@65: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "trailing values for --channel"); terom@65: terom@66: // load it terom@65: log_info("loading module '%s' from path '%s'", info.name, info.path); terom@65: terom@65: if (module_load(nexus->modules, &module, &info, err)) terom@65: return ERROR_CODE(err); terom@65: terom@65: // ok terom@65: return SUCCESS; terom@65: } terom@65: terom@65: /** terom@66: * Parse and apply a --config option, calling the module's conf func. terom@66: */ terom@66: static err_t apply_config (struct nexus *nexus, char *opt, struct error_info *err) terom@66: { terom@66: struct module *module; terom@66: const char *module_name, *conf_name; terom@66: char *conf_value; terom@66: terom@66: // parse the required fields terom@66: if ((module_name = strsep(&opt, ":")) == NULL) terom@66: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing field for --config"); terom@66: terom@66: if ((conf_name = strsep(&opt, ":")) == NULL) terom@66: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing field for --config"); terom@66: terom@66: // value is the rest of the data, might be NULL terom@66: conf_value = opt; terom@66: terom@66: // lookup the module terom@66: if ((module = module_get(nexus->modules, module_name)) == NULL) terom@66: RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "unknown module for --config"); terom@66: terom@66: // do the config terom@66: log_info("applying module '%s' config name '%s' with value: %s", module->info.name, conf_name, conf_value); terom@66: terom@66: if (module_conf(module, conf_name, conf_value, err)) terom@66: return ERROR_CODE(err); terom@66: terom@66: // ok terom@66: return SUCCESS; terom@66: } terom@66: terom@66: /** terom@63: * Parse arguments and apply them to the given nexus terom@63: */ terom@63: static err_t parse_args (struct nexus *nexus, int argc, char **argv, struct error_info *err) terom@63: { terom@63: int opt, option_index; terom@63: terom@63: // parse options terom@63: while ((opt = getopt_long(argc, argv, "hn:c:m:C:", options, &option_index)) != -1) { terom@63: switch (opt) { terom@63: case OPT_HELP: terom@83: usage(nexus, argv[0]); terom@63: terom@63: // XXX: return instead terom@63: exit(EXIT_SUCCESS); terom@63: terom@63: case OPT_NETWORK: terom@63: if (apply_network(nexus, optarg, err)) terom@63: return ERROR_CODE(err); terom@63: terom@63: break; terom@63: terom@63: case OPT_CHANNEL: terom@63: if (apply_channel(nexus, optarg, err)) terom@63: return ERROR_CODE(err); terom@63: terom@63: break; terom@63: terom@63: case OPT_MODULE: terom@65: if (apply_module(nexus, optarg, err)) terom@65: return ERROR_CODE(err); terom@65: terom@65: break; terom@65: terom@63: case OPT_CONFIG: terom@66: if (apply_config(nexus, optarg, err)) terom@66: return ERROR_CODE(err); terom@66: terom@66: break; terom@79: terom@79: case OPT_DEBUG: terom@79: set_log_level(LOG_DEBUG); terom@79: break; terom@63: terom@63: case '?': terom@83: usage(nexus, argv[0]); terom@63: return SET_ERROR(err, ERR_CMD_OPT); terom@63: } terom@63: } terom@63: terom@63: // ok terom@63: return SUCCESS; terom@63: } terom@63: terom@63: static void on_sigint (evutil_socket_t sig, short what, void *arg) terom@48: { terom@55: struct nexus *ctx = arg; terom@48: terom@48: (void) sig; terom@48: (void) what; terom@48: terom@70: log_info("Quitting..."); terom@48: terom@70: // unload the modules terom@70: modules_unload(ctx->modules); terom@48: terom@70: // quit the irc client terom@70: irc_client_quit(ctx->client, "Goodbye, cruel world ;("); terom@70: terom@70: // remove the signal handlers (ourself) terom@70: signals_free(ctx->signals); terom@71: terom@71: // now event_base_dispatch should return once everythings' shut down... terom@48: } terom@48: terom@15: int main (int argc, char **argv) terom@15: { terom@70: struct nexus _nexus, *nexus = &_nexus; terom@21: struct error_info err; terom@0: terom@63: // zero nexus terom@63: memset(nexus, 0, sizeof(*nexus)); terom@15: terom@9: // initialize libevent terom@63: if ((nexus->ev_base = event_base_new()) == NULL) terom@21: FATAL("event_base_new"); terom@48: terom@48: // initialize signal handlers terom@70: if ((ERROR_CODE(&err) = signals_create(&nexus->signals, nexus->ev_base))) terom@48: FATAL("signals_create"); terom@9: terom@48: // add our signal handlers terom@71: if (signal_ignore(SIGPIPE, &err)) terom@71: FATAL_ERROR(&err, "signals_ignore(SIGPIPE)"); terom@71: terom@71: if ((ERROR_CODE(&err) = signals_add(nexus->signals, SIGINT, &on_sigint, nexus))) terom@71: FATAL_ERROR(&err, "signals_add(SIGINT)"); terom@63: terom@63: // initialize sock module terom@63: if (sock_init(nexus->ev_base, &err)) terom@63: FATAL_ERROR(&err, "sock_init"); terom@55: terom@63: // modules terom@63: if ((ERROR_CODE(&err) = modules_create(&nexus->modules, nexus))) terom@63: FATAL_ERROR(&err, "modules_create"); terom@63: terom@63: // the IRC client terom@63: if (irc_client_create(&nexus->client, &err)) terom@63: FATAL_ERROR(&err, "irc_client_create"); terom@55: terom@63: // parse args terom@63: if (parse_args(nexus, argc, argv, &err)) terom@63: FATAL_ERROR(&err, "parse_args"); terom@71: terom@11: // run event loop terom@71: log_info("entering event loop"); terom@71: terom@63: if (event_base_dispatch(nexus->ev_base)) terom@21: FATAL("event_base_dispatch"); terom@0: terom@11: // ok, no cleanup terom@0: return 0; terom@0: } terom@0: