From 51e6e439eb525aeede4e05df4fad1c51850eb601 Mon Sep 17 00:00:00 2001 From: Tianling Shen Date: Sat, 3 Oct 2026 14:24:53 +0800 Subject: [PATCH] service: add workdir support for daemon instances Allow instances to select a working directory with the workdir option. Change directory after setting the process identity and fail startup if the directory cannot be entered. Preserve the existing behavior when workdir is omitted. Pass the directory to ujail through a new -W option, reusing its existing directory switch after jail setup. Track workdir changes, include the value in instance dumps, and release owned strings. Signed-off-by: Tianling Shen --- jail/jail.c | 13 +++++++++++-- service/instance.c | 29 +++++++++++++++++++++++++++++ service/instance.h | 1 + 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/jail/jail.c b/jail/jail.c index 49aabe3..3ee6a18 100644 --- a/jail/jail.c +++ b/jail/jail.c @@ -102,7 +102,7 @@ #define PR_MDWE_NO_INHERIT (1UL << 1) #endif -#define OPT_ARGS "a:A:b:cC:d:De:EfFG:h:iI:j:J:k:lm:M:n:NoO:pP:r:R:sS:uU:V:w:x:t:T:yY:Z" +#define OPT_ARGS "a:A:b:cC:d:De:EfFG:h:iI:j:J:k:lm:M:n:NoO:pP:r:R:sS:uU:V:w:W:x:t:T:yY:Z" #define JAIL_MAX_CREDENTIALS 16 static const char *cred_targets[JAIL_MAX_CREDENTIALS]; @@ -2464,6 +2464,7 @@ static void usage(void) fprintf(stderr, " -n \tthe name of the jail\n"); fprintf(stderr, " -e \timport environment variable\n"); fprintf(stderr, " -x \tappend KEY=VALUE lines from to the container env\n"); + fprintf(stderr, " -W \tworking directory for the jailed process\n"); fprintf(stderr, "namespace jail options:\n"); fprintf(stderr, " -h \tchange the hostname of the jail\n"); fprintf(stderr, " -N\t\tjail has network namespace\n"); @@ -4084,8 +4085,10 @@ static int parseOCIprocess(struct blob_attr *msg) if (tb[OCI_PROCESS_NONEWPRIVILEGES]) opts.no_new_privs = blobmsg_get_bool(tb[OCI_PROCESS_NONEWPRIVILEGES]); - if (tb[OCI_PROCESS_CWD]) + if (tb[OCI_PROCESS_CWD]) { + free(opts.cwd); opts.cwd = strdup(blobmsg_get_string(tb[OCI_PROCESS_CWD])); + } if (tb[OCI_PROCESS_ENV]) { res = parseOCIenvarray(tb[OCI_PROCESS_ENV], &opts.envp); @@ -6841,6 +6844,12 @@ int main(int argc, char **argv) case 'G': opts.group = optarg; break; + case 'W': + free(opts.cwd); + opts.cwd = strdup(optarg); + if (!opts.cwd) + free_and_exit(EXIT_FAILURE); + break; case 'O': opts.overlaydir = realpath(optarg, NULL); break; diff --git a/service/instance.c b/service/instance.c index 7c91fae..289f09d 100644 --- a/service/instance.c +++ b/service/instance.c @@ -46,6 +46,7 @@ enum { INSTANCE_ATTR_COMMAND, + INSTANCE_ATTR_WORKDIR, INSTANCE_ATTR_ENV, INSTANCE_ATTR_DATA, INSTANCE_ATTR_NETDEV, @@ -81,6 +82,7 @@ enum { static const struct blobmsg_policy instance_attr[__INSTANCE_ATTR_MAX] = { [INSTANCE_ATTR_COMMAND] = { "command", BLOBMSG_TYPE_ARRAY }, + [INSTANCE_ATTR_WORKDIR] = { "workdir", BLOBMSG_TYPE_STRING }, [INSTANCE_ATTR_ENV] = { "env", BLOBMSG_TYPE_TABLE }, [INSTANCE_ATTR_DATA] = { "data", BLOBMSG_TYPE_TABLE }, [INSTANCE_ATTR_NETDEV] = { "netdev", BLOBMSG_TYPE_ARRAY }, @@ -359,6 +361,11 @@ jail_run(struct service_instance *in, char **argv) argv[argc++] = in->group; } + if (in->workdir) { + argv[argc++] = "-W"; + argv[argc++] = in->workdir; + } + if (in->capabilities) { argv[argc++] = "-C"; argv[argc++] = in->capabilities; @@ -632,6 +639,12 @@ instance_run(struct service_instance *in, int _stdout, int _stderr) exit(127); } + if (!in->has_jail && in->workdir && chdir(in->workdir)) { + ERROR("failed to change directory to %s for %s::%s: %m\n", + in->workdir, in->srv->name, in->name); + exit(127); + } + execvp(argv[0], argv); exit(127); } @@ -1279,6 +1292,9 @@ instance_config_changed(struct service_instance *in, struct service_instance *in if (!blob_attr_equal(in->command, in_new->command)) return true; + if (string_changed(in->workdir, in_new->workdir)) + return true; + if (string_changed(in->bundle, in_new->bundle)) return true; @@ -1622,6 +1638,9 @@ instance_jail_parse(struct service_instance *in, struct blob_attr *attr) if (in->group) jail->argc += 2; + if (in->workdir) + jail->argc += 2; + if (in->extroot) jail->argc += 2; @@ -1736,6 +1755,12 @@ instance_config_parse(struct service_instance *in) } } + if (tb[INSTANCE_ATTR_WORKDIR]) { + in->workdir = strdup(blobmsg_get_string(tb[INSTANCE_ATTR_WORKDIR])); + if (!in->workdir) + return false; + } + if (tb[INSTANCE_ATTR_TRACE]) in->trace = blobmsg_get_bool(tb[INSTANCE_ATTR_TRACE]); @@ -1934,6 +1959,7 @@ instance_config_move(struct service_instance *in, struct service_instance *in_sr instance_config_move_strdup(&in->tmpoverlaysize, in_src->tmpoverlaysize); instance_config_move_strdup(&in->user, in_src->user); instance_config_move_strdup(&in->group, in_src->group); + instance_config_move_strdup(&in->workdir, in_src->workdir); instance_config_move_strdup(&in->jail.name, in_src->jail.name); instance_config_move_strdup(&in->jail.hostname, in_src->jail.hostname); instance_config_move_strdup(&in->jail.pidfile, in_src->jail.pidfile); @@ -2041,6 +2067,7 @@ instance_free(struct service_instance *in) free(in->data_blob); free(in->user); free(in->group); + free(in->workdir); free(in->extroot); free(in->overlaydir); free(in->tmpoverlaysize); @@ -2126,6 +2153,8 @@ void instance_dump(struct blob_buf *b, struct service_instance *in, int verbose) blobmsg_add_u64(b, "incarnation", in->incarnation); if (in->command) blobmsg_add_blob(b, in->command); + if (in->workdir) + blobmsg_add_string(b, "workdir", in->workdir); if (in->bundle) blobmsg_add_string(b, "bundle", in->bundle); blobmsg_add_u32(b, "term_timeout", in->term_timeout); diff --git a/service/instance.h b/service/instance.h index d5df8bd..8a25aaf 100644 --- a/service/instance.h +++ b/service/instance.h @@ -99,6 +99,7 @@ struct service_instance { char *seccomp_log; char *capabilities; char *pidfile; + char *workdir; char *extroot; char *overlaydir; char *tmpoverlaysize;