summaryrefslogtreecommitdiff
path: root/minecctl/misc-commands.c
diff options
context:
space:
mode:
Diffstat (limited to 'minecctl/misc-commands.c')
-rw-r--r--minecctl/misc-commands.c241
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);