diff options
Diffstat (limited to 'minecctl')
-rw-r--r-- | minecctl/meson.build | 1 | ||||
-rw-r--r-- | minecctl/minecctl-rcon.c | 80 | ||||
-rw-r--r-- | minecctl/minecctl.c | 468 | ||||
-rw-r--r-- | minecctl/minecctl.h | 22 | ||||
-rw-r--r-- | minecctl/misc.c | 215 | ||||
-rw-r--r-- | minecctl/misc.h | 14 |
6 files changed, 457 insertions, 343 deletions
diff --git a/minecctl/meson.build b/minecctl/meson.build index 9f320a6..6db6489 100644 --- a/minecctl/meson.build +++ b/minecctl/meson.build @@ -1,6 +1,7 @@ minecctl_sources = [ 'minecctl.c', 'minecctl-rcon.c', + 'misc.c', ] dep_readline = dependency('readline') diff --git a/minecctl/minecctl-rcon.c b/minecctl/minecctl-rcon.c index fd880f5..b97e03e 100644 --- a/minecctl/minecctl-rcon.c +++ b/minecctl/minecctl-rcon.c @@ -13,6 +13,7 @@ #include "minecctl.h" #include "minecctl-rcon.h" #include "rcon-protocol.h" +#include "misc.h" static void send_packet(int sfd, const char *buf, size_t len) @@ -101,31 +102,34 @@ send_msg(int sfd, char *buf, size_t len, enum rcon_packet_type type, } static int -rcon_login(struct cfg *cfg) +rcon_login(struct cfg *cfg, struct server *server) { char buf[4096]; int32_t rtype; const char *reply; int fd; - assert_die(cfg, "invalid arguments"); + assert_die(cfg && server, "invalid arguments"); - if (!cfg->password) - cfg->password = ask_password(); + if (list_empty(&server->rcon_addrs)) + info("server rcon addrs empty"); - if (!cfg->password) - die("Can't login - no password"); + fd = connect_any(&server->rcon_addrs, false); + + if (!server->rcon_password) + server->rcon_password = ask_password(); - fd = connect_any(&cfg->addrs, false); + if (!server->rcon_password) + die("Can't login - no password"); - send_msg(fd, buf, sizeof(buf), RCON_PACKET_LOGIN, cfg->password, + send_msg(fd, buf, sizeof(buf), RCON_PACKET_LOGIN, server->rcon_password, &rtype, &reply); /* An rcon password isn't exactly super-secret, but can't hurt */ explicit_bzero(buf, sizeof(buf)); - explicit_bzero(cfg->password, strlen(cfg->password)); - xfree(cfg->password); - cfg->password = NULL; + explicit_bzero(server->rcon_password, strlen(server->rcon_password)); + xfree(server->rcon_password); + server->rcon_password = NULL; if (rtype == RCON_PACKET_LOGIN_OK) info("Login ok"); @@ -233,10 +237,14 @@ do_status(struct cfg *cfg) { unsigned epacks, apacks; unsigned bannedplayers, bannedips; int fd; + struct server *server; - assert_die(cfg, "invalid arguments"); + if (list_empty(&cfg->servers)) + die("No servers defined"); - fd = rcon_login(cfg); + server = list_first_entry(&cfg->servers, struct server, list); + read_server_config(server); + fd = rcon_login(cfg, server); if (get_one_status(fd, buf, sizeof(buf), "seed", 1, "Seed : [ %[^]]]", &reply, tbuf)) @@ -311,10 +319,15 @@ get_player_count(int fd, unsigned *current, unsigned *max) void do_stop(struct cfg *cfg) { int fd; + struct server *server; + + if (list_empty(&cfg->servers)) + die("No servers defined"); - assert_die(cfg, "invalid arguments"); + server = list_first_entry(&cfg->servers, struct server, list); + read_server_config(server); + fd = rcon_login(cfg, server); - fd = rcon_login(cfg); if (cfg->force_stop) { unsigned current, _unused_ max; @@ -349,10 +362,15 @@ void do_pcount(struct cfg *cfg) { int fd; unsigned current, max; + struct server *server; + + if (list_empty(&cfg->servers)) + die("No servers defined"); - assert_die(cfg, "invalid arguments"); + server = list_first_entry(&cfg->servers, struct server, list); + read_server_config(server); + fd = rcon_login(cfg, server); - fd = rcon_login(cfg); if (get_player_count(fd, ¤t, &max)) info("Players: %u/%u", current, max); else @@ -364,23 +382,19 @@ do_console(struct cfg *cfg) { char *prompt; char *cmd; - const char *sname; int fd; + struct server *server; - assert_die(cfg, "invalid arguments"); - - fd = rcon_login(cfg); + if (list_empty(&cfg->servers)) + die("No servers defined"); - if (cfg->server) - sname = cfg->server->shortname; - else if (cfg->addrstr) - sname = cfg->addrstr; - else - die("can't find server name"); + server = list_first_entry(&cfg->servers, struct server, list); + read_server_config(server); + fd = rcon_login(cfg, server); prompt = alloca(strlen(program_invocation_short_name) + - STRLEN(" (") + strlen(sname) + STRLEN("): ") + 1); - sprintf(prompt, "%s (%s): ", program_invocation_short_name, sname); + STRLEN(" (") + strlen(server->shortname) + STRLEN("): ") + 1); + sprintf(prompt, "%s (%s): ", program_invocation_short_name, server->shortname); while (true) { char *tmp; @@ -415,10 +429,14 @@ do_console(struct cfg *cfg) void do_command(struct cfg *cfg) { int fd; + struct server *server; - assert_die(cfg, "invalid arguments"); + if (list_empty(&cfg->servers)) + die("No servers defined"); - fd = rcon_login(cfg); + server = list_first_entry(&cfg->servers, struct server, list); + read_server_config(server); + fd = rcon_login(cfg, server); send_cmd(fd, cfg->cmdstr); } diff --git a/minecctl/minecctl.c b/minecctl/minecctl.c index 7c01736..c8cc34d 100644 --- a/minecctl/minecctl.c +++ b/minecctl/minecctl.c @@ -2,7 +2,6 @@ #include <stdlib.h> #include <stdint.h> #include <inttypes.h> -#include <stdarg.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> @@ -19,143 +18,68 @@ #include "minecctl-rcon.h" #include "config-parser.h" #include "server-config-options.h" +#include "misc.h" #include "config.h" static struct cfg *cfg = NULL; -bool use_colors = false; - -/* FIXME: Can be shared */ static void -set_use_colors() -{ - int fd; - const char *e; - - if (getenv("NO_COLOR")) - return; - - fd = fileno(stderr); - if (fd < 0) - return; - - if (!isatty(fd)) - return; - - e = getenv("TERM"); - if (!e) - return; - - if (streq(e, "dumb")) - return; - - use_colors = true; -} - -void -__debug(_unused_ enum debug_lvl lvl, const char *fmt, ...) -{ - va_list ap; - - if (use_colors) { - if (lvl & DBG_ERROR) - fprintf(stderr, ANSI_RED); - else if (!(lvl & (DBG_INFO | DBG_VERBOSE))) - fprintf(stderr, ANSI_GREY); - } - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - if (use_colors && !(lvl & (DBG_INFO | DBG_VERBOSE))) - fprintf(stderr, ANSI_NORMAL); -} - -_noreturn_ void -__die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - exit(EXIT_FAILURE); -} - -void * -__zmalloc(const char *fn, int line, size_t size) +dump_config() { - void *ptr; - - assert_die(!empty_str(fn) && line > 0 && size > 0, "invalid arguments"); - - ptr = calloc(1, size); - if (!ptr) - die("malloc: %m"); - return ptr; + info("Configuration:"); + info("password : %s", cfg->password); + info("cfgdir : %s", cfg->cfgdir); + info("addrstr : %s", cfg->addrstr); + info("cmdstr : %s", cfg->cmdstr); + info("cmd : %p", cfg->cmd); + info("force stop : %s", cfg->force_stop ? "yes" : "no"); + info("servers : %sempty", list_empty(&cfg->servers) ? "" : "not "); + /* FIXME: dump servers */ } -char * -__xstrdup(const char *fn, int line, const char *s) +void +read_server_config(struct server *server) { - char *ptr; - - assert_die(!empty_str(fn) && line > 0 && !empty_str(s), "invalid arguments"); - - ptr = strdup(s); - if (!ptr) - die("strdup: %m"); - return ptr; -} + char buf[4096]; + size_t off = 0; + ssize_t r; + int dfd; + int fd; + char *pos = buf; -char * -__xstrndup(const char *fn, int line, const char *s, size_t n) -{ - char *ptr; + if (!server || !server->filename || server->file_read) + return; - assert_die(!empty_str(fn) && line > 0 && !empty_str(s) && n > 0, "invalid arguments"); + server->file_read = true; - ptr = strndup(s, n); - if (ptr) - die("strdup: %m"); - return ptr; -} + dfd = open(cfg->cfgdir, O_DIRECTORY | O_PATH | O_CLOEXEC); + if (dfd < 0) + die("Failed to open %s: %m", cfg->cfgdir); -void -__xfree(const char *fn, int line, void *ptr) -{ - assert_die(!empty_str(fn) && line > 0, "invalid arguments"); + fd = openat(dfd, server->filename, O_RDONLY | O_CLOEXEC); + if (fd < 0) + die("Failed to open %s: %m", server->filename); - free(ptr); -} + close(dfd); -static void -dump_config() -{ - assert_die(cfg, "cfg not set"); + while (true) { + r = read(fd, buf + off, sizeof(buf) - off - 1); + if (r < 0) + die("Failed to read %s: %m", server->filename); + else if (r == 0) + break; - info("Configuration:"); - info("password : %s", cfg->password); - info("cfgdir : %s", cfg->cfgdir); - info("addrstr : %s", cfg->addrstr); - info("cmdstr : %s", cfg->cmdstr); - info("server : %p", cfg->server); - info("cmd : %p", cfg->cmd); - info("force stop : %s", cfg->force_stop ? "yes" : "no"); - info("addrs : %sempty", list_empty(&cfg->addrs) ? "" : "not "); - info("mcaddrs : %sempty", list_empty(&cfg->mcaddrs) ? "" : "not "); - info("known_servers : %sempty", list_empty(&cfg->addrs) ? "" : "not "); -} + off += r; + if (off == sizeof(buf) - 1) + die("Failed to read %s: file too large", server->filename); + } -static void -parse_server_config(char *buf, const char *filename) -{ - assert_die(buf && filename && cfg, "invalid arguments"); + buf[off] = '\0'; + close(fd); - if (!config_parse_header(SERVER_CFG_HEADER, &buf)) - die("Unable to parse %s: invalid/missing header", filename); + if (!config_parse_header(SERVER_CFG_HEADER, &pos)) + die("Unable to parse %s: invalid/missing header", + server->filename); /* FIXME: this will cause superflous DNS lookups of other cfg entries */ while (true) { @@ -163,82 +87,41 @@ parse_server_config(char *buf, const char *filename) const char *keyname; struct cfg_value value; - if (!config_parse_line(filename, &buf, scfg_key_map, + if (!config_parse_line(server->filename, &pos, scfg_key_map, &key, &keyname, &value, false)) break; switch (key) { case SCFG_KEY_RCON: - if (!list_empty(&cfg->addrs)) - die("rcon address defined twice in %s", filename); - list_replace(&value.saddrs, &cfg->addrs); + if (!list_empty(&server->rcon_addrs)) + die("rcon address defined twice in %s", + server->filename); + list_replace(&value.saddrs, &server->rcon_addrs); break; case SCFG_KEY_RCON_PASSWORD: - if (!cfg->password) - cfg->password = xstrdup(value.str); + if (server->rcon_password) + die("rcon password defined twice in %s", + server->filename); + server->rcon_password = xstrdup(value.str); break; case SCFG_KEY_REMOTE: - if (!list_empty(&cfg->mcaddrs)) - die("rcon address defined twice in %s", filename); - list_replace(&value.saddrs, &cfg->mcaddrs); + if (!list_empty(&server->mc_addrs)) + die("rcon address defined twice in %s", + server->filename); + list_replace(&value.saddrs, &server->mc_addrs); default: continue; } - - if (cfg->password && - !list_empty(&cfg->addrs) && - !list_empty(&cfg->mcaddrs)) - break; } - if (!cfg->password) - verbose("rcon password not found in %s", filename); + if (!server->rcon_password) + verbose("rcon password not found in %s", server->filename); - if (list_empty(&cfg->addrs)) - verbose("rcon address not found in %s", filename); + if (list_empty(&server->rcon_addrs)) + verbose("rcon address not found in %s", server->filename); - if (list_empty(&cfg->mcaddrs)) - verbose("mc server address not found in %s", filename); -} - -static void -read_server_config() -{ - char buf[4096]; - size_t off = 0; - ssize_t r; - int dfd; - int fd; - - assert_die(cfg && cfg->server && cfg->cfgdir, "invalid arguments"); - - dfd = open(cfg->cfgdir, O_DIRECTORY | O_PATH | O_CLOEXEC); - if (dfd < 0) - die("Failed to open %s: %m", cfg->cfgdir); - - fd = openat(dfd, cfg->server->filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) - die("Failed to open %s: %m", cfg->server->filename); - - close(dfd); - - while (true) { - r = read(fd, buf + off, sizeof(buf) - off - 1); - if (r < 0) - die("Failed to read %s: %m", cfg->server->filename); - else if (r == 0) - break; - - off += r; - if (off == sizeof(buf) - 1) - die("Failed to read %s: file too large", cfg->server->filename); - } - - buf[off] = '\0'; - close(fd); - - parse_server_config(buf, cfg->server->filename); - dump_config(); + if (list_empty(&server->mc_addrs)) + verbose("mc server address not found in %s", server->filename); } _noreturn_ static void @@ -279,32 +162,21 @@ usage(bool no_error) exit(no_error ? EXIT_FAILURE : EXIT_SUCCESS); } -static char * -strv_join(char * const *strv) +static struct server * +allocate_server() { - size_t len = 0; - char *r, *to; - - for (unsigned i = 0; strv[i]; i++) - len += strlen(strv[i]) + 1; - - if (len == 0) - return NULL; - - r = zmalloc(len); - to = r; + struct server *server; - for (unsigned i = 0; strv[i]; i++) { - if (i > 0) - *(to++) = ' '; - to = stpcpy(to, strv[i]); - } + server = zmalloc(sizeof(*server)); + list_init(&server->rcon_addrs); + list_init(&server->mc_addrs); + list_init(&server->list); - return r; + return server; } static void -get_known_servers() +get_servers() { struct dirent *dent; DIR *dir; @@ -322,7 +194,7 @@ get_known_servers() if (!is_valid_server_config_filename(dent, NULL)) continue; - server = zmalloc(sizeof(*server)); + server = allocate_server(); server->filename = xstrdup(dent->d_name); suffix = strrchr(dent->d_name, '.'); @@ -330,112 +202,116 @@ get_known_servers() *suffix = '\0'; server->shortname = xstrdup(dent->d_name); - list_add(&server->list, &cfg->known_servers); + list_add(&server->list, &cfg->servers); } closedir(dir); } -static void -do_list(struct cfg *cfg) -{ - struct server *server; - - list_for_each_entry(server, &cfg->known_servers, list) - info("%s", server->shortname); -} - -static void -set_server(const char *name) +static bool +str_to_addrs(const char *str, struct list_head *list) { - struct server *server; + struct cfg_value value; + char *tmp = NULL; + bool rv = false; + + /* strtosockaddrs mangles the input string */ + tmp = xstrdup(str); + if (!strtosockaddrs(tmp, &value, false)) { + error("Unable to parse address: %s", str); + goto out; + } - assert_die(cfg, "invalid arguments"); - assert_die(!cfg->server, "can't set server twice"); + if (value.type != CFG_VAL_TYPE_ADDRS) { + error("Unexpected return value from strtosockaddrs"); + goto out; + } - list_for_each_entry(server, &cfg->known_servers, list) { - if (streq(name, server->shortname)) { - cfg->server = server; - return; - } + if (list_empty(&value.saddrs)) { + error("Found no valid addresses for %s", str); + goto out; } - error("\"%s\" is not a known server or command", name); - usage(false); + list_replace(&value.saddrs, list); + rv = true; + +out: + xfree(tmp); + return rv; } -char * -ask_password() +static bool +create_server_from_cmdline_args() { - struct termios old, new; - char *password = NULL; - size_t len = 0; - ssize_t r; + struct server *server; - assert_die(cfg, "invalid arguments"); + if (!cfg->addrstr && !cfg->mcaddrstr) + return false; - if (!isatty(STDIN_FILENO)) - return NULL; + server = allocate_server(); - if (tcgetattr(STDIN_FILENO, &old) < 0) - return NULL; + if (cfg->addrstr) { + if (!str_to_addrs(cfg->addrstr, &server->rcon_addrs)) + goto error; - new = old; - new.c_lflag &= ~ECHO; - new.c_lflag |= ICANON; - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new) < 0) - return NULL; + server->shortname = cfg->addrstr; + cfg->addrstr = NULL; + } - fprintf(stderr, "Password: "); - r = getline(&password, &len, stdin); + if (cfg->mcaddrstr) { + if (!str_to_addrs(cfg->mcaddrstr, &server->mc_addrs)) + goto error; - tcsetattr(STDIN_FILENO, TCSAFLUSH, &old); + if (!server->shortname) + server->shortname = cfg->mcaddrstr; + else + xfree(cfg->mcaddrstr); - if (r < 0) { - info("Error in getline: %m"); - clearerr(stdin); - free(password); - return NULL; + cfg->mcaddrstr = NULL; } - while (r > 0 && password[r - 1] == '\n') - password[--r] = '\0'; + if (cfg->password) { + server->rcon_password = cfg->password; + cfg->password = NULL; + } - return password; + list_add(&server->list, &cfg->servers); + return true; + +error: + /* FIXME: free addrs */ + xfree(server->shortname); + xfree(server); + return false; } -int -connect_any(struct list_head *addrs, bool may_fail) +static void +do_list(struct cfg *cfg) { - struct saddr *saddr; - bool connected = false; - int sfd; + struct server *server; - if (list_empty(addrs)) - die("No address to connect to"); + list_for_each_entry(server, &cfg->servers, list) + info("%s", server->shortname); +} - list_for_each_entry(saddr, addrs, list) { - sfd = socket(saddr->storage.ss_family, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (sfd < 0) - die("socket: %m"); +static void +set_server(const char *name) +{ + struct server *server; - socket_set_low_latency(sfd, true, true, true); + assert_die(cfg, "invalid arguments"); - if (connect(sfd, (struct sockaddr *)&saddr->storage, saddr->addrlen) < 0) { - close(sfd); - continue; + list_for_each_entry(server, &cfg->servers, list) { + if (streq(name, server->shortname)) { + /* This puts the chosen server first in the list */ + list_del(&server->list); + list_add(&server->list, &cfg->servers); + return; } - - connected = true; - break; } - if (!connected && may_fail) - return -1; - else if (!connected) - die("Failed to connect to remote host"); - else - return sfd; + error("\"%s\" is not a known server or command", name); + usage(false); } static inline void @@ -561,7 +437,6 @@ parse_cmdline(int argc, char * const *argv) usage(false); } - list_init(&cfg->addrs); cfg->cfgdir = DEFAULT_CFG_DIR; while (true) { @@ -632,52 +507,43 @@ parse_cmdline(int argc, char * const *argv) int main(int argc, char **argv) { + int rv = EXIT_FAILURE; + debug_mask = DBG_ERROR | DBG_INFO; set_use_colors(); cfg = zmalloc(sizeof(*cfg)); - list_init(&cfg->addrs); - list_init(&cfg->mcaddrs); - list_init(&cfg->known_servers); + list_init(&cfg->servers); parse_cmdline(argc, argv); - get_known_servers(); + get_servers(); parse_command(&argv[optind]); - if (!cfg->cmd) - die("Failed to parse command"); - - if (cfg->server) { - read_server_config(); - - } else if (cfg->addrstr) { - struct cfg_value value; - - /* FIXME: create a struct server here, fill in details like name */ - if (!strtosockaddrs(cfg->addrstr, &value, false)) - die("Unable to parse address: %s", cfg->addrstr); - - if (value.type != CFG_VAL_TYPE_ADDRS) - die("Unexpected return value from strtosockaddrs"); - - if (list_empty(&value.saddrs)) - die("Found no valid addresses for %s", cfg->addrstr); - - list_replace(&value.saddrs, &cfg->addrs); + if (!cfg->cmd) { + error("Failed to parse command"); + goto out; } + if (cfg->addrstr || cfg->mcaddrstr) + if (!create_server_from_cmdline_args()) + goto out; + + /* FIXME: Should return bool */ cfg->cmd(cfg); + rv = EXIT_SUCCESS; + +out: + /* FIXME: Not enough cleanup */ if (cfg->password) explicit_bzero(cfg->password, strlen(cfg->password)); - xfree(cfg->password); xfree(cfg->addrstr); xfree(cfg->mcaddrstr); xfree(cfg); - exit(EXIT_SUCCESS); + exit(rv); } diff --git a/minecctl/minecctl.h b/minecctl/minecctl.h index 405a217..7146a08 100644 --- a/minecctl/minecctl.h +++ b/minecctl/minecctl.h @@ -2,30 +2,30 @@ #define foominecctlhfoo struct server { + bool file_read; char *filename; char *shortname; + char *rcon_password; + struct list_head rcon_addrs; + struct list_head mc_addrs; struct list_head list; }; struct cfg { - char *password; + /* command line arguments */ char *cfgdir; + char *password; char *addrstr; char *mcaddrstr; char *cmdstr; - struct server *server; - void (*cmd)(struct cfg *cfg); bool force_stop; - struct list_head addrs; - struct list_head mcaddrs; - struct list_head known_servers; -}; -extern bool use_colors; - -char *ask_password(); + /* bookkeeping */ + void (*cmd)(struct cfg *cfg); + struct list_head servers; +}; -int connect_any(struct list_head *addrs, bool may_fail); +void read_server_config(struct server *server); #endif diff --git a/minecctl/misc.c b/minecctl/misc.c new file mode 100644 index 0000000..13b4377 --- /dev/null +++ b/minecctl/misc.c @@ -0,0 +1,215 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <string.h> +#include <termios.h> + +#include "utils.h" +#include "minecctl.h" + +bool use_colors = false; + +/* FIXME: Can be shared */ +void +set_use_colors() +{ + int fd; + const char *e; + + if (getenv("NO_COLOR")) + return; + + fd = fileno(stderr); + if (fd < 0) + return; + + if (!isatty(fd)) + return; + + e = getenv("TERM"); + if (!e) + return; + + if (streq(e, "dumb")) + return; + + use_colors = true; +} + +char * +strv_join(char * const *strv) +{ + size_t len = 0; + char *r, *to; + + for (unsigned i = 0; strv[i]; i++) + len += strlen(strv[i]) + 1; + + if (len == 0) + return NULL; + + r = zmalloc(len); + to = r; + + for (unsigned i = 0; strv[i]; i++) { + if (i > 0) + *(to++) = ' '; + to = stpcpy(to, strv[i]); + } + + return r; +} + +char * +ask_password() +{ + struct termios old, new; + char *password = NULL; + size_t len = 0; + ssize_t r; + + if (!isatty(STDIN_FILENO)) + return NULL; + + if (tcgetattr(STDIN_FILENO, &old) < 0) + return NULL; + + new = old; + new.c_lflag &= ~ECHO; + new.c_lflag |= ICANON; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new) < 0) + return NULL; + + fprintf(stderr, "Password: "); + r = getline(&password, &len, stdin); + + tcsetattr(STDIN_FILENO, TCSAFLUSH, &old); + + if (r < 0) { + info("Error in getline: %m"); + clearerr(stdin); + free(password); + return NULL; + } + + while (r > 0 && password[r - 1] == '\n') + password[--r] = '\0'; + + return password; +} + +int +connect_any(struct list_head *addrs, bool may_fail) +{ + struct saddr *saddr; + bool connected = false; + int sfd; + + if (list_empty(addrs)) + die("No address to connect to"); + + list_for_each_entry(saddr, addrs, list) { + verbose("Attempting connection to %s", saddr->addrstr); + sfd = socket(saddr->storage.ss_family, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sfd < 0) + die("socket: %m"); + + socket_set_low_latency(sfd, true, true, true); + + if (connect(sfd, (struct sockaddr *)&saddr->storage, saddr->addrlen) < 0) { + close(sfd); + continue; + } + + connected = true; + break; + } + + if (!connected && may_fail) + return -1; + else if (!connected) + die("Failed to connect to remote host"); + + return sfd; +} + +void +__debug(_unused_ enum debug_lvl lvl, const char *fmt, ...) +{ + va_list ap; + + if (use_colors) { + if (lvl & DBG_ERROR) + fprintf(stderr, ANSI_RED); + else if (!(lvl & (DBG_INFO | DBG_VERBOSE))) + fprintf(stderr, ANSI_GREY); + } + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (use_colors && !(lvl & (DBG_INFO | DBG_VERBOSE))) + fprintf(stderr, ANSI_NORMAL); +} + +_noreturn_ void +__die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + exit(EXIT_FAILURE); +} + +void * +__zmalloc(const char *fn, int line, size_t size) +{ + void *ptr; + + assert_die(!empty_str(fn) && line > 0 && size > 0, "invalid arguments"); + + ptr = calloc(1, size); + if (!ptr) + die("malloc: %m"); + return ptr; +} + +char * +__xstrdup(const char *fn, int line, const char *s) +{ + char *ptr; + + assert_die(!empty_str(fn) && line > 0 && !empty_str(s), "invalid arguments"); + + ptr = strdup(s); + if (!ptr) + die("strdup: %m"); + return ptr; +} + +char * +__xstrndup(const char *fn, int line, const char *s, size_t n) +{ + char *ptr; + + assert_die(!empty_str(fn) && line > 0 && !empty_str(s) && n > 0, "invalid arguments"); + + ptr = strndup(s, n); + if (ptr) + die("strdup: %m"); + return ptr; +} + +void +__xfree(const char *fn, int line, void *ptr) +{ + assert_die(!empty_str(fn) && line > 0, "invalid arguments"); + + free(ptr); +} + diff --git a/minecctl/misc.h b/minecctl/misc.h new file mode 100644 index 0000000..ca36dab --- /dev/null +++ b/minecctl/misc.h @@ -0,0 +1,14 @@ +#ifndef foomischfoo +#define foomischfoo + +extern bool use_colors; + +void set_use_colors(); + +char *strv_join(char * const *strv); + +int connect_any(struct list_head *addrs, bool may_fail); + +char *ask_password(); + +#endif |