[
  {
    "path": ".gitignore",
    "content": "/fakeinit\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2017 Hector Martin \"marcan\" <marcan@marcan.st>\nInit code based on Rich Felker's trivial init: https://ewontfix.com/14/\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# takeover.sh\n\nA script to completely take over a running Linux system remotely, allowing you\nto log into an in-memory rescue environment, unmount the original root\nfilesystem, and do anything you want, all without rebooting. Replace one distro\nwith another without touching a physical console.\n\n## WARNING WARNING WARNING WARNING\n\nThis is experimental. Do not use this script if you don't understand exactly\nhow it works. Do not use this script on any system you care about. Do not use\nthis script on any system you expect to be up. Do not run this script unless\nyou can afford to get physical access to fix a botched takeover. If anything\ngoes wrong, your system will most likely panic.\n\nThat said, this script will not (itself) make any permanent changes to your\nexisting root filesystem (assuming you run it from a tmpfs), so as long as you\ncan remotely reboot your box using an out-of-band mechanism, you *should* be OK.\nBut don't blame me if it eats your dog.\n\nThis script does not have any provisions for exiting *out* of the new\nenvironment back into something sane. You *will* have to reboot when you're\ndone. If you get anything wrong, your machine won't boot. Tough luck.\n\nThis is not a guide for newbies. I'm deliberately not giving you commands you\ncan copy and paste. If you can't figure out what to do exactly without\nhandholding, this script is not for you.\n\n## Compatibility\n\nThis script is designed for init systems that support the `telinit u` command to\nreload the init binary. This includes sysvinit and systemd. If your init system\nworks a different way, you will have to adapt it, or this might not work at\nall. You're on your own here.\n\nYou should always test this in a VM first. You can grab a tarball of your live\nroot filesystem, extract it into a VM image, get your VM up and running (boot\nloader setup is left as an exercise for the reader), then try the process there\nand see if it works. Hint: `mount --bind / /mnt` will get you a view of your\nroot filesystem on `/mnt` without any other filesystems that are mounted on top.\n\n## Usage\n\nYou need to decide on what rescue environment you want. I recommend\n[SystemRescueCD](https://www.system-rescue-cd.org/), which comes with many\nuseful tools (you have to loopmount the ISO and then use `unsquashfs`).\nObviously, whatever you pick has to fit into free RAM, with room to spare. If\nyour chosen rescue environment has `/lib/modules`, you may want to get rid of\nit to save space, as its kernel modules won't be useful on the host kernel\nanyway.\n\n1. Create a directory `/takeover` on your target system and mount a tmpfs on it\n2. Extract your rescue environment there. Make sure it works by chrooting into\n   it and running a few commands. Make sure you do not bork filesystem\n   permissions. Exit the chroot.\n3. Grab a recent copy of `busybox` (statically linked) and put it in\n   `/takeover/busybox`. You can find binaries\n   [here](https://www.busybox.net/downloads/binaries/1.26.2-defconfig-multiarch/).\n   Make sure it works by trying something like `/takeover/busybox sh`.\n4. Copy the contents of this repository into `/takeover`.\n5. Compile `fakeinit.c`. It must be compiled such that it works inside the\n   takeover environment. If your rescue environment has `gcc`, you can just\n   compile it inside the chroot: `chroot /takeover gcc /fakeinit.c -o /fakeinit`.\n   Otherwise, you might want to statically link it.\n6. Shut down as many services as you can on your host. `takeover.sh` will by\n   default set up an SSHd listening on port 80, though you may edit this in\n   the script.\n7. Run `sh /takeover/takeover.sh` and follow the prompts.\n\nIf everything worked, congratulations! You may now use your new SSH session\nto kill any remaining old daemons (`kill -9` is recommended to make sure they\ndon't try to do anything silly during shutdown), and then unmount all\nfilesystems under `/old_root`, including `/old_root` itself. You may want to\nfirst copy `/old_root/lib/modules` into your new tmpfs in case you need any old\nkernel modules.\n\nYou are now running entirely from RAM and should be able to do as you please.\nNote that you may still have to clean up LVM volumes (`dmsetup` is your friend)\nand similar before you can safely repartition your disk and install Gentoo\nLinux, which is of course the whole reason you're doing this crazy thing to\nbegin with. \n\nWhen you're done, unmount all filesystems, `sync`, then `reboot -f` or `echo b >\n/proc/sysrq-trigger` and cross your fingers.\n\n## Further reading\n\nI've been pointed to\n[this StackExchange answer](http://unix.stackexchange.com/questions/226872/how-to-shrink-root-filesystem-without-booting-a-livecd/227318#227318)\nwhich details how to manually perform a similar process, but using a subset of\nthe existing root filesystem instead of a rescue filesystem. This allows you\nto keep (a new copy of) the existing init system running, as well as essential\ndaemons, and then go back to the original root filesystem once you're done. This\nis a more useful version if, for example, you want to resize the original root\nfilesystem or re-configure disk partitions, but not actually install a different\ndistro, and you want to avoid rebooting at all.\n\n`takeover.sh` could be extended to support re-execing a new init once you're\ndone. This could be used to switch to a *new* distro entirely without\nrebooting, as long as you're happy using the old kernel. If you're interested,\npull requests welcome :-).\n"
  },
  {
    "path": "fakeinit.c",
    "content": "#define _XOPEN_SOURCE 700\n#include <signal.h>\n#include <unistd.h>\n#include <sys/wait.h>\n\nint main()\n{\n\tsigset_t set;\n\tint status, i;\n\n\tfor (i = 0; i < 1024; i++)\n\t\tclose(i);\n\n\tif (getpid() != 1) return 1;\n\n\tsigfillset(&set);\n\tsigprocmask(SIG_BLOCK, &set, 0);\n\n\tfor (;;) wait(&status);\n}\n\n"
  },
  {
    "path": "takeover.sh",
    "content": "#!/bin/sh\nset -e\n\nTO=/takeover\nOLD_INIT=$(readlink /proc/1/exe)\nPORT=80\n\ncd \"$TO\"\n\nif [ ! -e fakeinit ]; then\n    ./busybox echo \"Please compile fakeinit.c first\"\n    exit 1\nfi\n\n./busybox echo \"Please set a root password for sshd\"\n\n./busybox chroot . /bin/passwd\n\n./busybox echo \"Setting up target filesystem...\"\n./busybox rm -f etc/mtab\n./busybox ln -s /proc/mounts etc/mtab\n./busybox mkdir -p old_root\n\n./busybox echo \"Mounting pseudo-filesystems...\"\n./busybox mount -t tmpfs tmp tmp\n./busybox mount -t proc proc proc\n./busybox mount -t sysfs sys sys\nif ! ./busybox mount -t devtmpfs dev dev; then\n    ./busybox mount -t tmpfs dev dev\n    ./busybox cp -a /dev/* dev/\n    ./busybox rm -rf dev/pts\n    ./busybox mkdir dev/pts\nfi\n./busybox mount --bind /dev/pts dev/pts\n\nTTY=\"$(./busybox tty)\"\n\n./busybox echo \"Checking and switching TTY...\"\n\nexec <\"$TO/$TTY\" >\"$TO/$TTY\" 2>\"$TO/$TTY\"\n\n./busybox echo \"Type 'OK' to continue\"\n./busybox echo -n \"> \"\nread a\nif [ \"$a\" != \"OK\" ] ; then\n    exit 1\nfi\n\n./busybox echo \"Preparing init...\"\n./busybox cat >tmp/${OLD_INIT##*/} <<EOF\n#!${TO}/busybox sh\n\nexec <\"${TO}/${TTY}\" >\"${TO}/${TTY}\" 2>\"${TO}/${TTY}\"\ncd \"${TO}\"\n\n./busybox echo \"Init takeover successful\"\n./busybox echo \"Pivoting root...\"\n./busybox mount --make-rprivate /\n./busybox pivot_root . old_root\n./busybox echo \"Chrooting and running init...\"\nexec ./busybox chroot . /fakeinit\nEOF\n./busybox chmod +x tmp/${OLD_INIT##*/}\n\n./busybox echo \"Starting secondary sshd\"\n\n./busybox chroot . /usr/bin/ssh-keygen -A\n./busybox chroot . /usr/sbin/sshd -p $PORT -o PermitRootLogin=yes\n\n./busybox echo \"You should SSH into the secondary sshd now.\"\n./busybox echo \"Type OK to continue\"\n./busybox echo -n \"> \"\nread a\nif [ \"$a\" != \"OK\" ] ; then\n    exit 1\nfi\n\n./busybox echo \"About to take over init. This script will now pause for a few seconds.\"\n./busybox echo \"If the takeover was successful, you will see output from the new init.\"\n./busybox echo \"You may then kill the remnants of this session and any remaining\"\n./busybox echo \"processes from your new SSH session, and umount the old root filesystem.\"\n\n./busybox mount --bind tmp/${OLD_INIT##*/} ${OLD_INIT}\n\ntelinit u\n\n./busybox sleep 10\n\n"
  }
]