summaryrefslogtreecommitdiff
path: root/src/core/daemon_wrap.c
diff options
context:
space:
mode:
authorinfo@mode42.com <info@mode42.com>2026-08-08 03:54:55 +0000
committerinfo@mode42.com <info@mode42.com>2026-08-08 03:54:55 +0000
commit20cb29c2f8c5c87bc590896854a20b1473ceb358 (patch)
tree2857f41513a56ad41af97b57362639298aa7f033 /src/core/daemon_wrap.c
#2
Diffstat (limited to 'src/core/daemon_wrap.c')
-rw-r--r--src/core/daemon_wrap.c320
1 files changed, 320 insertions, 0 deletions
diff --git a/src/core/daemon_wrap.c b/src/core/daemon_wrap.c
new file mode 100644
index 0000000..04c990a
--- /dev/null
+++ b/src/core/daemon_wrap.c
@@ -0,0 +1,320 @@
+#if defined(__linux__)
+#define _DEFAULT_SOURCE
+#endif
+
+#include "hybbx/daemon_wrap.h"
+#include "hybbx/limits.h"
+#include "hybbx/util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void hybbx_daemon_launch_opts_defaults(hybbx_daemon_launch_opts_t *opts)
+{
+ if (opts == NULL) {
+ return;
+ }
+
+ memset(opts, 0, sizeof(*opts));
+ hybbx_strlcpy(opts->session, HYBBX_DAEMON_DEFAULT_SESSION,
+ sizeof(opts->session));
+}
+
+static int path_executable(const char *path)
+{
+ return path != NULL && path[0] != '\0' && access(path, X_OK) == 0;
+}
+
+static int find_in_path(const char *name, char *out, size_t out_len)
+{
+ const char *path_env;
+ char copy[HYBBX_PATH_MAX];
+ char *save = NULL;
+ char *dir;
+
+ if (name == NULL || out == NULL || out_len == 0) {
+ return 0;
+ }
+
+ path_env = getenv("PATH");
+ if (path_env == NULL || path_env[0] == '\0') {
+ return 0;
+ }
+
+ snprintf(copy, sizeof(copy), "%s", path_env);
+ for (dir = strtok_r(copy, ":", &save); dir != NULL;
+ dir = strtok_r(NULL, ":", &save)) {
+ if (hybbx_path_join(out, out_len, dir, name) != HYBBX_OK) {
+ continue;
+ }
+ if (path_executable(out)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int wrapped_child(void)
+{
+ const char *env = getenv(HYBBX_DAEMON_WRAP_ENV);
+
+ return env != NULL && env[0] == '1';
+}
+
+static int in_screen(void)
+{
+ const char *sty = getenv("STY");
+
+ return sty != NULL && sty[0] != '\0';
+}
+
+static int in_tmux(void)
+{
+ const char *tmux = getenv("TMUX");
+
+ return tmux != NULL && tmux[0] != '\0';
+}
+
+static int arg_is_launch_flag(const char *arg)
+{
+ if (arg == NULL) {
+ return 0;
+ }
+
+ return strcmp(arg, "-f") == 0 || strcmp(arg, "--foreground") == 0 ||
+ strcmp(arg, "--attach") == 0 || strcmp(arg, "--screen") == 0 ||
+ strcmp(arg, "--tmux") == 0;
+}
+
+static int build_inner_argv(char **out, int out_max,
+ const char *binary,
+ int argc, char **argv)
+{
+ int outc = 0;
+ int i;
+
+ if (out == NULL || out_max < 2 || binary == NULL) {
+ return -1;
+ }
+
+ out[outc++] = (char *)binary;
+
+ if (!wrapped_child()) {
+ out[outc++] = "-f";
+ }
+
+ for (i = 1; i < argc && outc < out_max - 1; i++) {
+ if (strcmp(argv[i], "--screen") == 0 || strcmp(argv[i], "--tmux") == 0) {
+ if (i + 1 < argc && argv[i + 1][0] != '-' &&
+ !arg_is_launch_flag(argv[i + 1])) {
+ i++;
+ }
+ continue;
+ }
+ if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--foreground") == 0 ||
+ strcmp(argv[i], "--attach") == 0) {
+ continue;
+ }
+
+ out[outc++] = argv[i];
+ }
+
+ out[outc] = NULL;
+ return outc;
+}
+
+static hybbx_result_t exec_screen_attach(const char *session)
+{
+ char screen_path[HYBBX_PATH_MAX];
+ char *args[8];
+ int n = 0;
+
+ if (!find_in_path("screen", screen_path, sizeof(screen_path))) {
+ fprintf(stderr, "hybbxd: GNU screen not found in PATH\n");
+ return HYBBX_ERR_IO;
+ }
+
+ args[n++] = screen_path;
+ args[n++] = "-r";
+ args[n++] = (char *)session;
+ args[n++] = NULL;
+
+ execv(screen_path, args);
+ fprintf(stderr, "hybbxd: screen attach failed\n");
+ return HYBBX_ERR_IO;
+}
+
+static hybbx_result_t exec_tmux_attach(const char *session)
+{
+ char tmux_path[HYBBX_PATH_MAX];
+ char *args[8];
+ int n = 0;
+
+ if (!find_in_path("tmux", tmux_path, sizeof(tmux_path))) {
+ fprintf(stderr, "hybbxd: tmux not found in PATH\n");
+ return HYBBX_ERR_IO;
+ }
+
+ args[n++] = tmux_path;
+ args[n++] = "attach";
+ args[n++] = "-t";
+ args[n++] = (char *)session;
+ args[n++] = NULL;
+
+ execv(tmux_path, args);
+ fprintf(stderr, "hybbxd: tmux attach failed\n");
+ return HYBBX_ERR_IO;
+}
+
+static hybbx_result_t spawn_screen(const char *session, const char *binary,
+ int argc, char **argv)
+{
+ char screen_path[HYBBX_PATH_MAX];
+ char *inner[HYBBX_CMD_TOKEN_MAX + 4];
+ char wrap_env[64];
+ int innerc;
+ char *args[HYBBX_CMD_TOKEN_MAX + 8];
+ int n = 0;
+
+ if (!find_in_path("screen", screen_path, sizeof(screen_path))) {
+ fprintf(stderr, "hybbxd: GNU screen not found in PATH\n");
+ return HYBBX_ERR_IO;
+ }
+
+ innerc = build_inner_argv(inner, (int)(sizeof(inner) / sizeof(inner[0])),
+ binary, argc, argv);
+ if (innerc < 0) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ snprintf(wrap_env, sizeof(wrap_env), "%s=1", HYBBX_DAEMON_WRAP_ENV);
+
+ args[n++] = screen_path;
+ args[n++] = "-dmS";
+ args[n++] = (char *)session;
+ args[n++] = "env";
+ args[n++] = wrap_env;
+ {
+ int i;
+
+ for (i = 0; i < innerc; i++) {
+ args[n++] = inner[i];
+ }
+ }
+ args[n++] = NULL;
+
+ execv(screen_path, args);
+ fprintf(stderr, "hybbxd: screen spawn failed\n");
+ return HYBBX_ERR_IO;
+}
+
+static hybbx_result_t spawn_tmux(const char *session, const char *binary,
+ int argc, char **argv)
+{
+ char tmux_path[HYBBX_PATH_MAX];
+ char *inner[HYBBX_CMD_TOKEN_MAX + 4];
+ char wrap_env[64];
+ int innerc;
+ char *args[HYBBX_CMD_TOKEN_MAX + 8];
+ int n = 0;
+
+ if (!find_in_path("tmux", tmux_path, sizeof(tmux_path))) {
+ fprintf(stderr, "hybbxd: tmux not found in PATH\n");
+ return HYBBX_ERR_IO;
+ }
+
+ innerc = build_inner_argv(inner, (int)(sizeof(inner) / sizeof(inner[0])),
+ binary, argc, argv);
+ if (innerc < 0) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ snprintf(wrap_env, sizeof(wrap_env), "%s=1", HYBBX_DAEMON_WRAP_ENV);
+
+ args[n++] = tmux_path;
+ args[n++] = "new-session";
+ args[n++] = "-d";
+ args[n++] = "-s";
+ args[n++] = (char *)session;
+ args[n++] = "env";
+ args[n++] = wrap_env;
+ {
+ int i;
+
+ for (i = 0; i < innerc; i++) {
+ args[n++] = inner[i];
+ }
+ }
+ args[n++] = NULL;
+
+ execv(tmux_path, args);
+ fprintf(stderr, "hybbxd: tmux spawn failed\n");
+ return HYBBX_ERR_IO;
+}
+
+hybbx_result_t hybbx_daemon_apply_launch_opts(const hybbx_daemon_launch_opts_t *opts,
+ const char *binary_path,
+ int argc, char **argv)
+{
+ const char *binary = binary_path;
+
+ if (opts == NULL) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (binary == NULL || binary[0] == '\0') {
+ binary = HYBBX_DAEMON_BINARY;
+ }
+
+ if (opts->use_screen && opts->use_tmux) {
+ fprintf(stderr, "hybbxd: use --screen or --tmux, not both\n");
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (opts->attach) {
+ if (opts->use_screen) {
+ return exec_screen_attach(opts->session);
+ }
+ if (opts->use_tmux) {
+ return exec_tmux_attach(opts->session);
+ }
+
+ fprintf(stderr, "hybbxd: --attach requires --screen or --tmux\n");
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (wrapped_child() || opts->foreground) {
+ return HYBBX_OK;
+ }
+
+ if (opts->use_screen) {
+ if (in_screen()) {
+ return HYBBX_OK;
+ }
+
+ if (spawn_screen(opts->session, binary, argc, argv) != HYBBX_OK) {
+ return HYBBX_ERR_IO;
+ }
+ printf("hybbxd: started in screen session '%s'\n", opts->session);
+ printf("attach: screen -r %s\n", opts->session);
+ exit(EXIT_SUCCESS);
+ }
+
+ if (opts->use_tmux) {
+ if (in_tmux()) {
+ return HYBBX_OK;
+ }
+
+ if (spawn_tmux(opts->session, binary, argc, argv) != HYBBX_OK) {
+ return HYBBX_ERR_IO;
+ }
+ printf("hybbxd: started in tmux session '%s'\n", opts->session);
+ printf("attach: tmux attach -t %s\n", opts->session);
+ exit(EXIT_SUCCESS);
+ }
+
+ return HYBBX_OK;
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com