vagga icon indicating copy to clipboard operation
vagga copied to clipboard

execvp: No such file or directory

Open piranna opened this issue 8 years ago • 18 comments

I've managed to fully run NodeOS on Docker, but vagga is giving me a strange execvp: No such file or directory error. Since NodeOS is musl-based instead of using glibc as system library, could it be that the source of the problem, by not being able to find the dynamic loader?

piranna avatar Jan 22 '17 18:01 piranna

More lines of log would be helpful, especially if you run RUST_LOG=debug vagga

Vagga almost never uses execvp (I'm not sure, if there are any cases at all). Or you say that you can't run vagga itself?

It may happen if you build vagga with cargo directly instead of building vagga with vagga, so vagga is linked against libc (rather than being statically compiled).

tailhook avatar Jan 22 '17 20:01 tailhook

The problem is not with vagga itself, in fact it starts and exec flawlessly the /init process, but the problem happens later. The image is mostly the same as the one used with Docker, so since it's doing a chroot() as first step I suspect maybe vagga is not doing it? It's the only thing I can think so far... If you have any suggestion about what I could test against for I would thank you it so much.

Anyway, the output of RUST_LOG=debug vagga run is:

INFO:vagga::process_util: Running "id" "-u" "-n"
INFO:vagga::process_util: Running "vagga_wrapper" "_build" "bootfs"
DEBUG:vagga::container::mount: Procfs mount "/home/piranna/Proyectos/NodeOS/.vagga/.mnt/proc"
DEBUG:vagga::container::mount: Pseusofs mount "/home/piranna/Proyectos/NodeOS/.vagga/.mnt/dev/pts" devpts newinstance
INFO:vagga::process_util: Running "vagga_version" "bootfs" "--settings" "{\"version_check\":true,\"proxy_env_vars\":true,\"ubuntu_mirror\":null,\"alpine_mirror\":null,\"uid_map\":null,\"push_image_script\":null,\"build_lock_wait\":false,\"auto_apply_sysctl\":false,\"environ\":{}}"
DEBUG:vagga::version::version: Versioning items: 3
DEBUG:vagga::version::version: Versioning setup: Step(Tar { url: "./out/latest/barebones.tar.gz", sha256: None, path: "/", subdir: "" })
DEBUG:vagga::version::version: Versioning setup: Step(Tar { url: "./out/latest/initramfs.tar.gz", sha256: None, path: "/", subdir: "" })
DEBUG:vagga::version::version: Versioning setup: Step(Tar { url: "./out/latest/usersfs.tar.gz", sha256: None, path: "/tmp", subdir: "" })
DEBUG:vagga::version: Got hash "e30b505619387cdc0a927dff68898c4d1dc069b13bbd86fb07d9a7d940f3cdde9b58ad457f6b1611a457e1885b4507653b64c0e1db8e99c6b40b94f28ed09b7e"
DEBUG:vagga::wrapper::build: Container path: "/vagga/base/.roots/bootfs.e30b5056" (force: false) true
DEBUG:vagga::wrapper::build: Path "/vagga/base/.roots/bootfs.e30b5056" is already built
INFO:vagga::process_util: Running "id" "-u" "-n"
INFO:vagga::process_util: Running "vagga_wrapper" "--root" "bootfs.e30b5056" "run"
DEBUG:vagga::container::mount: Procfs mount "/home/piranna/Proyectos/NodeOS/.vagga/.mnt/proc"
DEBUG:vagga::container::mount: Pseusofs mount "/home/piranna/Proyectos/NodeOS/.vagga/.mnt/dev/pts" devpts newinstance
DEBUG:vagga::container::mount: Pseusofs mount "/vagga/root/dev/pts" devpts newinstance
DEBUG:vagga::container::mount: Procfs mount "/vagga/root/proc"
INFO:vagga::process_util: Running "init"
mount procfs: Resource busy
mount devtmpfs: Operation not permitted
execvp: No such file or directory
/work: Resource busy
/sys/fs/fuse/connections: Invalid argument
/sys/kernel/debug: Invalid argument
/sys/firmware/efi/efivars: Invalid argument
/sys/fs/pstore: Invalid argument
/sys/fs/cgroup/pids: Invalid argument
/sys/fs/cgroup/freezer: Invalid argument
/sys/fs/cgroup/devices: Invalid argument
/sys/fs/cgroup/blkio: Invalid argument
/sys/fs/cgroup/cpu,cpuacct: Invalid argument
/sys/fs/cgroup/perf_event: Invalid argument
/sys/fs/cgroup/net_cls,net_prio: Invalid argument
/sys/fs/cgroup/cpuset: Invalid argument
/sys/fs/cgroup/memory: Invalid argument
/sys/fs/cgroup/hugetlb: Invalid argument
/sys/fs/cgroup/systemd: Invalid argument
/sys/fs/cgroup: Invalid argument
/sys/kernel/security: Invalid argument
/sys: Resource busy
/dev/mqueue: Invalid argument
/dev/hugepages: Invalid argument
/dev/shm: Invalid argument
/dev/pts: Invalid argument
/dev: Resource busy
/: Invalid argument
INFO:vagga::process_util: Running "id" "-u" "-n"
DEBUG:vagga::wrapper::clean: Removing "/home/piranna/Proyectos/NodeOS/.vagga/.transient/bootfs.e30b5056.28123"

The mount and unmount lines are normal and expected, they are mostly safe warnings, don't worry about them.

piranna avatar Jan 22 '17 23:01 piranna

INFO:vagga::process_util: Running "init"
mount procfs: Resource busy
mount devtmpfs: Operation not permitted
execvp: No such file or directory

Looks strange, so it can't mount few filesystems but tries to start process anyway?

Do these mounts fail in docker too?

Actually, I suspect that the issue is barely that environment is clean. In docker all ENV directives during build are kept when your're running commands. But in vagga build-time environment (!Env) and run-time environment (environ: {..}) are separate. Try to set PATH (or maybe all the variables) as in docker.

tailhook avatar Jan 22 '17 23:01 tailhook

Looks strange, so it can't mount few filesystems but tries to start process anyway?

Yes, because in Docker and vagga the filesystems would be already available. In other cases, the boot process would fail soon anyway too.

Do these mounts fail in docker too?

procfs fails because it's already available on its place, but devtmpfs success:

mount procfs: Resource busy
Hello! I'm a user init script :-)
Welcome to NodeOS!: username:  nodeos
·                 : password:  
~ > 

Actually, I suspect that the issue is barely that environment is clean. In docker all ENV directives during build are kept when your're running commands. But in vagga build-time environment (!Env) and run-time environment (environ: {..}) are separate. Try to set PATH (or maybe all the variables) as in docker.

I'm not paying too much attention during boot process to the environment variables, but I'll take a look on that. If it's of interest, the line dispatching the error is at https://github.com/piranna/nodeos-init/blob/ce02e341368d2bf603f8f73601d49925574fe969/init.c#L192

piranna avatar Jan 22 '17 23:01 piranna

Sorry for the delay, I have been busy and also I was not able to correctly get the environment variables (my build system has several steps and also found a bug on one of them...). Finally I was able to get them:

root=container
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

They are somewhat expected, theroot=container is the way I have say "you are in container so there's no need to mount it", and the other ones are fairly common, so no problem here. What I've seen is that when using printf() once the execv error doesn't show and just only start the umount messages, but after being able to print the environment variables (several calls to printf) the /sbin/init command (already written in Node.js, and executed by /init with execvp) could run, so seems there's a race condition somewhere (probably mine, I'll investigate).

On the other hand, /sbin/init shows me that the environment variables are empty (process.env = {}), while they are being set on vagga.yaml and the /init process get them. That's the first time this happens to me (both in Docker, QEmu and real hardware environment variables are preserved and inherited by the child processes, as usual), so maybe Vagga is doing something here?

piranna avatar Feb 02 '17 10:02 piranna

Just run everything under strace. You'll see execve commands with all correct paths and environment variables. Do you know how to run vagga under strace?

tailhook avatar Feb 02 '17 10:02 tailhook

Ok, don't pay attention to the last paragraph, seems that since I was moving the environ pointer to read it I got up to an empty list when it was time to exec the /sbin/init process.

Don't know why, but now I don't have the execvp problem and I'm able to normally exec /sbin/init, and by using a container I'm able to access the users folders. Now the problem I have is that this container is mounted as read-only (/etc/mtab says /dev/sda3 /tmp ext4 ro,relatime,data=ordered 0 0). My current vagga.yaml file is:

containers:
  bootfs:
    auto-clean: true
    hosts-file-path: null
    resolv-conf-path: null
    setup:
    - !Tar
      url: ./out/latest/barebones.tar.gz
    - !Tar
      url: ./out/latest/initramfs.tar.gz
#    - !Tar
#      url: ./out/latest/usersfs.tar.gz
#      path: /tmp
#    volumes:
#      /tmp: !Persistent usersfs
  usersfs:
    data-dirs: [/nodeos, /root]
    setup:
    - !Tar
      url: ./out/latest/usersfs.tar.gz

commands:
  run: !Command
    description: Start NodeOS
    container: bootfs
    volumes:
      /tmp: !Container usersfs
#    volumes:
#      /tmp: !Persistent
#        name: usersfs
    environ:
      root: 'container'
#       PWD: /
#      root: ''
#     pid1mode: exec
    run: [/init]
    write-mode: transient-hard-link-copy

piranna avatar Feb 02 '17 10:02 piranna

Ah, yes !Container volumes are always read-only, regardless of transient-hard-link-copy

tailhook avatar Feb 02 '17 10:02 tailhook

You're probably want !Persistent volume seeded from a container, if you want writable system there.

tailhook avatar Feb 02 '17 10:02 tailhook

Or a !Snapshot volume if you need temporary writable system (but be careful, it's tmpfs)

tailhook avatar Feb 02 '17 10:02 tailhook

Ok, I have tried so far with !Container and !Persistent in several ways without luck. I could use !Snapshot, but then the data is not persistent. I have tried to initialize the volume using a minimal container like I was proposed at https://github.com/tailhook/vagga/issues/367#issuecomment-263088528 and worked for me, but feels like using a cannon to kill a fly because I just only need to use a tarfile to fill the volume, that's why I find more simple my proposal of allowing to use tarfiles on the !Persistent.seed attribute.

Anyway, I've been able to use it and both fuse and overlayfs works, so the port is almost finished :-D But I've found another problem that's not happening on Docker: the /init child processes works without problem, but the grandchilds (the child of the child processes) don't seem to be executed. They are being executed as the root process of a new processes group, but this happens also when changing it to be on the same one. Could there be some security limitation imposed by vagga because they are executed inside a chroot? You have the code that exec them available at https://github.com/piranna/jocker/blob/master/lib/chrootSpawn, putting a trace there gets executed, but the process executed by spawn() don't print anything on the console also when it's the only thing they do (in Docker it works flawlessly) and also don't write to the filesystem, that's why I think they don't get executed at all, but the point is that there's no error at all... :-/

piranna avatar Feb 04 '17 21:02 piranna

Could there be some security limitation imposed by vagga because they are executed inside a chroot?

There are some limitations of chroot'ed containers imposed by kernel. Usually you need to use pivot_root instead of chroot to get rid of them. But I don't remember them well enough. You can't create user namespaces inside chroot, but I don't think you're doing that.

In cany case, if node.js doesn't show you exact error, just use strace to see what happens after execve or why the the system call returns error.

tailhook avatar Feb 05 '17 23:02 tailhook

I noticed that the CPU was very high, and investigating a bit found that the ExclFS filesystem was receiving a lot of requests for /dev/srandom and /dev/egd-pool, although they don't exists on the host system. Seems Node.js checks on loading for several entropy generators on /dev to find what of them are available, what I don't understand is that on Docker or real hardware it works correctly but on vagga it start an endless loop checking for them over and over... :-/ That's the reason why they don't print anything, because they can't be able to continue execution :-( It's strange, isn't it?

piranna avatar Feb 06 '17 18:02 piranna

Sure, it's strange. Firstly, /dev is pretty functional in container. Maybe you don't mount --bind it in chroot? Secondly, it shouldn't start endless loop for that (it's bug in node.js in this case).

tailhook avatar Feb 06 '17 18:02 tailhook

Maybe you don't mount --bind it in chroot?

I have it mounted, in fact it's bind'ed to an instance of ExclFS, that's showing the content of the real /dev and where I have seen it's getting the huge loop of requests.

Secondly, it shouldn't start endless loop for that (it's bug in node.js in this case).

I think it too, just wanted to know if you could have an idea what could be making it to fail, since it only happens on vagga :-/

piranna avatar Feb 06 '17 19:02 piranna

What does ExclFS do?

tailhook avatar Feb 06 '17 19:02 tailhook

BTW, there were some issues with translating uids, gids and pids in fuse, when using user namespaces. Here is the one of them https://github.com/gittup/tup/issues/184 Maybe there are more.

tailhook avatar Feb 06 '17 19:02 tailhook

What does ExclFS do?

It's a FUSE filesystem I've created. It records the files on an underlying directory (/dev in this case) that have been requested for access and show only the ones that are being used by the user or by nobody else, giving in fact access to each file to a single user each time. Doing this on /dev allow to the users to have direct access to the devices without needing explicit permissions and at the same time nobody can steal data or change the devices that are being used by others.

BTW, there were some issues with translating uids, gids and pids in fuse, when using user namespaces. Here is the one of them gittup/tup#184 Maybe there are more.

Interesting... I say that ExclFS was receiving the correct PID and uid, but didn't suspect there could be problems doing the translation in the other way, supossed it was fully supported... A good candidate for this problem, how could I be able to check it?

piranna avatar Feb 06 '17 19:02 piranna