/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include "shared/utils.h" #include "misc.h" #include "minecctl.h" static const char *get_homedir() { const char *e; struct passwd *passwd; uid_t uid; e = getenv("HOME"); if (e && e[0] == '/') return e; uid = getuid(); if (uid == 0) return "/root"; passwd = getpwuid(uid); if (passwd && passwd->pw_dir[0] == '/') return passwd->pw_dir; return NULL; } int open_subdir(int dfd, const char *subdir, bool nofail) { int sfd; if (nofail && mkdirat(dfd, subdir, 0777) < 0 && errno != EEXIST) { error("Unable to create subdirectory %s: %m", subdir); return -1; } sfd = openat(dfd, subdir, O_PATH | O_CLOEXEC | O_DIRECTORY); if (sfd < 0 && nofail) error("Unable to open subdirectory %s: %m", subdir); return sfd; } int open_xdg_dir(const char *envname, const char *altpath, bool nofail) { const char *e; const char *h; int dfd, hfd; e = getenv(envname); if (e && e[0] == '/') { dfd = open(e, O_PATH | O_CLOEXEC | O_DIRECTORY); if (dfd < 0) error("Unable to open $%s(%s): %m", envname, e); return dfd; } h = get_homedir(); if (!h) { error("Unable to determine home directory"); return -1; } hfd = open(h, O_PATH | O_CLOEXEC | O_DIRECTORY); if (hfd < 0) { error("Unable to open $HOME(%s): %m", h); return -1; } dfd = open_subdir(hfd, altpath, nofail); close(hfd); return dfd; } /* 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; enable_colors(); } char **strv_copy(char *const *strv) { size_t len = 1; char **rv; char **to; for (char *const *str = strv; *str; str++) len++; rv = zmalloc(len * sizeof(char *)); to = rv; for (char *const *str = strv; *str; str++) *(to++) = xstrdup(*str); return rv; } char **strv_from_strs(const char *first, ...) { size_t len = 1; va_list ap; char **rv, **to; if (first) { va_start(ap, first); while (va_arg(ap, const char *)) len++; va_end(ap); } rv = zmalloc(len * sizeof(char *)); to = rv; *(to++) = first ? xstrdup(first) : NULL; if (first) { const char *str; va_start(ap, first); while ((str = va_arg(ap, const char *))) *(to++) = xstrdup(str); va_end(ap); *to = NULL; } return rv; } char *strv_join(char *const *strv) { size_t len = 0; char *rv, *to; for (char *const *str = strv; *str; str++) len += strlen(*str) + 1; if (len == 0) return NULL; rv = zmalloc(len); to = rv; for (char *const *str = strv; *str; str++) { if (str != strv) *(to++) = ' '; to = stpcpy(to, *str); } return rv; } void strv_free(char **strv) { if (!strv) return; for (char **str = strv; *str; str++) xfree(*str); xfree(strv); } int connect_any(struct list_head *addrs, struct saddr **rsaddr, const char **error) { struct saddr *saddr; int sfd; if (list_empty(addrs)) { *error = "no address to connect to"; return -1; } list_for_each_entry(saddr, addrs, list) { verbose("Attempting connection to %s", saddr->addrstr); sfd = socket(saddr->st.ss_family, SOCK_STREAM | SOCK_CLOEXEC, 0); if (sfd < 0) { *error = "failed to create socket"; return -1; } socket_set_low_latency(sfd, true, true, true); if (connect(sfd, (struct sockaddr *)&saddr->st, saddr->addrlen) < 0) { close(sfd); sfd = -1; continue; } if (rsaddr) *rsaddr = saddr; break; } if (sfd < 0) { *error = "failed to connect to remote host"; return -1; } return sfd; } 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; } void __debug(_unused_ enum debug_lvl lvl, const char *fmt, ...) { va_list ap; const char *color = NULL; if (lvl & DBG_ERROR) color = ansi_red; else if (!(lvl & (DBG_INFO | DBG_VERBOSE))) color = ansi_grey; if (color) fprintf(stderr, "%s", color); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); if (color) fprintf(stderr, "%s", 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); }