diff --git a/BuildGuide.md b/BuildGuide.md new file mode 100644 index 000000000..229f22aae --- /dev/null +++ b/BuildGuide.md @@ -0,0 +1,175 @@ +##### Initial Notes & Recommendations +* Due to issues to cross-compile the image from a desktop, this guide will take you step-by-step through the process of compiling EmbassyOS directly on a Raspberry Pi 4 (4GB or 8GB) +* This process will go faster if you have an SSD/NVMe USB drive available. +* This build guide does **not** require a large microSD card, especially if your final build wil be used on an SSD/NVMe USB drive. +* Basic know-how of linux commands and terminal use is recommended. +* Follow the guide carefully and do not skip any steps. + +# :hammer_and_wrench: Build Guide +1. Flash [Raspberry Pi OS Lite](https://www.raspberrypi.org/software/operating-systems/) to a microSD and configure your raspi to boot from SSD/NVMe USB drive + 1. After flashing, create an empty text file called `ssh` in the `boot` partition of the microSD, then proceed with booting the raspi with the flashed microSD (check your router for the IP assigned to your raspi) + 1. Do the usual initial update/config + ``` + sudo apt update + sudo raspi-config + ``` + 1. Change `Advanced Options->Boot Order` + 1. Select `USB Boot` *(it will try to boot from microSD first if it's available)* + 1. Select `Finish`, then `Yes` to reboot + 1. After reboot, `sudo shutdown now` to power off the raspi and remove the microSD + +2. Flash the *Raspi OS Lite* (from step 1) to your SSD/NVMe drive + > :information_source: Don't worry about rootfs partition size (raspi will increase it for you on initial boot) + + > :information_source: Every time you re-flash your SSD/NVMe you need to first boot with a microSD and set *Boot Order* again + + 1. Don't forget to create the empty `ssh` file + 1. Connect the drive (remember to remove the microSD) to the raspi and start it up + 1. Use `sudo raspi-config` to change the default password + 1. Optional: `sudo apt upgrade -y` + 1. Optional: `sudo nano /etc/apt/sources.list.d/vscode.list` comment the last line which contains `packages.microsoft.com` + +3. Install GHC + ``` + sudo apt update + sudo apt install -y ghc + + #test: + ghc --version + + #example of output: + The Glorious Glasgow Haskell Compilation System, version 8.4.4 + ``` + +4. Compile Stack: + 1. Install Stack v2.1.3 + ``` + cd ~/ + wget -qO- https://raw.githubusercontent.com/commercialhaskell/stack/v2.1.3/etc/scripts/get-stack.sh | sh + + #test with + stack --version + + #example output: + Version 2.1.3, Git revision 636e3a759d51127df2b62f90772def126cdf6d1f (7735 commits) arm hpack-0.31.2 + ``` + + 1. Use current Stack to compile Stack v2.5.1: + ``` + git clone --depth 1 --branch v2.5.1 https://github.com/commercialhaskell/stack.git + cd stack + sudo apt install -y screen + screen + ``` + > :information_source: Build (>=3.5h total... We are using `screen` in case of session timeout issues) + + > :memo: If you get disconected you can reattach last sesion again by executing `screen -r` + ``` + stack build --stack-yaml=stack-ghc-84.yaml --system-ghc + + #Install + stack install --stack-yaml=stack-ghc-84.yaml --system-ghc + export PATH=~/.local/bin:$PATH + ``` + +5. Clone EmbassyOS & try to *make* the `agent`: + 1. First attempt + > :information_source: The first time you run **make** you'll get an error + + ``` + sudo apt install -y llvm-9 libgmp-dev + export PATH=/usr/lib/llvm-9/bin:$PATH + cd ~/ + git clone https://github.com/Start9Labs/embassy-os.git + cd embassy-os/ + make agent + ``` + > :memo: This will install ghc-8.10.2, then attempt to build but will give errors (in next steps we deal with errors) + 1. Confirm your cpu info + ``` + cat /proc/cpuinfo | grep Hardware + ``` + 1. If your "Hardware" is [BCM2711](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/README.md) then: + 1. Change `C compiler flags` to `-marm -mcpu=cortex-a72` in the GHC settings: + ``` + nano ~/.stack/programs/arm-linux/ghc-8.10.2/lib/ghc-8.10.2/settings + ``` + 1. To prevent gcc errors we delete the `setup-exe-src` folder + ``` + rm -rf ~/.stack/setup-exe-src/ + ``` + +6. Install requirements for step 7 + 1. Install NVM + ``` + cd ~/ && curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm + [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion + nvm --version + ``` + 1. Install Node.js & NPM + ``` + nvm install node + ``` + 1. Install Ionic CLI + ``` + npm install -g @ionic/cli + ``` + 1. Install Dependencies + ``` + sudo apt-get install -y build-essential openssl libssl-dev libc6-dev clang libclang-dev libavahi-client-dev upx ca-certificates + ``` + 1. Install Rust + ``` + cd ~/ && curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o- | bash + + #Choose option 1 + source $HOME/.cargo/env + + #Check rust & cargo versions + rustc --version + cargo --version + ``` + +7. Finally, getting to build the **.img** + 1. At this stage you hava a working development environment to build your **embassy.img**. + Before you do that you can choose to enable SSH login for user `pi` in case something will go wrong or just skip to the next step. + ``` + cd ~/embassy-os + sed -e '/passwd -l pi/ s/^#*/#/' -i setup.sh + ``` + > :warning: Default password for user `pi` is `raspberry`, change it the next you login. + 1. Build the `embassy.img` + ``` + cd ~/embassy-os + make + + #Depending from your hadware this can take 1-2h+ + #Wait for the "DONE!" message and take note of your product_key + exit + ``` +8. Flash the `embassy.img` to a microSD + 1. Copy `embassy.img` from the raspi to your PC with scp + ``` + scp pi@raspi_IP:~/embassy-os/embassy.img . + ``` + 1. Connect to raspi again to do `sudo shutdown now`, after a complete shutdown disconnect SSD/NVMe drive + 1. Flash `embassy.img` to a microSD (do this before flashing to the SSD/NVMe, to be sure it works) + +9. Prepare for initial setup + 1. Boot raspi using flashed microSD + 1. After a few minutes, the raspi should reboot itself and make it's first [sounds](#embassy-sounds-explained). + > :information_source: If needed, you can check the `agent` log with: `journalctl -u agent -ef` + 1. Proceed with the [initial setup process of EmbassyOS](https://docs.start9labs.com/user-manual/initial-setup.html) + 1. If all went well you can safely flash `embassy.img` to an SSD/NVMe and repeat step 9 + +### Embassy sounds explained +Sound :notes: | Indicating +------- | -------- +Bep | Device is powering on +Chime | Device is ready for setup +Mario "Coin" | EmbassyOS has started +Mario "Death" | Device is about to Shutdown/Reboot +Mario "Power Up" | EmbassyOS update sequence +Beethoven | Update failed :( diff --git a/Makefile b/Makefile index cb9146e62..bb5880208 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,15 @@ +UNAME := $(shell uname -m) + +EMBASSY_SRC := buster.img product_key appmgr/target/armv7-unknown-linux-gnueabihf/release/appmgr ui/www agent/dist/agent agent/config/agent.service lifeline/target/armv7-unknown-linux-gnueabihf/release/lifeline lifeline/lifeline.service setup.sh setup.service docker-daemon.json +APPMGR_RELEASE_SRC := appmgr/target/armv7-unknown-linux-gnueabihf/release/appmgr +LIFELINE_RELEASE_SRC := lifeline/target/armv7-unknown-linux-gnueabihf/release/lifeline + +ifeq ($(UNAME), armv7l) + EMBASSY_SRC := buster.img product_key appmgr/target/release/appmgr ui/www agent/dist/agent agent/config/agent.service lifeline/target/release/lifeline lifeline/lifeline.service setup.sh setup.service docker-daemon.json + APPMGR_RELEASE_SRC := appmgr/target/release/appmgr + LIFELINE_RELEASE_SRC := lifeline/target/release/lifeline +endif + APPMGR_SRC := $(shell find appmgr/src) appmgr/Cargo.toml appmgr/Cargo.lock LIFELINE_SRC := $(shell find lifeline/src) lifeline/Cargo.toml lifeline/Cargo.lock AGENT_SRC := $(shell find agent/src) $(shell find agent/config) agent/stack.yaml agent/package.yaml agent/build.sh @@ -13,7 +25,8 @@ UI_SRC := $(shell find ui/src) \ all: embassy.img -embassy.img: buster.img product_key appmgr/target/armv7-unknown-linux-gnueabihf/release/appmgr ui/www agent/dist/agent agent/config/agent.service lifeline/target/armv7-unknown-linux-gnueabihf/release/lifeline lifeline/lifeline.service setup.sh setup.service docker-daemon.json +embassy.img: $(EMBASSY_SRC) + chmod +x make_image.sh sudo ./make_image.sh buster.img: @@ -26,11 +39,16 @@ product_key: echo "X\c" > product_key cat /dev/random | base32 | head -c11 | tr '[:upper:]' '[:lower:]' >> product_key -appmgr/target/armv7-unknown-linux-gnueabihf/release/appmgr: $(APPMGR_SRC) +$(APPMGR_RELEASE_SRC): $(APPMGR_SRC) +ifeq ($(UNAME), armv7l) + cd appmgr && cargo update && cargo build --release --features=production + arm-linux-gnueabihf-strip appmgr/target/release/appmgr +else docker run --rm -it -v ~/.cargo/registry:/root/.cargo/registry -v "$(shell pwd)":/home/rust/src start9/rust-arm-cross:latest sh -c "(cd appmgr && cargo build --release --features=production)" docker run --rm -it -v ~/.cargo/registry:/root/.cargo/registry -v "$(shell pwd)":/home/rust/src start9/rust-arm-cross:latest arm-linux-gnueabi-strip appmgr/target/armv7-unknown-linux-gnueabihf/release/appmgr +endif -appmgr: appmgr/target/armv7-unknown-linux-gnueabihf/release/appmgr +appmgr: $(APPMGR_RELEASE_SRC) agent/dist/agent: $(AGENT_SRC) (cd agent && ./build.sh) @@ -45,9 +63,13 @@ ui/www: $(UI_SRC) ui/node_modules ui: ui/www -lifeline/target/armv7-unknown-linux-gnueabihf/release/lifeline: $(LIFELINE_SRC) +$(LIFELINE_RELEASE_SRC): $(LIFELINE_SRC) +ifeq ($(UNAME), armv7l) + cd lifeline && cargo build --release + arm-linux-gnueabihf-strip lifeline/target/release/lifeline +else docker run --rm -it -v ~/.cargo/registry:/root/.cargo/registry -v "$(shell pwd)":/home/rust/src start9/rust-arm-cross:latest sh -c "(cd lifeline && cargo build --release)" docker run --rm -it -v ~/.cargo/registry:/root/.cargo/registry -v "$(shell pwd)":/home/rust/src start9/rust-arm-cross:latest arm-linux-gnueabi-strip lifeline/target/armv7-unknown-linux-gnueabihf/release/lifeline +endif -lifeline: lifeline/target/armv7-unknown-linux-gnueabihf/release/lifeline - +lifeline: $(LIFELINE_RELEASE_SRC) diff --git a/make_image.sh b/make_image.sh index ef6e34596..ee41c3d3c 100644 --- a/make_image.sh +++ b/make_image.sh @@ -1,5 +1,10 @@ #!/bin/bash - +arch=$(uname -m) +if [[ $arch == armv7l ]]; then + dev_target="target" +else + dev_target="target/armv7-unknown-linux-musleabihf" +fi mv buster.img embassy.img product_key=$(cat product_key) loopdev=$(losetup -f -P embassy.img --show) @@ -9,6 +14,12 @@ mkdir -p "${root_mountpoint}" mkdir -p "${boot_mountpoint}" mount "${loopdev}p2" "${root_mountpoint}" mount "${loopdev}p1" "${boot_mountpoint}" +mkdir -p "${root_mountpoint}/root/agent" +mkdir -p "${root_mountpoint}/etc/docker" +mkdir -p "${root_mountpoint}/home/pi/.ssh" +echo -n "" > "${root_mountpoint}/home/pi/.ssh/authorized_keys" +chown -R pi:pi "${root_mountpoint}/home/pi/.ssh" +echo -n "" > "${boot_mountpoint}/ssh" echo "${product_key}" > "${root_mountpoint}/root/agent/product_key" echo -n "start9-" > "${root_mountpoint}/etc/hostname" echo -n "${product_key}" | shasum -t -a 256 | cut -c1-8 >> "${root_mountpoint}/etc/hostname" @@ -18,20 +29,23 @@ echo -n "${product_key}" | shasum -t -a 256 | cut -c1-8 >> "${root_mountpoint}/e mv "${root_mountpoint}/etc/hosts.tmp" "${root_mountpoint}/etc/hosts" cp agent/dist/agent "${root_mountpoint}/usr/local/bin/agent" chmod 700 "${root_mountpoint}/usr/local/bin/agent" -cp appmgr/target/armv7-unknown-linux-musleabihf/release/appmgr "${root_mountpoint}/usr/local/bin/appmgr" +cp "appmgr/${dev_target}/release/appmgr" "${root_mountpoint}/usr/local/bin/appmgr" chmod 700 "${root_mountpoint}/usr/local/bin/appmgr" -cp lifeline/target/armv7-unknown-linux-musleabihf/release/lifeline "${root_mountpoint}/usr/local/bin/lifeline" +cp "lifeline/${dev_target}/release/lifeline" "${root_mountpoint}/usr/local/bin/lifeline" chmod 700 "${root_mountpoint}/usr/local/bin/lifeline" cp docker-daemon.json "${root_mountpoint}/etc/docker/daemon.json" cp setup.sh "${root_mountpoint}/root/setup.sh" chmod 700 "${root_mountpoint}/root/setup.sh" -cp setup.service /etc/systemd/system/setup.service -cp lifeline/lifeline.service /etc/systemd/system/lifeline.service -cp agent/config/agent.service /etc/systemd/system/agent.service -cat "${boot_mountpoint}/config.txt" | grep -v "dtoverlay=pwm-2chan" > "${boot_mountpoint}/config.txt.tmp" +cp setup.service "${root_mountpoint}/etc/systemd/system/setup.service" +ln -s /etc/systemd/system/setup.service "${root_mountpoint}/etc/systemd/system/getty.target.wants/setup.service" +cp lifeline/lifeline.service "${root_mountpoint}/etc/systemd/system/lifeline.service" +cp agent/config/agent.service "${root_mountpoint}/etc/systemd/system/agent.service" +cat "${boot_mountpoint}/config.txt" | grep -v "dtoverlay=" > "${boot_mountpoint}/config.txt.tmp" echo "dtoverlay=pwm-2chan" >> "${boot_mountpoint}/config.txt.tmp" +mv "${boot_mountpoint}/config.txt.tmp" "${boot_mountpoint}/config.txt" umount "${root_mountpoint}" rm -r "${root_mountpoint}" umount "${boot_mountpoint}" rm -r "${boot_mountpoint}" -losetup -d ${loopdev} \ No newline at end of file +losetup -d ${loopdev} +echo "DONE! Here is your EmbassyOS key: ${product_key}" \ No newline at end of file diff --git a/setup.service b/setup.service index b4e3c9b90..31aca3e1b 100644 --- a/setup.service +++ b/setup.service @@ -1,10 +1,15 @@ [Unit] Description=Boot process for system setup. +After=rc-local.service +Before=getty.target +ConditionFileNotEmpty=/root/setup.sh [Service] Type=oneshot ExecStart=/root/setup.sh +ExecStartPost=/root/setup-s2.sh RemainAfterExit=true [Install] -WantedBy=multi-user.target +WantedBy=basic.target + diff --git a/setup.sh b/setup.sh index 37ac26ccc..e7a092f9c 100644 --- a/setup.sh +++ b/setup.sh @@ -1,15 +1,26 @@ #!/bin/bash -apt update -apt install -y libsecp256k1-0 -apt install -y tor -apt install -y docker.io -apt install -y iotop -apt install -y bmon -apt autoremove -y mkdir -p /root/volumes mkdir -p /root/tmp/appmgr mkdir -p /root/agent mkdir -p /root/appmgr/tor +apt-get update -y +apt-get install -y tor +apt-get install -y iotop +apt-get install -y bmon +apt-get install -y libavahi-client3 +apt-get install -y libsecp256k1-0 +apt-get install -y docker.io needrestart- +mv /root/setup.sh /root/setup-s1.sh.done +cat <> /root/setup-s2.sh +#!/bin/bash +apt-get update -y +apt-get install -y tor +apt-get install -y iotop +apt-get install -y bmon +apt-get install -y libavahi-client3 +apt-get install -y libsecp256k1-0 +apt-get install -y docker.io needrestart- +apt-get autoremove -y systemctl enable lifeline systemctl enable agent systemctl enable ssh @@ -17,5 +28,8 @@ systemctl enable avahi-daemon passwd -l root passwd -l pi sync -systemctl disable setup.service -reboot \ No newline at end of file +systemctl disable setup +mv /root/setup-s2.sh /root/setup-s2.sh.done +reboot +EOT +chmod +x /root/setup-s2.sh \ No newline at end of file