[OpenWrt-Devel] [PATCH RFC procd] jail: add option to provide /dev/console to containers

Etienne Champetier champetier.etienne at gmail.com
Sun Apr 26 09:47:38 EDT 2020


Hi Daniel

Le dim. 26 avr. 2020 à 06:53, Daniel Golle <daniel at makrotopia.org> a écrit :
>
> Hi Etienne,
>
> On Sat, Apr 25, 2020 at 10:37:01PM -0400, Etienne Champetier wrote:
> > Hi Daniel,
> >
> > Le sam. 25 avr. 2020 à 19:48, Daniel Golle <daniel at makrotopia.org> a écrit :
> > >
> > > Create UNIX/98 PTY, pass master fd to procd and setup mount-bind of
> > > slave PTS device on /dev/console inside jail.
> > > Allow attaching to an instance's console by using the newly introduced
> > > ujail-console command (no multiplexing for now).
> >
> > Just curious how far you want to push ujail ?
> > ie do you want a docker lite / what features do you want to add ?
>
> More like runc-lite with ubus support :)
> Ideally with roughly 80% of features covered with about 20% of the
> code...
> Most features have been added by now, next steps would be cleaning up
> and fixing things. For now, userns support needs a lot more love to
> function properly, starting with deciding about quite fundamental
> things like this one:
>
> https://aur.archlinux.org/cgit/aur.git/tree/ubuntu-unprivileged-overlayfs.patch?h=linux-userns
>
> (or use FUSE-based approach like LXC, but that's a lot of overhead...)
>
> What I'm still planning for procd/ujail is basic support for cgroupsv2
> (including a way to use cgroup-devices via static BPF, similar to how
> we generate BPF for seccomp filter)
> So no volume/image-management, no docker index, no freezer/migration,
> but good enough to boot Debian, Arch or Alpine rootfs.
>
> See my incomplete prototype tool: https://guthub.com/dangowrt/uxc
>
> The idea is to add useful features to procd for single services up to
> full-system containers running systemd inside (and everything in
> between). Think of network-namespaces which is already useful now to
> wire up any service with veth device(s) in it's own netns or tmpoverlay
> which is useful eg. for transmission which wants to create temporary
> files at run-time having a random names...
>
> Anyway, all testing, feedback and review is highly appreciated!

Nice!

One really useful feature that I would like to have is ambient
capabilities, to easily run as non root many daemons
(and ideally it should be in procd / always available)

Thanks for all this work
Etienne

>
>
> Cheers
>
>
>
> Daniel
>
>
>
>
> >
> > Regards
> > Etienne
> >
> > >
> > > Signed-off-by: Daniel Golle <daniel at makrotopia.org>
> > > ---
> > >  CMakeLists.txt     |   6 ++
> > >  jail/console.c     | 209 +++++++++++++++++++++++++++++++++++++++++++++
> > >  jail/jail.c        |  83 +++++++++++++++++-
> > >  service/instance.c |  70 +++++++++++++++
> > >  service/instance.h |   3 +
> > >  service/service.c  |  71 +++++++++++++++
> > >  6 files changed, 438 insertions(+), 4 deletions(-)
> > >  create mode 100644 jail/console.c
> > >
> > > diff --git a/CMakeLists.txt b/CMakeLists.txt
> > > index cff47cf..3eb79f9 100644
> > > --- a/CMakeLists.txt
> > > +++ b/CMakeLists.txt
> > > @@ -110,6 +110,12 @@ INSTALL(TARGETS ujail
> > >         RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
> > >  )
> > >  ADD_DEPENDENCIES(ujail capabilities-names-h)
> > > +
> > > +ADD_EXECUTABLE(ujail-console jail/console.c)
> > > +TARGET_LINK_LIBRARIES(ujail-console ${ubox} ${ubus} ${blobmsg_json})
> > > +INSTALL(TARGETS ujail-console
> > > +       RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
> > > +)
> > >  endif()
> > >
> > >  IF(UTRACE_SUPPORT)
> > > diff --git a/jail/console.c b/jail/console.c
> > > new file mode 100644
> > > index 0000000..75ce9c5
> > > --- /dev/null
> > > +++ b/jail/console.c
> > > @@ -0,0 +1,209 @@
> > > +/*
> > > + * Copyright (C) 2020 Daniel Golle <daniel at makrotopia.org>
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify
> > > + * it under the terms of the GNU Lesser General Public License version 2.1
> > > + * as published by the Free Software Foundation
> > > + *
> > > + * This program is distributed in the hope that it will be useful,
> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > + * GNU General Public License for more details.
> > > + */
> > > +
> > > +#include <stdlib.h>
> > > +#include <fcntl.h>
> > > +#include <libubox/ustream.h>
> > > +#include <libubus.h>
> > > +#include <signal.h>
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <unistd.h>
> > > +#include <fcntl.h>
> > > +#include <errno.h>
> > > +#include <sys/types.h>
> > > +#include <termios.h>
> > > +
> > > +static inline int setup_tios(int fd, struct termios *oldtios)
> > > +{
> > > +       struct termios newtios;
> > > +
> > > +       if (!isatty(fd)) {
> > > +               return -1;
> > > +       }
> > > +
> > > +       /* Get current termios */
> > > +       if (tcgetattr(fd, oldtios))
> > > +               return -1;
> > > +
> > > +       newtios = *oldtios;
> > > +
> > > +       /* Remove the echo characters and signal reception, the echo
> > > +        * will be done with master proxying */
> > > +       newtios.c_iflag &= ~IGNBRK;
> > > +       newtios.c_iflag &= BRKINT;
> > > +       newtios.c_lflag &= ~(ECHO|ICANON|ISIG);
> > > +       newtios.c_cc[VMIN] = 1;
> > > +       newtios.c_cc[VTIME] = 0;
> > > +
> > > +       /* Set new attributes */
> > > +       if (tcsetattr(fd, TCSAFLUSH, &newtios))
> > > +               return -1;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +
> > > +
> > > +#define OPT_ARGS       "i:s:"
> > > +
> > > +static struct ustream_fd cufd;
> > > +static struct ustream_fd lufd;
> > > +
> > > +static void usage()
> > > +{
> > > +       fprintf(stderr, "ujail-console -s <service> [-i <instance>]\n");
> > > +       exit(1);
> > > +}
> > > +
> > > +static void client_cb(struct ustream *s, int bytes)
> > > +{
> > > +       char *buf;
> > > +       int len, rv;
> > > +
> > > +       do {
> > > +               buf = ustream_get_read_buf(s, &len);
> > > +               if (!buf)
> > > +                       break;
> > > +
> > > +               rv = ustream_write(&lufd.stream, buf, len, false);
> > > +
> > > +               if (rv > 0)
> > > +                       ustream_consume(s, rv);
> > > +
> > > +               if (rv <= len)
> > > +                       break;
> > > +       } while(1);
> > > +}
> > > +
> > > +static void local_cb(struct ustream *s, int bytes)
> > > +{
> > > +       char *buf;
> > > +       int len, rv;
> > > +
> > > +       do {
> > > +               buf = ustream_get_read_buf(s, &len);
> > > +               if (!buf)
> > > +                       break;
> > > +
> > > +               if ((len > 0) && (buf[0] == 2))
> > > +                               uloop_end();
> > > +
> > > +               rv = ustream_write(&cufd.stream, buf, len, false);
> > > +
> > > +               if (rv > 0)
> > > +                       ustream_consume(s, rv);
> > > +
> > > +               if (rv <= len)
> > > +                       break;
> > > +       } while(1);
> > > +}
> > > +
> > > +int main(int argc, char **argv)
> > > +{
> > > +       struct ubus_context *ctx;
> > > +       uint32_t id;
> > > +       static struct blob_buf req;
> > > +       char *service_name = NULL, *instance_name = NULL;
> > > +       int client_fd, server_fd, tty_fd;
> > > +       struct termios oldtermios;
> > > +       int ch;
> > > +
> > > +       while ((ch = getopt(argc, argv, OPT_ARGS)) != -1) {
> > > +               switch (ch) {
> > > +               case 'i':
> > > +                       instance_name = optarg;
> > > +                       break;
> > > +               case 's':
> > > +                       service_name = optarg;
> > > +                       break;
> > > +               default:
> > > +                       usage();
> > > +               }
> > > +       }
> > > +
> > > +       if (!service_name)
> > > +               usage();
> > > +
> > > +       ctx = ubus_connect(NULL);
> > > +       if (!ctx) {
> > > +               fprintf(stderr, "can't connect to ubus!\n");
> > > +               return -1;
> > > +       }
> > > +
> > > +       /* open pseudo-terminal pair */
> > > +       client_fd = posix_openpt(O_RDWR | O_NOCTTY);
> > > +       if (client_fd < 0) {
> > > +               fprintf(stderr, "can't create virtual console!\n");
> > > +               ubus_free(ctx);
> > > +               return -1;
> > > +       }
> > > +       setup_tios(client_fd, &oldtermios);
> > > +       grantpt(client_fd);
> > > +       unlockpt(client_fd);
> > > +       server_fd = open(ptsname(client_fd), O_RDWR | O_NOCTTY);
> > > +       if (server_fd < 0) {
> > > +               fprintf(stderr, "can't open virtual console!\n");
> > > +               close(client_fd);
> > > +               ubus_free(ctx);
> > > +               return -1;
> > > +       }
> > > +
> > > +       setup_tios(server_fd, &oldtermios);
> > > +       tty_fd = open("/dev/tty", O_RDWR);
> > > +       setup_tios(tty_fd, &oldtermios);
> > > +
> > > +       /* register server-side with procd */
> > > +       blob_buf_init(&req, 0);
> > > +       blobmsg_add_string(&req, "name", service_name);
> > > +       if (instance_name)
> > > +               blobmsg_add_string(&req, "instance", instance_name);
> > > +
> > > +       if (ubus_lookup_id(ctx, "service", &id) ||
> > > +           ubus_invoke_fd(ctx, id, "console_attach", req.head, NULL, NULL, 3000, server_fd)) {
> > > +               fprintf(stderr, "ubus request failed\n");
> > > +               close(server_fd);
> > > +               close(client_fd);
> > > +               blob_buf_free(&req);
> > > +               ubus_free(ctx);
> > > +               return -2;
> > > +       }
> > > +
> > > +       close(server_fd);
> > > +       blob_buf_free(&req);
> > > +       ubus_free(ctx);
> > > +
> > > +       uloop_init();
> > > +
> > > +       /* forward between stdio and client_fd until detach is requested */
> > > +       lufd.stream.notify_read = local_cb;
> > > +       ustream_fd_init(&lufd, tty_fd);
> > > +
> > > +       cufd.stream.notify_read = client_cb;
> > > +/* ToDo: handle remote close and other events */
> > > +//     cufd.stream.notify_state = client_state_cb;
> > > +       ustream_fd_init(&cufd, client_fd);
> > > +
> > > +       fprintf(stderr, "attaching to jail console. press [CTRL]+[B] to exit.\n");
> > > +       close(0);
> > > +       close(1);
> > > +       close(2);
> > > +       uloop_run();
> > > +
> > > +       tcsetattr(tty_fd, TCSAFLUSH, &oldtermios);
> > > +       ustream_free(&lufd.stream);
> > > +       ustream_free(&cufd.stream);
> > > +       close(client_fd);
> > > +
> > > +       return 0;
> > > +}
> > > diff --git a/jail/jail.c b/jail/jail.c
> > > index 8ae477c..9257001 100644
> > > --- a/jail/jail.c
> > > +++ b/jail/jail.c
> > > @@ -40,7 +40,7 @@
> > >  #include <libubus.h>
> > >
> > >  #define STACK_SIZE     (1024 * 1024)
> > > -#define OPT_ARGS       "S:C:n:h:r:w:d:psulocU:G:NR:fFO:T:E"
> > > +#define OPT_ARGS       "S:C:n:h:r:w:d:psulocU:G:NR:fFO:T:Ey"
> > >
> > >  static struct {
> > >         char *name;
> > > @@ -58,6 +58,7 @@ static struct {
> > >         int procfs;
> > >         int ronly;
> > >         int sysfs;
> > > +       int console;
> > >         int pw_uid;
> > >         int pw_gid;
> > >         int gr_gid;
> > > @@ -71,6 +72,8 @@ int debug = 0;
> > >
> > >  static char child_stack[STACK_SIZE];
> > >
> > > +int console_fd;
> > > +
> > >  static int mkdir_p(char *dir, mode_t mask)
> > >  {
> > >         char *l = strrchr(dir, '/');
> > > @@ -184,6 +187,72 @@ out:
> > >         return ret;
> > >  }
> > >
> > > +static void pass_console(int console_fd)
> > > +{
> > > +       struct ubus_context *ctx = ubus_connect(NULL);
> > > +       static struct blob_buf req;
> > > +       uint32_t id;
> > > +
> > > +       if (!ctx)
> > > +               return;
> > > +
> > > +       blob_buf_init(&req, 0);
> > > +       blobmsg_add_string(&req, "name", opts.name);
> > > +
> > > +       if (ubus_lookup_id(ctx, "service", &id) ||
> > > +           ubus_invoke_fd(ctx, id, "console_set", req.head, NULL, NULL, 3000, console_fd))
> > > +               INFO("ubus request failed\n");
> > > +
> > > +       close(console_fd);
> > > +       blob_buf_free(&req);
> > > +       ubus_free(ctx);
> > > +}
> > > +
> > > +static int create_dev_console(const char *jail_root)
> > > +{
> > > +       char *console_fname;
> > > +       char dev_console_path[PATH_MAX];
> > > +       int slave_console_fd;
> > > +
> > > +       /* Open UNIX/98 virtual console */
> > > +       console_fd = posix_openpt(O_RDWR | O_NOCTTY);
> > > +       if (console_fd == -1)
> > > +               return -1;
> > > +
> > > +       console_fname = ptsname(console_fd);
> > > +       DEBUG("got console fd %d and PTS client name %s\n", console_fd, console_fname);
> > > +       if (!console_fname)
> > > +               goto no_console;
> > > +
> > > +       grantpt(console_fd);
> > > +       unlockpt(console_fd);
> > > +
> > > +       /* pass PTY master to procd */
> > > +       pass_console(console_fd);
> > > +
> > > +       /* mount-bind PTY slave to /dev/console in jail */
> > > +       snprintf(dev_console_path, sizeof(dev_console_path), "%s/dev/console", jail_root);
> > > +       close(creat(dev_console_path, 0620));
> > > +
> > > +       if (mount(console_fname, dev_console_path, NULL, MS_BIND, NULL))
> > > +               goto no_console;
> > > +
> > > +       /* use PTY slave for stdio */
> > > +       slave_console_fd = open(console_fname, O_RDWR | O_NOCTTY);
> > > +       dup2(slave_console_fd, 0);
> > > +       dup2(slave_console_fd, 1);
> > > +       dup2(slave_console_fd, 2);
> > > +       close(slave_console_fd);
> > > +
> > > +       INFO("using guest console %s\n", console_fname);
> > > +
> > > +       return 0;
> > > +
> > > +no_console:
> > > +       close(console_fd);
> > > +       return 1;
> > > +}
> > > +
> > >  static int build_jail_fs(void)
> > >  {
> > >         char jail_root[] = "/tmp/ujail-XXXXXX";
> > > @@ -247,6 +316,9 @@ static int build_jail_fs(void)
> > >         if (mount(NULL, tmpdevdir, "tmpfs", MS_NOATIME | MS_NOEXEC | MS_NOSUID, "size=1M"))
> > >                 return -1;
> > >
> > > +       if (opts.console)
> > > +               create_dev_console(jail_root);
> > > +
> > >         if (mount_all(jail_root)) {
> > >                 ERROR("mount_all() failed\n");
> > >                 return -1;
> > > @@ -468,6 +540,7 @@ static void usage(void)
> > >         fprintf(stderr, "  -O <dir>\tdirectory for r/w overlayfs\n");
> > >         fprintf(stderr, "  -T <size>\tuse tmpfs r/w overlayfs with <size>\n");
> > >         fprintf(stderr, "  -E\t\tfail if jail cannot be setup\n");
> > > +       fprintf(stderr, "  -y\t\tprovide jail console\n");
> > >         fprintf(stderr, "\nWarning: by default root inside the jail is the same\n\
> > >  and he has the same powers as root outside the jail,\n\
> > >  thus he can escape the jail and/or break stuff.\n\
> > > @@ -486,7 +559,6 @@ static int exec_jail(void *pipes_ptr)
> > >         close(pipes[0]);
> > >         close(pipes[3]);
> > >
> > > -
> > >         buf[0] = 'i';
> > >         if (write(pipes[1], buf, 1) < 1) {
> > >                 ERROR("can't write to parent\n");
> > > @@ -720,6 +792,9 @@ int main(int argc, char **argv)
> > >                 case 'E':
> > >                         opts.require_jail = 1;
> > >                         break;
> > > +               case 'y':
> > > +                       opts.console = 1;
> > > +                       break;
> > >                 }
> > >         }
> > >
> > > @@ -788,9 +863,9 @@ int main(int argc, char **argv)
> > >                         add_mount("/dev/null", 0, -1);
> > >                         add_mount("/dev/random", 0, -1);
> > >                         add_mount("/dev/urandom", 0, -1);
> > > -                       add_mount("/dev/tty", 0, -1);
> > >                         add_mount("/dev/zero", 0, -1);
> > > -                       add_mount("/dev/console", 0, -1);
> > > +                       add_mount("/dev/ptmx", 0, -1);
> > > +                       add_mount("/dev/tty", 0, -1);
> > >
> > >                         if (!opts.extroot && (opts.user || opts.group)) {
> > >                                 add_mount("/etc/passwd", 0, -1);
> > > diff --git a/service/instance.c b/service/instance.c
> > > index 75fd91f..142208a 100644
> > > --- a/service/instance.c
> > > +++ b/service/instance.c
> > > @@ -109,6 +109,7 @@ enum {
> > >         JAIL_ATTR_NETNS,
> > >         JAIL_ATTR_USERNS,
> > >         JAIL_ATTR_CGROUPSNS,
> > > +       JAIL_ATTR_CONSOLE,
> > >         JAIL_ATTR_REQUIREJAIL,
> > >         __JAIL_ATTR_MAX,
> > >  };
> > > @@ -125,6 +126,7 @@ static const struct blobmsg_policy jail_attr[__JAIL_ATTR_MAX] = {
> > >         [JAIL_ATTR_NETNS] = { "netns", BLOBMSG_TYPE_BOOL },
> > >         [JAIL_ATTR_USERNS] = { "userns", BLOBMSG_TYPE_BOOL },
> > >         [JAIL_ATTR_CGROUPSNS] = { "cgroupsns", BLOBMSG_TYPE_BOOL },
> > > +       [JAIL_ATTR_CONSOLE] = { "console", BLOBMSG_TYPE_BOOL },
> > >         [JAIL_ATTR_REQUIREJAIL] = { "requirejail", BLOBMSG_TYPE_BOOL },
> > >  };
> > >
> > > @@ -274,6 +276,9 @@ jail_run(struct service_instance *in, char **argv)
> > >         if (jail->cgroupsns)
> > >                 argv[argc++] = "-F";
> > >
> > > +       if (jail->console)
> > > +               argv[argc++] = "-y";
> > > +
> > >         if (in->extroot) {
> > >                 argv[argc++] = "-R";
> > >                 argv[argc++] = in->extroot;
> > > @@ -453,6 +458,18 @@ instance_free_stdio(struct service_instance *in)
> > >                 close(in->_stderr.fd.fd);
> > >                 in->_stderr.fd.fd = -1;
> > >         }
> > > +
> > > +       if (in->console.fd.fd > -1) {
> > > +               ustream_free(&in->console.stream);
> > > +               close(in->console.fd.fd);
> > > +               in->console.fd.fd = -1;
> > > +       }
> > > +
> > > +       if (in->console_client.fd.fd > -1) {
> > > +               ustream_free(&in->console_client.stream);
> > > +               close(in->console_client.fd.fd);
> > > +               in->console_client.fd.fd = -1;
> > > +       }
> > >  }
> > >
> > >  void
> > > @@ -570,6 +587,46 @@ instance_stdout(struct ustream *s, int bytes)
> > >                        container_of(s, struct service_instance, _stdout.stream));
> > >  }
> > >
> > > +static void
> > > +instance_console(struct ustream *s, int bytes)
> > > +{
> > > +       struct service_instance *in = container_of(s, struct service_instance, console.stream);
> > > +       char *buf;
> > > +       int len;
> > > +
> > > +       do {
> > > +               buf = ustream_get_read_buf(s, &len);
> > > +               if (!buf)
> > > +                       break;
> > > +
> > > +               ulog(LOG_INFO, "out: %s\n", buf);
> > > +
> > > +               /* test if console client is attached */
> > > +               if (in->console_client.fd.fd > -1)
> > > +                       ustream_write(&in->console_client.stream, buf, len, false);
> > > +
> > > +               ustream_consume(s, len);
> > > +       } while (1);
> > > +}
> > > +
> > > +static void
> > > +instance_console_client(struct ustream *s, int bytes)
> > > +{
> > > +       struct service_instance *in = container_of(s, struct service_instance, console_client.stream);
> > > +       char *buf;
> > > +       int len;
> > > +
> > > +       do {
> > > +               buf = ustream_get_read_buf(s, &len);
> > > +               if (!buf)
> > > +                       break;
> > > +
> > > +               ulog(LOG_INFO, "in: %s\n", buf);
> > > +               ustream_write(&in->console.stream, buf, len, false);
> > > +               ustream_consume(s, len);
> > > +       } while (1);
> > > +}
> > > +
> > >  static void
> > >  instance_stderr(struct ustream *s, int bytes)
> > >  {
> > > @@ -905,6 +962,10 @@ instance_jail_parse(struct service_instance *in, struct blob_attr *attr)
> > >                 jail->cgroupsns = blobmsg_get_bool(tb[JAIL_ATTR_CGROUPSNS]);
> > >                 jail->argc++;
> > >         }
> > > +       if (tb[JAIL_ATTR_CONSOLE]) {
> > > +               jail->console = blobmsg_get_bool(tb[JAIL_ATTR_CONSOLE]);
> > > +               jail->argc++;
> > > +       }
> > >
> > >         if (tb[JAIL_ATTR_MOUNT]) {
> > >                 struct blob_attr *cur;
> > > @@ -1232,6 +1293,14 @@ instance_init(struct service_instance *in, struct service *s, struct blob_attr *
> > >         in->_stderr.stream.string_data = true;
> > >         in->_stderr.stream.notify_read = instance_stderr;
> > >
> > > +       in->console.fd.fd = -2;
> > > +       in->console.stream.string_data = true;
> > > +       in->console.stream.notify_read = instance_console;
> > > +
> > > +       in->console_client.fd.fd = -2;
> > > +       in->console_client.stream.string_data = true;
> > > +       in->console_client.stream.notify_read = instance_console_client;
> > > +
> > >         blobmsg_list_init(&in->netdev, struct instance_netdev, node, instance_netdev_cmp);
> > >         blobmsg_list_init(&in->file, struct instance_file, node, instance_file_cmp);
> > >         blobmsg_list_simple_init(&in->env);
> > > @@ -1335,6 +1404,7 @@ void instance_dump(struct blob_buf *b, struct service_instance *in, int verbose)
> > >                 blobmsg_add_u8(b, "netns", in->jail.netns);
> > >                 blobmsg_add_u8(b, "userns", in->jail.userns);
> > >                 blobmsg_add_u8(b, "cgroupsns", in->jail.cgroupsns);
> > > +               blobmsg_add_u8(b, "console", (in->console.fd.fd > -1));
> > >                 blobmsg_close_table(b, r);
> > >                 if (!avl_is_empty(&in->jail.mount.avl)) {
> > >                         struct blobmsg_list_node *var;
> > > diff --git a/service/instance.h b/service/instance.h
> > > index 43a6561..4400cd4 100644
> > > --- a/service/instance.h
> > > +++ b/service/instance.h
> > > @@ -32,6 +32,7 @@ struct jail {
> > >         bool netns;
> > >         bool userns;
> > >         bool cgroupsns;
> > > +       bool console;
> > >         char *name;
> > >         char *hostname;
> > >         struct blobmsg_list mount;
> > > @@ -82,6 +83,8 @@ struct service_instance {
> > >         struct uloop_timeout timeout;
> > >         struct ustream_fd _stdout;
> > >         struct ustream_fd _stderr;
> > > +       struct ustream_fd console;
> > > +       struct ustream_fd console_client;
> > >
> > >         struct blob_attr *command;
> > >         struct blob_attr *trigger;
> > > diff --git a/service/service.c b/service/service.c
> > > index 755147c..1d26291 100644
> > > --- a/service/service.c
> > > +++ b/service/service.c
> > > @@ -274,6 +274,17 @@ static const struct blobmsg_policy get_data_policy[] = {
> > >         [DATA_TYPE] = { "type", BLOBMSG_TYPE_STRING },
> > >  };
> > >
> > > +enum {
> > > +       SERVICE_CONSOLE_NAME,
> > > +       SERVICE_CONSOLE_INSTANCE,
> > > +       __SERVICE_CONSOLE_MAX,
> > > +};
> > > +
> > > +static const struct blobmsg_policy service_console_policy[__SERVICE_CONSOLE_MAX] = {
> > > +       [SERVICE_CONSOLE_NAME] = { "name", BLOBMSG_TYPE_STRING },
> > > +       [SERVICE_CONSOLE_INSTANCE] = { "instance", BLOBMSG_TYPE_STRING },
> > > +};
> > > +
> > >  static int
> > >  service_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
> > >                    struct ubus_request_data *req, const char *method,
> > > @@ -672,6 +683,64 @@ service_get_data(struct ubus_context *ctx, struct ubus_object *obj,
> > >         return 0;
> > >  }
> > >
> > > +static int
> > > +service_handle_console(struct ubus_context *ctx, struct ubus_object *obj,
> > > +                       struct ubus_request_data *req, const char *method,
> > > +                       struct blob_attr *msg)
> > > +{
> > > +       bool attach = !strcmp(method, "console_attach");
> > > +       struct blob_attr *tb[__SERVICE_CONSOLE_MAX];
> > > +       struct service *s;
> > > +       struct service_instance *in;
> > > +       int console_fd = -1;
> > > +
> > > +       console_fd = ubus_request_get_caller_fd(req);
> > > +       if (console_fd < 0)
> > > +               return UBUS_STATUS_INVALID_ARGUMENT;
> > > +
> > > +       if (!msg)
> > > +               goto err_console_fd;
> > > +
> > > +       blobmsg_parse(service_console_policy, __SERVICE_CONSOLE_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
> > > +       if (!tb[SERVICE_CONSOLE_NAME])
> > > +               goto err_console_fd;
> > > +
> > > +       s = avl_find_element(&services, blobmsg_data(tb[SERVICE_CONSOLE_NAME]), s, avl);
> > > +       if (!s)
> > > +               goto err_console_fd;
> > > +
> > > +       if (tb[SERVICE_CONSOLE_INSTANCE]) {
> > > +               in = vlist_find(&s->instances, blobmsg_data(tb[SERVICE_CONSOLE_INSTANCE]), in, node);
> > > +       } else {
> > > +               /* use first element in instances list */
> > > +               vlist_for_each_element(&s->instances, in, node)
> > > +                       break;
> > > +       }
> > > +       if (!in)
> > > +               goto err_console_fd;
> > > +
> > > +       if (attach) {
> > > +               if (in->console.fd.fd < 0) {
> > > +                       close(console_fd);
> > > +                       return UBUS_STATUS_NOT_SUPPORTED;
> > > +               }
> > > +
> > > +               /* close and replace existing attached console */
> > > +               if (in->console_client.fd.fd > -1)
> > > +                       close(in->console_client.fd.fd);
> > > +
> > > +               ustream_fd_init(&in->console_client, console_fd);
> > > +       } else {
> > > +               ustream_fd_init(&in->console, console_fd);
> > > +       }
> > > +
> > > +       return UBUS_STATUS_OK;
> > > +err_console_fd:
> > > +       close(console_fd);
> > > +       return UBUS_STATUS_INVALID_ARGUMENT;
> > > +}
> > > +
> > > +
> > >  static struct ubus_method main_object_methods[] = {
> > >         UBUS_METHOD("set", service_handle_set, service_set_attrs),
> > >         UBUS_METHOD("add", service_handle_set, service_set_attrs),
> > > @@ -684,6 +753,8 @@ static struct ubus_method main_object_methods[] = {
> > >         UBUS_METHOD("validate", service_handle_validate, validate_policy),
> > >         UBUS_METHOD("get_data", service_get_data, get_data_policy),
> > >         UBUS_METHOD("state", service_handle_state, service_state_attrs),
> > > +       UBUS_METHOD("console_set", service_handle_console, service_console_policy),
> > > +       UBUS_METHOD("console_attach", service_handle_console, service_console_policy),
> > >  };
> > >
> > >  static struct ubus_object_type main_object_type =
> > > --
> > > 2.26.2
> > >
> > >
> > > _______________________________________________
> > > openwrt-devel mailing list
> > > openwrt-devel at lists.openwrt.org
> > > https://lists.openwrt.org/mailman/listinfo/openwrt-devel
> >
> > _______________________________________________
> > openwrt-devel mailing list
> > openwrt-devel at lists.openwrt.org
> > https://lists.openwrt.org/mailman/listinfo/openwrt-devel

_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list