mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
merge from master and fix typescript errors
This commit is contained in:
240
.github/workflows/startos-iso.yaml
vendored
240
.github/workflows/startos-iso.yaml
vendored
@@ -12,9 +12,9 @@ on:
|
||||
- dev
|
||||
- unstable
|
||||
- dev-unstable
|
||||
- podman
|
||||
- dev-podman
|
||||
- dev-unstable-podman
|
||||
- docker
|
||||
- dev-docker
|
||||
- dev-unstable-docker
|
||||
runner:
|
||||
type: choice
|
||||
description: Runner
|
||||
@@ -31,6 +31,13 @@ on:
|
||||
- aarch64
|
||||
- aarch64-nonfree
|
||||
- raspberrypi
|
||||
deploy:
|
||||
type: choice
|
||||
description: Deploy
|
||||
options:
|
||||
- NONE
|
||||
- alpha
|
||||
- beta
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@@ -42,11 +49,55 @@ on:
|
||||
|
||||
env:
|
||||
NODEJS_VERSION: "18.15.0"
|
||||
ENVIRONMENT: '${{ fromJson(format(''["{0}", ""]'', github.event.inputs.environment || ''dev-podman''))[github.event.inputs.environment == ''NONE''] }}'
|
||||
ENVIRONMENT: '${{ fromJson(format(''["{0}", ""]'', github.event.inputs.environment || ''dev''))[github.event.inputs.environment == ''NONE''] }}'
|
||||
|
||||
jobs:
|
||||
all:
|
||||
name: Build
|
||||
compile:
|
||||
name: Compile Base Binaries
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
arch: >-
|
||||
${{
|
||||
fromJson('{
|
||||
"x86_64": ["x86_64"],
|
||||
"x86_64-nonfree": ["x86_64"],
|
||||
"aarch64": ["aarch64"],
|
||||
"aarch64-nonfree": ["aarch64"],
|
||||
"raspberrypi": ["aarch64"],
|
||||
"ALL": ["x86_64", "aarch64"]
|
||||
}')[github.event.inputs.platform || 'ALL']
|
||||
}}
|
||||
runs-on: ${{ fromJson('["ubuntu-22.04", "buildjet-32vcpu-ubuntu-2204"]')[github.event.inputs.runner == 'fast'] }}
|
||||
steps:
|
||||
- run: |
|
||||
sudo mount -t tmpfs tmpfs .
|
||||
if: ${{ github.event.inputs.runner == 'fast' }}
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODEJS_VERSION }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Make
|
||||
run: make ARCH=${{ matrix.arch }} compiled-${{ matrix.arch }}.tar
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: compiled-${{ matrix.arch }}.tar
|
||||
path: compiled-${{ matrix.arch }}.tar
|
||||
image:
|
||||
name: Build Image
|
||||
needs: [compile]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -68,86 +119,30 @@ jobs:
|
||||
format(
|
||||
'["ubuntu-22.04", "{0}"]',
|
||||
fromJson('{
|
||||
"x86_64": ["buildjet-32vcpu-ubuntu-2204", "buildjet-32vcpu-ubuntu-2204"],
|
||||
"x86_64-nonfree": ["buildjet-32vcpu-ubuntu-2204", "buildjet-32vcpu-ubuntu-2204"],
|
||||
"aarch64": ["buildjet-16vcpu-ubuntu-2204-arm", "buildjet-32vcpu-ubuntu-2204-arm"],
|
||||
"aarch64-nonfree": ["buildjet-16vcpu-ubuntu-2204-arm", "buildjet-32vcpu-ubuntu-2204-arm"],
|
||||
"raspberrypi": ["buildjet-16vcpu-ubuntu-2204-arm", "buildjet-32vcpu-ubuntu-2204-arm"],
|
||||
}')[matrix.platform][github.event.inputs.platform == matrix.platform]
|
||||
"x86_64": "buildjet-8vcpu-ubuntu-2204",
|
||||
"x86_64-nonfree": "buildjet-8vcpu-ubuntu-2204",
|
||||
"aarch64": "buildjet-8vcpu-ubuntu-2204-arm",
|
||||
"aarch64-nonfree": "buildjet-8vcpu-ubuntu-2204-arm",
|
||||
"raspberrypi": "buildjet-8vcpu-ubuntu-2204-arm",
|
||||
}')[matrix.platform]
|
||||
)
|
||||
)[github.event.inputs.runner == 'fast']
|
||||
}}
|
||||
env:
|
||||
ARCH: >-
|
||||
${{
|
||||
fromJson('{
|
||||
"x86_64": "x86_64",
|
||||
"x86_64-nonfree": "x86_64",
|
||||
"aarch64": "aarch64",
|
||||
"aarch64-nonfree": "aarch64",
|
||||
"raspberrypi": "aarch64",
|
||||
}')[matrix.platform]
|
||||
}}
|
||||
steps:
|
||||
- name: Free space
|
||||
run: df -h && rm -rf /opt/hostedtoolcache* && df -h
|
||||
if: ${{ github.event.inputs.runner != 'fast' }}
|
||||
|
||||
- run: |
|
||||
sudo mount -t tmpfs tmpfs .
|
||||
if: ${{ github.event.inputs.runner == 'fast' && (matrix.platform == 'x86_64' || matrix.platform == 'x86_64-nonfree' || github.event.inputs.platform == matrix.platform) }}
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Start9Labs/embassy-os-deb
|
||||
path: embassy-os-deb
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
path: embassy-os-deb/embassyos-0.3.x
|
||||
|
||||
- run: |
|
||||
cp -r debian embassyos-0.3.x/
|
||||
VERSION=0.3.x ./control.sh
|
||||
cp embassyos-0.3.x/backend/startd.service embassyos-0.3.x/debian/embassyos.startd.service
|
||||
working-directory: embassy-os-deb
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODEJS_VERSION }}
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: /var/lib/docker
|
||||
key: ${{ runner.os }}-${{ matrix.platform }}-docker-cache
|
||||
|
||||
- name: Get npm cache directory
|
||||
id: npm-cache-dir
|
||||
run: |
|
||||
echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||
- uses: actions/cache@v3
|
||||
id: npm-cache
|
||||
with:
|
||||
path: ${{ steps.npm-cache-dir.outputs.dir }}
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install \
|
||||
debmake \
|
||||
debhelper-compat \
|
||||
crossbuild-essential-arm64
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Run dpkg build
|
||||
working-directory: embassy-os-deb
|
||||
run: "make VERSION=0.3.x TAG=${{ github.ref_name }}"
|
||||
env:
|
||||
OS_ARCH: ${{ matrix.platform }}
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Start9Labs/startos-image-recipes
|
||||
path: startos-image-recipes
|
||||
ref: feature/podman
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -166,52 +161,77 @@ jobs:
|
||||
- run: sudo mount -t tmpfs tmpfs /var/tmp/debspawn
|
||||
if: ${{ github.event.inputs.runner == 'fast' && (matrix.platform == 'x86_64' || matrix.platform == 'x86_64-nonfree') }}
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- name: Download compiled artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: /var/lib/debspawn
|
||||
key: ${{ runner.os }}-${{ matrix.platform }}-debspawn-init
|
||||
name: compiled-${{ env.ARCH }}.tar
|
||||
|
||||
- run: "mkdir -p startos-image-recipes/overlays/deb"
|
||||
- name: Extract compiled artifacts
|
||||
run: tar -xvf compiled-${{ env.ARCH }}.tar
|
||||
|
||||
- run: "mv embassy-os-deb/embassyos_0.3.x-1_*.deb startos-image-recipes/overlays/deb/"
|
||||
|
||||
- run: "sudo rm -rf embassy-os-deb ${{ steps.npm-cache-dir.outputs.dir }} $HOME/.cargo"
|
||||
- name: Prevent rebuild of compiled artifacts
|
||||
run: |
|
||||
mkdir -p frontend/dist/raw
|
||||
PLATFORM=${{ matrix.platform }} make -t compiled-${{ env.ARCH }}.tar
|
||||
|
||||
- name: Run iso build
|
||||
working-directory: startos-image-recipes
|
||||
run: |
|
||||
./run-local-build.sh ${{ matrix.platform }}
|
||||
run: PLATFORM=${{ matrix.platform }} make iso
|
||||
if: ${{ matrix.platform != 'raspberrypi' }}
|
||||
|
||||
- name: Run img build
|
||||
run: PLATFORM=${{ matrix.platform }} make img
|
||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.platform }}.squashfs
|
||||
path: startos-image-recipes/results/*.squashfs
|
||||
path: results/*.squashfs
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.platform }}.iso
|
||||
path: startos-image-recipes/results/*.iso
|
||||
path: results/*.iso
|
||||
if: ${{ matrix.platform != 'raspberrypi' }}
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
path: start-os
|
||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
||||
|
||||
- run: "mv startos-image-recipes/results/startos-*_raspberrypi.squashfs start-os/startos.raspberrypi.squashfs"
|
||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
||||
|
||||
- run: rm -rf startos-image-recipes
|
||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
||||
|
||||
- name: Build image
|
||||
working-directory: start-os
|
||||
run: make startos_raspberrypi.img
|
||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: raspberrypi.img
|
||||
path: start-os/startos-*_raspberrypi.img
|
||||
name: ${{ matrix.platform }}.img
|
||||
path: results/*.img
|
||||
if: ${{ matrix.platform == 'raspberrypi' }}
|
||||
|
||||
- name: Upload OTA to registry
|
||||
run: >-
|
||||
PLATFORM=${{ matrix.platform }} make upload-ota TARGET="${{
|
||||
fromJson('{
|
||||
"alpha": "alpha-registry-x.start9.com",
|
||||
"beta": "beta-registry.start9.com",
|
||||
}')[github.event.inputs.deploy]
|
||||
}}" KEY="${{
|
||||
fromJson(
|
||||
format('{{
|
||||
"alpha": "{0}",
|
||||
"beta": "{1}",
|
||||
}}', secrets.ALPHA_INDEX_KEY, secrets.BETA_INDEX_KEY)
|
||||
)[github.event.inputs.deploy]
|
||||
}}"
|
||||
if: ${{ github.event.inputs.deploy != '' && github.event.inputs.deploy != 'NONE' }}
|
||||
|
||||
index:
|
||||
if: ${{ github.event.inputs.deploy != '' && github.event.inputs.deploy != 'NONE' }}
|
||||
needs: [image]
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- run: >-
|
||||
curl "https://${{
|
||||
fromJson('{
|
||||
"alpha": "alpha-registry-x.start9.com",
|
||||
"beta": "beta-registry.start9.com",
|
||||
}')[github.event.inputs.deploy]
|
||||
}}:8443/resync.cgi?key=${{
|
||||
fromJson(
|
||||
format('{{
|
||||
"alpha": "{0}",
|
||||
"beta": "{1}",
|
||||
}}', secrets.ALPHA_INDEX_KEY, secrets.BETA_INDEX_KEY)
|
||||
)[github.event.inputs.deploy]
|
||||
}}"
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -16,13 +16,15 @@ deploy_web.sh
|
||||
secrets.db
|
||||
.vscode/
|
||||
/cargo-deps/**/*
|
||||
/PLATFORM.txt
|
||||
/ENVIRONMENT.txt
|
||||
/GIT_HASH.txt
|
||||
/VERSION.txt
|
||||
/embassyos-*.tar.gz
|
||||
/eos-*.tar.gz
|
||||
/*.deb
|
||||
/target
|
||||
/*.squashfs
|
||||
/debian
|
||||
/DEBIAN
|
||||
/results
|
||||
/dpkg-workdir
|
||||
/compiled.tar
|
||||
/compiled-*.tar
|
||||
5607
Cargo.lock
generated
5607
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
163
Makefile
163
Makefile
@@ -1,25 +1,31 @@
|
||||
OS_ARCH := $(shell echo "${OS_ARCH}")
|
||||
ARCH := $(shell if [ "$(OS_ARCH)" = "raspberrypi" ]; then echo aarch64; else echo $(OS_ARCH) | sed 's/-nonfree$$//g'; fi)
|
||||
ENVIRONMENT_FILE = $(shell ./check-environment.sh)
|
||||
GIT_HASH_FILE = $(shell ./check-git-hash.sh)
|
||||
VERSION_FILE = $(shell ./check-version.sh)
|
||||
PLATFORM_FILE := $(shell ./check-platform.sh)
|
||||
ENVIRONMENT_FILE := $(shell ./check-environment.sh)
|
||||
GIT_HASH_FILE := $(shell ./check-git-hash.sh)
|
||||
VERSION_FILE := $(shell ./check-version.sh)
|
||||
BASENAME := $(shell ./basename.sh)
|
||||
PLATFORM := $(shell if [ -f ./PLATFORM.txt ]; then cat ./PLATFORM.txt; else echo unknown; fi)
|
||||
ARCH := $(shell if [ "$(PLATFORM)" = "raspberrypi" ]; then echo aarch64; else echo $(PLATFORM) | sed 's/-nonfree$$//g'; fi)
|
||||
IMAGE_TYPE=$(shell if [ "$(PLATFORM)" = raspberrypi ]; then echo img; else echo iso; fi)
|
||||
EMBASSY_BINS := backend/target/$(ARCH)-unknown-linux-gnu/release/startbox libs/target/aarch64-unknown-linux-musl/release/embassy_container_init libs/target/x86_64-unknown-linux-musl/release/embassy_container_init
|
||||
EMBASSY_UIS := frontend/dist/raw/ui frontend/dist/raw/setup-wizard frontend/dist/raw/diagnostic-ui frontend/dist/raw/install-wizard
|
||||
BUILD_SRC := $(shell find build)
|
||||
BUILD_SRC := $(shell git ls-files build) build/lib/depends build/lib/conflicts
|
||||
DEBIAN_SRC := $(shell git ls-files debian/)
|
||||
IMAGE_RECIPE_SRC := $(shell git ls-files image-recipe/)
|
||||
EMBASSY_SRC := backend/startd.service $(BUILD_SRC)
|
||||
COMPAT_SRC := $(shell find system-images/compat/ -not -path 'system-images/compat/target/*' -and -not -name *.tar -and -not -name target)
|
||||
UTILS_SRC := $(shell find system-images/utils/ -not -name *.tar)
|
||||
BINFMT_SRC := $(shell find system-images/binfmt/ -not -name *.tar)
|
||||
BACKEND_SRC := $(shell find backend/src) $(shell find backend/migrations) $(shell find patch-db/*/src) $(shell find libs/*/src) libs/*/Cargo.toml backend/Cargo.toml backend/Cargo.lock frontend/dist/static
|
||||
FRONTEND_SHARED_SRC := $(shell find frontend/projects/shared) $(shell ls -p frontend/ | grep -v / | sed 's/^/frontend\//g') frontend/package.json frontend/node_modules frontend/config.json patch-db/client/dist frontend/patchdb-ui-seed.json
|
||||
FRONTEND_UI_SRC := $(shell find frontend/projects/ui)
|
||||
FRONTEND_SETUP_WIZARD_SRC := $(shell find frontend/projects/setup-wizard)
|
||||
FRONTEND_DIAGNOSTIC_UI_SRC := $(shell find frontend/projects/diagnostic-ui)
|
||||
FRONTEND_INSTALL_WIZARD_SRC := $(shell find frontend/projects/install-wizard)
|
||||
PATCH_DB_CLIENT_SRC := $(shell find patch-db/client -not -path patch-db/client/dist -and -not -path patch-db/client/node_modules)
|
||||
COMPAT_SRC := $(shell git ls-files system-images/compat/)
|
||||
UTILS_SRC := $(shell git ls-files system-images/utils/)
|
||||
BINFMT_SRC := $(shell git ls-files system-images/binfmt/)
|
||||
BACKEND_SRC := $(shell git ls-files backend) $(shell git ls-files --recurse-submodules patch-db) $(shell git ls-files libs) frontend/dist/static
|
||||
FRONTEND_SHARED_SRC := $(shell git ls-files frontend/projects/shared) $(shell ls -p frontend/ | grep -v / | sed 's/^/frontend\//g') frontend/node_modules frontend/config.json patch-db/client/dist frontend/patchdb-ui-seed.json
|
||||
FRONTEND_UI_SRC := $(shell git ls-files frontend/projects/ui)
|
||||
FRONTEND_SETUP_WIZARD_SRC := $(shell git ls-files frontend/projects/setup-wizard)
|
||||
FRONTEND_DIAGNOSTIC_UI_SRC := $(shell git ls-files frontend/projects/diagnostic-ui)
|
||||
FRONTEND_INSTALL_WIZARD_SRC := $(shell git ls-files frontend/projects/install-wizard)
|
||||
PATCH_DB_CLIENT_SRC := $(shell git ls-files --recurse-submodules patch-db/client)
|
||||
GZIP_BIN := $(shell which pigz || which gzip)
|
||||
TAR_BIN := $(shell which gtar || which tar)
|
||||
ALL_TARGETS := $(EMBASSY_BINS) system-images/compat/docker-images/$(ARCH).tar system-images/utils/docker-images/$(ARCH).tar system-images/binfmt/docker-images/$(ARCH).tar $(EMBASSY_SRC) $(shell if [ "$(OS_ARCH)" = "raspberrypi" ]; then echo cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep; fi) $(shell /bin/bash -c 'if [[ "${ENVIRONMENT}" =~ (^|-)unstable($$|-) ]]; then echo cargo-deps/$(ARCH)-unknown-linux-gnu/release/tokio-console; fi') $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) $(VERSION_FILE)
|
||||
COMPILED_TARGETS := $(EMBASSY_BINS) system-images/compat/docker-images/$(ARCH).tar system-images/utils/docker-images/$(ARCH).tar system-images/binfmt/docker-images/$(ARCH).tar
|
||||
ALL_TARGETS := $(EMBASSY_SRC) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) $(VERSION_FILE) $(COMPILED_TARGETS) $(shell if [ "$(PLATFORM)" = "raspberrypi" ]; then echo cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep; fi) $(shell /bin/bash -c 'if [[ "${ENVIRONMENT}" =~ (^|-)unstable($$|-) ]]; then echo cargo-deps/$(ARCH)-unknown-linux-gnu/release/tokio-console; fi') $(PLATFORM_FILE)
|
||||
|
||||
ifeq ($(REMOTE),)
|
||||
mkdir = mkdir -p $1
|
||||
@@ -40,14 +46,14 @@ define cp
|
||||
endef
|
||||
endif
|
||||
|
||||
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
.PHONY: all gzip install clean format sdk snapshots frontends ui backend reflash startos_raspberrypi.img sudo wormhole
|
||||
.PHONY: all metadata install clean format sdk snapshots frontends ui backend reflash deb $(IMAGE_TYPE) squashfs sudo wormhole
|
||||
|
||||
all: $(ALL_TARGETS)
|
||||
|
||||
metadata: $(VERSION_FILE) $(PLATFORM_FILE) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE)
|
||||
|
||||
sudo:
|
||||
sudo true
|
||||
|
||||
@@ -64,9 +70,13 @@ clean:
|
||||
rm -rf patch-db/client/dist
|
||||
rm -rf patch-db/target
|
||||
rm -rf cargo-deps
|
||||
rm ENVIRONMENT.txt
|
||||
rm GIT_HASH.txt
|
||||
rm VERSION.txt
|
||||
rm -rf dpkg-workdir
|
||||
rm -rf image-recipe/deb
|
||||
rm -rf results
|
||||
rm -f ENVIRONMENT.txt
|
||||
rm -f PLATFORM.txt
|
||||
rm -f GIT_HASH.txt
|
||||
rm -f VERSION.txt
|
||||
|
||||
format:
|
||||
cd backend && cargo +nightly fmt
|
||||
@@ -75,8 +85,20 @@ format:
|
||||
sdk:
|
||||
cd backend/ && ./install-sdk.sh
|
||||
|
||||
startos_raspberrypi.img: $(BUILD_SRC) startos.raspberrypi.squashfs $(VERSION_FILE) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) | sudo
|
||||
./build/raspberrypi/make-image.sh
|
||||
deb: results/$(BASENAME).deb
|
||||
|
||||
debian/control: build/lib/depends build/lib/conflicts
|
||||
./debuild/control.sh
|
||||
|
||||
results/$(BASENAME).deb: dpkg-build.sh $(DEBIAN_SRC) $(VERSION_FILE) $(PLATFORM_FILE) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE)
|
||||
PLATFORM=$(PLATFORM) ./dpkg-build.sh
|
||||
|
||||
$(IMAGE_TYPE): results/$(BASENAME).$(IMAGE_TYPE)
|
||||
|
||||
squashfs: results/$(BASENAME).squashfs
|
||||
|
||||
results/$(BASENAME).$(IMAGE_TYPE) results/$(BASENAME).squashfs: $(IMAGE_RECIPE_SRC) results/$(BASENAME).deb
|
||||
./image-recipe/run-local-build.sh "results/$(BASENAME).deb"
|
||||
|
||||
# For creating os images. DO NOT USE
|
||||
install: $(ALL_TARGETS)
|
||||
@@ -88,58 +110,68 @@ install: $(ALL_TARGETS)
|
||||
$(call ln,/usr/bin/startbox,$(DESTDIR)/usr/bin/start-deno)
|
||||
$(call ln,/usr/bin/startbox,$(DESTDIR)/usr/bin/avahi-alias)
|
||||
$(call ln,/usr/bin/startbox,$(DESTDIR)/usr/bin/embassy-cli)
|
||||
if [ "$(OS_ARCH)" = "raspberrypi" ]; then $(call cp,cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep,$(DESTDIR)/usr/bin/pi-beep); fi
|
||||
if [ "$(PLATFORM)" = "raspberrypi" ]; then $(call cp,cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep,$(DESTDIR)/usr/bin/pi-beep); fi
|
||||
if /bin/bash -c '[[ "${ENVIRONMENT}" =~ (^|-)unstable($$|-) ]]'; then $(call cp,cargo-deps/$(ARCH)-unknown-linux-gnu/release/tokio-console,$(DESTDIR)/usr/bin/tokio-console); fi
|
||||
|
||||
$(call mkdir,$(DESTDIR)/lib/systemd/system)
|
||||
$(call cp,backend/startd.service,$(DESTDIR)/lib/systemd/system/startd.service)
|
||||
|
||||
$(call mkdir,$(DESTDIR)/usr/lib)
|
||||
$(call rm,$(DESTDIR)/usr/lib/embassy)
|
||||
$(call cp,build/lib,$(DESTDIR)/usr/lib/embassy)
|
||||
$(call rm,$(DESTDIR)/usr/lib/startos)
|
||||
$(call cp,build/lib,$(DESTDIR)/usr/lib/startos)
|
||||
|
||||
$(call cp,ENVIRONMENT.txt,$(DESTDIR)/usr/lib/embassy/ENVIRONMENT.txt)
|
||||
$(call cp,GIT_HASH.txt,$(DESTDIR)/usr/lib/embassy/GIT_HASH.txt)
|
||||
$(call cp,VERSION.txt,$(DESTDIR)/usr/lib/embassy/VERSION.txt)
|
||||
$(call cp,PLATFORM.txt,$(DESTDIR)/usr/lib/startos/PLATFORM.txt)
|
||||
$(call cp,ENVIRONMENT.txt,$(DESTDIR)/usr/lib/startos/ENVIRONMENT.txt)
|
||||
$(call cp,GIT_HASH.txt,$(DESTDIR)/usr/lib/startos/GIT_HASH.txt)
|
||||
$(call cp,VERSION.txt,$(DESTDIR)/usr/lib/startos/VERSION.txt)
|
||||
|
||||
$(call mkdir,$(DESTDIR)/usr/lib/embassy/container)
|
||||
$(call cp,libs/target/aarch64-unknown-linux-musl/release/embassy_container_init,$(DESTDIR)/usr/lib/embassy/container/embassy_container_init.arm64)
|
||||
$(call cp,libs/target/x86_64-unknown-linux-musl/release/embassy_container_init,$(DESTDIR)/usr/lib/embassy/container/embassy_container_init.amd64)
|
||||
$(call mkdir,$(DESTDIR)/usr/lib/startos/container)
|
||||
$(call cp,libs/target/aarch64-unknown-linux-musl/release/embassy_container_init,$(DESTDIR)/usr/lib/startos/container/embassy_container_init.arm64)
|
||||
$(call cp,libs/target/x86_64-unknown-linux-musl/release/embassy_container_init,$(DESTDIR)/usr/lib/startos/container/embassy_container_init.amd64)
|
||||
|
||||
$(call mkdir,$(DESTDIR)/usr/lib/embassy/system-images)
|
||||
$(call cp,system-images/compat/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/embassy/system-images/compat.tar)
|
||||
$(call cp,system-images/utils/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/embassy/system-images/utils.tar)
|
||||
$(call cp,system-images/binfmt/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/embassy/system-images/binfmt.tar)
|
||||
$(call mkdir,$(DESTDIR)/usr/lib/startos/system-images)
|
||||
$(call cp,system-images/compat/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/startos/system-images/compat.tar)
|
||||
$(call cp,system-images/utils/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/startos/system-images/utils.tar)
|
||||
$(call cp,system-images/binfmt/docker-images/$(ARCH).tar,$(DESTDIR)/usr/lib/startos/system-images/binfmt.tar)
|
||||
|
||||
update-overlay:
|
||||
update-overlay: $(ALL_TARGETS)
|
||||
@echo "\033[33m!!! THIS WILL ONLY REFLASH YOUR DEVICE IN MEMORY !!!\033[0m"
|
||||
@echo "\033[33mALL CHANGES WILL BE REVERTED IF YOU RESTART THE DEVICE\033[0m"
|
||||
@if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi
|
||||
@if [ "`ssh $(REMOTE) 'cat /usr/lib/embassy/VERSION.txt'`" != "`cat ./VERSION.txt`" ]; then >&2 echo "StartOS requires migrations: update-overlay is unavailable." && false; fi
|
||||
@if [ "`ssh $(REMOTE) 'cat /usr/lib/startos/VERSION.txt'`" != "`cat ./VERSION.txt`" ]; then >&2 echo "StartOS requires migrations: update-overlay is unavailable." && false; fi
|
||||
$(call ssh,"sudo systemctl stop startd")
|
||||
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) OS_ARCH=$(OS_ARCH)
|
||||
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) PLATFORM=$(PLATFORM)
|
||||
$(call ssh,"sudo systemctl start startd")
|
||||
|
||||
wormhole: backend/target/$(ARCH)-unknown-linux-gnu/release/startbox
|
||||
@wormhole send backend/target/$(ARCH)-unknown-linux-gnu/release/startbox 2>&1 | awk -Winteractive '/wormhole receive/ { printf "sudo /usr/lib/embassy/scripts/chroot-and-upgrade \"cd /usr/bin && rm startbox && wormhole receive --accept-file %s && chmod +x startbox\"\n", $$3 }'
|
||||
@wormhole send backend/target/$(ARCH)-unknown-linux-gnu/release/startbox 2>&1 | awk -Winteractive '/wormhole receive/ { printf "sudo /usr/lib/startos/scripts/chroot-and-upgrade \"cd /usr/bin && rm startbox && wormhole receive --accept-file %s && chmod +x startbox\"\n", $$3 }'
|
||||
|
||||
update:
|
||||
update: $(ALL_TARGETS)
|
||||
@if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi
|
||||
$(call ssh,"sudo rsync -a --delete --force --info=progress2 /media/embassy/embassyfs/current/ /media/embassy/next/")
|
||||
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) DESTDIR=/media/embassy/next OS_ARCH=$(OS_ARCH)
|
||||
$(call ssh,'sudo NO_SYNC=1 /media/embassy/next/usr/lib/embassy/scripts/chroot-and-upgrade "apt-get install -y $(shell cat ./build/lib/depends)"')
|
||||
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) DESTDIR=/media/embassy/next PLATFORM=$(PLATFORM)
|
||||
$(call ssh,'sudo NO_SYNC=1 /media/embassy/next/usr/lib/startos/scripts/chroot-and-upgrade "apt-get install -y $(shell cat ./build/lib/depends)"')
|
||||
|
||||
emulate-reflash:
|
||||
emulate-reflash: $(ALL_TARGETS)
|
||||
@if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi
|
||||
$(call ssh,"sudo rsync -a --delete --force --info=progress2 /media/embassy/embassyfs/current/ /media/embassy/next/")
|
||||
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) DESTDIR=/media/embassy/next OS_ARCH=$(OS_ARCH)
|
||||
$(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) DESTDIR=/media/embassy/next PLATFORM=$(PLATFORM)
|
||||
$(call ssh,"sudo touch /media/embassy/config/upgrade && sudo rm -f /media/embassy/config/disk.guid && sudo sync && sudo reboot")
|
||||
|
||||
system-images/compat/docker-images/aarch64.tar system-images/compat/docker-images/x86_64.tar: $(COMPAT_SRC) backend/Cargo.lock | sudo
|
||||
cd system-images/compat && make && touch docker-images/*.tar
|
||||
upload-ota: results/$(BASENAME).squashfs
|
||||
TARGET=$(TARGET) KEY=$(KEY) ./upload-ota.sh
|
||||
|
||||
system-images/utils/docker-images/aarch64.tar system-images/utils/docker-images/x86_64.tar: $(UTILS_SRC) | sudo
|
||||
cd system-images/utils && make && touch docker-images/*.tar
|
||||
build/lib/depends build/lib/conflicts: build/dpkg-deps/*
|
||||
build/dpkg-deps/generate.sh
|
||||
|
||||
system-images/binfmt/docker-images/aarch64.tar system-images/binfmt/docker-images/x86_64.tar: $(BINFMT_SRC) | sudo
|
||||
cd system-images/binfmt && make && touch docker-images/*.tar
|
||||
system-images/compat/docker-images/$(ARCH).tar: $(COMPAT_SRC) backend/Cargo.lock
|
||||
cd system-images/compat && make docker-images/$(ARCH).tar && touch docker-images/$(ARCH).tar
|
||||
|
||||
system-images/utils/docker-images/$(ARCH).tar: $(UTILS_SRC)
|
||||
cd system-images/utils && make docker-images/$(ARCH).tar && touch docker-images/$(ARCH).tar
|
||||
|
||||
system-images/binfmt/docker-images/$(ARCH).tar: $(BINFMT_SRC)
|
||||
cd system-images/binfmt && make docker-images/$(ARCH).tar && touch docker-images/$(ARCH).tar
|
||||
|
||||
snapshots: libs/snapshot_creator/Cargo.toml
|
||||
cd libs/ && ./build-v8-snapshot.sh
|
||||
@@ -152,30 +184,26 @@ $(EMBASSY_BINS): $(BACKEND_SRC) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) frontend/pa
|
||||
frontend/node_modules: frontend/package.json
|
||||
npm --prefix frontend ci
|
||||
|
||||
frontend/dist/raw/ui: $(FRONTEND_UI_SRC) $(FRONTEND_SHARED_SRC) $(ENVIRONMENT_FILE)
|
||||
frontend/dist/raw/ui: $(FRONTEND_UI_SRC) $(FRONTEND_SHARED_SRC)
|
||||
npm --prefix frontend run build:ui
|
||||
|
||||
frontend/dist/raw/setup-wizard: $(FRONTEND_SETUP_WIZARD_SRC) $(FRONTEND_SHARED_SRC) $(ENVIRONMENT_FILE)
|
||||
frontend/dist/raw/setup-wizard: $(FRONTEND_SETUP_WIZARD_SRC) $(FRONTEND_SHARED_SRC)
|
||||
npm --prefix frontend run build:setup
|
||||
|
||||
frontend/dist/raw/diagnostic-ui: $(FRONTEND_DIAGNOSTIC_UI_SRC) $(FRONTEND_SHARED_SRC) $(ENVIRONMENT_FILE)
|
||||
frontend/dist/raw/diagnostic-ui: $(FRONTEND_DIAGNOSTIC_UI_SRC) $(FRONTEND_SHARED_SRC)
|
||||
npm --prefix frontend run build:dui
|
||||
|
||||
frontend/dist/raw/install-wizard: $(FRONTEND_INSTALL_WIZARD_SRC) $(FRONTEND_SHARED_SRC) $(ENVIRONMENT_FILE)
|
||||
frontend/dist/raw/install-wizard: $(FRONTEND_INSTALL_WIZARD_SRC) $(FRONTEND_SHARED_SRC)
|
||||
npm --prefix frontend run build:install-wiz
|
||||
|
||||
frontend/dist/static: $(EMBASSY_UIS)
|
||||
frontend/dist/static: $(EMBASSY_UIS) $(ENVIRONMENT_FILE)
|
||||
./compress-uis.sh
|
||||
|
||||
frontend/config.json: $(GIT_HASH_FILE) frontend/config-sample.json
|
||||
jq '.useMocks = false' frontend/config-sample.json > frontend/config.json
|
||||
jq '.packageArch = "$(ARCH)"' frontend/config.json > frontend/config.json.tmp
|
||||
jq '.osArch = "$(OS_ARCH)"' frontend/config.json.tmp > frontend/config.json
|
||||
rm frontend/config.json.tmp
|
||||
npm --prefix frontend run-script build-config
|
||||
jq '.useMocks = false' frontend/config-sample.json | jq '.gitHash = "$(shell cat GIT_HASH.txt)"' > frontend/config.json
|
||||
|
||||
frontend/patchdb-ui-seed.json: frontend/package.json
|
||||
jq '."ack-welcome" = $(shell yq '.version' frontend/package.json)' frontend/patchdb-ui-seed.json > ui-seed.tmp
|
||||
jq '."ack-welcome" = $(shell jq '.version' frontend/package.json)' frontend/patchdb-ui-seed.json > ui-seed.tmp
|
||||
mv ui-seed.tmp frontend/patchdb-ui-seed.json
|
||||
|
||||
patch-db/client/node_modules: patch-db/client/package.json
|
||||
@@ -186,7 +214,7 @@ patch-db/client/dist: $(PATCH_DB_CLIENT_SRC) patch-db/client/node_modules
|
||||
npm --prefix frontend run build:deps
|
||||
|
||||
# used by github actions
|
||||
backend-$(ARCH).tar: $(EMBASSY_BINS)
|
||||
compiled-$(ARCH).tar: $(COMPILED_TARGETS) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) $(VERSION_FILE)
|
||||
tar -cvf $@ $^
|
||||
|
||||
# this is a convenience step to build all frontends - it is not referenced elsewhere in this file
|
||||
@@ -195,10 +223,7 @@ frontends: $(EMBASSY_UIS)
|
||||
# this is a convenience step to build the UI
|
||||
ui: frontend/dist/raw/ui
|
||||
|
||||
# used by github actions
|
||||
backend: $(EMBASSY_BINS)
|
||||
|
||||
cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep: | sudo
|
||||
cargo-deps/aarch64-unknown-linux-gnu/release/pi-beep:
|
||||
ARCH=aarch64 ./build-cargo-dep.sh pi-beep
|
||||
|
||||
cargo-deps/$(ARCH)-unknown-linux-gnu/release/tokio-console: | sudo
|
||||
|
||||
14
backend/.sqlx/query-b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d.json
generated
Normal file
14
backend/.sqlx/query-b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d.json
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM network_keys WHERE package = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d"
|
||||
}
|
||||
24
backend/Cargo.lock
generated
24
backend/Cargo.lock
generated
@@ -1906,28 +1906,6 @@ version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||
|
||||
[[package]]
|
||||
name = "git-version"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6b0decc02f4636b9ccad390dcbe77b722a77efedfa393caf8379a51d5c61899"
|
||||
dependencies = [
|
||||
"git-version-macro",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git-version-macro"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
@@ -4970,12 +4948,12 @@ dependencies = [
|
||||
"digest 0.10.7",
|
||||
"divrem",
|
||||
"ed25519 2.2.3",
|
||||
"ed25519-dalek 1.0.1",
|
||||
"ed25519-dalek 2.0.0",
|
||||
"embassy_container_init",
|
||||
"emver",
|
||||
"fd-lock-rs",
|
||||
"futures",
|
||||
"git-version",
|
||||
"gpt",
|
||||
"helpers",
|
||||
"hex",
|
||||
|
||||
@@ -15,6 +15,7 @@ name = "start-os"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/Start9Labs/start-os"
|
||||
version = "0.3.5"
|
||||
license = "MIT"
|
||||
|
||||
[lib]
|
||||
name = "startos"
|
||||
@@ -31,7 +32,7 @@ cli = []
|
||||
daemon = []
|
||||
default = ["cli", "sdk", "daemon", "js_engine"]
|
||||
dev = []
|
||||
podman = []
|
||||
docker = []
|
||||
sdk = []
|
||||
unstable = ["console-subscriber", "tokio/tracing"]
|
||||
|
||||
@@ -66,18 +67,17 @@ divrem = "1.0.0"
|
||||
ed25519 = { version = "2.2.3", features = ["pkcs8", "pem", "alloc"] }
|
||||
ed25519-dalek = { version = "2.0.0", features = [
|
||||
"serde",
|
||||
"hazmat",
|
||||
"zeroize",
|
||||
"rand_core",
|
||||
"digest",
|
||||
] }
|
||||
ed25519-dalek-v1 = { package = "ed25519-dalek", version = "1" }
|
||||
embassy_container_init = { path = "../libs/embassy_container_init" }
|
||||
emver = { version = "0.1.7", git = "https://github.com/Start9Labs/emver-rs.git", features = [
|
||||
"serde",
|
||||
] }
|
||||
fd-lock-rs = "0.1.4"
|
||||
futures = "0.3.28"
|
||||
git-version = "0.3.5"
|
||||
gpt = "3.1.0"
|
||||
helpers = { path = "../libs/helpers" }
|
||||
hex = "0.4.3"
|
||||
|
||||
@@ -3,11 +3,6 @@
|
||||
set -e
|
||||
shopt -s expand_aliases
|
||||
|
||||
if [ -z "$OS_ARCH" ]; then
|
||||
>&2 echo '$OS_ARCH is required'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
ARCH=$(uname -m)
|
||||
fi
|
||||
@@ -23,27 +18,17 @@ if tty -s; then
|
||||
fi
|
||||
|
||||
cd ..
|
||||
FLAGS=""
|
||||
FEATURES="$(echo $ENVIRONMENT | sed 's/-/,/g')"
|
||||
RUSTFLAGS=""
|
||||
if [[ "$ENVIRONMENT" =~ (^|-)podman($|-) ]]; then
|
||||
FLAGS="podman,$FLAGS"
|
||||
fi
|
||||
if [[ "$ENVIRONMENT" =~ (^|-)unstable($|-) ]]; then
|
||||
FLAGS="unstable,$FLAGS"
|
||||
RUSTFLAGS="$RUSTFLAGS --cfg tokio_unstable"
|
||||
fi
|
||||
if [[ "$ENVIRONMENT" =~ (^|-)dev($|-) ]]; then
|
||||
FLAGS="dev,$FLAGS"
|
||||
fi
|
||||
|
||||
alias 'rust-gnu-builder'='docker run $USE_TTY --rm -e "OS_ARCH=$OS_ARCH" -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/usr/local/cargo/registry -v "$(pwd)":/home/rust/src -w /home/rust/src -P start9/rust-arm-cross:aarch64'
|
||||
alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "OS_ARCH=$OS_ARCH" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$(pwd)":/home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
|
||||
alias 'rust-gnu-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/usr/local/cargo/registry -v "$(pwd)":/home/rust/src -w /home/rust/src -P start9/rust-arm-cross:aarch64'
|
||||
alias 'rust-musl-builder'='docker run $USE_TTY --rm -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$(pwd)":/home/rust/src -P messense/rust-musl-cross:$ARCH-musl'
|
||||
|
||||
set +e
|
||||
fail=
|
||||
echo "FLAGS=\"$FLAGS\""
|
||||
echo "FEATURES=\"$FEATURES\""
|
||||
echo "RUSTFLAGS=\"$RUSTFLAGS\""
|
||||
rust-gnu-builder sh -c "(cd backend && cargo build --release --features avahi-alias,$FLAGS --locked --target=$ARCH-unknown-linux-gnu)"
|
||||
rust-gnu-builder sh -c "(cd backend && cargo build --release --features avahi-alias,$FEATURES --locked --target=$ARCH-unknown-linux-gnu)"
|
||||
if test $? -ne 0; then
|
||||
fail=true
|
||||
fi
|
||||
|
||||
@@ -11,8 +11,8 @@ fi
|
||||
frontend="../frontend/dist/static"
|
||||
[ -d "$frontend" ] || mkdir -p "$frontend"
|
||||
|
||||
if [ -z "$OS_ARCH" ]; then
|
||||
export OS_ARCH=$(uname -m)
|
||||
if [ -z "$PLATFORM" ]; then
|
||||
export PLATFORM=$(uname -m)
|
||||
fi
|
||||
|
||||
cargo install --path=. --no-default-features --features=js_engine,sdk,cli --locked
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use digest::Digest;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use ed25519_dalek::SecretKey;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use openssl::x509::X509;
|
||||
@@ -14,7 +15,7 @@ fn hash_password(password: &str) -> Result<String, Error> {
|
||||
argon2::hash_encoded(
|
||||
password.as_bytes(),
|
||||
&rand::random::<[u8; 16]>()[..],
|
||||
&argon2::Config::default(),
|
||||
&argon2::Config::rfc9106_low_mem(),
|
||||
)
|
||||
.with_kind(crate::ErrorKind::PasswordHashGeneration)
|
||||
}
|
||||
@@ -29,11 +30,11 @@ pub struct AccountInfo {
|
||||
pub root_ca_cert: X509,
|
||||
}
|
||||
impl AccountInfo {
|
||||
pub fn new(password: &str) -> Result<Self, Error> {
|
||||
pub fn new(password: &str, start_time: SystemTime) -> Result<Self, Error> {
|
||||
let server_id = generate_id();
|
||||
let hostname = generate_hostname();
|
||||
let root_ca_key = generate_key()?;
|
||||
let root_ca_cert = make_root_cert(&root_ca_key, &hostname)?;
|
||||
let root_ca_cert = make_root_cert(&root_ca_key, &hostname, start_time)?;
|
||||
Ok(Self {
|
||||
server_id,
|
||||
hostname,
|
||||
|
||||
@@ -84,7 +84,7 @@ fn gen_pwd() {
|
||||
argon2::hash_encoded(
|
||||
b"testing1234",
|
||||
&rand::random::<[u8; 16]>()[..],
|
||||
&argon2::Config::default()
|
||||
&argon2::Config::rfc9106_low_mem()
|
||||
)
|
||||
.unwrap()
|
||||
)
|
||||
|
||||
@@ -189,7 +189,7 @@ pub async fn recover_full_embassy(
|
||||
os_backup.account.password = argon2::hash_encoded(
|
||||
embassy_password.as_bytes(),
|
||||
&rand::random::<[u8; 16]>()[..],
|
||||
&argon2::Config::default(),
|
||||
&argon2::Config::rfc9106_low_mem(),
|
||||
)
|
||||
.with_kind(ErrorKind::PasswordHashGeneration)?;
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
use clap::Arg;
|
||||
use rpc_toolkit::command;
|
||||
use rpc_toolkit::run_cli;
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use rpc_toolkit::{command, run_cli, Context};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::procedure::js_scripts::ExecuteArgs;
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::util::logger::EmbassyLogger;
|
||||
use crate::util::serde::{display_serializable, parse_stdin_deserializable};
|
||||
use crate::version::{Current, VersionT};
|
||||
use crate::Error;
|
||||
@@ -16,6 +12,9 @@ lazy_static::lazy_static! {
|
||||
static ref VERSION_STRING: String = Current::new().semver().to_string();
|
||||
}
|
||||
|
||||
struct DenoContext;
|
||||
impl Context for DenoContext {}
|
||||
|
||||
#[command(subcommands(execute, sandbox))]
|
||||
fn deno_api() -> Result<(), Error> {
|
||||
Ok(())
|
||||
@@ -70,13 +69,11 @@ impl PackageLogger {
|
||||
use tracing_subscriber::prelude::*;
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
|
||||
let filter_layer = EnvFilter::builder()
|
||||
.with_default_directive(
|
||||
format!("{}=info", std::module_path!().split("::").next().unwrap())
|
||||
.parse()
|
||||
.unwrap(),
|
||||
)
|
||||
.from_env_lossy();
|
||||
let filter_layer = EnvFilter::default().add_directive(
|
||||
format!("{}=warn", std::module_path!().split("::").next().unwrap())
|
||||
.parse()
|
||||
.unwrap(),
|
||||
);
|
||||
let fmt_layer = fmt::layer().with_writer(std::io::stderr).with_target(true);
|
||||
let journald_layer = tracing_journald::layer()
|
||||
.unwrap()
|
||||
@@ -103,16 +100,8 @@ fn inner_main() -> Result<(), Error> {
|
||||
command: deno_api,
|
||||
app: app => app
|
||||
.name("StartOS Deno Executor")
|
||||
.version(&**VERSION_STRING)
|
||||
.arg(
|
||||
clap::Arg::with_name("config")
|
||||
.short('c')
|
||||
.long("config")
|
||||
.takes_value(true),
|
||||
),
|
||||
context: matches => {
|
||||
CliContext::init(matches)?
|
||||
},
|
||||
.version(&**VERSION_STRING),
|
||||
context: _m => DenoContext,
|
||||
exit: |e: RpcError| {
|
||||
match e.data {
|
||||
Some(Value::String(s)) => eprintln!("{}: {}", e.message, s),
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::net::web_server::WebServer;
|
||||
use crate::shutdown::Shutdown;
|
||||
use crate::sound::CHIME;
|
||||
use crate::util::Invoke;
|
||||
use crate::{Error, ErrorKind, ResultExt, OS_ARCH};
|
||||
use crate::{Error, ErrorKind, ResultExt, PLATFORM};
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error> {
|
||||
@@ -30,19 +30,19 @@ async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Er
|
||||
|
||||
Command::new("ln")
|
||||
.arg("-sf")
|
||||
.arg("/usr/lib/embassy/scripts/fake-apt")
|
||||
.arg("/usr/lib/startos/scripts/fake-apt")
|
||||
.arg("/usr/local/bin/apt")
|
||||
.invoke(crate::ErrorKind::OpenSsh)
|
||||
.await?;
|
||||
Command::new("ln")
|
||||
.arg("-sf")
|
||||
.arg("/usr/lib/embassy/scripts/fake-apt")
|
||||
.arg("/usr/lib/startos/scripts/fake-apt")
|
||||
.arg("/usr/local/bin/apt-get")
|
||||
.invoke(crate::ErrorKind::OpenSsh)
|
||||
.await?;
|
||||
Command::new("ln")
|
||||
.arg("-sf")
|
||||
.arg("/usr/lib/embassy/scripts/fake-apt")
|
||||
.arg("/usr/lib/startos/scripts/fake-apt")
|
||||
.arg("/usr/local/bin/aptitude")
|
||||
.invoke(crate::ErrorKind::OpenSsh)
|
||||
.await?;
|
||||
@@ -177,7 +177,7 @@ async fn run_script_if_exists<P: AsRef<Path>>(path: P) {
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn inner_main(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error> {
|
||||
if OS_ARCH == "raspberrypi" && tokio::fs::metadata(STANDBY_MODE_PATH).await.is_ok() {
|
||||
if &*PLATFORM == "raspberrypi" && tokio::fs::metadata(STANDBY_MODE_PATH).await.is_ok() {
|
||||
tokio::fs::remove_file(STANDBY_MODE_PATH).await?;
|
||||
Command::new("sync").invoke(ErrorKind::Filesystem).await?;
|
||||
crate::sound::SHUTDOWN.play().await?;
|
||||
|
||||
@@ -14,9 +14,8 @@ use rpc_toolkit::command;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::s9pk::manifest::{PackageId};
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::util::display_none;
|
||||
use crate::util::serde::{display_serializable, parse_stdin_deserializable, IoFormat};
|
||||
use crate::Error;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
@@ -105,7 +105,7 @@ where
|
||||
rng: &mut R,
|
||||
timeout: &Option<Duration>,
|
||||
) -> Result<Value, Self::Error> {
|
||||
self.gen_with(self.default_spec().borrow(), rng, timeout)
|
||||
self.gen_with(self.default_spec(), rng, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ use serde::Deserialize;
|
||||
use sqlx::postgres::PgConnectOptions;
|
||||
use sqlx::PgPool;
|
||||
use tokio::sync::{broadcast, oneshot, Mutex, RwLock};
|
||||
use tokio::time::Instant;
|
||||
use tracing::instrument;
|
||||
|
||||
use super::setup::CURRENT_SECRET;
|
||||
@@ -29,7 +30,7 @@ use crate::install::cleanup::{cleanup_failed, uninstall};
|
||||
use crate::manager::ManagerMap;
|
||||
use crate::middleware::auth::HashSessionToken;
|
||||
use crate::net::net_controller::NetController;
|
||||
use crate::net::ssl::SslManager;
|
||||
use crate::net::ssl::{root_ca_start_time, SslManager};
|
||||
use crate::net::wifi::WpaCli;
|
||||
use crate::notifications::NotificationManager;
|
||||
use crate::shutdown::Shutdown;
|
||||
@@ -123,6 +124,7 @@ pub struct RpcContextSeed {
|
||||
pub current_secret: Arc<Jwk>,
|
||||
pub client: Client,
|
||||
pub hardware: Hardware,
|
||||
pub start_time: Instant,
|
||||
}
|
||||
|
||||
pub struct Hardware {
|
||||
@@ -158,7 +160,7 @@ impl RpcContext {
|
||||
base.dns_bind
|
||||
.as_deref()
|
||||
.unwrap_or(&[SocketAddr::from(([127, 0, 0, 1], 53))]),
|
||||
SslManager::new(&account)?,
|
||||
SslManager::new(&account, root_ca_start_time().await?)?,
|
||||
&account.hostname,
|
||||
&account.key,
|
||||
)
|
||||
@@ -214,6 +216,7 @@ impl RpcContext {
|
||||
.build()
|
||||
.with_kind(crate::ErrorKind::ParseUrl)?,
|
||||
hardware: Hardware { devices, ram },
|
||||
start_time: Instant::now(),
|
||||
});
|
||||
|
||||
let res = Self(seed.clone());
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::sync::Arc;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use emver::VersionRange;
|
||||
use imbl_value::InternedString;
|
||||
use ipnet::{Ipv4Net, Ipv6Net};
|
||||
use isocountry::CountryCode;
|
||||
use itertools::Itertools;
|
||||
@@ -24,6 +25,7 @@ use crate::s9pk::manifest::{Manifest, PackageId};
|
||||
use crate::status::Status;
|
||||
use crate::util::Version;
|
||||
use crate::version::{Current, VersionT};
|
||||
use crate::{ARCH, PLATFORM};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
@@ -40,6 +42,8 @@ impl Database {
|
||||
let lan_address = account.hostname.lan_address().parse().unwrap();
|
||||
Database {
|
||||
server_info: ServerInfo {
|
||||
arch: get_arch(),
|
||||
platform: get_platform(),
|
||||
id: account.server_id.clone(),
|
||||
version: Current::new().semver().into(),
|
||||
hostname: account.hostname.no_dot_host_name(),
|
||||
@@ -55,6 +59,8 @@ impl Database {
|
||||
backup_progress: None,
|
||||
updated: false,
|
||||
update_progress: None,
|
||||
shutting_down: false,
|
||||
restarting: false,
|
||||
},
|
||||
wifi: WifiInfo {
|
||||
ssids: Vec::new(),
|
||||
@@ -77,8 +83,8 @@ impl Database {
|
||||
.iter()
|
||||
.map(|x| format!("{x:X}"))
|
||||
.join(":"),
|
||||
system_start_time: Utc::now().to_rfc3339(),
|
||||
zram: false,
|
||||
ntp_synced: false,
|
||||
zram: true,
|
||||
},
|
||||
package_data: AllPackageData::default(),
|
||||
lan_port_forwards: LanPortForwards::new(),
|
||||
@@ -90,10 +96,22 @@ impl Database {
|
||||
|
||||
pub type DatabaseModel = Model<Database>;
|
||||
|
||||
fn get_arch() -> InternedString {
|
||||
(*ARCH).into()
|
||||
}
|
||||
|
||||
fn get_platform() -> InternedString {
|
||||
(&*PLATFORM).into()
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[model = "Model<Self>"]
|
||||
pub struct ServerInfo {
|
||||
#[serde(default = "get_arch")]
|
||||
pub arch: InternedString,
|
||||
#[serde(default = "get_platform")]
|
||||
pub platform: InternedString,
|
||||
pub id: String,
|
||||
pub hostname: String,
|
||||
pub version: Version,
|
||||
@@ -112,7 +130,8 @@ pub struct ServerInfo {
|
||||
pub password_hash: String,
|
||||
pub pubkey: String,
|
||||
pub ca_fingerprint: String,
|
||||
pub system_start_time: String,
|
||||
#[serde(default)]
|
||||
pub ntp_synced: bool,
|
||||
#[serde(default)]
|
||||
pub zram: bool,
|
||||
}
|
||||
@@ -152,6 +171,10 @@ pub struct ServerStatus {
|
||||
pub backup_progress: Option<BTreeMap<PackageId, BackupProgress>>,
|
||||
pub updated: bool,
|
||||
pub update_progress: Option<UpdateProgress>,
|
||||
#[serde(default)]
|
||||
pub shutting_down: bool,
|
||||
#[serde(default)]
|
||||
pub restarting: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, HasModel)]
|
||||
|
||||
@@ -23,6 +23,7 @@ pub async fn btrfs_check_repair(logicalname: impl AsRef<Path>) -> Result<Require
|
||||
Command::new("btrfs")
|
||||
.arg("check")
|
||||
.arg("--repair")
|
||||
.arg("--force")
|
||||
.arg(logicalname.as_ref())
|
||||
.invoke(crate::ErrorKind::DiskManagement)
|
||||
.await?;
|
||||
|
||||
@@ -84,7 +84,7 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
argon2::hash_encoded(
|
||||
password.as_bytes(),
|
||||
&rand::random::<[u8; 16]>()[..],
|
||||
&argon2::Config::default(),
|
||||
&argon2::Config::rfc9106_low_mem(),
|
||||
)
|
||||
.with_kind(crate::ErrorKind::PasswordHashGeneration)?,
|
||||
);
|
||||
@@ -134,7 +134,7 @@ impl<G: GenericMountGuard> BackupMountGuard<G> {
|
||||
argon2::hash_encoded(
|
||||
new_password.as_bytes(),
|
||||
&rand::random::<[u8; 16]>()[..],
|
||||
&argon2::Config::default(),
|
||||
&argon2::Config::rfc9106_low_mem(),
|
||||
)
|
||||
.with_kind(crate::ErrorKind::PasswordHashGeneration)?,
|
||||
);
|
||||
|
||||
@@ -2,13 +2,12 @@ use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::Path;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use color_eyre::eyre::eyre;
|
||||
use digest::generic_array::GenericArray;
|
||||
use digest::{Digest, OutputSizeUser};
|
||||
use sha2::Sha256;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
use super::{FileSystem, MountType};
|
||||
use crate::util::Invoke;
|
||||
use crate::{Error, ResultExt};
|
||||
|
||||
pub async fn mount_ecryptfs<P0: AsRef<Path>, P1: AsRef<Path>>(
|
||||
@@ -17,7 +16,7 @@ pub async fn mount_ecryptfs<P0: AsRef<Path>, P1: AsRef<Path>>(
|
||||
key: &str,
|
||||
) -> Result<(), Error> {
|
||||
tokio::fs::create_dir_all(dst.as_ref()).await?;
|
||||
let mut ecryptfs = tokio::process::Command::new("mount")
|
||||
tokio::process::Command::new("mount")
|
||||
.arg("-t")
|
||||
.arg("ecryptfs")
|
||||
.arg(src.as_ref())
|
||||
@@ -25,22 +24,9 @@ pub async fn mount_ecryptfs<P0: AsRef<Path>, P1: AsRef<Path>>(
|
||||
.arg("-o")
|
||||
// for more information `man ecryptfs`
|
||||
.arg(format!("key=passphrase:passphrase_passwd={},ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=n,ecryptfs_enable_filename_crypto=y,no_sig_cache", key))
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.spawn()?;
|
||||
let mut stdin = ecryptfs.stdin.take().unwrap();
|
||||
let mut stderr = ecryptfs.stderr.take().unwrap();
|
||||
stdin.write_all(b"\n").await?;
|
||||
stdin.flush().await?;
|
||||
stdin.shutdown().await?;
|
||||
drop(stdin);
|
||||
let mut err = String::new();
|
||||
stderr.read_to_string(&mut err).await?;
|
||||
if !ecryptfs.wait().await?.success() {
|
||||
Err(Error::new(eyre!("{}", err), crate::ErrorKind::Filesystem))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
.input(Some(&mut std::io::Cursor::new(b"\n")))
|
||||
.invoke(crate::ErrorKind::Filesystem).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct EcryptFS<EncryptedDir: AsRef<Path>, Key: AsRef<str>> {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use std::path::Path;
|
||||
use std::process::Stdio;
|
||||
|
||||
use async_compression::tokio::bufread::GzipDecoder;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncRead, AsyncWriteExt, BufReader};
|
||||
use tokio::io::{AsyncRead, BufReader};
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::disk::fsck::RequiresReboot;
|
||||
@@ -23,7 +22,7 @@ pub async fn update_firmware() -> Result<RequiresReboot, Error> {
|
||||
if product_name.is_empty() {
|
||||
return Ok(RequiresReboot(false));
|
||||
}
|
||||
let firmware_dir = Path::new("/usr/lib/embassy/firmware").join(&product_name);
|
||||
let firmware_dir = Path::new("/usr/lib/startos/firmware").join(&product_name);
|
||||
if tokio::fs::metadata(&firmware_dir).await.is_ok() {
|
||||
let current_firmware = String::from_utf8(
|
||||
Command::new("dmidecode")
|
||||
@@ -44,36 +43,25 @@ pub async fn update_firmware() -> Result<RequiresReboot, Error> {
|
||||
let mut firmware_read_dir = tokio::fs::read_dir(&firmware_dir).await?;
|
||||
while let Some(entry) = firmware_read_dir.next_entry().await? {
|
||||
let filename = entry.file_name().to_string_lossy().into_owned();
|
||||
let rdr: Option<Box<dyn AsyncRead + Unpin>> = if filename.ends_with(".rom.gz") {
|
||||
Some(Box::new(GzipDecoder::new(BufReader::new(
|
||||
File::open(entry.path()).await?,
|
||||
))))
|
||||
} else if filename.ends_with(".rom") {
|
||||
Some(Box::new(File::open(entry.path()).await?))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let rdr: Option<Box<dyn AsyncRead + Unpin + Send>> =
|
||||
if filename.ends_with(".rom.gz") {
|
||||
Some(Box::new(GzipDecoder::new(BufReader::new(
|
||||
File::open(entry.path()).await?,
|
||||
))))
|
||||
} else if filename.ends_with(".rom") {
|
||||
Some(Box::new(File::open(entry.path()).await?))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(mut rdr) = rdr {
|
||||
let mut flashrom = Command::new("flashrom")
|
||||
Command::new("flashrom")
|
||||
.arg("-p")
|
||||
.arg("internal")
|
||||
.arg("-w-")
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()?;
|
||||
let mut rom_dest = flashrom.stdin.take().or_not_found("stdin")?;
|
||||
tokio::io::copy(&mut rdr, &mut rom_dest).await?;
|
||||
rom_dest.flush().await?;
|
||||
rom_dest.shutdown().await?;
|
||||
drop(rom_dest);
|
||||
let o = flashrom.wait_with_output().await?;
|
||||
if !o.status.success() {
|
||||
return Err(Error::new(
|
||||
eyre!("{}", std::str::from_utf8(&o.stderr)?),
|
||||
ErrorKind::Firmware,
|
||||
));
|
||||
} else {
|
||||
return Ok(RequiresReboot(true));
|
||||
}
|
||||
.input(Some(&mut rdr))
|
||||
.invoke(ErrorKind::Firmware)
|
||||
.await?;
|
||||
return Ok(RequiresReboot(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fs::Permissions;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use helpers::NonDetachingJoinHandle;
|
||||
@@ -19,7 +19,6 @@ use crate::install::PKG_ARCHIVE_DIR;
|
||||
use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH;
|
||||
use crate::prelude::*;
|
||||
use crate::sound::BEP;
|
||||
use crate::system::time;
|
||||
use crate::util::cpupower::{
|
||||
current_governor, get_available_governors, set_governor, GOVERNOR_PERFORMANCE,
|
||||
};
|
||||
@@ -255,6 +254,17 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
}
|
||||
}
|
||||
crate::disk::mount::util::bind(&log_dir, "/var/log/journal", false).await?;
|
||||
match Command::new("chattr")
|
||||
.arg("-R")
|
||||
.arg("+C")
|
||||
.arg("/var/log/journal")
|
||||
.invoke(ErrorKind::Filesystem)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) if e.source.to_string().contains("Operation not supported") => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}?;
|
||||
Command::new("systemctl")
|
||||
.arg("restart")
|
||||
.arg("systemd-journald")
|
||||
@@ -263,6 +273,9 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
tracing::info!("Mounted Logs");
|
||||
|
||||
let tmp_dir = cfg.datadir().join("package-data/tmp");
|
||||
if should_rebuild && tokio::fs::metadata(&tmp_dir).await.is_ok() {
|
||||
tokio::fs::remove_dir_all(&tmp_dir).await?;
|
||||
}
|
||||
if tokio::fs::metadata(&tmp_dir).await.is_err() {
|
||||
tokio::fs::create_dir_all(&tmp_dir).await?;
|
||||
}
|
||||
@@ -275,9 +288,6 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
.datadir()
|
||||
.join(format!("package-data/tmp/{CONTAINER_TOOL}"));
|
||||
let tmp_docker_exists = tokio::fs::metadata(&tmp_docker).await.is_ok();
|
||||
if should_rebuild && tmp_docker_exists {
|
||||
tokio::fs::remove_dir_all(&tmp_docker).await?;
|
||||
}
|
||||
if CONTAINER_TOOL == "docker" {
|
||||
Command::new("systemctl")
|
||||
.arg("stop")
|
||||
@@ -309,7 +319,7 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
}
|
||||
|
||||
tracing::info!("Loading System Docker Images");
|
||||
crate::install::load_images("/usr/lib/embassy/system-images").await?;
|
||||
crate::install::load_images("/usr/lib/startos/system-images").await?;
|
||||
tracing::info!("Loaded System Docker Images");
|
||||
|
||||
tracing::info!("Loading Package Docker Images");
|
||||
@@ -361,15 +371,28 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut warn_time_not_synced = true;
|
||||
for _ in 0..60 {
|
||||
let mut time_not_synced = true;
|
||||
let mut not_made_progress = 0u32;
|
||||
for _ in 0..1800 {
|
||||
if check_time_is_synchronized().await? {
|
||||
warn_time_not_synced = false;
|
||||
time_not_synced = false;
|
||||
break;
|
||||
}
|
||||
let t = SystemTime::now();
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
if t.elapsed()
|
||||
.map(|t| t > Duration::from_secs_f64(1.1))
|
||||
.unwrap_or(true)
|
||||
{
|
||||
not_made_progress = 0;
|
||||
} else {
|
||||
not_made_progress += 1;
|
||||
}
|
||||
if not_made_progress > 30 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if warn_time_not_synced {
|
||||
if time_not_synced {
|
||||
tracing::warn!("Timed out waiting for system time to synchronize");
|
||||
} else {
|
||||
tracing::info!("Syncronized system clock");
|
||||
@@ -383,9 +406,24 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
|
||||
updated: false,
|
||||
update_progress: None,
|
||||
backup_progress: None,
|
||||
shutting_down: false,
|
||||
restarting: false,
|
||||
};
|
||||
|
||||
server_info.system_start_time = time().await?;
|
||||
server_info.ntp_synced = if time_not_synced {
|
||||
let db = db.clone();
|
||||
tokio::spawn(async move {
|
||||
while !check_time_is_synchronized().await.unwrap() {
|
||||
tokio::time::sleep(Duration::from_secs(30)).await;
|
||||
}
|
||||
db.mutate(|v| v.as_server_info_mut().as_ntp_synced_mut().ser(&true))
|
||||
.await
|
||||
.unwrap()
|
||||
});
|
||||
false
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
db.mutate(|v| {
|
||||
v.as_server_info_mut().ser(&server_info)?;
|
||||
|
||||
@@ -173,7 +173,7 @@ where
|
||||
);
|
||||
cleanup(ctx, id, &version).await?;
|
||||
cleanup_folder(volume_dir, Arc::new(dependents_paths)).await;
|
||||
remove_tor_keys(secrets, id).await?;
|
||||
remove_network_keys(secrets, id).await?;
|
||||
|
||||
ctx.db
|
||||
.mutate(|d| {
|
||||
@@ -188,12 +188,15 @@ where
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn remove_tor_keys<Ex>(secrets: &mut Ex, id: &PackageId) -> Result<(), Error>
|
||||
pub async fn remove_network_keys<Ex>(secrets: &mut Ex, id: &PackageId) -> Result<(), Error>
|
||||
where
|
||||
for<'a> &'a mut Ex: Executor<'a, Database = Postgres>,
|
||||
{
|
||||
sqlx::query!("DELETE FROM network_keys WHERE package = $1", &*id)
|
||||
.execute(&mut *secrets)
|
||||
.await?;
|
||||
sqlx::query!("DELETE FROM tor WHERE package = $1", &*id)
|
||||
.execute(secrets)
|
||||
.execute(&mut *secrets)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::collections::BTreeMap;
|
||||
use std::io::SeekFrom;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Stdio;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -49,9 +48,9 @@ use crate::s9pk::manifest::{Manifest, PackageId};
|
||||
use crate::s9pk::reader::S9pkReader;
|
||||
use crate::status::{MainStatus, Status};
|
||||
use crate::util::docker::CONTAINER_TOOL;
|
||||
use crate::util::io::{copy_and_shutdown, response_to_reader};
|
||||
use crate::util::io::response_to_reader;
|
||||
use crate::util::serde::{display_serializable, Port};
|
||||
use crate::util::{display_none, AsyncFileExt, Version};
|
||||
use crate::util::{display_none, AsyncFileExt, Invoke, Version};
|
||||
use crate::volume::{asset_dir, script_dir};
|
||||
use crate::{Error, ErrorKind, ResultExt};
|
||||
|
||||
@@ -838,15 +837,15 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
||||
None
|
||||
};
|
||||
|
||||
let icon_path = if let Some(marketplace_url) = &marketplace_url {
|
||||
if let Some(manifest) = &manifest {
|
||||
let dir = ctx
|
||||
.datadir
|
||||
.join(PKG_PUBLIC_DIR)
|
||||
.join(&manifest.id)
|
||||
.join(manifest.version.as_str());
|
||||
let icon_path = dir.join(format!("icon.{}", manifest.assets.icon_type()));
|
||||
if tokio::fs::metadata(&icon_path).await.is_err() {
|
||||
let icon_path = if let Some(manifest) = &manifest {
|
||||
let dir = ctx
|
||||
.datadir
|
||||
.join(PKG_PUBLIC_DIR)
|
||||
.join(&manifest.id)
|
||||
.join(manifest.version.as_str());
|
||||
let icon_path = dir.join(format!("icon.{}", manifest.assets.icon_type()));
|
||||
if tokio::fs::metadata(&icon_path).await.is_err() {
|
||||
if let Some(marketplace_url) = &marketplace_url {
|
||||
tokio::fs::create_dir_all(&dir).await?;
|
||||
let icon = ctx
|
||||
.client
|
||||
@@ -864,10 +863,12 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
||||
let mut dst = File::create(&icon_path).await?;
|
||||
tokio::io::copy(&mut response_to_reader(icon), &mut dst).await?;
|
||||
dst.sync_all().await?;
|
||||
Some(icon_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Some(icon_path)
|
||||
} else {
|
||||
None
|
||||
Some(icon_path)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -951,32 +952,11 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin + Send + Sync>(
|
||||
tracing::info!("Install {}@{}: Unpacking Docker Images", pkg_id, version);
|
||||
progress
|
||||
.track_read_during(ctx.db.clone(), pkg_id, || async {
|
||||
let mut load = Command::new(CONTAINER_TOOL)
|
||||
Command::new(CONTAINER_TOOL)
|
||||
.arg("load")
|
||||
.stdin(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
let load_in = load.stdin.take().ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("Could not write to stdin of docker load"),
|
||||
crate::ErrorKind::Docker,
|
||||
)
|
||||
})?;
|
||||
let mut docker_rdr = rdr.docker_images().await?;
|
||||
copy_and_shutdown(&mut docker_rdr, load_in).await?;
|
||||
let res = load.wait_with_output().await?;
|
||||
if !res.status.success() {
|
||||
Err(Error::new(
|
||||
eyre!(
|
||||
"{}",
|
||||
String::from_utf8(res.stderr)
|
||||
.unwrap_or_else(|e| format!("Could not parse stderr: {}", e))
|
||||
),
|
||||
crate::ErrorKind::Docker,
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
.input(Some(&mut rdr.docker_images().await?))
|
||||
.invoke(ErrorKind::Docker)
|
||||
.await
|
||||
})
|
||||
.await?;
|
||||
tracing::info!("Install {}@{}: Unpacked Docker Images", pkg_id, version,);
|
||||
@@ -1273,57 +1253,36 @@ pub fn load_images<'a, P: AsRef<Path> + 'a + Send + Sync>(
|
||||
let path = entry.path();
|
||||
let ext = path.extension().and_then(|ext| ext.to_str());
|
||||
if ext == Some("tar") || ext == Some("s9pk") {
|
||||
let mut load = Command::new(CONTAINER_TOOL)
|
||||
.arg("load")
|
||||
.stdin(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
let load_in = load.stdin.take().ok_or_else(|| {
|
||||
Error::new(
|
||||
eyre!("Could not write to stdin of docker load"),
|
||||
crate::ErrorKind::Docker,
|
||||
)
|
||||
})?;
|
||||
match ext {
|
||||
Some("tar") => {
|
||||
copy_and_shutdown(&mut File::open(&path).await?, load_in)
|
||||
.await?
|
||||
}
|
||||
Some("s9pk") => match async {
|
||||
let mut reader = S9pkReader::open(&path, true).await?;
|
||||
copy_and_shutdown(&mut reader.docker_images().await?, load_in)
|
||||
.await?;
|
||||
Ok::<_, Error>(())
|
||||
}
|
||||
.await
|
||||
{
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
"Error loading docker images from s9pk: {e}"
|
||||
);
|
||||
tracing::debug!("{e:?}");
|
||||
return Ok(());
|
||||
if let Err(e) = async {
|
||||
match ext {
|
||||
Some("tar") => {
|
||||
Command::new(CONTAINER_TOOL)
|
||||
.arg("load")
|
||||
.input(Some(&mut File::open(&path).await?))
|
||||
.invoke(ErrorKind::Docker)
|
||||
.await
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let res = load.wait_with_output().await?;
|
||||
if !res.status.success() {
|
||||
Err(Error::new(
|
||||
eyre!(
|
||||
"{}",
|
||||
String::from_utf8(res.stderr).unwrap_or_else(|e| format!(
|
||||
"Could not parse stderr: {}",
|
||||
e
|
||||
))
|
||||
),
|
||||
crate::ErrorKind::Docker,
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
Some("s9pk") => {
|
||||
Command::new(CONTAINER_TOOL)
|
||||
.arg("load")
|
||||
.input(Some(
|
||||
&mut S9pkReader::open(&path, true)
|
||||
.await?
|
||||
.docker_images()
|
||||
.await?,
|
||||
))
|
||||
.invoke(ErrorKind::Docker)
|
||||
.await
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
.await
|
||||
{
|
||||
tracing::error!("Error loading docker images from s9pk: {e}");
|
||||
tracing::debug!("{e:?}");
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,12 +5,21 @@ pub const DEFAULT_MARKETPLACE: &str = "https://registry.start9.com";
|
||||
pub const BUFFER_SIZE: usize = 1024;
|
||||
pub const HOST_IP: [u8; 4] = [172, 18, 0, 1];
|
||||
pub const TARGET: &str = current_platform::CURRENT_PLATFORM;
|
||||
pub const OS_ARCH: &str = env!("OS_ARCH");
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref ARCH: &'static str = {
|
||||
let (arch, _) = TARGET.split_once("-").unwrap();
|
||||
arch
|
||||
};
|
||||
pub static ref PLATFORM: String = {
|
||||
if let Ok(platform) = std::fs::read_to_string("/usr/lib/startos/PLATFORM.txt") {
|
||||
platform
|
||||
} else {
|
||||
ARCH.to_string()
|
||||
}
|
||||
};
|
||||
pub static ref SOURCE_DATE: SystemTime = {
|
||||
std::fs::metadata(std::env::current_exe().unwrap()).unwrap().modified().unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
pub mod account;
|
||||
@@ -56,6 +65,8 @@ pub mod util;
|
||||
pub mod version;
|
||||
pub mod volume;
|
||||
|
||||
use std::time::SystemTime;
|
||||
|
||||
pub use config::Config;
|
||||
pub use error::{Error, ErrorKind, ResultExt};
|
||||
use rpc_toolkit::command;
|
||||
|
||||
@@ -136,7 +136,13 @@ pub struct LogEntry {
|
||||
}
|
||||
impl std::fmt::Display for LogEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{} {}", self.timestamp, self.message)
|
||||
write!(
|
||||
f,
|
||||
"{} {}",
|
||||
self.timestamp
|
||||
.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
|
||||
self.message
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +151,7 @@ pub struct JournalctlEntry {
|
||||
#[serde(rename = "__REALTIME_TIMESTAMP")]
|
||||
pub timestamp: String,
|
||||
#[serde(rename = "MESSAGE")]
|
||||
#[serde(deserialize_with = "deserialize_string_or_utf8_array")]
|
||||
#[serde(deserialize_with = "deserialize_log_message")]
|
||||
pub message: String,
|
||||
#[serde(rename = "__CURSOR")]
|
||||
pub cursor: String,
|
||||
@@ -164,7 +170,7 @@ impl JournalctlEntry {
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_string_or_utf8_array<'de, D: serde::de::Deserializer<'de>>(
|
||||
fn deserialize_log_message<'de, D: serde::de::Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> std::result::Result<String, D::Error> {
|
||||
struct Visitor;
|
||||
@@ -177,13 +183,7 @@ fn deserialize_string_or_utf8_array<'de, D: serde::de::Deserializer<'de>>(
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(v.to_owned())
|
||||
}
|
||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(v)
|
||||
Ok(v.trim().to_owned())
|
||||
}
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
@@ -201,6 +201,7 @@ fn deserialize_string_or_utf8_array<'de, D: serde::de::Deserializer<'de>>(
|
||||
.flatten()
|
||||
.collect::<Result<Vec<u8>, _>>()?,
|
||||
)
|
||||
.map(|s| s.trim().to_owned())
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
@@ -374,12 +375,12 @@ pub async fn journalctl(
|
||||
cmd.arg(format!("_COMM={}", SYSTEM_UNIT));
|
||||
}
|
||||
LogSource::Container(id) => {
|
||||
#[cfg(feature = "podman")]
|
||||
#[cfg(not(feature = "docker"))]
|
||||
cmd.arg(format!(
|
||||
"SYSLOG_IDENTIFIER={}",
|
||||
DockerProcedure::container_name(&id, None)
|
||||
));
|
||||
#[cfg(not(feature = "podman"))]
|
||||
#[cfg(feature = "docker")]
|
||||
cmd.arg(format!(
|
||||
"CONTAINER_NAME={}",
|
||||
DockerProcedure::container_name(&id, None)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use models::ErrorKind;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::procedure::docker::DockerProcedure;
|
||||
use crate::procedure::PackageProcedure;
|
||||
use crate::s9pk::manifest::Manifest;
|
||||
use crate::util::docker::stop_container;
|
||||
use crate::Error;
|
||||
@@ -16,11 +18,13 @@ impl ManagerSeed {
|
||||
pub async fn stop_container(&self) -> Result<(), Error> {
|
||||
match stop_container(
|
||||
&self.container_name,
|
||||
self.manifest
|
||||
.containers
|
||||
.as_ref()
|
||||
.and_then(|c| c.main.sigterm_timeout)
|
||||
.map(|d| *d),
|
||||
match &self.manifest.main {
|
||||
PackageProcedure::Docker(DockerProcedure {
|
||||
sigterm_timeout: Some(sigterm_timeout),
|
||||
..
|
||||
}) => Some(**sigterm_timeout),
|
||||
_ => None,
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -13,7 +13,8 @@ pub fn pbkdf2(password: impl AsRef<[u8]>, salt: impl AsRef<[u8]>) -> CipherKey<A
|
||||
salt.as_ref(),
|
||||
1000,
|
||||
aeskey.as_mut_slice(),
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
aeskey
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::path::Path;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use futures::FutureExt;
|
||||
use libc::time_t;
|
||||
use openssl::asn1::{Asn1Integer, Asn1Time};
|
||||
use openssl::bn::{BigNum, MsbOption};
|
||||
use openssl::ec::{EcGroup, EcKey};
|
||||
@@ -20,12 +21,20 @@ use tracing::instrument;
|
||||
use crate::account::AccountInfo;
|
||||
use crate::context::RpcContext;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::init::check_time_is_synchronized;
|
||||
use crate::net::dhcp::ips;
|
||||
use crate::net::keys::{Key, KeyInfo};
|
||||
use crate::{Error, ErrorKind, ResultExt};
|
||||
use crate::{Error, ErrorKind, ResultExt, SOURCE_DATE};
|
||||
|
||||
static CERTIFICATE_VERSION: i32 = 2; // X509 version 3 is actually encoded as '2' in the cert because fuck you.
|
||||
|
||||
fn unix_time(time: SystemTime) -> time_t {
|
||||
time.duration_since(UNIX_EPOCH)
|
||||
.map(|d| d.as_secs() as time_t)
|
||||
.or_else(|_| UNIX_EPOCH.elapsed().map(|d| -(d.as_secs() as time_t)))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct CertPair {
|
||||
pub ed25519: X509,
|
||||
@@ -55,9 +64,13 @@ impl CertPair {
|
||||
}),
|
||||
);
|
||||
if cert
|
||||
.not_after()
|
||||
.compare(Asn1Time::days_from_now(30)?.as_ref())?
|
||||
== Ordering::Greater
|
||||
.not_before()
|
||||
.compare(Asn1Time::days_from_now(0)?.as_ref())?
|
||||
== Ordering::Less
|
||||
&& cert
|
||||
.not_after()
|
||||
.compare(Asn1Time::days_from_now(30)?.as_ref())?
|
||||
== Ordering::Greater
|
||||
&& ips.is_superset(&ip)
|
||||
{
|
||||
return Ok(cert.clone());
|
||||
@@ -80,6 +93,14 @@ impl CertPair {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn root_ca_start_time() -> Result<SystemTime, Error> {
|
||||
Ok(if check_time_is_synchronized().await? {
|
||||
SystemTime::now()
|
||||
} else {
|
||||
*SOURCE_DATE
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SslManager {
|
||||
hostname: Hostname,
|
||||
@@ -89,9 +110,13 @@ pub struct SslManager {
|
||||
cert_cache: RwLock<BTreeMap<Key, CertPair>>,
|
||||
}
|
||||
impl SslManager {
|
||||
pub fn new(account: &AccountInfo) -> Result<Self, Error> {
|
||||
pub fn new(account: &AccountInfo, start_time: SystemTime) -> Result<Self, Error> {
|
||||
let int_key = generate_key()?;
|
||||
let int_cert = make_int_cert((&account.root_ca_key, &account.root_ca_cert), &int_key)?;
|
||||
let int_cert = make_int_cert(
|
||||
(&account.root_ca_key, &account.root_ca_cert),
|
||||
&int_key,
|
||||
start_time,
|
||||
)?;
|
||||
Ok(Self {
|
||||
hostname: account.hostname.clone(),
|
||||
root_cert: account.root_ca_cert.clone(),
|
||||
@@ -160,14 +185,20 @@ pub fn generate_key() -> Result<PKey<Private>, Error> {
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub fn make_root_cert(root_key: &PKey<Private>, hostname: &Hostname) -> Result<X509, Error> {
|
||||
pub fn make_root_cert(
|
||||
root_key: &PKey<Private>,
|
||||
hostname: &Hostname,
|
||||
start_time: SystemTime,
|
||||
) -> Result<X509, Error> {
|
||||
let mut builder = X509Builder::new()?;
|
||||
builder.set_version(CERTIFICATE_VERSION)?;
|
||||
|
||||
let embargo = Asn1Time::days_from_now(0)?;
|
||||
let unix_start_time = unix_time(start_time);
|
||||
|
||||
let embargo = Asn1Time::from_unix(unix_start_time - 86400)?;
|
||||
builder.set_not_before(&embargo)?;
|
||||
|
||||
let expiration = Asn1Time::days_from_now(3650)?;
|
||||
let expiration = Asn1Time::from_unix(unix_start_time + (10 * 364 * 86400))?;
|
||||
builder.set_not_after(&expiration)?;
|
||||
|
||||
builder.set_serial_number(&*rand_serial()?)?;
|
||||
@@ -214,14 +245,17 @@ pub fn make_root_cert(root_key: &PKey<Private>, hostname: &Hostname) -> Result<X
|
||||
pub fn make_int_cert(
|
||||
signer: (&PKey<Private>, &X509),
|
||||
applicant: &PKey<Private>,
|
||||
start_time: SystemTime,
|
||||
) -> Result<X509, Error> {
|
||||
let mut builder = X509Builder::new()?;
|
||||
builder.set_version(CERTIFICATE_VERSION)?;
|
||||
|
||||
let embargo = Asn1Time::days_from_now(0)?;
|
||||
let unix_start_time = unix_time(start_time);
|
||||
|
||||
let embargo = Asn1Time::from_unix(unix_start_time - 86400)?;
|
||||
builder.set_not_before(&embargo)?;
|
||||
|
||||
let expiration = Asn1Time::days_from_now(3650)?;
|
||||
let expiration = Asn1Time::from_unix(unix_start_time + (10 * 364 * 86400))?;
|
||||
builder.set_not_after(&expiration)?;
|
||||
|
||||
builder.set_serial_number(&*rand_serial()?)?;
|
||||
@@ -344,17 +378,10 @@ pub fn make_leaf_cert(
|
||||
let mut builder = X509Builder::new()?;
|
||||
builder.set_version(CERTIFICATE_VERSION)?;
|
||||
|
||||
let embargo = Asn1Time::from_unix(
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|d| d.as_secs() as i64)
|
||||
.or_else(|_| UNIX_EPOCH.elapsed().map(|d| -(d.as_secs() as i64)))
|
||||
.unwrap_or_default()
|
||||
- 86400,
|
||||
)?;
|
||||
let embargo = Asn1Time::from_unix(unix_time(SystemTime::now()) - 86400)?;
|
||||
builder.set_not_before(&embargo)?;
|
||||
|
||||
// Google Apple and Mozilla reject certificate horizons longer than 397 days
|
||||
// Google Apple and Mozilla reject certificate horizons longer than 398 days
|
||||
// https://techbeacon.com/security/google-apple-mozilla-enforce-1-year-max-security-certifications
|
||||
let expiration = Asn1Time::days_from_now(397)?;
|
||||
builder.set_not_after(&expiration)?;
|
||||
|
||||
@@ -272,7 +272,14 @@ impl VHostServer {
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::OpenSsl)?;
|
||||
let mut tls_stream =
|
||||
mid.into_stream(Arc::new(cfg)).await?;
|
||||
match mid.into_stream(Arc::new(cfg)).await {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
tracing::trace!( "VHostController: failed to accept TLS connection on port {port}: {e}");
|
||||
tracing::trace!("{e:?}");
|
||||
return Ok(())
|
||||
}
|
||||
};
|
||||
tls_stream.get_mut().0.stop_buffering();
|
||||
tokio::io::copy_bidirectional(
|
||||
&mut tls_stream,
|
||||
@@ -287,7 +294,14 @@ impl VHostServer {
|
||||
cfg.alpn_protocols.push(proto.into());
|
||||
}
|
||||
let mut tls_stream =
|
||||
mid.into_stream(Arc::new(cfg)).await?;
|
||||
match mid.into_stream(Arc::new(cfg)).await {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
tracing::trace!( "VHostController: failed to accept TLS connection on port {port}: {e}");
|
||||
tracing::trace!("{e:?}");
|
||||
return Ok(())
|
||||
}
|
||||
};
|
||||
tls_stream.get_mut().0.stop_buffering();
|
||||
tokio::io::copy_bidirectional(
|
||||
&mut tls_stream,
|
||||
@@ -298,7 +312,14 @@ impl VHostServer {
|
||||
Err(AlpnInfo::Specified(alpn)) => {
|
||||
cfg.alpn_protocols = alpn;
|
||||
let mut tls_stream =
|
||||
mid.into_stream(Arc::new(cfg)).await?;
|
||||
match mid.into_stream(Arc::new(cfg)).await {
|
||||
Ok(a) => a,
|
||||
Err(e) => {
|
||||
tracing::trace!( "VHostController: failed to accept TLS connection on port {port}: {e}");
|
||||
tracing::trace!("{e:?}");
|
||||
return Ok(())
|
||||
}
|
||||
};
|
||||
tls_stream.get_mut().0.stop_buffering();
|
||||
tokio::io::copy_bidirectional(
|
||||
&mut tls_stream,
|
||||
@@ -308,10 +329,12 @@ impl VHostServer {
|
||||
}
|
||||
}
|
||||
.map_or_else(
|
||||
|e| match e.kind() {
|
||||
std::io::ErrorKind::UnexpectedEof => Ok(()),
|
||||
|e| {
|
||||
use std::io::ErrorKind as E;
|
||||
match e.kind() {
|
||||
E::UnexpectedEof | E::BrokenPipe | E::ConnectionAborted | E::ConnectionReset | E::ConnectionRefused | E::TimedOut | E::Interrupted | E::NotConnected => Ok(()),
|
||||
_ => Err(e),
|
||||
},
|
||||
}},
|
||||
|_| Ok(()),
|
||||
)?;
|
||||
} else {
|
||||
@@ -327,8 +350,10 @@ impl VHostServer {
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Error in VHostController on port {port}: {e}");
|
||||
tracing::debug!("{e:?}");
|
||||
tracing::trace!(
|
||||
"VHostController: failed to accept connection on port {port}: {e}"
|
||||
);
|
||||
tracing::trace!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ use std::os::unix::prelude::FileTypeExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use async_stream::stream;
|
||||
use color_eyre::eyre::eyre;
|
||||
use color_eyre::Report;
|
||||
use futures::future::{BoxFuture, Either as EitherFuture};
|
||||
use futures::{FutureExt, TryStreamExt};
|
||||
use helpers::{NonDetachingJoinHandle, UnixRpcClient};
|
||||
@@ -396,7 +394,7 @@ impl DockerProcedure {
|
||||
|
||||
cmd.arg("exec");
|
||||
|
||||
cmd.args(self.docker_args_inject(pkg_id).await?);
|
||||
cmd.args(self.docker_args_inject(pkg_id));
|
||||
let input_buf = if let (Some(input), Some(format)) = (&input, &self.io_format) {
|
||||
cmd.stdin(std::process::Stdio::piped());
|
||||
Some(format.to_vec(input)?)
|
||||
@@ -756,7 +754,7 @@ impl DockerProcedure {
|
||||
+ self.args.len(), // [ARG...]
|
||||
)
|
||||
}
|
||||
async fn docker_args_inject(&self, pkg_id: &PackageId) -> Result<Vec<Cow<'_, OsStr>>, Error> {
|
||||
fn docker_args_inject(&self, pkg_id: &PackageId) -> Vec<Cow<'_, OsStr>> {
|
||||
let mut res = self.new_docker_args();
|
||||
if let Some(shm_size_mb) = self.shm_size_mb {
|
||||
res.push(OsStr::new("--shm-size").into());
|
||||
@@ -769,7 +767,7 @@ impl DockerProcedure {
|
||||
|
||||
res.extend(self.args.iter().map(|s| OsStr::new(s).into()));
|
||||
|
||||
Ok(res)
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,7 +811,7 @@ impl LongRunning {
|
||||
socket_path: &Path,
|
||||
) -> Result<tokio::process::Command, Error> {
|
||||
const INIT_EXEC: &str = "/start9/bin/embassy_container_init";
|
||||
const BIND_LOCATION: &str = "/usr/lib/embassy/container/";
|
||||
const BIND_LOCATION: &str = "/usr/lib/startos/container/";
|
||||
tracing::trace!("setup_long_running_docker_cmd");
|
||||
|
||||
remove_container(container_name, true).await?;
|
||||
@@ -892,23 +890,12 @@ async fn buf_reader_to_lines(
|
||||
reader: impl AsyncBufRead + Unpin,
|
||||
limit: impl Into<Option<usize>>,
|
||||
) -> Result<Vec<String>, Error> {
|
||||
let lines = stream! {
|
||||
let mut lines = reader.lines();
|
||||
while let Some(line) = lines.next_line().await? {
|
||||
yield Ok::<_, Report>(line);
|
||||
}
|
||||
};
|
||||
let output: RingVec<String> = lines
|
||||
.try_fold(
|
||||
RingVec::new(limit.into().unwrap_or(1000)),
|
||||
|mut acc, line| async move {
|
||||
acc.push(line);
|
||||
Ok(acc)
|
||||
},
|
||||
)
|
||||
.await
|
||||
.with_kind(crate::ErrorKind::Unknown)?;
|
||||
let output: Vec<String> = output.value.into_iter().collect();
|
||||
let mut lines = reader.lines();
|
||||
let mut answer = RingVec::new(limit.into().unwrap_or(1000));
|
||||
while let Some(line) = lines.next_line().await? {
|
||||
answer.push(line);
|
||||
}
|
||||
let output: Vec<String> = answer.value.into_iter().collect();
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
@@ -973,4 +960,11 @@ mod tests {
|
||||
assert_eq!(CAPACITY_IN, ring.value.capacity());
|
||||
assert_eq!(CAPACITY_IN, ring.value.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tests_buf_reader_to_lines() {
|
||||
let mut reader = BufReader::new("hello\nworld\n".as_bytes());
|
||||
let lines = futures::executor::block_on(buf_reader_to_lines(&mut reader, None)).unwrap();
|
||||
assert_eq!(lines, vec!["hello", "world"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::Stdio,
|
||||
};
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
use embassy_container_init::ProcessGroupId;
|
||||
use helpers::UnixRpcClient;
|
||||
pub use js_engine::JsError;
|
||||
@@ -19,8 +15,8 @@ use tracing::instrument;
|
||||
use super::ProcedureName;
|
||||
use crate::prelude::*;
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::util::io::to_json_async_writer;
|
||||
use crate::util::Version;
|
||||
use crate::util::serde::IoFormat;
|
||||
use crate::util::{Invoke, Version};
|
||||
use crate::volume::Volumes;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@@ -85,45 +81,23 @@ impl JsProcedure {
|
||||
_gid: ProcessGroupId,
|
||||
_rpc_client: Option<Arc<UnixRpcClient>>,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
let runner_argument = ExecuteArgs {
|
||||
procedure: self.clone(),
|
||||
directory: directory.clone(),
|
||||
pkg_id: pkg_id.clone(),
|
||||
pkg_version: pkg_version.clone(),
|
||||
name,
|
||||
volumes: volumes.clone(),
|
||||
input: input.and_then(|x| serde_json::to_value(x).ok()),
|
||||
};
|
||||
let mut runner = Command::new("start-deno")
|
||||
Command::new("start-deno")
|
||||
.arg("execute")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.kill_on_drop(true)
|
||||
.spawn()?;
|
||||
to_json_async_writer(
|
||||
&mut runner.stdin.take().or_not_found("stdin")?,
|
||||
&runner_argument,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let res = if let Some(timeout) = timeout {
|
||||
tokio::time::timeout(timeout, runner.wait_with_output())
|
||||
.await
|
||||
.with_kind(ErrorKind::Timeout)??
|
||||
} else {
|
||||
runner.wait_with_output().await?
|
||||
};
|
||||
|
||||
if res.status.success() {
|
||||
serde_json::from_str::<Result<O, (i32, String)>>(std::str::from_utf8(&res.stdout)?)
|
||||
.with_kind(ErrorKind::Deserialization)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!("{}", String::from_utf8(res.stderr)?),
|
||||
ErrorKind::Javascript,
|
||||
))
|
||||
}
|
||||
.input(Some(&mut std::io::Cursor::new(IoFormat::Json.to_vec(
|
||||
&ExecuteArgs {
|
||||
procedure: self.clone(),
|
||||
directory: directory.clone(),
|
||||
pkg_id: pkg_id.clone(),
|
||||
pkg_version: pkg_version.clone(),
|
||||
name,
|
||||
volumes: volumes.clone(),
|
||||
input: input.and_then(|x| serde_json::to_value(x).ok()),
|
||||
},
|
||||
)?)))
|
||||
.timeout(timeout)
|
||||
.invoke(ErrorKind::Javascript)
|
||||
.await
|
||||
.and_then(|res| IoFormat::Json.from_slice(&res))
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
@@ -137,45 +111,23 @@ impl JsProcedure {
|
||||
timeout: Option<Duration>,
|
||||
name: ProcedureName,
|
||||
) -> Result<Result<O, (i32, String)>, Error> {
|
||||
let runner_argument = ExecuteArgs {
|
||||
procedure: self.clone(),
|
||||
directory: directory.clone(),
|
||||
pkg_id: pkg_id.clone(),
|
||||
pkg_version: pkg_version.clone(),
|
||||
name,
|
||||
volumes: volumes.clone(),
|
||||
input: input.and_then(|x| serde_json::to_value(x).ok()),
|
||||
};
|
||||
let mut runner = Command::new("start-deno")
|
||||
Command::new("start-deno")
|
||||
.arg("sandbox")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.kill_on_drop(true)
|
||||
.spawn()?;
|
||||
to_json_async_writer(
|
||||
&mut runner.stdin.take().or_not_found("stdin")?,
|
||||
&runner_argument,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let res = if let Some(timeout) = timeout {
|
||||
tokio::time::timeout(timeout, runner.wait_with_output())
|
||||
.await
|
||||
.with_kind(ErrorKind::Timeout)??
|
||||
} else {
|
||||
runner.wait_with_output().await?
|
||||
};
|
||||
|
||||
if res.status.success() {
|
||||
serde_json::from_str::<Result<O, (i32, String)>>(std::str::from_utf8(&res.stdout)?)
|
||||
.with_kind(ErrorKind::Deserialization)
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!("{}", String::from_utf8(res.stderr)?),
|
||||
ErrorKind::Javascript,
|
||||
))
|
||||
}
|
||||
.input(Some(&mut std::io::Cursor::new(IoFormat::Json.to_vec(
|
||||
&ExecuteArgs {
|
||||
procedure: self.clone(),
|
||||
directory: directory.clone(),
|
||||
pkg_id: pkg_id.clone(),
|
||||
pkg_version: pkg_version.clone(),
|
||||
name,
|
||||
volumes: volumes.clone(),
|
||||
input: input.and_then(|x| serde_json::to_value(x).ok()),
|
||||
},
|
||||
)?)))
|
||||
.timeout(timeout)
|
||||
.invoke(ErrorKind::Javascript)
|
||||
.await
|
||||
.and_then(|res| IoFormat::Json.from_slice(&res))
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
|
||||
@@ -172,7 +172,13 @@ impl<'de> Deserialize<'de> for NoOutput {
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let _ = Value::deserialize(deserializer)?;
|
||||
let _ = Value::deserialize(deserializer);
|
||||
Ok(NoOutput)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deser_no_output() {
|
||||
serde_json::from_str::<NoOutput>("").unwrap();
|
||||
serde_json::from_str::<Result<NoOutput, NoOutput>>("{\"Ok\": null}").unwrap();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ pub fn with_query_params(ctx: RpcContext, mut url: Url) -> Url {
|
||||
"os.compat",
|
||||
&crate::version::Current::new().compat().to_string(),
|
||||
)
|
||||
.append_pair("os.arch", crate::OS_ARCH)
|
||||
.append_pair("os.arch", &*crate::PLATFORM)
|
||||
.append_pair("hardware.arch", &*crate::ARCH)
|
||||
.append_pair("hardware.ram", &ctx.hardware.ram.to_string());
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ use crate::disk::REPAIR_DISK_PATH;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::init::{init, InitResult};
|
||||
use crate::middleware::encrypt::EncryptedWire;
|
||||
use crate::net::ssl::root_ca_start_time;
|
||||
use crate::prelude::*;
|
||||
use crate::util::io::{dir_copy, dir_size, Counter};
|
||||
use crate::{Error, ErrorKind, ResultExt};
|
||||
@@ -378,7 +379,7 @@ async fn fresh_setup(
|
||||
ctx: &SetupContext,
|
||||
embassy_password: &str,
|
||||
) -> Result<(Hostname, OnionAddressV3, X509), Error> {
|
||||
let account = AccountInfo::new(embassy_password)?;
|
||||
let account = AccountInfo::new(embassy_password, root_ca_start_time().await?)?;
|
||||
let sqlite_pool = ctx.secret_store().await?;
|
||||
account.save(&sqlite_pool).await?;
|
||||
sqlite_pool.close().await;
|
||||
|
||||
@@ -6,10 +6,11 @@ use rpc_toolkit::command;
|
||||
use crate::context::RpcContext;
|
||||
use crate::disk::main::export;
|
||||
use crate::init::{STANDBY_MODE_PATH, SYSTEM_REBUILD_PATH};
|
||||
use crate::prelude::*;
|
||||
use crate::sound::SHUTDOWN;
|
||||
use crate::util::docker::CONTAINER_TOOL;
|
||||
use crate::util::{display_none, Invoke};
|
||||
use crate::{Error, OS_ARCH};
|
||||
use crate::PLATFORM;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Shutdown {
|
||||
@@ -60,7 +61,7 @@ impl Shutdown {
|
||||
tracing::debug!("{:?}", e);
|
||||
}
|
||||
}
|
||||
if OS_ARCH != "raspberrypi" || self.restart {
|
||||
if &*PLATFORM != "raspberrypi" || self.restart {
|
||||
if let Err(e) = SHUTDOWN.play().await {
|
||||
tracing::error!("Error Playing Shutdown Song: {}", e);
|
||||
tracing::debug!("{:?}", e);
|
||||
@@ -68,7 +69,7 @@ impl Shutdown {
|
||||
}
|
||||
});
|
||||
drop(rt);
|
||||
if OS_ARCH == "raspberrypi" {
|
||||
if &*PLATFORM == "raspberrypi" {
|
||||
if !self.restart {
|
||||
std::fs::write(STANDBY_MODE_PATH, "").unwrap();
|
||||
Command::new("sync").spawn().unwrap().wait().unwrap();
|
||||
@@ -90,6 +91,14 @@ impl Shutdown {
|
||||
|
||||
#[command(display(display_none))]
|
||||
pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> {
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
db.as_server_info_mut()
|
||||
.as_status_info_mut()
|
||||
.as_shutting_down_mut()
|
||||
.ser(&true)
|
||||
})
|
||||
.await?;
|
||||
ctx.shutdown
|
||||
.send(Some(Shutdown {
|
||||
export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())),
|
||||
@@ -102,6 +111,14 @@ pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> {
|
||||
|
||||
#[command(display(display_none))]
|
||||
pub async fn restart(#[context] ctx: RpcContext) -> Result<(), Error> {
|
||||
ctx.db
|
||||
.mutate(|db| {
|
||||
db.as_server_info_mut()
|
||||
.as_status_info_mut()
|
||||
.as_restarting_mut()
|
||||
.ser(&true)
|
||||
})
|
||||
.await?;
|
||||
ctx.shutdown
|
||||
.send(Some(Shutdown {
|
||||
export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use chrono::Utc;
|
||||
use clap::ArgMatches;
|
||||
use color_eyre::eyre::eyre;
|
||||
use futures::FutureExt;
|
||||
use rpc_toolkit::command;
|
||||
@@ -84,9 +85,65 @@ pub async fn zram(#[context] ctx: RpcContext, #[arg] enable: bool) -> Result<(),
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn time() -> Result<String, Error> {
|
||||
Ok(Utc::now().to_rfc3339())
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct TimeInfo {
|
||||
now: String,
|
||||
uptime: u64,
|
||||
}
|
||||
|
||||
fn display_time(arg: TimeInfo, matches: &ArgMatches) {
|
||||
use std::fmt::Write;
|
||||
|
||||
use prettytable::*;
|
||||
|
||||
if matches.is_present("format") {
|
||||
return display_serializable(arg, matches);
|
||||
}
|
||||
|
||||
let days = arg.uptime / (24 * 60 * 60);
|
||||
let days_s = arg.uptime % (24 * 60 * 60);
|
||||
let hours = days_s / (60 * 60);
|
||||
let hours_s = arg.uptime % (60 * 60);
|
||||
let minutes = hours_s / 60;
|
||||
let seconds = arg.uptime % 60;
|
||||
let mut uptime_string = String::new();
|
||||
if days > 0 {
|
||||
write!(&mut uptime_string, "{days} days").unwrap();
|
||||
}
|
||||
if hours > 0 {
|
||||
if !uptime_string.is_empty() {
|
||||
uptime_string += ", ";
|
||||
}
|
||||
write!(&mut uptime_string, "{hours} hours").unwrap();
|
||||
}
|
||||
if minutes > 0 {
|
||||
if !uptime_string.is_empty() {
|
||||
uptime_string += ", ";
|
||||
}
|
||||
write!(&mut uptime_string, "{minutes} minutes").unwrap();
|
||||
}
|
||||
if !uptime_string.is_empty() {
|
||||
uptime_string += ", ";
|
||||
}
|
||||
write!(&mut uptime_string, "{seconds} seconds").unwrap();
|
||||
|
||||
let mut table = Table::new();
|
||||
table.add_row(row![bc -> "NOW", &arg.now]);
|
||||
table.add_row(row![bc -> "UPTIME", &uptime_string]);
|
||||
table.print_tty(false).unwrap();
|
||||
}
|
||||
|
||||
#[command(display(display_time))]
|
||||
pub async fn time(
|
||||
#[context] ctx: RpcContext,
|
||||
#[allow(unused_variables)]
|
||||
#[arg(long = "format")]
|
||||
format: Option<IoFormat>,
|
||||
) -> Result<TimeInfo, Error> {
|
||||
Ok(TimeInfo {
|
||||
now: Utc::now().to_rfc3339(),
|
||||
uptime: ctx.start_time.elapsed().as_secs(),
|
||||
})
|
||||
}
|
||||
|
||||
#[command(
|
||||
@@ -303,60 +360,44 @@ impl<'de> Deserialize<'de> for GigaBytes {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct MetricsGeneral {
|
||||
#[serde(rename = "Temperature")]
|
||||
temperature: Option<Celsius>,
|
||||
pub temperature: Option<Celsius>,
|
||||
}
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct MetricsMemory {
|
||||
#[serde(rename = "Percentage Used")]
|
||||
pub percentage_used: Percentage,
|
||||
#[serde(rename = "Total")]
|
||||
pub total: MebiBytes,
|
||||
#[serde(rename = "Available")]
|
||||
pub available: MebiBytes,
|
||||
#[serde(rename = "Used")]
|
||||
pub used: MebiBytes,
|
||||
#[serde(rename = "Swap Total")]
|
||||
pub swap_total: MebiBytes,
|
||||
#[serde(rename = "Swap Free")]
|
||||
pub swap_free: MebiBytes,
|
||||
#[serde(rename = "Swap Used")]
|
||||
pub swap_used: MebiBytes,
|
||||
pub zram_total: MebiBytes,
|
||||
pub zram_available: MebiBytes,
|
||||
pub zram_used: MebiBytes,
|
||||
}
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct MetricsCpu {
|
||||
#[serde(rename = "User Space")]
|
||||
user_space: Percentage,
|
||||
#[serde(rename = "Kernel Space")]
|
||||
kernel_space: Percentage,
|
||||
#[serde(rename = "I/O Wait")]
|
||||
wait: Percentage,
|
||||
#[serde(rename = "Idle")]
|
||||
percentage_used: Percentage,
|
||||
idle: Percentage,
|
||||
#[serde(rename = "Usage")]
|
||||
usage: Percentage,
|
||||
user_space: Percentage,
|
||||
kernel_space: Percentage,
|
||||
wait: Percentage,
|
||||
}
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct MetricsDisk {
|
||||
#[serde(rename = "Size")]
|
||||
size: GigaBytes,
|
||||
#[serde(rename = "Used")]
|
||||
percentage_used: Percentage,
|
||||
used: GigaBytes,
|
||||
#[serde(rename = "Available")]
|
||||
available: GigaBytes,
|
||||
#[serde(rename = "Percentage Used")]
|
||||
used_percentage: Percentage,
|
||||
capacity: GigaBytes,
|
||||
}
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Metrics {
|
||||
#[serde(rename = "General")]
|
||||
general: MetricsGeneral,
|
||||
#[serde(rename = "Memory")]
|
||||
memory: MetricsMemory,
|
||||
#[serde(rename = "CPU")]
|
||||
cpu: MetricsCpu,
|
||||
#[serde(rename = "Disk")]
|
||||
disk: MetricsDisk,
|
||||
}
|
||||
|
||||
@@ -682,7 +723,7 @@ async fn get_cpu_info(last: &mut ProcStat) -> Result<MetricsCpu, Error> {
|
||||
kernel_space: Percentage((new.system() - last.system()) as f64 * 100.0 / total_diff as f64),
|
||||
idle: Percentage((new.idle - last.idle) as f64 * 100.0 / total_diff as f64),
|
||||
wait: Percentage((new.iowait - last.iowait) as f64 * 100.0 / total_diff as f64),
|
||||
usage: Percentage((new.used() - last.used()) as f64 * 100.0 / total_diff as f64),
|
||||
percentage_used: Percentage((new.used() - last.used()) as f64 * 100.0 / total_diff as f64),
|
||||
};
|
||||
*last = new;
|
||||
Ok(res)
|
||||
@@ -695,8 +736,8 @@ pub struct MemInfo {
|
||||
buffers: Option<u64>,
|
||||
cached: Option<u64>,
|
||||
slab: Option<u64>,
|
||||
swap_total: Option<u64>,
|
||||
swap_free: Option<u64>,
|
||||
zram_total: Option<u64>,
|
||||
zram_free: Option<u64>,
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
|
||||
@@ -708,8 +749,8 @@ pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
|
||||
buffers: None,
|
||||
cached: None,
|
||||
slab: None,
|
||||
swap_total: None,
|
||||
swap_free: None,
|
||||
zram_total: None,
|
||||
zram_free: None,
|
||||
};
|
||||
fn get_num_kb(l: &str) -> Result<u64, Error> {
|
||||
let e = Error::new(
|
||||
@@ -734,8 +775,8 @@ pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
|
||||
_ if entry.starts_with("Buffers") => mem_info.buffers = Some(get_num_kb(entry)?),
|
||||
_ if entry.starts_with("Cached") => mem_info.cached = Some(get_num_kb(entry)?),
|
||||
_ if entry.starts_with("Slab") => mem_info.slab = Some(get_num_kb(entry)?),
|
||||
_ if entry.starts_with("SwapTotal") => mem_info.swap_total = Some(get_num_kb(entry)?),
|
||||
_ if entry.starts_with("SwapFree") => mem_info.swap_free = Some(get_num_kb(entry)?),
|
||||
_ if entry.starts_with("SwapTotal") => mem_info.zram_total = Some(get_num_kb(entry)?),
|
||||
_ if entry.starts_with("SwapFree") => mem_info.zram_free = Some(get_num_kb(entry)?),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -751,24 +792,24 @@ pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
|
||||
let buffers = ensure_present(mem_info.buffers, "Buffers")?;
|
||||
let cached = ensure_present(mem_info.cached, "Cached")?;
|
||||
let slab = ensure_present(mem_info.slab, "Slab")?;
|
||||
let swap_total_k = ensure_present(mem_info.swap_total, "SwapTotal")?;
|
||||
let swap_free_k = ensure_present(mem_info.swap_free, "SwapFree")?;
|
||||
let zram_total_k = ensure_present(mem_info.zram_total, "SwapTotal")?;
|
||||
let zram_free_k = ensure_present(mem_info.zram_free, "SwapFree")?;
|
||||
|
||||
let total = MebiBytes(mem_total as f64 / 1024.0);
|
||||
let available = MebiBytes(mem_available as f64 / 1024.0);
|
||||
let used = MebiBytes((mem_total - mem_free - buffers - cached - slab) as f64 / 1024.0);
|
||||
let swap_total = MebiBytes(swap_total_k as f64 / 1024.0);
|
||||
let swap_free = MebiBytes(swap_free_k as f64 / 1024.0);
|
||||
let swap_used = MebiBytes((swap_total_k - swap_free_k) as f64 / 1024.0);
|
||||
let zram_total = MebiBytes(zram_total_k as f64 / 1024.0);
|
||||
let zram_available = MebiBytes(zram_free_k as f64 / 1024.0);
|
||||
let zram_used = MebiBytes((zram_total_k - zram_free_k) as f64 / 1024.0);
|
||||
let percentage_used = Percentage((total.0 - available.0) / total.0 * 100.0);
|
||||
Ok(MetricsMemory {
|
||||
percentage_used,
|
||||
total,
|
||||
available,
|
||||
used,
|
||||
swap_total,
|
||||
swap_free,
|
||||
swap_used,
|
||||
zram_total,
|
||||
zram_available,
|
||||
zram_used,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -792,10 +833,10 @@ async fn get_disk_info() -> Result<MetricsDisk, Error> {
|
||||
let total_percentage = total_used as f64 / total_size as f64 * 100.0f64;
|
||||
|
||||
Ok(MetricsDisk {
|
||||
size: GigaBytes(total_size as f64 / 1_000_000_000.0),
|
||||
capacity: GigaBytes(total_size as f64 / 1_000_000_000.0),
|
||||
used: GigaBytes(total_used as f64 / 1_000_000_000.0),
|
||||
available: GigaBytes(total_available as f64 / 1_000_000_000.0),
|
||||
used_percentage: Percentage(total_percentage as f64),
|
||||
percentage_used: Percentage(total_percentage as f64),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ use crate::sound::{
|
||||
};
|
||||
use crate::update::latest_information::LatestInformation;
|
||||
use crate::util::Invoke;
|
||||
use crate::{Error, ErrorKind, ResultExt, OS_ARCH};
|
||||
use crate::{Error, ErrorKind, ResultExt, PLATFORM};
|
||||
|
||||
mod latest_information;
|
||||
|
||||
@@ -231,7 +231,7 @@ impl EosUrl {
|
||||
.host_str()
|
||||
.ok_or_else(|| Error::new(eyre!("Could not get host of base"), ErrorKind::ParseUrl))?;
|
||||
let version: &Version = &self.version;
|
||||
Ok(format!("{host}::{version}/{OS_ARCH}/")
|
||||
Ok(format!("{host}::{version}/{}/", &*PLATFORM)
|
||||
.parse()
|
||||
.map_err(|_| Error::new(eyre!("Could not parse path"), ErrorKind::ParseUrl))?)
|
||||
}
|
||||
@@ -297,7 +297,7 @@ async fn sync_boot() -> Result<(), Error> {
|
||||
.await?
|
||||
.wait()
|
||||
.await?;
|
||||
if OS_ARCH != "raspberrypi" {
|
||||
if &*PLATFORM != "raspberrypi" {
|
||||
let dev_mnt =
|
||||
MountGuard::mount(&Bind::new("/dev"), "/media/embassy/next/dev", ReadWrite).await?;
|
||||
let sys_mnt =
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
use ed25519_dalek::hazmat::ExpandedSecretKey;
|
||||
use ed25519_dalek::{SecretKey, EXPANDED_SECRET_KEY_LENGTH};
|
||||
|
||||
#[inline]
|
||||
pub fn ed25519_expand_key(key: &SecretKey) -> [u8; EXPANDED_SECRET_KEY_LENGTH] {
|
||||
let key = ExpandedSecretKey::from(key);
|
||||
|
||||
let mut bytes: [u8; 64] = [0u8; 64];
|
||||
|
||||
bytes[..32].copy_from_slice(key.scalar.as_bytes());
|
||||
bytes[32..].copy_from_slice(&key.hash_prefix[..]);
|
||||
bytes
|
||||
ed25519_dalek_v1::ExpandedSecretKey::from(
|
||||
&ed25519_dalek_v1::SecretKey::from_bytes(key).unwrap(),
|
||||
)
|
||||
.to_bytes()
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ use tokio::process::Command;
|
||||
|
||||
use crate::util::Invoke;
|
||||
|
||||
#[cfg(not(feature = "podman"))]
|
||||
#[cfg(feature = "docker")]
|
||||
pub const CONTAINER_TOOL: &str = "docker";
|
||||
#[cfg(feature = "podman")]
|
||||
#[cfg(not(feature = "docker"))]
|
||||
pub const CONTAINER_TOOL: &str = "podman";
|
||||
|
||||
#[cfg(not(feature = "podman"))]
|
||||
#[cfg(feature = "docker")]
|
||||
pub const CONTAINER_DATADIR: &str = "/var/lib/docker";
|
||||
#[cfg(feature = "podman")]
|
||||
#[cfg(not(feature = "docker"))]
|
||||
pub const CONTAINER_DATADIR: &str = "/var/lib/containers";
|
||||
|
||||
pub struct DockerImageSha(String);
|
||||
|
||||
@@ -26,13 +26,13 @@ use crate::shutdown::Shutdown;
|
||||
use crate::{Error, ErrorKind, ResultExt as _};
|
||||
pub mod config;
|
||||
pub mod cpupower;
|
||||
pub mod crypto;
|
||||
pub mod docker;
|
||||
pub mod http_reader;
|
||||
pub mod io;
|
||||
pub mod logger;
|
||||
pub mod lshw;
|
||||
pub mod serde;
|
||||
pub mod crypto;
|
||||
|
||||
#[derive(Clone, Copy, Debug, ::serde::Deserialize, ::serde::Serialize)]
|
||||
pub enum Never {}
|
||||
@@ -50,30 +50,113 @@ impl std::fmt::Display for Never {
|
||||
impl std::error::Error for Never {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait Invoke {
|
||||
pub trait Invoke<'a> {
|
||||
type Extended<'ext>
|
||||
where
|
||||
Self: 'ext,
|
||||
'ext: 'a;
|
||||
fn timeout<'ext: 'a>(&'ext mut self, timeout: Option<Duration>) -> Self::Extended<'ext>;
|
||||
fn input<'ext: 'a, Input: tokio::io::AsyncRead + Unpin + Send>(
|
||||
&'ext mut self,
|
||||
input: Option<&'ext mut Input>,
|
||||
) -> Self::Extended<'ext>;
|
||||
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error>;
|
||||
async fn invoke_timeout(
|
||||
&mut self,
|
||||
error_kind: crate::ErrorKind,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<Vec<u8>, Error>;
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl Invoke for tokio::process::Command {
|
||||
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error> {
|
||||
self.invoke_timeout(error_kind, None).await
|
||||
|
||||
pub struct ExtendedCommand<'a> {
|
||||
cmd: &'a mut tokio::process::Command,
|
||||
timeout: Option<Duration>,
|
||||
input: Option<&'a mut (dyn tokio::io::AsyncRead + Unpin + Send)>,
|
||||
}
|
||||
impl<'a> std::ops::Deref for ExtendedCommand<'a> {
|
||||
type Target = tokio::process::Command;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.cmd
|
||||
}
|
||||
async fn invoke_timeout(
|
||||
&mut self,
|
||||
error_kind: crate::ErrorKind,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
self.kill_on_drop(true);
|
||||
self.stdout(Stdio::piped());
|
||||
self.stderr(Stdio::piped());
|
||||
let res = match timeout {
|
||||
None => self.output().await?,
|
||||
Some(t) => tokio::time::timeout(t, self.output())
|
||||
}
|
||||
impl<'a> std::ops::DerefMut for ExtendedCommand<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.cmd
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> Invoke<'a> for tokio::process::Command {
|
||||
type Extended<'ext> = ExtendedCommand<'ext>
|
||||
where
|
||||
Self: 'ext,
|
||||
'ext: 'a;
|
||||
fn timeout<'ext: 'a>(&'ext mut self, timeout: Option<Duration>) -> Self::Extended<'ext> {
|
||||
ExtendedCommand {
|
||||
cmd: self,
|
||||
timeout,
|
||||
input: None,
|
||||
}
|
||||
}
|
||||
fn input<'ext: 'a, Input: tokio::io::AsyncRead + Unpin + Send>(
|
||||
&'ext mut self,
|
||||
input: Option<&'ext mut Input>,
|
||||
) -> Self::Extended<'ext> {
|
||||
ExtendedCommand {
|
||||
cmd: self,
|
||||
timeout: None,
|
||||
input: if let Some(input) = input {
|
||||
Some(&mut *input)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error> {
|
||||
ExtendedCommand {
|
||||
cmd: self,
|
||||
timeout: None,
|
||||
input: None,
|
||||
}
|
||||
.invoke(error_kind)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> Invoke<'a> for ExtendedCommand<'a> {
|
||||
type Extended<'ext> = &'ext mut ExtendedCommand<'ext>
|
||||
where
|
||||
Self: 'ext,
|
||||
'ext: 'a;
|
||||
fn timeout<'ext: 'a>(&'ext mut self, timeout: Option<Duration>) -> Self::Extended<'ext> {
|
||||
self.timeout = timeout;
|
||||
self
|
||||
}
|
||||
fn input<'ext: 'a, Input: tokio::io::AsyncRead + Unpin + Send>(
|
||||
&'ext mut self,
|
||||
input: Option<&'ext mut Input>,
|
||||
) -> Self::Extended<'ext> {
|
||||
self.input = if let Some(input) = input {
|
||||
Some(&mut *input)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self
|
||||
}
|
||||
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error> {
|
||||
self.cmd.kill_on_drop(true);
|
||||
if self.input.is_some() {
|
||||
self.cmd.stdin(Stdio::piped());
|
||||
}
|
||||
self.cmd.stdout(Stdio::piped());
|
||||
self.cmd.stderr(Stdio::piped());
|
||||
let mut child = self.cmd.spawn()?;
|
||||
if let (Some(mut stdin), Some(input)) = (child.stdin.take(), self.input.take()) {
|
||||
use tokio::io::AsyncWriteExt;
|
||||
tokio::io::copy(input, &mut stdin).await?;
|
||||
stdin.flush().await?;
|
||||
stdin.shutdown().await?;
|
||||
drop(stdin);
|
||||
}
|
||||
let res = match self.timeout {
|
||||
None => child.wait_with_output().await?,
|
||||
Some(t) => tokio::time::timeout(t, child.wait_with_output())
|
||||
.await
|
||||
.with_kind(ErrorKind::Timeout)??,
|
||||
};
|
||||
|
||||
@@ -200,8 +200,7 @@ pub async fn init(db: &PatchDb, secrets: &PgPool) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub const COMMIT_HASH: &str =
|
||||
git_version::git_version!(args = ["--always", "--abbrev=40", "--dirty=-modified"]);
|
||||
pub const COMMIT_HASH: &str = include_str!("../../../GIT_HASH.txt");
|
||||
|
||||
#[command(rename = "git-info", local, metadata(authenticated = false))]
|
||||
pub fn git_info() -> Result<&'static str, Error> {
|
||||
|
||||
@@ -27,6 +27,12 @@ impl Volumes {
|
||||
volume
|
||||
.validate(interfaces)
|
||||
.with_ctx(|_| (crate::ErrorKind::ValidateS9pk, format!("Volume {}", id)))?;
|
||||
if let Volume::Backup { .. } = volume {
|
||||
return Err(Error::new(
|
||||
eyre!("Invalid volume type \"backup\""),
|
||||
ErrorKind::ParseS9pk,
|
||||
)); // Volume::Backup is for internal use and shouldn't be declared in manifest
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -131,7 +137,6 @@ pub enum Volume {
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
Certificate { interface_id: InterfaceId },
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(skip)]
|
||||
Backup { readonly: bool },
|
||||
}
|
||||
impl Volume {
|
||||
|
||||
@@ -855,7 +855,7 @@ export const action = {
|
||||
},
|
||||
/**
|
||||
* Created this test because of issue
|
||||
* https://github.com/Start9Labs/embassy-os/issues/1737
|
||||
* https://github.com/Start9Labs/start-os/issues/1737
|
||||
* which that we couldn't create a dir that was deeply nested, and the parents where
|
||||
* not created yet. Found this out during the migrations, where the parent would die.
|
||||
* @param {*} effects
|
||||
@@ -931,7 +931,7 @@ export const action = {
|
||||
},
|
||||
/**
|
||||
* Created this test because of issue
|
||||
* https://github.com/Start9Labs/embassy-os/issues/2121
|
||||
* https://github.com/Start9Labs/start-os/issues/2121
|
||||
* That the empty in the create dies
|
||||
* @param {*} effects
|
||||
* @param {*} _input
|
||||
|
||||
@@ -15,7 +15,7 @@ docker run -d --rm --name=tmp_postgres -e POSTGRES_PASSWORD=password -v $TMP_DIR
|
||||
PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tmp_postgres)
|
||||
|
||||
DATABASE_URL=postgres://postgres:password@$PG_IP/postgres cargo sqlx migrate run
|
||||
DATABASE_URL=postgres://postgres:password@$PG_IP/postgres OS_ARCH=$(uname -m) cargo sqlx prepare -- --lib --profile=test
|
||||
DATABASE_URL=postgres://postgres:password@$PG_IP/postgres PLATFORM=$(uname -m) cargo sqlx prepare -- --lib --profile=test
|
||||
)
|
||||
|
||||
docker stop tmp_postgres
|
||||
|
||||
19
basename.sh
Executable file
19
basename.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
PLATFORM="$(if [ -f ./PLATFORM.txt ]; then cat ./PLATFORM.txt; else echo unknown; fi)"
|
||||
VERSION="$(cat ./VERSION.txt)"
|
||||
GIT_HASH="$(cat ./GIT_HASH.txt)"
|
||||
if [[ "$GIT_HASH" =~ ^@ ]]; then
|
||||
GIT_HASH=unknown
|
||||
else
|
||||
GIT_HASH="$(echo -n "$GIT_HASH" | head -c 7)"
|
||||
fi
|
||||
STARTOS_ENV="$(cat ./ENVIRONMENT.txt)"
|
||||
VERSION_FULL="${VERSION}-${GIT_HASH}"
|
||||
if [ -n "$STARTOS_ENV" ]; then
|
||||
VERSION_FULL="$VERSION_FULL~${STARTOS_ENV}"
|
||||
fi
|
||||
|
||||
echo -n "startos-${VERSION_FULL}_${PLATFORM}"
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
shopt -s expand_aliases
|
||||
|
||||
if [ "$0" != "./build-cargo-dep.sh" ]; then
|
||||
>&2 echo "Must be run from embassy-os directory"
|
||||
>&2 echo "Must be run from start-os directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
2
build/.gitignore
vendored
Normal file
2
build/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
lib/depends
|
||||
lib/conflicts
|
||||
@@ -1,5 +1,5 @@
|
||||
openresolv
|
||||
dhcpcd5
|
||||
firewalld
|
||||
nginx
|
||||
nginx-common
|
||||
nginx-common
|
||||
openresolv
|
||||
@@ -6,21 +6,15 @@ bmon
|
||||
btrfs-progs
|
||||
ca-certificates
|
||||
cifs-utils
|
||||
containerd.io
|
||||
cryptsetup
|
||||
curl
|
||||
dmidecode
|
||||
docker-ce
|
||||
docker-ce-cli
|
||||
docker-compose-plugin
|
||||
dosfstools
|
||||
e2fsprogs
|
||||
ecryptfs-utils
|
||||
exfatprogs
|
||||
flashrom
|
||||
gdb
|
||||
grub-common
|
||||
heaptrack
|
||||
htop
|
||||
httpdirfs
|
||||
iotop
|
||||
5
build/dpkg-deps/docker.depends
Normal file
5
build/dpkg-deps/docker.depends
Normal file
@@ -0,0 +1,5 @@
|
||||
+ containerd.io
|
||||
+ docker-ce
|
||||
+ docker-ce-cli
|
||||
+ docker-compose-plugin
|
||||
- podman
|
||||
43
build/dpkg-deps/generate.sh
Executable file
43
build/dpkg-deps/generate.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
IFS="-" read -ra FEATURES <<< "$ENVIRONMENT"
|
||||
|
||||
feature_file_checker='
|
||||
/^#/ { next }
|
||||
/^\+ [a-z0-9]+$/ { next }
|
||||
/^- [a-z0-9]+$/ { next }
|
||||
{ exit 1 }
|
||||
'
|
||||
|
||||
for type in conflicts depends; do
|
||||
pkgs=()
|
||||
for feature in ${FEATURES[@]}; do
|
||||
file="$feature.$type"
|
||||
if [ -f $file ]; then
|
||||
# TODO check for syntax errrors
|
||||
cat $file | awk "$feature_file_checker"
|
||||
for pkg in $(cat $file | awk '/^\+/ {print $2}'); do
|
||||
pkgs+=($pkg)
|
||||
done
|
||||
fi
|
||||
done
|
||||
for pkg in $(cat $type); do
|
||||
SKIP=
|
||||
for feature in ${FEATURES[@]}; do
|
||||
file="$feature.$type"
|
||||
if [ -f $file ]; then
|
||||
if grep "^- $pkg$" $file; then
|
||||
SKIP=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ -z $SKIP ]; then
|
||||
pkgs+=($pkg)
|
||||
fi
|
||||
done
|
||||
(IFS=$'\n'; echo "${pkgs[*]}") | sort -u > ../lib/$type
|
||||
done
|
||||
2
build/dpkg-deps/unstable.depends
Normal file
2
build/dpkg-deps/unstable.depends
Normal file
@@ -0,0 +1,2 @@
|
||||
+ gdb
|
||||
+ heaptrack
|
||||
@@ -2,23 +2,33 @@
|
||||
printf "\n"
|
||||
printf "Welcome to\n"
|
||||
cat << "ASCII"
|
||||
╭ ━ ━ ━ ╮ ╭ ╮ ╱ ╱ ╱ ╱ ╱ ╭ ╮ ╭ ━ ━ ━ ┳ ━ ━ ━ ╮
|
||||
┃ ╭ ━ ╮ ┣ ╯ ╰ ╮ ╱ ╱ ╱ ╭ ╯ ╰ ┫ ╭ ━ ╮ ┃ ╭ ━ ╮ ┃
|
||||
┃ ╰ ━ ━ ╋ ╮ ╭ ╋ ━ ━ ┳ ┻ ╮ ╭ ┫ ┃ ╱ ┃ ┃ ╰ ━ ━ ╮
|
||||
╰ ━ ━ ╮ ┃ ┃ ┃ ┃ ╭ ╮ ┃ ╭ ┫ ┃ ┃ ┃ ╱ ┃ ┣ ━ ━ ╮ ┃
|
||||
┃ ╰ ━ ╯ ┃ ┃ ╰ ┫ ╭ ╮ ┃ ┃ ┃ ╰ ┫ ╰ ━ ╯ ┃ ╰ ━ ╯ ┃
|
||||
╰ ━ ━ ━ ╯ ╰ ━ ┻ ╯ ╰ ┻ ╯ ╰ ━ ┻ ━ ━ ━ ┻ ━ ━ ━ ╯
|
||||
|
||||
███████
|
||||
█ █ █
|
||||
█ █ █ █
|
||||
█ █ █ █
|
||||
█ █ █ █
|
||||
█ █ █ █
|
||||
█ █
|
||||
███████
|
||||
|
||||
_____ __ ___ __ __
|
||||
(_ | /\ |__) | / \(_
|
||||
__) | / \| \ | \__/__)
|
||||
ASCII
|
||||
printf " v$(cat /usr/lib/startos/VERSION.txt)\n\n"
|
||||
printf " %s (%s %s)\n" "$(uname -o)" "$(uname -r)" "$(uname -m)"
|
||||
printf " $(start-cli --version | sed 's/StartOS CLI /StartOS v/g') - $(start-cli git-info)"
|
||||
if [ -n "$(cat /usr/lib/embassy/ENVIRONMENT.txt)" ]; then
|
||||
printf " ~ $(cat /usr/lib/embassy/ENVIRONMENT.txt)\n"
|
||||
printf " Git Hash: $(cat /usr/lib/startos/GIT_HASH.txt)"
|
||||
if [ -n "$(cat /usr/lib/startos/ENVIRONMENT.txt)" ]; then
|
||||
printf " ~ $(cat /usr/lib/startos/ENVIRONMENT.txt)\n"
|
||||
else
|
||||
printf "\n"
|
||||
fi
|
||||
|
||||
printf "\n"
|
||||
printf " * Documentation: https://start9.com\n"
|
||||
printf " * Documentation: https://docs.start9.com\n"
|
||||
printf " * Management: https://%s.local\n" "$(hostname)"
|
||||
printf " * Support: https://t.me/start9_labs\n"
|
||||
printf " * Support: https://start9.com/contact\n"
|
||||
printf " * Source Code: https://github.com/Start9Labs/start-os\n"
|
||||
printf " * License: MIT\n"
|
||||
printf "\n"
|
||||
|
||||
@@ -72,7 +72,7 @@ user_pref("messaging-system.rsexperimentloader.enabled", false);
|
||||
user_pref("network.allow-experiments", false);
|
||||
user_pref("network.captive-portal-service.enabled", false);
|
||||
user_pref("network.connectivity-service.enabled", false);
|
||||
user_pref("network.proxy.autoconfig_url", "file:///usr/lib/embassy/proxy.pac");
|
||||
user_pref("network.proxy.autoconfig_url", "file:///usr/lib/startos/proxy.pac");
|
||||
user_pref("network.proxy.socks_remote_dns", true);
|
||||
user_pref("network.proxy.type", 2);
|
||||
user_pref("signon.rememberSignons", false);
|
||||
@@ -91,11 +91,11 @@ EOT
|
||||
while ! curl "http://localhost" > /dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
while ! /usr/lib/embassy/scripts/check-monitor; do
|
||||
while ! /usr/lib/startos/scripts/check-monitor; do
|
||||
sleep 15
|
||||
done
|
||||
(
|
||||
while /usr/lib/embassy/scripts/check-monitor; do
|
||||
while /usr/lib/startos/scripts/check-monitor; do
|
||||
sleep 15
|
||||
done
|
||||
killall firefox-esr
|
||||
|
||||
@@ -13,7 +13,7 @@ fi
|
||||
>&2 echo ' sudo rm /usr/local/bin/apt'
|
||||
>&2 echo
|
||||
>&2 echo 'Otherwise, what you probably want to do is run:'
|
||||
>&2 echo ' sudo /usr/lib/embassy/scripts/chroot-and-upgrade'
|
||||
>&2 echo ' sudo /usr/lib/startos/scripts/chroot-and-upgrade'
|
||||
>&2 echo 'You can run apt in this context to add packages to your system.'
|
||||
>&2 echo 'When you are done with your changes, type "exit" and the device will reboot into a system with the changes applied.'
|
||||
>&2 echo 'This is still NOT RECOMMENDED if you don'"'"'t know what you are doing, but at least isn'"'"'t guaranteed to break things.'
|
||||
|
||||
@@ -14,7 +14,7 @@ while [ -n "$1" ]; do
|
||||
done
|
||||
|
||||
if [ ${#TO_INSTALL[@]} -ne 0 ]; then
|
||||
/usr/lib/embassy/scripts/chroot-and-upgrade << EOF
|
||||
/usr/lib/startos/scripts/chroot-and-upgrade << EOF
|
||||
apt-get update && apt-get install -y ${TO_INSTALL[@]}
|
||||
EOF
|
||||
fi
|
||||
@@ -60,12 +60,12 @@ sudo mount `partition_for ${OUTPUT_DEVICE} 2` $TMPDIR
|
||||
sudo mkdir $TMPDIR/boot
|
||||
sudo mount `partition_for ${OUTPUT_DEVICE} 1` $TMPDIR/boot
|
||||
sudo unsquashfs -f -d $TMPDIR startos.raspberrypi.squashfs
|
||||
REAL_GIT_HASH=$(cat $TMPDIR/usr/lib/embassy/GIT_HASH.txt)
|
||||
REAL_VERSION=$(cat $TMPDIR/usr/lib/embassy/VERSION.txt)
|
||||
REAL_ENVIRONMENT=$(cat $TMPDIR/usr/lib/embassy/ENVIRONMENT.txt)
|
||||
sudo sed -i 's| boot=embassy| init=/usr/lib/embassy/scripts/init_resize\.sh|' $TMPDIR/boot/cmdline.txt
|
||||
REAL_GIT_HASH=$(cat $TMPDIR/usr/lib/startos/GIT_HASH.txt)
|
||||
REAL_VERSION=$(cat $TMPDIR/usr/lib/startos/VERSION.txt)
|
||||
REAL_ENVIRONMENT=$(cat $TMPDIR/usr/lib/startos/ENVIRONMENT.txt)
|
||||
sudo sed -i 's| boot=embassy| init=/usr/lib/startos/scripts/init_resize\.sh|' $TMPDIR/boot/cmdline.txt
|
||||
sudo cp ./build/raspberrypi/fstab $TMPDIR/etc/
|
||||
sudo cp ./build/raspberrypi/init_resize.sh $TMPDIR/usr/lib/embassy/scripts/init_resize.sh
|
||||
sudo cp ./build/raspberrypi/init_resize.sh $TMPDIR/usr/lib/startos/scripts/init_resize.sh
|
||||
sudo umount $TMPDIR/boot
|
||||
sudo umount $TMPDIR
|
||||
sudo losetup -d $OUTPUT_DEVICE
|
||||
|
||||
9
build/registry/cleanDanglingImages
Normal file
9
build/registry/cleanDanglingImages
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
for image in $(find /root/resources/eos/ -type f -name '*.squashfs' -mmin +240 -exec realpath {} \;); do
|
||||
if ! mount | grep "^$image" > /dev/null; then
|
||||
>&2 echo "Removing dangling image: $image"
|
||||
rm $image
|
||||
fi
|
||||
done
|
||||
find /root/resources/eos -type d -empty -delete
|
||||
45
build/registry/downloadIndexActionResult
Normal file
45
build/registry/downloadIndexActionResult
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
RUN_ID=$1
|
||||
|
||||
if [ -z "$RUN_ID" ]; then
|
||||
>&2 echo usage: $0 '<run-id>'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TMP_DIR=/var/tmp/action-run-results/$RUN_ID
|
||||
|
||||
rm -rf $TMP_DIR
|
||||
mkdir -p $TMP_DIR
|
||||
|
||||
cd $TMP_DIR
|
||||
|
||||
for arch in x86_64 x86_64-nonfree aarch64 aarch64-nonfree raspberrypi; do
|
||||
gh run download -R Start9Labs/start-os $RUN_ID -n $arch.squashfs
|
||||
done
|
||||
|
||||
VERSION=
|
||||
HASH=
|
||||
for file in $(ls *.squashfs); do
|
||||
if [[ $file =~ ^startos-([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?)-([a-f0-9]{7}(~[a-z-]+)?|unknown)_([a-z0-9_-]+).squashfs$ ]]; then
|
||||
if [ -n "$VERSION" ] && [ "$VERSION" != "${BASH_REMATCH[1]}" ]; then
|
||||
>&2 echo "VERSION MISMATCH: expected $VERSION got ${BASH_REMATCH[1]}"
|
||||
exit 2
|
||||
fi
|
||||
if [ -n "$HASH" ] && [ "$HASH" != "${BASH_REMATCH[3]}" ]; then
|
||||
>&2 echo "HASH MISMATCH: expected $HASH got ${BASH_REMATCH[3]}"
|
||||
exit 3
|
||||
fi
|
||||
VERSION="${BASH_REMATCH[1]}"
|
||||
HASH="${BASH_REMATCH[3]}"
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p /root/resources/eos/$VERSION
|
||||
rm -rf /root/resources/eos/$VERSION/$HASH
|
||||
mv $TMP_DIR /root/resources/eos/$VERSION/$HASH
|
||||
|
||||
cd /root/resources/eos/$VERSION
|
||||
setOsCommitHash $HASH
|
||||
22
build/registry/resync.cgi
Normal file
22
build/registry/resync.cgi
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
declare -A params
|
||||
while IFS='=' read -r -d '&' key value && [[ -n "$key" ]]; do
|
||||
params["$key"]=$value
|
||||
done <<<"${QUERY_STRING}&"
|
||||
|
||||
index_key="${params['key']}"
|
||||
if [ -z "$index_key" ] || [ "$index_key" != "$(cat /var/www/index_key.txt)" ]; then
|
||||
echo "HTTP/1.1 401 UNAUTHORIZED"
|
||||
echo "Content-Type: text/html"
|
||||
echo
|
||||
echo "UNAUTHORIZED"
|
||||
exit
|
||||
fi
|
||||
|
||||
touch /tmp/resync
|
||||
|
||||
echo "HTTP/1.1 200 OK"
|
||||
echo "Content-Type: text/html"
|
||||
echo
|
||||
echo "OK: Upload successful"
|
||||
@@ -6,12 +6,14 @@
|
||||
# Then we are going to make sure that each of these files is then put on the rsyncd server
|
||||
# so the embassies can pull them down
|
||||
|
||||
date >> /var/log/resyncRsyncRegistry.runlog
|
||||
|
||||
|
||||
cat > /etc/rsyncd.conf << RD
|
||||
uid = root
|
||||
gid = root
|
||||
use chroot = yes
|
||||
max connections = 50
|
||||
max connections = 4
|
||||
pid file = /var/run/rsyncd.pid
|
||||
exclude = lost+found/
|
||||
timeout = 900
|
||||
@@ -27,7 +29,7 @@ do
|
||||
filename=${dir##*/}
|
||||
version=$(echo $directory | sed -r 's/.*\///')
|
||||
version_dir="/srv/rsync/$version"
|
||||
type=$(echo "$filename" | sed -r "s/^.*?\.(\w+)\.squashfs$/\1/")
|
||||
type=$(echo "$filename" | sed -r "s/^.*?\.([a-z0-9_-]+)\.squashfs$/\1/")
|
||||
new_dir="$version_dir/$type"
|
||||
|
||||
|
||||
@@ -51,4 +53,4 @@ INSERTING
|
||||
done
|
||||
|
||||
echo "Created rsyncd.conf file, restarting service"
|
||||
systemctl restart rsync
|
||||
systemctl restart rsync
|
||||
39
build/registry/setOsCommitHash
Normal file
39
build/registry/setOsCommitHash
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the current directory
|
||||
PWD=$(pwd)
|
||||
HASH=$1
|
||||
|
||||
if [ -z "$HASH" ]; then
|
||||
>&2 echo "usage: setOsCommitHash <hash>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Define the expected pattern for the directory
|
||||
pattern="/root/resources/eos/"
|
||||
|
||||
# Check if the current directory matches the pattern
|
||||
if [[ $PWD =~ ^$pattern([0-9.]+)$ ]]; then
|
||||
# Extract the version number from the directory path
|
||||
version="${BASH_REMATCH[1]}"
|
||||
else
|
||||
>&2 echo "MUST BE IN OS VERSION DIRECTORY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [ -d "$HASH" ]; then
|
||||
>&2 echo "$HASH: No such directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for file in $(ls $HASH/startos-$version-${HASH}_*.squashfs); do
|
||||
if [[ $file =~ ^$HASH/startos-$version-${HASH}_([a-z0-9_-]+).squashfs$ ]]; then
|
||||
arch="${BASH_REMATCH[1]}"
|
||||
echo "Found arch $arch"
|
||||
umount /srv/rsync/$version/$arch
|
||||
rm eos.$arch.squashfs
|
||||
ln -s $file eos.$arch.squashfs
|
||||
fi
|
||||
done
|
||||
|
||||
resyncRsyncRegistry
|
||||
48
build/registry/upload.cgi
Normal file
48
build/registry/upload.cgi
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
declare -A params
|
||||
while IFS='=' read -r -d '&' key value && [[ -n "$key" ]]; do
|
||||
params["$key"]=$value
|
||||
done <<<"${QUERY_STRING}&"
|
||||
|
||||
index_key="${params['key']}"
|
||||
if [ -z "$index_key" ] || [ "$index_key" != "$(cat /var/www/index_key.txt)" ]; then
|
||||
echo "HTTP/1.1 401 UNAUTHORIZED"
|
||||
echo "Content-Type: text/html"
|
||||
echo
|
||||
echo "UNAUTHORIZED"
|
||||
exit
|
||||
fi
|
||||
|
||||
git_hash="${params['gitHash']}"
|
||||
version="${params['version']}"
|
||||
platform="${params['platform']}"
|
||||
shasum="${params['shasum']}"
|
||||
if [ -z "$git_hash" ] || [ -z "$version" ] || [ -z "$platform" ] || [ -z "$shasum" ]; then
|
||||
echo "HTTP/1.1 400 BAD REQUEST"
|
||||
echo "Content-Type: text/html"
|
||||
echo
|
||||
echo "BAD REQUEST: missing param"
|
||||
exit
|
||||
fi
|
||||
|
||||
tmp_file=$(mktemp /var/tmp/tmp.XXXXXXXXXX.squashfs)
|
||||
cat > $tmp_file
|
||||
|
||||
if ! sha256sum $tmp_file | grep "$shasum"; then
|
||||
rm $tmp_file
|
||||
echo "HTTP/1.1 400 BAD REQUEST"
|
||||
echo "Content-Type: text/html"
|
||||
echo
|
||||
echo "BAD REQUEST: shasum mismatch"
|
||||
fi
|
||||
|
||||
mkdir -p /var/www/resources/eos/${version}/${git_hash}
|
||||
mv $tmp_file /var/www/resources/eos/${version}/${git_hash}/startos-${version}-${git_hash}_${platform}.squashfs
|
||||
rm /var/www/resources/eos/${version}/eos.${platform}.squashfs
|
||||
ln -rs /var/www/resources/eos/${version}/${git_hash}/startos-${version}-${git_hash}_${platform}.squashfs /var/www/resources/eos/${version}/eos.${platform}.squashfs
|
||||
|
||||
echo "HTTP/1.1 200 OK"
|
||||
echo "Content-Type: text/html"
|
||||
echo
|
||||
echo "OK: Upload successful"
|
||||
@@ -1,6 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
GIT_HASH="$(git describe --always --abbrev=40 --dirty=-modified)"
|
||||
if [ "$GIT_BRANCH_AS_HASH" != 1 ]; then
|
||||
GIT_HASH="$(git describe --always --abbrev=40 --dirty=-modified)"
|
||||
else
|
||||
GIT_HASH="@$(git rev-parse --abbrev-ref HEAD)"
|
||||
fi
|
||||
|
||||
if ! [ -f ./GIT_HASH.txt ] || [ "$(cat ./GIT_HASH.txt)" != "$GIT_HASH" ]; then
|
||||
echo -n "$GIT_HASH" > ./GIT_HASH.txt
|
||||
|
||||
8
check-platform.sh
Executable file
8
check-platform.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
if ! [ -f ./PLATFORM.txt ] || [ "$(cat ./PLATFORM.txt)" != "$PLATFORM" ] && [ -n "$PLATFORM" ]; then
|
||||
>&2 echo "Updating PLATFORM.txt to \"$PLATFORM\""
|
||||
echo -n "$PLATFORM" > ./PLATFORM.txt
|
||||
fi
|
||||
|
||||
echo -n ./PLATFORM.txt
|
||||
28
build/lib/scripts/postinst → debian/postinst
vendored
28
build/lib/scripts/postinst → debian/postinst
vendored
@@ -8,10 +8,10 @@ fi
|
||||
|
||||
if [ -f /usr/sbin/grub-probe ]; then
|
||||
mv /usr/sbin/grub-probe /usr/sbin/grub-probe-default
|
||||
ln -s /usr/lib/embassy/scripts/grub-probe-eos /usr/sbin/grub-probe
|
||||
ln -s /usr/lib/startos/scripts/grub-probe-eos /usr/sbin/grub-probe
|
||||
fi
|
||||
|
||||
cp /usr/lib/embassy/scripts/embassy-initramfs-module /etc/initramfs-tools/scripts/embassy
|
||||
cp /usr/lib/startos/scripts/embassy-initramfs-module /etc/initramfs-tools/scripts/embassy
|
||||
|
||||
if ! grep overlay /etc/initramfs-tools/modules > /dev/null; then
|
||||
echo overlay >> /etc/initramfs-tools/modules
|
||||
@@ -21,6 +21,7 @@ update-initramfs -u -k all
|
||||
|
||||
if [ -f /etc/default/grub ]; then
|
||||
sed -i '/\(^\|#\)GRUB_CMDLINE_LINUX=/c\GRUB_CMDLINE_LINUX="boot=embassy"' /etc/default/grub
|
||||
sed -i '/\(^\|#\)GRUB_DISTRIBUTOR=/c\GRUB_DISTRIBUTOR="StartOS v$(cat /usr/lib/startos/VERSION.txt)"' /etc/default/grub
|
||||
fi
|
||||
|
||||
# change timezone
|
||||
@@ -46,6 +47,7 @@ dns=systemd-resolved
|
||||
[ifupdown]
|
||||
managed=true
|
||||
EOF
|
||||
$SYSTEMCTL enable startd.service
|
||||
$SYSTEMCTL enable systemd-resolved.service
|
||||
$SYSTEMCTL enable systemd-networkd-wait-online.service
|
||||
$SYSTEMCTL enable ssh.service
|
||||
@@ -70,20 +72,20 @@ fi
|
||||
|
||||
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
|
||||
sed -i 's/Restart=on-failure/Restart=always/g' /lib/systemd/system/tor@default.service
|
||||
sed -i 's/ExecStart=\/usr\/bin\/dockerd/ExecStart=\/usr\/bin\/dockerd --exec-opt native.cgroupdriver=systemd/g' /lib/systemd/system/docker.service
|
||||
sed -i '/\(^\|#\)entries-per-entry-group-max=/c\entries-per-entry-group-max=128' /etc/avahi/avahi-daemon.conf
|
||||
sed -i '/\(^\|#\)Storage=/c\Storage=persistent' /etc/systemd/journald.conf
|
||||
sed -i '/\(^\|#\)Compress=/c\Compress=yes' /etc/systemd/journald.conf
|
||||
sed -i '/\(^\|#\)SystemMaxUse=/c\SystemMaxUse=1G' /etc/systemd/journald.conf
|
||||
sed -i '/\(^\|#\)ForwardToSyslog=/c\ForwardToSyslog=no' /etc/systemd/journald.conf
|
||||
sed -i '/^\s*#\?\s*issue_discards\s*=\s*/c\issue_discards = 1' /etc/lvm/lvm.conf
|
||||
mkdir -p /etc/docker
|
||||
cat > /etc/docker/daemon.json << EOF
|
||||
{
|
||||
"storage-driver": "overlay2"
|
||||
}
|
||||
EOF
|
||||
podman network create -d bridge --subnet 172.18.0.1/24 --opt com.docker.network.bridge.name=br-start9 start9
|
||||
|
||||
if cat /usr/lib/startos/ENVIRONMENT.txt | grep '(^|-)docker(-|$)'; then
|
||||
sed -i 's/ExecStart=\/usr\/bin\/dockerd/ExecStart=\/usr\/bin\/dockerd --exec-opt native.cgroupdriver=systemd/g' /lib/systemd/system/docker.service
|
||||
mkdir -p /etc/docker
|
||||
echo '{ "storage-driver": "overlay2" }' > /etc/docker/daemon.json
|
||||
else
|
||||
podman network create -d bridge --subnet 172.18.0.1/24 --opt com.docker.network.bridge.name=br-start9 start9
|
||||
fi
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# fix to suppress docker warning, fixed in 21.xx release of docker cli: https://github.com/docker/cli/pull/2934
|
||||
@@ -100,7 +102,7 @@ CookieAuthentication 1
|
||||
EOF
|
||||
|
||||
rm -rf /var/lib/tor/*
|
||||
ln -sf /usr/lib/embassy/scripts/tor-check.sh /usr/bin/tor-check
|
||||
ln -sf /usr/lib/startos/scripts/tor-check.sh /usr/bin/tor-check
|
||||
|
||||
echo "fs.inotify.max_user_watches=1048576" > /etc/sysctl.d/97-embassy.conf
|
||||
|
||||
@@ -113,9 +115,9 @@ dpkg-reconfigure --frontend noninteractive locales
|
||||
|
||||
groupadd embassy
|
||||
|
||||
ln -s /usr/lib/embassy/scripts/dhclient-exit-hook /etc/dhcp/dhclient-exit-hooks.d/embassy
|
||||
ln -s /usr/lib/startos/scripts/dhclient-exit-hook /etc/dhcp/dhclient-exit-hooks.d/embassy
|
||||
|
||||
rm -f /etc/motd
|
||||
ln -sf /usr/lib/embassy/motd /etc/update-motd.d/00-embassy
|
||||
ln -sf /usr/lib/startos/motd /etc/update-motd.d/00-embassy
|
||||
chmod -x /etc/update-motd.d/*
|
||||
chmod +x /etc/update-motd.d/00-embassy
|
||||
48
dpkg-build.sh
Executable file
48
dpkg-build.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
|
||||
BASENAME=$(./basename.sh)
|
||||
VERSION=$(cat ./VERSION.txt)
|
||||
if [ "$PLATFORM" = "x86_64" ] || [ "$PLATFORM" = "x86_64-nonfree" ]; then
|
||||
DEB_ARCH=amd64
|
||||
elif [ "$PLATFORM" = "aarch64" ] || [ "$PLATFORM" = "aarch64-nonfree" ] || [ "$PLATFORM" = "raspberrypi" ]; then
|
||||
DEB_ARCH=arm64
|
||||
else
|
||||
DEB_ARCH="$PLATFORM"
|
||||
fi
|
||||
|
||||
rm -rf dpkg-workdir/$BASENAME
|
||||
mkdir -p dpkg-workdir/$BASENAME
|
||||
|
||||
make install DESTDIR=dpkg-workdir/$BASENAME
|
||||
|
||||
DEPENDS=$(cat dpkg-workdir/$BASENAME/usr/lib/startos/depends | tr $'\n' ',' | sed 's/,,\+/,/g' | sed 's/,$//')
|
||||
CONFLICTS=$(cat dpkg-workdir/$BASENAME/usr/lib/startos/conflicts | tr $'\n' ',' | sed 's/,,\+/,/g' | sed 's/,$//')
|
||||
|
||||
cp -r debian dpkg-workdir/$BASENAME/DEBIAN
|
||||
cat > dpkg-workdir/$BASENAME/DEBIAN/control << EOF
|
||||
Package: startos
|
||||
Version: ${VERSION}
|
||||
Section: unknown
|
||||
Priority: required
|
||||
Maintainer: Aiden McClelland <aiden@start9.com>
|
||||
Homepage: https://start9.com
|
||||
Architecture: ${DEB_ARCH}
|
||||
Multi-Arch: foreign
|
||||
Depends: ${DEPENDS}
|
||||
Conflicts: ${CONFLICTS}
|
||||
Description: StartOS Debian Package
|
||||
EOF
|
||||
|
||||
cd dpkg-workdir/$BASENAME
|
||||
find . -type f -not -path "./DEBIAN/*" -exec md5sum {} \; | sort -k 2 | sed 's/\.\/\(.*\)/\1/' > DEBIAN/md5sums
|
||||
cd ../..
|
||||
|
||||
cd dpkg-workdir
|
||||
dpkg-deb --root-owner-group -b $BASENAME
|
||||
mkdir -p ../results
|
||||
mv $BASENAME.deb ../results/$BASENAME.deb
|
||||
rm -rf $BASENAME
|
||||
@@ -1,8 +1,6 @@
|
||||
{
|
||||
"useMocks": true,
|
||||
"enableWidgets": false,
|
||||
"packageArch": "aarch64",
|
||||
"osArch": "raspberrypi",
|
||||
"ui": {
|
||||
"api": {
|
||||
"url": "rpc",
|
||||
|
||||
13319
frontend/package-lock.json
generated
13319
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -47,8 +47,9 @@
|
||||
"@maskito/angular": "^0.10.0",
|
||||
"@maskito/core": "^0.10.0",
|
||||
"@materia-ui/ngx-monaco-editor": "^6.0.0",
|
||||
"@start9labs/argon2": "^0.1.0",
|
||||
"@start9labs/argon2": "^0.2.2",
|
||||
"@start9labs/emver": "^0.1.5",
|
||||
"@start9labs/start-sdk": "0.4.0-rev0.lib0.rc5",
|
||||
"@taiga-ui/addon-charts": "3.28.0",
|
||||
"@taiga-ui/cdk": "3.28.0",
|
||||
"@taiga-ui/core": "3.28.0",
|
||||
@@ -76,7 +77,6 @@
|
||||
"patch-db-client": "file: ../../../patch-db/client",
|
||||
"pbkdf2": "^3.1.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"@start9labs/start-sdk": "0.4.0-rev0.lib0.rc5",
|
||||
"swiper": "^8.2.4",
|
||||
"ts-matches": "^5.2.1",
|
||||
"tslib": "^2.3.0",
|
||||
@@ -115,4 +115,4 @@
|
||||
"pre-commit": "lint-staged --concurrent false"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
[type]="!unmasked1 ? 'password' : 'text'"
|
||||
placeholder="Enter Password"
|
||||
(ionChange)="validate()"
|
||||
maxlength="64"
|
||||
></ion-input>
|
||||
<ion-button fill="clear" color="light" (click)="unmasked1 = !unmasked1">
|
||||
<ion-icon
|
||||
@@ -48,7 +47,6 @@
|
||||
[type]="!unmasked2 ? 'password' : 'text'"
|
||||
placeholder="Retype Password"
|
||||
(ionChange)="checkVer()"
|
||||
maxlength="64"
|
||||
></ion-input>
|
||||
<ion-button
|
||||
fill="clear"
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<p>
|
||||
Download your server's Root CA and
|
||||
<a
|
||||
href="https://docs.start9.com/latest/user-manual/connecting/connecting-lan"
|
||||
href="https://docs.start9.com/0.3.5.x/user-manual/connecting-lan"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
style="color: #6866cc; font-weight: bold; text-decoration: none"
|
||||
@@ -104,7 +104,7 @@
|
||||
<span style="font-weight: bold">Note:</span>
|
||||
This address will only work from a Tor-enabled browser.
|
||||
<a
|
||||
href="https://docs.start9.com/latest/user-manual/connecting/connecting-tor"
|
||||
href="https://docs.start9.com/0.3.5.x/user-manual/connecting-tor"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
style="color: #6866cc; font-weight: bold; text-decoration: none"
|
||||
|
||||
@@ -8,23 +8,21 @@
|
||||
<ion-card>
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col responsiveCol sizeXs="12" class="ion-text-center">
|
||||
<div class="inline" style="margin-bottom: 3rem">
|
||||
<div class="inline mb-12">
|
||||
<ion-icon
|
||||
name="checkmark-circle-outline"
|
||||
color="success"
|
||||
></ion-icon>
|
||||
<h1>Setup Complete!</h1>
|
||||
</div>
|
||||
<div class="card-container">
|
||||
<ion-card id="exit" (click)="exitKiosk()">
|
||||
<div class="container">
|
||||
<div class="inline">
|
||||
<p>Continue to login</p>
|
||||
<ion-icon name="log-in-outline"></ion-icon>
|
||||
</div>
|
||||
</div>
|
||||
</ion-card>
|
||||
</div>
|
||||
<ion-button
|
||||
shape="round"
|
||||
class="login-button mb-12"
|
||||
(click)="exitKiosk()"
|
||||
>
|
||||
Continue to Login
|
||||
<ion-icon name="log-in-outline" slot="end"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-card>
|
||||
@@ -34,8 +32,8 @@
|
||||
<ion-card *ngIf="lanAddress">
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col responsiveCol sizeXs="12" class="ion-text-center">
|
||||
<div style="margin-bottom: 4rem">
|
||||
<div class="inline">
|
||||
<div class="mb-12">
|
||||
<div class="inline-container setup">
|
||||
<ion-icon
|
||||
name="checkmark-circle-outline"
|
||||
color="success"
|
||||
@@ -52,35 +50,40 @@
|
||||
<div class="card-container">
|
||||
<ion-card id="information" (click)="download()">
|
||||
<ion-card-content>
|
||||
<ion-card-title>
|
||||
Download permanent address info
|
||||
</ion-card-title>
|
||||
<ion-card-title>Download address info</ion-card-title>
|
||||
<p>
|
||||
start.local was for setup purposes only. It will no
|
||||
longer work.
|
||||
</p>
|
||||
</ion-card-content>
|
||||
<ion-footer>
|
||||
<div class="container">
|
||||
<div class="inline">
|
||||
<p>Download</p>
|
||||
<ion-icon name="download-outline"></ion-icon>
|
||||
</div>
|
||||
<div class="inline-container">
|
||||
<p class="action-text">Download</p>
|
||||
<ion-icon slot="end" name="download-outline"></ion-icon>
|
||||
</div>
|
||||
</ion-footer>
|
||||
</ion-card>
|
||||
<ion-card
|
||||
[disabled]="disableLogin"
|
||||
id="launch"
|
||||
[disabled]="disableLogin"
|
||||
href="{{ lanAddress }}"
|
||||
target="_blank"
|
||||
>
|
||||
<div class="container">
|
||||
<div class="inline">
|
||||
<p>Login to StartOS</p>
|
||||
<ion-icon name="open-outline"></ion-icon>
|
||||
<ion-card-content>
|
||||
<ion-card-title>Trust your Root CA</ion-card-title>
|
||||
<p>
|
||||
In the new tab, follow instructions to trust your
|
||||
server's Root CA and log in.
|
||||
</p>
|
||||
</ion-card-content>
|
||||
<ion-footer>
|
||||
<div class="container">
|
||||
<div class="inline-container">
|
||||
<p class="action-text">Open</p>
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ion-footer>
|
||||
</ion-card>
|
||||
</div>
|
||||
</ion-col>
|
||||
|
||||
@@ -18,19 +18,24 @@ ion-content {
|
||||
|
||||
ion-grid {
|
||||
max-width: 760px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.grid-center-wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.inline-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
ion-card {
|
||||
padding: 3rem;
|
||||
padding: 2.4rem;
|
||||
|
||||
h1 {
|
||||
color: var(--ion-color-success);
|
||||
@@ -44,14 +49,14 @@ ion-card {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
// download info card
|
||||
ion-card {
|
||||
max-width: 91%;
|
||||
min-width: 91%;
|
||||
min-height: 260px;
|
||||
width: 80%;
|
||||
background: #615F5F;
|
||||
color: var(--ion-text-color);
|
||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 44px;
|
||||
margin: auto;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@@ -70,14 +75,6 @@ ion-card {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
ion-card-content {
|
||||
padding-bottom: 4rem;
|
||||
|
||||
p {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
ion-footer {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
@@ -100,19 +97,24 @@ ion-card {
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#exit {
|
||||
background: var(--color-accent);
|
||||
height: 100%;
|
||||
|
||||
.container p {
|
||||
font-size: 1.4rem !important;
|
||||
font-weight: bold;
|
||||
.login-button {
|
||||
--background: var(--color-accent);
|
||||
--padding-bottom: 2.5rem;
|
||||
--padding-top: 2.5rem;
|
||||
--padding-start: 2.5rem;
|
||||
--padding-end: 2.5rem;
|
||||
--border-radius: 44px;
|
||||
font-size: 1.4rem !important;
|
||||
font-weight: bold;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
transition: all 350ms ease;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:hover {
|
||||
transition-property: transform;
|
||||
transform: scale(1.05);
|
||||
transition-delay: 40ms;
|
||||
}
|
||||
|
||||
ion-icon {
|
||||
@@ -120,40 +122,62 @@ ion-card {
|
||||
}
|
||||
}
|
||||
|
||||
#launch {
|
||||
background: var(--alt-blue);
|
||||
height: 100%;
|
||||
|
||||
.container p {
|
||||
font-size: 1.4rem !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ion-icon {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
.launch-button {
|
||||
--background: var(--alt-blue);
|
||||
}
|
||||
|
||||
#information:after {
|
||||
#information:after, #launch:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 80%;
|
||||
top: 79%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--color-accent);
|
||||
}
|
||||
|
||||
#launch:after {
|
||||
background: var(--alt-blue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.card-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
.mb-12 {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.emphasis-warn {
|
||||
font-weight: 600;
|
||||
color: var(--ion-color-warning);
|
||||
.pb-2 {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.pt-1 {
|
||||
padding-top: 0.25rem;
|
||||
}
|
||||
|
||||
|
||||
.action-text {
|
||||
font-variant-caps: all-small-caps;
|
||||
padding-right: 0.5rem;
|
||||
font-size: 1.5rem !important;
|
||||
letter-spacing: 0.03rem;
|
||||
padding-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.setup {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ion-card {
|
||||
ion-card {
|
||||
width: 100%;
|
||||
padding-bottom: unset;
|
||||
}
|
||||
#information:after {
|
||||
top: 84%;
|
||||
}
|
||||
#launch:after {
|
||||
top: 85%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,18 +45,7 @@ export class SuccessPage {
|
||||
|
||||
async ngAfterViewInit() {
|
||||
this.ngZone.runOutsideAngular(() => this.initMatrix())
|
||||
try {
|
||||
const ret = await this.api.complete()
|
||||
if (!this.isKiosk) {
|
||||
this.torAddress = ret['tor-address']
|
||||
this.lanAddress = ret['lan-address'].replace(/^https:/, 'http:')
|
||||
this.cert = ret['root-ca']
|
||||
|
||||
await this.api.exit()
|
||||
}
|
||||
} catch (e: any) {
|
||||
await this.errCtrl.present(e)
|
||||
}
|
||||
setTimeout(() => this.complete(), 1000)
|
||||
}
|
||||
|
||||
download() {
|
||||
@@ -83,6 +72,21 @@ export class SuccessPage {
|
||||
this.api.exit()
|
||||
}
|
||||
|
||||
private async complete() {
|
||||
try {
|
||||
const ret = await this.api.complete()
|
||||
if (!this.isKiosk) {
|
||||
this.torAddress = ret['tor-address'].replace(/^https:/, 'http:')
|
||||
this.lanAddress = ret['lan-address'].replace(/^https:/, 'http:')
|
||||
this.cert = ret['root-ca']
|
||||
|
||||
await this.api.exit()
|
||||
}
|
||||
} catch (e: any) {
|
||||
await this.errCtrl.present(e)
|
||||
}
|
||||
}
|
||||
|
||||
private initMatrix() {
|
||||
this.ctx = this.canvas.nativeElement.getContext('2d')!
|
||||
this.canvas.nativeElement.width = window.innerWidth
|
||||
|
||||
@@ -218,7 +218,7 @@ ion-toast {
|
||||
* {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 0.3rem;
|
||||
padding-left: 0px 0.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Directive, HostListener, Inject } from '@angular/core'
|
||||
import { DOCUMENT } from '@angular/common'
|
||||
import { debounce } from '@start9labs/shared'
|
||||
import { debounce } from '../../util/misc.util'
|
||||
|
||||
@Directive({
|
||||
selector: '[appEnter]',
|
||||
@@ -27,6 +27,8 @@ export * from './directives/responsive-col/responsive-col.module'
|
||||
export * from './directives/responsive-col/responsive-col-viewport.directive'
|
||||
export * from './directives/safe-links/safe-links.directive'
|
||||
export * from './directives/safe-links/safe-links.module'
|
||||
export * from './directives/enter/enter.directive'
|
||||
export * from './directives/enter/enter.module'
|
||||
|
||||
export * from './mocks/get-setup-status'
|
||||
|
||||
|
||||
@@ -53,9 +53,9 @@ export function getErrorMessage(
|
||||
} else if (e.code === 0) {
|
||||
message =
|
||||
'Request Error. Your browser blocked the request. This is usually caused by a corrupt browser cache or an overly aggressive ad blocker. Please clear your browser cache and/or adjust your ad blocker and try again'
|
||||
link = 'https://docs.start9.com/0.3.5.x/support/common-issues#request-error'
|
||||
} else if (!e.message) {
|
||||
message = 'Unknown Error'
|
||||
link = 'https://docs.start9.com/latest/support/faq'
|
||||
} else {
|
||||
message = e.message
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
export type WorkspaceConfig = {
|
||||
packageArch: 'aarch64' | 'x86_64'
|
||||
osArch: 'aarch64' | 'x86_64' | 'raspberrypi'
|
||||
gitHash: string
|
||||
useMocks: boolean
|
||||
enableWidgets: boolean
|
||||
|
||||
@@ -26,10 +26,7 @@
|
||||
type="overlay"
|
||||
side="end"
|
||||
class="right-menu container"
|
||||
[class.container_offline]="
|
||||
(authService.isVerified$ | async) &&
|
||||
!(connection.connected$ | async)
|
||||
"
|
||||
[class.container_offline]="offline$ | async"
|
||||
[class.right-menu_hidden]="!drawer.open"
|
||||
[style.--side-width.px]="drawer.width"
|
||||
>
|
||||
@@ -47,10 +44,7 @@
|
||||
[responsiveColViewport]="viewport"
|
||||
id="main-content"
|
||||
class="container"
|
||||
[class.container_offline]="
|
||||
(authService.isVerified$ | async) &&
|
||||
!(connection.connected$ | async)
|
||||
"
|
||||
[class.container_offline]="offline$ | async"
|
||||
>
|
||||
<ion-content
|
||||
#viewport="viewport"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, inject, OnDestroy } from '@angular/core'
|
||||
import { merge } from 'rxjs'
|
||||
import { combineLatest, map, merge, startWith } from 'rxjs'
|
||||
import { AuthService } from './services/auth.service'
|
||||
import { SplitPaneTracker } from './services/split-pane.service'
|
||||
import { PatchDataService } from './services/patch-data.service'
|
||||
@@ -25,6 +25,19 @@ export class AppComponent implements OnDestroy {
|
||||
readonly sidebarOpen$ = this.splitPane.sidebarOpen$
|
||||
readonly widgetDrawer$ = this.clientStorageService.widgetDrawer$
|
||||
readonly theme$ = inject(THEME)
|
||||
readonly offline$ = combineLatest([
|
||||
this.authService.isVerified$,
|
||||
this.connection.connected$,
|
||||
this.patch
|
||||
.watch$('server-info', 'status-info')
|
||||
.pipe(startWith({ restarting: false, 'shutting-down': false })),
|
||||
]).pipe(
|
||||
map(
|
||||
([verified, connected, status]) =>
|
||||
verified &&
|
||||
(!connected || status.restarting || status['shutting-down']),
|
||||
),
|
||||
)
|
||||
|
||||
constructor(
|
||||
private readonly titleService: Title,
|
||||
|
||||
@@ -11,11 +11,12 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
||||
import { IonicModule } from '@ionic/angular'
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
|
||||
import {
|
||||
MarkdownModule,
|
||||
DarkThemeModule,
|
||||
EnterModule,
|
||||
LightThemeModule,
|
||||
MarkdownModule,
|
||||
ResponsiveColModule,
|
||||
SharedPipesModule,
|
||||
LightThemeModule,
|
||||
} from '@start9labs/shared'
|
||||
|
||||
import { AppComponent } from './app.component'
|
||||
@@ -24,7 +25,6 @@ import { OSWelcomePageModule } from './common/os-welcome/os-welcome.module'
|
||||
import { PreloaderModule } from './app/preloader/preloader.module'
|
||||
import { FooterModule } from './app/footer/footer.module'
|
||||
import { MenuModule } from './app/menu/menu.module'
|
||||
import { EnterModule } from './app/enter/enter.module'
|
||||
import { APP_PROVIDERS } from './app.providers'
|
||||
import { PatchDbModule } from './services/patch-db/patch-db.module'
|
||||
import { ToastContainerModule } from './common/toast-container/toast-container.module'
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { PatchDB } from 'patch-db-client'
|
||||
import { combineLatest, map, Observable, startWith } from 'rxjs'
|
||||
import { ConnectionService } from 'src/app/services/connection.service'
|
||||
import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
|
||||
@Component({
|
||||
selector: 'connection-bar',
|
||||
@@ -19,8 +21,11 @@ export class ConnectionBarComponent {
|
||||
}> = combineLatest([
|
||||
this.connectionService.networkConnected$,
|
||||
this.websocket$.pipe(startWith(false)),
|
||||
this.patch
|
||||
.watch$('server-info', 'status-info')
|
||||
.pipe(startWith({ restarting: false, 'shutting-down': false })),
|
||||
]).pipe(
|
||||
map(([network, websocket]) => {
|
||||
map(([network, websocket, status]) => {
|
||||
if (!network)
|
||||
return {
|
||||
message: 'No Internet',
|
||||
@@ -35,6 +40,20 @@ export class ConnectionBarComponent {
|
||||
icon: 'cloud-offline-outline',
|
||||
dots: true,
|
||||
}
|
||||
if (status['shutting-down'])
|
||||
return {
|
||||
message: 'Shutting Down',
|
||||
color: 'dark',
|
||||
icon: 'power',
|
||||
dots: true,
|
||||
}
|
||||
if (status.restarting)
|
||||
return {
|
||||
message: 'Restarting',
|
||||
color: 'dark',
|
||||
icon: 'power',
|
||||
dots: true,
|
||||
}
|
||||
|
||||
return {
|
||||
message: 'Connected',
|
||||
@@ -45,5 +64,8 @@ export class ConnectionBarComponent {
|
||||
}),
|
||||
)
|
||||
|
||||
constructor(private readonly connectionService: ConnectionService) {}
|
||||
constructor(
|
||||
private readonly connectionService: ConnectionService,
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,17 @@
|
||||
<ion-label class="label montserrat" routerLinkActive="label_selected">
|
||||
{{ page.title }}
|
||||
</ion-label>
|
||||
<ion-icon
|
||||
*ngIf="page.url === '/system' && (warning$ | async)"
|
||||
color="warning"
|
||||
size="small"
|
||||
name="warning"
|
||||
></ion-icon>
|
||||
<ion-icon
|
||||
*ngIf="page.url === '/system' && (showEOSUpdate$ | async)"
|
||||
color="success"
|
||||
size="small"
|
||||
name="rocket-outline"
|
||||
name="rocket"
|
||||
></ion-icon>
|
||||
<ion-badge
|
||||
*ngIf="page.url === '/updates' && (updateCount$ | async) as updateCount"
|
||||
|
||||
@@ -11,7 +11,9 @@ import {
|
||||
filter,
|
||||
first,
|
||||
map,
|
||||
merge,
|
||||
Observable,
|
||||
of,
|
||||
pairwise,
|
||||
startWith,
|
||||
switchMap,
|
||||
@@ -22,6 +24,7 @@ import { DataModel } from 'src/app/services/patch-db/data-model'
|
||||
import { SplitPaneTracker } from 'src/app/services/split-pane.service'
|
||||
import { Emver, THEME } from '@start9labs/shared'
|
||||
import { ConnectionService } from 'src/app/services/connection.service'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
|
||||
@Component({
|
||||
selector: 'app-menu',
|
||||
@@ -112,6 +115,11 @@ export class MenuComponent {
|
||||
|
||||
readonly theme$ = inject(THEME)
|
||||
|
||||
readonly warning$ = merge(
|
||||
of(this.config.isTorHttp()),
|
||||
this.patch.watch$('server-info', 'ntp-synced').pipe(map(synced => !synced)),
|
||||
)
|
||||
|
||||
constructor(
|
||||
private readonly patch: PatchDB<DataModel>,
|
||||
private readonly eosService: EOSService,
|
||||
@@ -120,5 +128,6 @@ export class MenuComponent {
|
||||
private readonly splitPane: SplitPaneTracker,
|
||||
private readonly emver: Emver,
|
||||
private readonly connectionService: ConnectionService,
|
||||
private readonly config: ConfigService,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -1,106 +1,102 @@
|
||||
<ion-grid class="grid-wiz">
|
||||
<img width="60px" height="60px" src="/assets/img/icon.png" alt="StartOS" />
|
||||
<ion-row>
|
||||
<ion-col class="ion-text-center">
|
||||
<div class="center-container">
|
||||
<ng-container *ngIf="!caTrusted; else trusted">
|
||||
<ion-card id="untrusted" class="text-center">
|
||||
<ion-icon name="lock-closed-outline" class="wiz-icon"></ion-icon>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-row>
|
||||
<ion-col class="ion-text-center">
|
||||
<h2><b>Trust your Root Certificate Authority (CA)</b></h2>
|
||||
<h1>Trust Your Root CA</h1>
|
||||
<p>
|
||||
Download and trust your server's Root CA to establish secure, encrypted
|
||||
(
|
||||
<b>HTTPS</b>
|
||||
) connections with your server
|
||||
Download and trust your server's Root Certificate Authority to establish
|
||||
a secure (HTTPS) connection. You will need to repeat this on every
|
||||
device you use to connect to your server.
|
||||
</p>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-row>
|
||||
<ion-col sizeXs="12" sizeLg="4">
|
||||
<div class="wiz-card">
|
||||
<ion-row class="ion-justify-content-between">
|
||||
<b class="wiz-step">1</b>
|
||||
<tui-tooltip
|
||||
content="Your server uses its Root CA to generate SSL/TLS certificates for itself and its installed services. These certificates are used to encrypt network traffic with your client devices."
|
||||
direction="right"
|
||||
></tui-tooltip>
|
||||
</ion-row>
|
||||
<div class="ion-text-center">
|
||||
<h2>Download Root CA</h2>
|
||||
<p>Download your server's Root CA</p>
|
||||
</div>
|
||||
<ion-button class="wiz-card-button" shape="round" (click)="download()">
|
||||
<ion-icon slot="start" name="download-outline"></ion-icon>
|
||||
Download
|
||||
</ion-button>
|
||||
</div>
|
||||
</ion-col>
|
||||
<ion-col sizeXs="12" sizeLg="4">
|
||||
<div class="wiz-card" [class.disabled]="!downloadClicked">
|
||||
<ion-row class="ion-justify-content-between">
|
||||
<b class="wiz-step">2</b>
|
||||
<tui-tooltip
|
||||
content="By trusting your server's Root CA, your device can verify the authenticity of its encrypted communications with your server and installed services. You will need to trust the Root CA on every device used to connect to your server."
|
||||
direction="right"
|
||||
></tui-tooltip>
|
||||
</ion-row>
|
||||
<div class="ion-text-center">
|
||||
<h2>Trust Root CA</h2>
|
||||
<p>Follow instructions for your OS</p>
|
||||
</div>
|
||||
<ion-button
|
||||
class="wiz-card-button"
|
||||
shape="round"
|
||||
(click)="instructions()"
|
||||
[disabled]="!downloadClicked"
|
||||
>
|
||||
View Docs
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</ion-col>
|
||||
<ion-col sizeXs="12" sizeLg="4">
|
||||
<div class="wiz-card" [class.disabled]="!polling && !caTrusted">
|
||||
<b class="wiz-step">3</b>
|
||||
<div class="ion-text-center">
|
||||
<h2>Go To Login</h2>
|
||||
<p *ngIf="instructionsClicked; else space" class="inline-center">
|
||||
<ion-spinner
|
||||
class="wiz-spinner"
|
||||
*ngIf="!caTrusted; else trusted"
|
||||
></ion-spinner>
|
||||
<ng-template #trusted>
|
||||
<ion-icon name="ribbon-outline" color="success"></ion-icon>
|
||||
</ng-template>
|
||||
{{ caTrusted ? 'Root CA trusted!' : 'Waiting for trust...' }}
|
||||
</p>
|
||||
<ng-template #space>
|
||||
<!-- to keep alignment -->
|
||||
<p><br /></p>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ion-button
|
||||
class="wiz-card-button"
|
||||
shape="round"
|
||||
(click)="launchHttps()"
|
||||
[disabled]="!caTrusted"
|
||||
>
|
||||
Open
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-row>
|
||||
<ion-col class="ion-text-center">
|
||||
<ol>
|
||||
<li>
|
||||
<b>Bookmark this page</b>
|
||||
- Save this page so you can access it later. You can also find the
|
||||
address in the
|
||||
<code>StartOS-info.html</code>
|
||||
file downloaded at the end of initial setup.
|
||||
</li>
|
||||
<li>
|
||||
<b>Download your server's Root CA</b>
|
||||
- Your server uses its Root CA to generate SSL/TLS certificates for
|
||||
itself and installed services. These certificates are then used to
|
||||
encrypt network traffic with your client devices.
|
||||
<br />
|
||||
<ion-button
|
||||
strong
|
||||
size="small"
|
||||
shape="round"
|
||||
color="tertiary"
|
||||
(click)="download()"
|
||||
>
|
||||
Download
|
||||
<ion-icon slot="end" name="download-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</li>
|
||||
<li>
|
||||
<b>Trust your server's Root CA</b>
|
||||
- Follow instructions for your OS. By trusting your server's Root CA,
|
||||
your device can verify the authenticity of encrypted communications
|
||||
with your server.
|
||||
<br />
|
||||
<ion-button
|
||||
strong
|
||||
size="small"
|
||||
shape="round"
|
||||
color="primary"
|
||||
href="https://docs.start9.com/0.3.5.x/user-manual/trust-ca#establishing-trust"
|
||||
target="_blank"
|
||||
noreferrer
|
||||
>
|
||||
View Instructions
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</li>
|
||||
<li>
|
||||
<b>Test</b>
|
||||
- Refresh the page. If refreshing the page does not work, you may need
|
||||
to quit and re-open your browser, then revisit this page.
|
||||
<br />
|
||||
<ion-button
|
||||
strong
|
||||
size="small"
|
||||
shape="round"
|
||||
class="refresh"
|
||||
(click)="refresh()"
|
||||
>
|
||||
Refresh
|
||||
<ion-icon slot="end" name="refresh"></ion-icon>
|
||||
</ion-button>
|
||||
</li>
|
||||
</ol>
|
||||
<ion-button fill="clear" (click)="launchHttps()" [disabled]="caTrusted">
|
||||
Skip
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
<span class="skip_detail">(not recommended)</span>
|
||||
</ion-card>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #trusted>
|
||||
<ion-card id="trusted" class="text-center">
|
||||
<ion-icon
|
||||
name="shield-checkmark-outline"
|
||||
class="wiz-icon"
|
||||
color="success"
|
||||
></ion-icon>
|
||||
<h1>Root CA Trusted!</h1>
|
||||
<p>
|
||||
You have successfully trusted your server's Root CA and may now log in
|
||||
securely.
|
||||
</p>
|
||||
<ion-button strong (click)="launchHttps()" color="tertiary" shape="round">
|
||||
Go to login
|
||||
<ion-icon slot="end" name="open-outline"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-card>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<a
|
||||
id="install-cert"
|
||||
href="/eos/local.crt"
|
||||
|
||||
@@ -1,44 +1,83 @@
|
||||
.grid-wiz {
|
||||
--ion-grid-padding: 36px;
|
||||
height: 100%
|
||||
#trusted {
|
||||
max-width: 40%;
|
||||
}
|
||||
|
||||
.wiz-icon {
|
||||
font-size: 84px;
|
||||
#untrusted {
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.wiz-card {
|
||||
.center-container {
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
ion-card {
|
||||
color: var(--ion-color-dark);
|
||||
background: #414141;
|
||||
margin: 24px;
|
||||
padding: 16px;
|
||||
height: 280px;
|
||||
border-radius: 16px;
|
||||
display: grid;
|
||||
box-shadow: 0 4px 4px rgba(17, 17, 17, 0.144);
|
||||
border-radius: 35px;
|
||||
padding: 1.5rem;
|
||||
width: 100%;
|
||||
|
||||
& h2 {
|
||||
font-weight: 600;
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 21px;
|
||||
line-height: 25px;
|
||||
margin-bottom: 30px;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wiz-card-button {
|
||||
justify-self: center;
|
||||
white-space: normal;
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wiz-spinner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
ol {
|
||||
font-size: 17px;
|
||||
line-height: 25px;
|
||||
text-align: left;
|
||||
|
||||
li {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
ion-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.disabled {
|
||||
filter: saturate(0.2) contrast(0.5)
|
||||
.refresh {
|
||||
--background: var(--ion-color-success-shade);
|
||||
}
|
||||
|
||||
.wiz-step {
|
||||
margin-top: 4px;
|
||||
.wiz-icon {
|
||||
font-size: 64px;
|
||||
}
|
||||
|
||||
.inline-center {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
.skip_detail {
|
||||
display: block;
|
||||
font-size: 0.8rem;
|
||||
margin-top: -13px;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
#trusted, #untrusted {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 701px) and (max-width: 1200px) {
|
||||
#trusted, #untrusted {
|
||||
max-width: 75%;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, Inject } from '@angular/core'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { ConfigService } from 'src/app/services/config.service'
|
||||
import { pauseFor, RELATIVE_URL } from '@start9labs/shared'
|
||||
import { RELATIVE_URL } from '@start9labs/shared'
|
||||
import { DOCUMENT } from '@angular/common'
|
||||
import { WINDOW } from '@ng-web-apis/common'
|
||||
|
||||
@@ -11,9 +11,6 @@ import { WINDOW } from '@ng-web-apis/common'
|
||||
styleUrls: ['./ca-wizard.component.scss'],
|
||||
})
|
||||
export class CAWizardComponent {
|
||||
downloadClicked = false
|
||||
instructionsClicked = false
|
||||
polling = false
|
||||
caTrusted = false
|
||||
|
||||
constructor(
|
||||
@@ -25,51 +22,27 @@ export class CAWizardComponent {
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
if (!this.config.isSecure()) {
|
||||
await this.testHttps().catch(e =>
|
||||
console.warn('Failed Https connection attempt'),
|
||||
)
|
||||
}
|
||||
await this.testHttps().catch(e =>
|
||||
console.warn('Failed Https connection attempt'),
|
||||
)
|
||||
}
|
||||
|
||||
download() {
|
||||
this.downloadClicked = true
|
||||
this.document.getElementById('install-cert')?.click()
|
||||
}
|
||||
|
||||
instructions() {
|
||||
this.windowRef.open(
|
||||
'https://docs.start9.com/0.3.5.x/getting-started/trust-ca/#trust-root-ca',
|
||||
'_blank',
|
||||
'noreferrer',
|
||||
)
|
||||
this.instructionsClicked = true
|
||||
this.startDaemon()
|
||||
}
|
||||
|
||||
private async startDaemon(): Promise<void> {
|
||||
this.polling = true
|
||||
while (this.polling) {
|
||||
try {
|
||||
await this.testHttps()
|
||||
this.polling = false
|
||||
} catch (e) {
|
||||
console.warn('Failed Https connection attempt')
|
||||
await pauseFor(2000)
|
||||
}
|
||||
}
|
||||
refresh() {
|
||||
this.document.location.reload()
|
||||
}
|
||||
|
||||
launchHttps() {
|
||||
const host = this.config.getHost()
|
||||
this.windowRef.open(`https://${host}`, '_blank', 'noreferrer')
|
||||
this.windowRef.open(`https://${host}`, '_self')
|
||||
}
|
||||
|
||||
private async testHttps() {
|
||||
const url = `https://${this.document.location.host}${this.relativeUrl}`
|
||||
await this.api.echo({ message: 'ping' }, url).then(() => {
|
||||
this.downloadClicked = true
|
||||
this.instructionsClicked = true
|
||||
this.caTrusted = true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,7 +11,17 @@
|
||||
<ion-icon slot="start" name="warning-outline"></ion-icon>
|
||||
<ion-label>
|
||||
<h2 style="font-weight: bold">Http detected</h2>
|
||||
<p style="font-weight: 600">Tor is faster over https.</p>
|
||||
<p style="font-weight: 600">
|
||||
Tor is faster over https. Your Root CA must be trusted.
|
||||
<a
|
||||
href="https://docs.start9.com/0.3.5.x/user-manual/trust-ca"
|
||||
target="_blank"
|
||||
noreferrer
|
||||
style="color: black"
|
||||
>
|
||||
View instructions
|
||||
</a>
|
||||
</p>
|
||||
</ion-label>
|
||||
<ion-button slot="end" color="light" (click)="launchHttps()">
|
||||
Open Https
|
||||
@@ -48,7 +58,6 @@
|
||||
[type]="unmasked ? 'text' : 'password'"
|
||||
[(ngModel)]="password"
|
||||
(ionChange)="error = ''"
|
||||
maxlength="64"
|
||||
></ion-input>
|
||||
<ion-button
|
||||
slot="end"
|
||||
|
||||
@@ -29,7 +29,7 @@ export class LoginPage {
|
||||
|
||||
launchHttps() {
|
||||
const host = this.config.getHost()
|
||||
this.windowRef.open(`https://${host}`, '_blank', 'noreferrer')
|
||||
this.windowRef.open(`https://${host}`, '_self')
|
||||
}
|
||||
|
||||
async submit() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.metric-note {
|
||||
ion-note {
|
||||
font-size: 16px;
|
||||
color: white;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export class AppShowStatusComponent {
|
||||
PrimaryStatus.Running,
|
||||
PrimaryStatus.Starting,
|
||||
PrimaryStatus.Restarting,
|
||||
].includes(this.status.primary)
|
||||
].includes(this.status.primary as PrimaryStatus)
|
||||
}
|
||||
|
||||
get isStopped(): boolean {
|
||||
|
||||
@@ -6,18 +6,25 @@
|
||||
[style.font-style]="style"
|
||||
[style.font-weight]="weight"
|
||||
>
|
||||
<span *ngIf="!installProgress">
|
||||
{{ (connected$ | async) ? rendering.display : 'Unknown' }}
|
||||
<span *ngIf="rendering.showDots" class="loading-dots"></span>
|
||||
{{ (connected$ | async) ? rendering.display : 'Unknown' }}
|
||||
|
||||
<span
|
||||
*ngIf="
|
||||
rendering.display === PR[PS.Stopping].display &&
|
||||
(sigtermTimeout | durationToSeconds) > 30
|
||||
"
|
||||
>
|
||||
this may take a while
|
||||
</span>
|
||||
|
||||
<span *ngIf="installProgress">
|
||||
<ion-text
|
||||
*ngIf="installProgress | installProgressDisplay as progress"
|
||||
color="primary"
|
||||
>
|
||||
Installing
|
||||
<span class="loading-dots"></span>
|
||||
{{ progress }}
|
||||
</ion-text>
|
||||
</span>
|
||||
|
||||
<span *ngIf="rendering.showDots" class="loading-dots"></span>
|
||||
</p>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<h2>
|
||||
For a secure local connection and faster Tor experience,
|
||||
<a
|
||||
href="https://docs.start9.com/0.3.5.x/getting-started/connecting-lan"
|
||||
href="https://docs.start9.com/0.3.5.x/user-manual/connecting-lan"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<logs
|
||||
[fetchLogs]="fetchLogs()"
|
||||
[followLogs]="followLogs()"
|
||||
context="eos"
|
||||
context="start-os"
|
||||
defaultBack="system"
|
||||
pageTitle="OS Logs"
|
||||
class="ion-page"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { Metrics } from 'src/app/services/api/api.types'
|
||||
import { ApiService } from 'src/app/services/api/embassy-api.service'
|
||||
import { TimeInfo, TimeService } from 'src/app/services/time-service'
|
||||
import { TimeService } from 'src/app/services/time-service'
|
||||
import {
|
||||
catchError,
|
||||
combineLatest,
|
||||
@@ -29,9 +29,24 @@ export class ServerMetricsPage {
|
||||
private readonly connectionService: ConnectionService,
|
||||
) {}
|
||||
|
||||
private getServerData$(): Observable<[TimeInfo, Metrics]> {
|
||||
private getServerData$(): Observable<
|
||||
[
|
||||
{
|
||||
value: number
|
||||
synced: boolean
|
||||
},
|
||||
{
|
||||
days: number
|
||||
hours: number
|
||||
minutes: number
|
||||
seconds: number
|
||||
},
|
||||
Metrics,
|
||||
]
|
||||
> {
|
||||
return combineLatest([
|
||||
this.timeService.getTimeInfo$(),
|
||||
this.timeService.now$,
|
||||
this.timeService.uptime$,
|
||||
this.getMetrics$(),
|
||||
]).pipe(
|
||||
catchError(() => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user