Re: Hello world and couple of questions

From: Martin \ <et.code_at_ethome.sk>
Date: Mon, 7 Sep 2015 19:03:29 +0200

On Mon, 7 Sep 2015 17:13:43 +0200
Laurent Bercot <ska-supervision_at_skarnet.org> wrote:

>
> Hi Martin !
>
> I'm interested in learning what you did: what is needed for FreeBSD to run a
> script as init ? Why can't the kernel boot on stage 1 directly ?
>

Seems this is absolutely minimal hack I ended up with:
(BTW: I still have commented parts lingering around, so I am not posting whole
file, and I also left original comments there)
--------------------------------------------
/*
 * Start a session and allocate a controlling terminal.
 * Only called by children of init after forking.
 */
static void
open_console(void)
{
        int fd;

        /*
         * Try to open /dev/console. Open the device with O_NONBLOCK to
         * prevent potential blocking on a carrier.
         */
        revoke("/dev/console");
        if ((fd = open("/dev/console", O_RDWR | O_NONBLOCK)) != -1) {
                (void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
                if (login_tty(fd) == 0)
                        return;
                close(fd);
        }

        /* No luck. Log output to file if possible. */
        if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
                stall("cannot open null device.");
                _exit(1);
        }
        if (fd != STDIN_FILENO) {
                dup2(fd, STDIN_FILENO);
                close(fd);
        }
        fd = open(_PATH_INITLOG, O_WRONLY | O_APPEND | O_CREAT, 0644);
        if (fd == -1)
                dup2(STDIN_FILENO, STDOUT_FILENO);
        else if (fd != STDOUT_FILENO) {
                dup2(fd, STDOUT_FILENO);
                close(fd);
        }
        dup2(STDOUT_FILENO, STDERR_FILENO);
}


/*
 * The mother of all processes.
 */
int
main(int argc, char *argv[])
{
        state_t initial_transition = runcom;
        char kenv_value[PATH_MAX];
        int c;
        struct sigaction sa;
        sigset_t mask;

        /* Dispose of random users. */
        if (getuid() != 0)
                errx(1, "%s", strerror(EPERM));

        /* System V users like to reexec init. */
        if (getpid() != 1) {
                        errx(1, "already running");
        }
        /*
         * Note that this does NOT open a file...
         * Does 'init' deserve its own facility number?
         */
        openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);

        /*
         * Create an initial session.
         */

        if (setsid() < 0)
                warning("initial setsid() failed: %m");


        /*
         * Establish an initial user so that programs running
         * single user do not freak out and die (like passwd).
         */
        if (setlogin("root") < 0)
                warning("setlogin() failed: %m");

        /*
         * Paranoia.
         */
        close(0);
        close(1);
        close(2);


        /*
         * Additional check if devfs needs to be mounted:
         * If "/" and "/dev" have the same device number,
         * then it hasn't been mounted yet.
         */
        if (!devfs) {
                struct stat stst;
                dev_t root_devno;

                stat("/", &stst);
                root_devno = stst.st_dev;
                if (stat("/dev", &stst) != 0)
                        warning("Can't stat /dev: %m");
                else if (stst.st_dev == root_devno)
                        devfs++;
        }

        if (devfs) {
                struct iovec iov[4];
                char *s;
                int i;

                char _fstype[] = "fstype";
                char _devfs[] = "devfs";
                char _fspath[] = "fspath";
                char _path_dev[]= _PATH_DEV;

                iov[0].iov_base = _fstype;
                iov[0].iov_len = sizeof(_fstype);
                iov[1].iov_base = _devfs;
                iov[1].iov_len = sizeof(_devfs);
                iov[2].iov_base = _fspath;
                iov[2].iov_len = sizeof(_fspath);
                /*
                 * Try to avoid the trailing slash in _PATH_DEV.
                 * Be *very* defensive.
                 */
                s = strdup(_PATH_DEV);
                if (s != NULL) {
                        i = strlen(s);
                        if (i > 0 && s[i - 1] == '/')
                                s[i - 1] = '\0';
                        iov[3].iov_base = s;
                        iov[3].iov_len = strlen(s) + 1;
                } else {
                        iov[3].iov_base = _path_dev;
                        iov[3].iov_len = sizeof(_path_dev);
                }
                nmount(iov, 4, 0);
                if (s != NULL)
                        free(s);
        }

  open_console();

  // _at_eto: exec into execlineb
        char _S6_EXECLINE_PATH[] = "/s6bin/execlineb";
        char _S6_EXECLINE_NAME[] = "execlineb";
        char _S6_EXECLINE_PARAM[] = "-S0";
        char _S6_EXECLINE_SCRIPT[] = "/etc/s6/boot/run";

  argv[0] = _S6_EXECLINE_NAME;
        argv[1] = _S6_EXECLINE_PARAM;
        argv[2] = _S6_EXECLINE_SCRIPT;
        argv[3] = 0;
  execv(_S6_EXECLINE_PATH, argv);

        /*
         * Should never reach here.
         */
        return 1;
}
--------------------------------------------

Keep in mind I really have no clue what I am doing :D.
I am using FreeBSD bootloader capability to switch inits through config file
so original init stays in place when something goes wrong. Original init also
handles failures, parses bootloader defined variables and invocation parameters,
does single user mode and lot more, So this is really just dumb hack.

> > One problem I am having though, is "uncaught" logger dance, as unlike on
> > linux, stage 1 dies with "broken pipe" signal when it tries to write to
> > logger fifo (as it has no reader yet).
> ...
> There's an example of a minimal stage 1 in the examples/ subdirectory
> of the s6 tarball. For another example, you can install the
> s6-linux-init package: the s6-linux-init-maker tool will work on
> FreeBSD. The generated scripts won't, but you can still look at them
> to see how they perform a minimal stage 1 and delegate all the real
> stuff to stage 2.

I see. Well I consulted many s6 examples how this is done on linux.
FreeBSD has it's own rc.d framework and I am still learning how it works.
Unfortunately some tasks need to be done before supervisor spins-up so I just
hacked them down with 'foreground' distilling actual "shell calls" to external
utils, so I get writable root and really basic tasks done. FreeBSD original
setup handles quite lot of stuff and technologies there.
I don't consider this to be really usable yet.

For now I am really just playing with this as side project.

At this experimental stage and after reading your suggestion, I decided my
current approach with "uncaught" log is sufficient for now. Thanks for
enlgihtement about fifo handling on posix.

> It's certainly a bug, but it probably has more to do with the toolchain
> and the way the programs are linked than with the s6 code. Can you please
> send me a log of your build, as well as a strace (or ktrace, or whatever
> FreeBSD tool can give a dump of the sequence of system calls) of the
> failing s6-svstat invocation ?

I see so this is clang issue. Well I have yet to learn ktrace, so I am putting
this on hold until I get some time to study it. I hope I will get some chance to
get these dumps correctly later this week.

Thanks very much for help for now.

 eto
Received on Mon Sep 07 2015 - 17:03:29 UTC

This archive was generated by hypermail 2.3.0 : Sun May 09 2021 - 19:44:19 UTC