/** * Copyright (c) 2018 Cornell University. * * Author: Ted Yin * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include #include #include "salticidae/util.h" namespace salticidae { const char *TTY_COLOR_RED = "\x1b[31m"; const char *TTY_COLOR_GREEN = "\x1b[32m"; const char *TTY_COLOR_YELLOW = "\x1b[33m"; const char *TTY_COLOR_BLUE = "\x1b[34m"; const char *TTY_COLOR_MAGENTA = "\x1b[35m"; const char *TTY_COLOR_CYAN = "\x1b[36m"; const char *TTY_COLOR_RESET = "\x1b[0m"; void sec2tv(double t, struct timeval &tv) { tv.tv_sec = trunc(t); tv.tv_usec = trunc((t - tv.tv_sec) * 1e6); } double gen_rand_timeout(double base_timeout, double alpha) { return base_timeout + rand() / (double)RAND_MAX * alpha * base_timeout; } std::string vstringprintf(const char *fmt, va_list _ap) { int guessed_size = 1024; std::string buff; va_list ap; va_copy(ap, _ap); buff.resize(guessed_size); int nwrote = vsnprintf(&buff[0], guessed_size, fmt, _ap); if (nwrote < 0) buff = ""; else { buff.resize(nwrote); if (nwrote > guessed_size) { if (vsnprintf(&buff[0], nwrote, fmt, ap) != nwrote) buff = ""; } } va_end(ap); return std::move(buff); } std::string stringprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); auto ret = vstringprintf(fmt, ap); va_end(ap); return std::move(ret); } const std::string get_current_datetime() { /* credit: http://stackoverflow.com/a/41381479/544806 */ char fmt[64], buf[64]; struct timeval tv; gettimeofday(&tv, nullptr); struct tm *tmp = localtime(&tv.tv_sec); strftime(fmt, sizeof fmt, "%Y-%m-%d %H:%M:%S.%%06u", tmp); snprintf(buf, sizeof buf, fmt, tv.tv_usec); return std::string(buf); } SalticidaeError::SalticidaeError() : msg("unknown") {} void Logger::set_color() { if (is_tty()) { color_info = TTY_COLOR_GREEN; color_debug = TTY_COLOR_BLUE; color_warning = TTY_COLOR_YELLOW; color_error = TTY_COLOR_RED; } else { color_info = color_debug = color_warning = color_error = nullptr; } } void Logger::write(const char *tag, const char *color, const char *fmt, va_list ap) { std::string buff = color ? color : ""; buff += stringprintf("%s [%s %s] ", get_current_datetime().c_str(), prefix, tag); if (color) buff += TTY_COLOR_RESET; buff += vstringprintf(fmt, ap); buff.push_back('\n'); ::write(output, &buff[0], buff.length()); } void Logger::info(const char *fmt, ...) { va_list ap; va_start(ap, fmt); write("info", color_info, fmt, ap); va_end(ap); } void Logger::debug(const char *fmt, ...) { va_list ap; va_start(ap, fmt); write("debug", color_debug, fmt, ap); va_end(ap); } void Logger::warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); write("warn", color_warning, fmt, ap); va_end(ap); } void Logger::error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); write("error", color_error, fmt, ap); va_end(ap); } Logger logger("net"); void ElapsedTime::start() { struct timezone tz; gettimeofday(&t0, &tz); cpu_t0 = clock(); } void ElapsedTime::stop(bool show_info) { struct timeval t1; struct timezone tz; gettimeofday(&t1, &tz); cpu_elapsed_sec = (float)clock() / CLOCKS_PER_SEC - (float)cpu_t0 / CLOCKS_PER_SEC; elapsed_sec = (t1.tv_sec + t1.tv_usec * 1e-6) - (t0.tv_sec + t0.tv_usec * 1e-6); if (show_info) SALTICIDAE_LOG_INFO("elapsed: %.3f (wall) %.3f (cpu)", elapsed_sec, cpu_elapsed_sec); } Config::Opt::Opt(const std::string &optname, const std::string &optdoc, const optval_t &optval, Action action, char short_opt, int idx): optname(optname), optdoc(optdoc), optval(optval), action(action), short_opt(short_opt) { opt.name = this->optname.c_str(); opt.has_arg = action == SWITCH_ON ? no_argument : required_argument; opt.flag = nullptr; opt.val = 0x100 + idx; } void Config::add_opt(const std::string &optname, const optval_t &optval, Action action, char short_opt, const std::string &optdoc) { if (conf.count(optname)) throw SalticidaeError("option name already exists"); opts.push_back(new Opt(optname, optdoc, optval, action, short_opt, opts.size())); auto opt = opts.back().get(); conf.insert(std::make_pair(optname, opt)); if (short_opt != -1) conf.insert(std::make_pair(std::string(1, short_opt), opt)); } void Config::update(Opt &p, const char *optval) { switch (p.action) { case SWITCH_ON: p.optval->switch_on(); break; case SET_VAL: p.optval->set_val(optval); break; case APPEND: p.optval->append(optval); break; default: throw SalticidaeError("unknown action"); } } void Config::update(const std::string &optname, const char *optval) { assert(conf.count(optname)); update(*(conf.find(optname)->second), optval); } bool Config::load(const std::string &fname) { static const size_t BUFF_SIZE = 1024; FILE *conf_f = fopen(fname.c_str(), "r"); char buff[BUFF_SIZE]; /* load configuration from file */ if (conf_f) { while (fgets(buff, BUFF_SIZE, conf_f)) { if (strlen(buff) == BUFF_SIZE - 1) { fclose(conf_f); throw SalticidaeError("configuration file line too long"); } std::string line(buff); size_t pos = line.find("="); if (pos == std::string::npos) continue; std::string optname = trim(line.substr(0, pos)); std::string optval = trim(line.substr(pos + 1)); if (!conf.count(optname)) { SALTICIDAE_LOG_WARN("ignoring option name in conf file: %s", optname.c_str()); continue; } update(optname, optval.c_str()); } fclose(conf_f); return true; } else return false; } size_t Config::parse(int argc, char **argv) { if (load(conf_fname)) SALTICIDAE_LOG_INFO("loaded configuration from %s", conf_fname.c_str()); size_t nopts = opts.size(); auto longopts = BoxObj(new struct option[nopts + 1]); int ind; std::string shortopts; for (size_t i = 0; i < nopts; i++) { const auto &opt = opts[i]; longopts[i] = opt->opt; if (opt->short_opt != -1) { shortopts += opt->short_opt; if (longopts[i].has_arg == required_argument) shortopts += ":"; } } longopts[nopts] = {0, 0, 0, 0}; for (;;) { int id = getopt_long(argc, argv, shortopts.c_str(), longopts.get(), &ind); if (id == -1) break; if (id == '?') throw SalticidaeError("invalid option format"); if (id >= 0x100) update(*(opts[id - 0x100]), optarg); else update(std::string(1, (char)id), optarg); } return optind; } void Config::print_help(FILE *output) { int width = 0; for (const auto &opt: opts) width = std::max(width, (int)opt->optname.length()); for (const auto &opt: opts) { fprintf(output, " --%s%*c", opt->optname.c_str(), width - (int)opt->optname.length() + 4, ' '); if (opt->short_opt != -1) fprintf(output, "-%c\t", opt->short_opt); else fprintf(output, "\t\t"); fprintf(output, "%s\n", opt->optdoc.c_str()); } } std::vector split(const std::string &s, const std::string &delim) { std::vector res; size_t lastpos = 0; for (;;) { size_t pos = s.find(delim, lastpos); if (pos == std::string::npos) break; res.push_back(s.substr(lastpos, pos - lastpos)); lastpos = pos + delim.length(); } res.push_back(s.substr(lastpos, s.length() - lastpos)); return res; } std::string trim(const std::string &s, const std::string &space) { const auto new_begin = s.find_first_not_of(space); if (new_begin == std::string::npos) return ""; const auto new_end = s.find_last_not_of(space); return s.substr(new_begin, new_end - new_begin + 1); } std::vector trim_all(const std::vector &ss) { std::vector res; for (const auto &s: ss) res.push_back(trim(s)); return res; } }