diff options
Diffstat (limited to 'minecctl/misc-commands.c')
-rw-r--r-- | minecctl/misc-commands.c | 241 |
1 files changed, 238 insertions, 3 deletions
diff --git a/minecctl/misc-commands.c b/minecctl/misc-commands.c index e23de37..054279b 100644 --- a/minecctl/misc-commands.c +++ b/minecctl/misc-commands.c @@ -1,4 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include <limits.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> + #include "shared/utils.h" #include "minecctl.h" #include "server.h" @@ -6,11 +15,239 @@ #include "rcon-commands.h" #include "mc-commands.h" #include "shared/systemd.h" +#include "misc.h" + +static bool create_link(int dfd, const char *source, const char *target) +{ + if (symlinkat(target, dfd, source) < 0) { + error("Unable to create link %s -> %s: %m", source, target); + return false; + } + + return true; +} + +static bool write_cfg_file(int dfd, const char *name, const char *content) +{ + int fd; + ssize_t len = strlen(content); + ssize_t done = 0, r; + bool rv = true; + + fd = openat(dfd, name, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0644); + if (fd < 0) { + error("Unable to create file %s: %m", name); + return false; + } + + while (done < len) { + r = write(fd, content + done, len - done); + if (r < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + error("Unable to write file %s: %m", name); + rv = false; + break; + } + + done += r; + } + + close(fd); + return rv; +} + +static const char txt_server_readme[] = "This is the server readme\n"; +static const char txt_server_eula[] = "This is the server eula\n"; +static const char txt_server_properties[] = "This is the server properties\n"; +static const char txt_server_conf[] = "This is the server conf\n"; +static const char txt_minecproxy_conf[] = "This is the minecproxy conf\n"; +static const char txt_minecproxy_service[] = "This is the minecproxy service\n"; +static const char txt_minecserver_service[] = "This is the minecserver service\n"; + +static bool find_user_service(int xfd, const char *service) +{ + int dfd; + /* FIXME: Make this a macro, make paths #defines */ + char sub_path[STRLEN("systemd/user/") + strlen(service) + 1]; + char etc_path[STRLEN("/etc/systemd/user/") + strlen(service) + 1]; + char usr_path[STRLEN("/usr/lib/systemd/user/") + strlen(service) + 1]; + + sprintf(sub_path, "systemd/user/%s", service); + sprintf(etc_path, "/etc/systemd/user/%s", service); + sprintf(usr_path, "/usr/lib/systemd/user/%s", service); + + /* + * We need to check (in order of precedence): + * ~/.config/systemd/user/ - user-created + * /etc/systemd/user/ - admin-created + * ~/.local/share/systemd/user/ - user-installed packages + * /usr/lib/systemd/user/ - system-installed packages + */ + + if (faccessat(xfd, sub_path, R_OK, 0) == 0) { + info("User service %s already installed in " + "$XDG_CONFIG_HOME/systemd/user/", service); + return true; + } + + if (access(etc_path, R_OK) == 0) { + info("User service %s already installed in " + "/etc/systemd/user/", service); + return true; + } + + dfd = open_xdg_shared_dir(); + if (dfd >= 0) { + if (faccessat(dfd, sub_path, R_OK, 0) == 0) { + info("User service %s already installed in " + "$XDG_DATA_HOME/systemd/user/", service); + close(dfd); + return true; + } + close(dfd); + } + + if (access(usr_path, R_OK) == 0) { + info("User service %s already installed in " + "/usr/lib/systemd/user/", service); + return true; + } + + return false; +} + +static bool create_user_service(int xfd, const char *service, + const char *content) +{ + int sfd, ufd = -1; + bool rv = false; + + if (find_user_service(xfd, service)) + return true; + + sfd = open_subdir(xfd, "systemd", true); + if (sfd < 0) + goto out; + + ufd = open_subdir(sfd, "user", true); + if (ufd < 0) + goto out; + + if (!write_cfg_file(ufd, service, content)) + goto out; + + info("Created user service file $XDG_CONFIG_HOME/systemd/user/%s", + service); + rv = true; + +out: + if (ufd >= 0) + close(ufd); + if (sfd >= 0) + close(sfd); + return rv; +} + +static bool create_config_tree(int xfd) +{ + int mfd = -1, sfd = -1, efd = -1, cfd = -1; + bool rv = false; + + mfd = open_subdir(xfd, "minecproxy", true); + if (mfd < 0) + goto out; + + sfd = open_subdir(mfd, "servers", true); + if (sfd < 0) + goto out; + + if (!write_cfg_file(sfd, "README.TXT", txt_server_readme)) + goto out; + + efd = open_subdir(sfd, "example-server", true); + if (efd < 0) + goto out; + + if (!write_cfg_file(efd, "eula.txt", txt_server_eula)) + goto out; + + if (!write_cfg_file(efd, "server.properties", txt_server_properties)) + goto out; + + if (!create_link(efd, "server.jar", "../server.jar")) + goto out; + + cfd = open_subdir(mfd, "config", true); + if (cfd < 0) + goto out; + + if (!write_cfg_file(cfd, "example-server.mcserver", txt_server_conf)) + goto out; + + if (!write_cfg_file(cfd, "minecproxy.conf", txt_minecproxy_conf)) + goto out; + + info("Created configuration in $XDG_CONFIG_HOME/minecproxy"); + rv = true; + +out: + if (mfd >= 0) + close(mfd); + if (sfd >= 0) + close(sfd); + if (efd >= 0) + close(efd); + if (cfd >= 0) + close(cfd); + return rv; +} + +bool do_init(struct cfg *cfg) +{ + int xfd; + bool rv = false; + + if (cfg->dir_path) { + xfd = open(cfg->dir_path, O_PATH | O_CLOEXEC | O_DIRECTORY); + if (xfd < 0) + error("Failed to open dir %s: %m", cfg->dir_path); + } else + xfd = open_xdg_cfg_dir(true); + + if (xfd < 0) + goto out; + + if (!create_config_tree(xfd)) + goto out; + + if (cfg->dir_path) { + info("Option -c used, not creating systemd services"); + rv = true; + goto out; + } + + if (!create_user_service(xfd, "minecserver@.service", + txt_minecserver_service)) + goto out; + + if (!create_user_service(xfd, "minecproxy.service", + txt_minecproxy_service)) + goto out; + + rv = true; +out: + if (xfd >= 0) + close(xfd); + return rv; +} bool do_list(struct cfg *cfg) { struct server *server; + server_load_all_known(cfg); + /* server->scfg.filename check excludes servers created from cmdline */ list_for_each_entry(server, &cfg->servers, list) if (server->scfg.filename) @@ -112,10 +349,8 @@ bool do_pcount(struct cfg *cfg) const char *error; server = server_get_default(cfg); - if (!server) { - error("failed to get default server"); + if (!server) return false; - } if (do_rcon_pcount(cfg, server, &online, &max, &error)) info("Rcon says %u/%u", online, max); |