Merge branch 'staging' of github.com:FarmBot/farmbot_os into staging
This commit is contained in:
commit
da50938c3e
|
@ -1 +1,2 @@
|
|||
erlang 20.0
|
||||
elixir 1.5.0
|
||||
|
|
|
@ -27,17 +27,23 @@ config :farmbot, data_path: "/root"
|
|||
config :farmbot, :init, [
|
||||
# Load consolidated protocols
|
||||
Farmbot.Target.Protocols,
|
||||
|
||||
# Autodetects if a Arduino is plugged in and configures accordingly.
|
||||
Farmbot.Firmware.UartHandler.AutoDetector,
|
||||
|
||||
Farmbot.Target.ConfigMigration.BeforeNetwork,
|
||||
|
||||
# Allows for first boot configuration.
|
||||
Farmbot.Target.Bootstrap.Configurator,
|
||||
|
||||
# Start up Network
|
||||
Farmbot.Target.Network,
|
||||
|
||||
# Wait for time time come up.
|
||||
Farmbot.Target.Network.WaitForTime,
|
||||
|
||||
Farmbot.Target.ConfigMigration.AfterNetwork,
|
||||
|
||||
# Debug stuff
|
||||
Farmbot.System.Debug,
|
||||
Farmbot.Target.Uevent.Supervisor
|
||||
|
@ -78,3 +84,7 @@ config :nerves_init_gadget,
|
|||
config :bootloader,
|
||||
init: [:nerves_runtime, :nerves_init_gadget],
|
||||
app: :farmbot
|
||||
|
||||
if Mix.Project.config[:target] == "rpi3" do
|
||||
config :nerves, :firmware, fwup_conf: "fwup_interim.conf"
|
||||
end
|
||||
|
|
518
fwup_interim.conf
Normal file
518
fwup_interim.conf
Normal file
|
@ -0,0 +1,518 @@
|
|||
# Firmware configuration file for the Raspberry Pi 3
|
||||
|
||||
#
|
||||
# Firmware metadata
|
||||
#
|
||||
|
||||
# All of these can be overriden using environment variables of the same name.
|
||||
#
|
||||
# Run 'fwup -m' to query values in a .fw file.
|
||||
# Use 'fw_printenv' to query values on the target.
|
||||
#
|
||||
# These are used by Nerves libraries to introspect.
|
||||
define(NERVES_FW_PRODUCT, "Nerves Firmware")
|
||||
define(NERVES_FW_DESCRIPTION, "")
|
||||
define(NERVES_FW_VERSION, "${NERVES_SDK_VERSION}")
|
||||
define(NERVES_FW_PLATFORM, "rpi3")
|
||||
define(NERVES_FW_ARCHITECTURE, "arm")
|
||||
define(NERVES_FW_AUTHOR, "The Nerves Team")
|
||||
|
||||
define(NERVES_FW_DEVPATH, "/dev/mmcblk0")
|
||||
define(NERVES_FW_APPLICATION_PART0_DEVPATH, "/dev/mmcblk0p3") # Linux part number is 1-based
|
||||
define(NERVES_FW_APPLICATION_PART0_FSTYPE, "ext4")
|
||||
define(NERVES_FW_APPLICATION_PART0_TARGET, "/root")
|
||||
|
||||
# Default paths if not specified via the commandline
|
||||
define(ROOTFS, "${NERVES_SYSTEM}/images/rootfs.squashfs")
|
||||
|
||||
# This configuration file will create an image that has an MBR and the
|
||||
# following 3 partitions:
|
||||
#
|
||||
# +----------------------------+
|
||||
# | MBR |
|
||||
# +----------------------------+
|
||||
# | Firmware configuration data|
|
||||
# | (formatted as uboot env) |
|
||||
# +----------------------------+
|
||||
# | p0*: Boot A (FAT32) |
|
||||
# | zImage, bootcode.bin, |
|
||||
# | config.txt, etc. |
|
||||
# +----------------------------+
|
||||
# | p0*: Boot B (FAT32) |
|
||||
# +----------------------------+
|
||||
# | p1*: Rootfs A (squashfs) |
|
||||
# +----------------------------+
|
||||
# | p1*: Rootfs B (squashfs) |
|
||||
# +----------------------------+
|
||||
# | p2: Application (ext4) |
|
||||
# +----------------------------+
|
||||
#
|
||||
# The p0/p1 partition points to whichever of configurations A or B that is
|
||||
# active.
|
||||
#
|
||||
# The image is sized to be less than 1 GB so that it fits on nearly any SDCard
|
||||
# around. If you have a larger SDCard and need more space, feel free to bump
|
||||
# the partition sizes below.
|
||||
|
||||
# The Raspberry Pi is incredibly picky on the partition sizes and in ways that
|
||||
# I don't understand. Test changes one at a time to make sure that they boot.
|
||||
# (Sizes are in 512 byte blocks)
|
||||
define(UBOOT_ENV_OFFSET, 16)
|
||||
define(UBOOT_ENV_COUNT, 16) # 8 KB
|
||||
|
||||
define(BOOT_A_PART_OFFSET, 63)
|
||||
define(BOOT_A_PART_COUNT, 38630)
|
||||
define-eval(BOOT_B_PART_OFFSET, "${BOOT_A_PART_OFFSET} + ${BOOT_A_PART_COUNT}")
|
||||
define(BOOT_B_PART_COUNT, ${BOOT_A_PART_COUNT})
|
||||
|
||||
# Let the rootfs have room to grow up to 128 MiB and align it to the nearest 1
|
||||
# MB boundary
|
||||
define(ROOTFS_A_PART_OFFSET, 77324)
|
||||
define(ROOTFS_A_PART_COUNT, 289044)
|
||||
define-eval(ROOTFS_B_PART_OFFSET, "${ROOTFS_A_PART_OFFSET} + ${ROOTFS_A_PART_COUNT}")
|
||||
define(ROOTFS_B_PART_COUNT, ${ROOTFS_A_PART_COUNT})
|
||||
|
||||
# Application partition. This partition can occupy all of the remaining space.
|
||||
# Size it to fit the destination.
|
||||
define-eval(APP_PART_OFFSET, "${ROOTFS_B_PART_OFFSET} + ${ROOTFS_B_PART_COUNT}")
|
||||
define(APP_PART_COUNT, 1048576)
|
||||
|
||||
# Firmware archive metadata
|
||||
meta-product = ${NERVES_FW_PRODUCT}
|
||||
meta-description = ${NERVES_FW_DESCRIPTION}
|
||||
meta-version = ${NERVES_FW_VERSION}
|
||||
meta-platform = ${NERVES_FW_PLATFORM}
|
||||
meta-architecture = ${NERVES_FW_ARCHITECTURE}
|
||||
meta-author = ${NERVES_FW_AUTHOR}
|
||||
meta-vcs-identifier = ${NERVES_FW_VCS_IDENTIFIER}
|
||||
meta-misc = ${NERVES_FW_MISC}
|
||||
|
||||
# File resources are listed in the order that they are included in the .fw file
|
||||
# This is important, since this is the order that they're written on a firmware
|
||||
# update due to the event driven nature of the update system.
|
||||
file-resource bootcode.bin {
|
||||
host-path = "${NERVES_SYSTEM}/images/rpi-firmware/bootcode.bin"
|
||||
}
|
||||
file-resource fixup.dat {
|
||||
host-path = "${NERVES_SYSTEM}/images/rpi-firmware/fixup.dat"
|
||||
}
|
||||
file-resource start.elf {
|
||||
host-path = "${NERVES_SYSTEM}/images/rpi-firmware/start.elf"
|
||||
}
|
||||
file-resource config.txt {
|
||||
host-path = "${NERVES_SYSTEM}/images/config.txt"
|
||||
}
|
||||
file-resource cmdline.txt {
|
||||
host-path = "${NERVES_SYSTEM}/images/cmdline.txt"
|
||||
}
|
||||
file-resource zImage {
|
||||
host-path = "${NERVES_SYSTEM}/images/zImage"
|
||||
}
|
||||
file-resource bcm2710-rpi-3-b.dtb {
|
||||
host-path = "${NERVES_SYSTEM}/images/bcm2710-rpi-3-b.dtb"
|
||||
}
|
||||
file-resource bcm2710-rpi-cm3.dtb {
|
||||
host-path = "${NERVES_SYSTEM}/images/bcm2710-rpi-cm3.dtb"
|
||||
}
|
||||
file-resource w1-gpio-pullup.dtbo {
|
||||
host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/w1-gpio-pullup.dtbo"
|
||||
}
|
||||
|
||||
file-resource rootfs.img {
|
||||
host-path = ${ROOTFS}
|
||||
|
||||
# Error out if the rootfs size exceeds the partition size
|
||||
assert-size-lte = ${ROOTFS_A_PART_COUNT}
|
||||
}
|
||||
|
||||
mbr mbr-a {
|
||||
partition 0 {
|
||||
block-offset = ${BOOT_A_PART_OFFSET}
|
||||
block-count = ${BOOT_A_PART_COUNT}
|
||||
type = 0xc # FAT32
|
||||
boot = true
|
||||
}
|
||||
partition 1 {
|
||||
block-offset = ${ROOTFS_A_PART_OFFSET}
|
||||
block-count = ${ROOTFS_A_PART_COUNT}
|
||||
type = 0x83 # Linux
|
||||
}
|
||||
partition 2 {
|
||||
block-offset = ${APP_PART_OFFSET}
|
||||
block-count = ${APP_PART_COUNT}
|
||||
type = 0x83 # Linux
|
||||
}
|
||||
# partition 3 is unused
|
||||
}
|
||||
|
||||
mbr mbr-b {
|
||||
partition 0 {
|
||||
block-offset = ${BOOT_B_PART_OFFSET}
|
||||
block-count = ${BOOT_B_PART_COUNT}
|
||||
type = 0xc # FAT32
|
||||
boot = true
|
||||
}
|
||||
partition 1 {
|
||||
block-offset = ${ROOTFS_B_PART_OFFSET}
|
||||
block-count = ${ROOTFS_B_PART_COUNT}
|
||||
type = 0x83 # Linux
|
||||
}
|
||||
partition 2 {
|
||||
block-offset = ${APP_PART_OFFSET}
|
||||
block-count = ${APP_PART_COUNT}
|
||||
type = 0x83 # Linux
|
||||
}
|
||||
# partition 3 is unused
|
||||
}
|
||||
|
||||
# Location where installed firmware information is stored.
|
||||
# While this is called "u-boot", u-boot isn't involved in this
|
||||
# setup. It just provides a convenient key/value store format.
|
||||
uboot-environment uboot-env {
|
||||
block-offset = ${UBOOT_ENV_OFFSET}
|
||||
block-count = ${UBOOT_ENV_COUNT}
|
||||
}
|
||||
|
||||
# This firmware task writes everything to the destination media
|
||||
task complete {
|
||||
# Only match if not mounted
|
||||
require-unmounted-destination = true
|
||||
|
||||
on-init {
|
||||
mbr_write(mbr-a)
|
||||
|
||||
uboot_clearenv(uboot-env)
|
||||
uboot_setenv(uboot-env, "nerves_fw_active", "a")
|
||||
uboot_setenv(uboot-env, "nerves_fw_devpath", ${NERVES_FW_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_product", ${NERVES_FW_PRODUCT})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_version", ${NERVES_FW_VERSION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_platform", ${NERVES_FW_PLATFORM})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_author", ${NERVES_FW_AUTHOR})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_misc", ${NERVES_FW_MISC})
|
||||
|
||||
fat_mkfs(${BOOT_A_PART_OFFSET}, ${BOOT_A_PART_COUNT})
|
||||
fat_setlabel(${BOOT_A_PART_OFFSET}, "BOOT-A")
|
||||
fat_mkdir(${BOOT_A_PART_OFFSET}, "overlays")
|
||||
}
|
||||
|
||||
on-resource config.txt { fat_write(${BOOT_A_PART_OFFSET}, "config.txt") }
|
||||
on-resource cmdline.txt { fat_write(${BOOT_A_PART_OFFSET}, "cmdline.txt") }
|
||||
on-resource bootcode.bin { fat_write(${BOOT_A_PART_OFFSET}, "bootcode.bin") }
|
||||
on-resource start.elf { fat_write(${BOOT_A_PART_OFFSET}, "start.elf") }
|
||||
on-resource fixup.dat { fat_write(${BOOT_A_PART_OFFSET}, "fixup.dat") }
|
||||
on-resource zImage { fat_write(${BOOT_A_PART_OFFSET}, "zImage") }
|
||||
on-resource bcm2710-rpi-3-b.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2710-rpi-3-b.dtb") }
|
||||
on-resource bcm2710-rpi-cm3.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2710-rpi-cm3.dtb") }
|
||||
on-resource w1-gpio-pullup.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/w1-gpio-pullup.dtbo") }
|
||||
|
||||
on-resource rootfs.img {
|
||||
# write to the first rootfs partition
|
||||
raw_write(${ROOTFS_A_PART_OFFSET})
|
||||
}
|
||||
|
||||
on-finish {
|
||||
# Clear out any old data in the B partition that might be mistaken for
|
||||
# a file system. This is mostly to avoid confusion in humans when
|
||||
# reprogramming SDCards with unknown contents.
|
||||
raw_memset(${BOOT_B_PART_OFFSET}, 256, 0xff)
|
||||
raw_memset(${ROOTFS_B_PART_OFFSET}, 256, 0xff)
|
||||
|
||||
# Invalidate the application data partition so that it is guaranteed to
|
||||
# trigger the corrupt filesystem detection code on first boot and get
|
||||
# formatted. If this isn't done and an old SDCard is reused, the
|
||||
# application data could be in a weird state.
|
||||
raw_memset(${APP_PART_OFFSET}, 256, 0xff)
|
||||
}
|
||||
}
|
||||
|
||||
task upgrade.a {
|
||||
# This task upgrades the A partition
|
||||
require-partition-offset(1, ${ROOTFS_B_PART_OFFSET})
|
||||
|
||||
on-init {
|
||||
info("Initializing u-boot")
|
||||
uboot_clearenv(uboot-env)
|
||||
uboot_setenv(uboot-env, "nerves_fw_active", "a")
|
||||
uboot_setenv(uboot-env, "nerves_fw_devpath", ${NERVES_FW_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_product", ${NERVES_FW_PRODUCT})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_version", ${NERVES_FW_VERSION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_platform", ${NERVES_FW_PLATFORM})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_author", ${NERVES_FW_AUTHOR})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_misc", ${NERVES_FW_MISC})
|
||||
|
||||
info("Upgrading partition A")
|
||||
|
||||
# Clear some firmware information just in case this update gets
|
||||
# interrupted midway.
|
||||
uboot_unsetenv(uboot-env, "a.nerves_fw_version")
|
||||
|
||||
# Reset the previous contents of the A boot partition
|
||||
fat_mkfs(${BOOT_A_PART_OFFSET}, ${BOOT_A_PART_COUNT})
|
||||
fat_setlabel(${BOOT_A_PART_OFFSET}, "BOOT-A")
|
||||
fat_mkdir(${BOOT_A_PART_OFFSET}, "overlays")
|
||||
|
||||
# Indicate that the entire partition can be cleared
|
||||
trim(${ROOTFS_A_PART_OFFSET}, ${ROOTFS_A_PART_COUNT})
|
||||
}
|
||||
|
||||
# Write the new boot partition files and rootfs. The MBR still points
|
||||
# to the B partition, so an error or power failure during this part
|
||||
# won't hurt anything.
|
||||
on-resource config.txt { fat_write(${BOOT_A_PART_OFFSET}, "config.txt") }
|
||||
on-resource cmdline.txt { fat_write(${BOOT_A_PART_OFFSET}, "cmdline.txt") }
|
||||
on-resource bootcode.bin { fat_write(${BOOT_A_PART_OFFSET}, "bootcode.bin") }
|
||||
on-resource start.elf { fat_write(${BOOT_A_PART_OFFSET}, "start.elf") }
|
||||
on-resource fixup.dat { fat_write(${BOOT_A_PART_OFFSET}, "fixup.dat") }
|
||||
on-resource zImage { fat_write(${BOOT_A_PART_OFFSET}, "zImage") }
|
||||
on-resource bcm2710-rpi-3-b.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2710-rpi-3-b.dtb") }
|
||||
on-resource bcm2710-rpi-cm3.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2710-rpi-cm3.dtb") }
|
||||
on-resource w1-gpio-pullup.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/w1-gpio-pullup.dtbo") }
|
||||
on-resource rootfs.img { raw_write(${ROOTFS_A_PART_OFFSET}) }
|
||||
|
||||
on-finish {
|
||||
# Update firmware metadata
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_product", ${NERVES_FW_PRODUCT})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_version", ${NERVES_FW_VERSION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_platform", ${NERVES_FW_PLATFORM})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_author", ${NERVES_FW_AUTHOR})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_misc", ${NERVES_FW_MISC})
|
||||
|
||||
# Switch over to boot the new firmware
|
||||
uboot_setenv(uboot-env, "nerves_fw_active", "a")
|
||||
mbr_write(mbr-a)
|
||||
}
|
||||
|
||||
on-error {
|
||||
}
|
||||
}
|
||||
|
||||
task upgrade.b {
|
||||
# This task upgrades the B partition
|
||||
require-partition-offset(1, ${ROOTFS_A_PART_OFFSET})
|
||||
|
||||
on-init {
|
||||
info("Initializing u-boot")
|
||||
uboot_clearenv(uboot-env)
|
||||
uboot_setenv(uboot-env, "nerves_fw_active", "a")
|
||||
uboot_setenv(uboot-env, "nerves_fw_devpath", ${NERVES_FW_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_product", ${NERVES_FW_PRODUCT})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_version", ${NERVES_FW_VERSION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_platform", ${NERVES_FW_PLATFORM})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_author", ${NERVES_FW_AUTHOR})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_misc", ${NERVES_FW_MISC})
|
||||
info("Upgrading partition B")
|
||||
|
||||
# Clear some firmware information just in case this update gets
|
||||
# interrupted midway.
|
||||
uboot_unsetenv(uboot-env, "b.nerves_fw_version")
|
||||
|
||||
# Reset the previous contents of the B boot partition
|
||||
fat_mkfs(${BOOT_B_PART_OFFSET}, ${BOOT_B_PART_COUNT})
|
||||
fat_setlabel(${BOOT_B_PART_OFFSET}, "BOOT-B")
|
||||
fat_mkdir(${BOOT_B_PART_OFFSET}, "overlays")
|
||||
|
||||
trim(${ROOTFS_B_PART_OFFSET}, ${ROOTFS_B_PART_COUNT})
|
||||
}
|
||||
|
||||
# Write the new boot partition files and rootfs. The MBR still points
|
||||
# to the A partition, so an error or power failure during this part
|
||||
# won't hurt anything.
|
||||
on-resource config.txt { fat_write(${BOOT_B_PART_OFFSET}, "config.txt") }
|
||||
on-resource cmdline.txt { fat_write(${BOOT_B_PART_OFFSET}, "cmdline.txt") }
|
||||
on-resource bootcode.bin { fat_write(${BOOT_B_PART_OFFSET}, "bootcode.bin") }
|
||||
on-resource start.elf { fat_write(${BOOT_B_PART_OFFSET}, "start.elf") }
|
||||
on-resource fixup.dat { fat_write(${BOOT_B_PART_OFFSET}, "fixup.dat") }
|
||||
on-resource zImage { fat_write(${BOOT_B_PART_OFFSET}, "zImage") }
|
||||
on-resource bcm2710-rpi-3-b.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2710-rpi-3-b.dtb") }
|
||||
on-resource bcm2710-rpi-cm3.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2710-rpi-cm3.dtb") }
|
||||
on-resource w1-gpio-pullup.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/w1-gpio-pullup.dtbo") }
|
||||
on-resource rootfs.img { raw_write(${ROOTFS_B_PART_OFFSET}) }
|
||||
|
||||
on-finish {
|
||||
# Update firmware metadata
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_product", ${NERVES_FW_PRODUCT})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_version", ${NERVES_FW_VERSION})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_platform", ${NERVES_FW_PLATFORM})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_author", ${NERVES_FW_AUTHOR})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_misc", ${NERVES_FW_MISC})
|
||||
|
||||
# Switch over to boot the new firmware
|
||||
uboot_setenv(uboot-env, "nerves_fw_active", "b")
|
||||
mbr_write(mbr-b)
|
||||
}
|
||||
|
||||
on-error {
|
||||
}
|
||||
}
|
||||
|
||||
task upgrade.unexpected {
|
||||
# This task upgrades the B partition
|
||||
require-partition-offset(1, ${ROOTFS_A_PART_OFFSET})
|
||||
|
||||
on-init {
|
||||
info("Initializing u-boot")
|
||||
uboot_clearenv(uboot-env)
|
||||
uboot_setenv(uboot-env, "nerves_fw_active", "a")
|
||||
uboot_setenv(uboot-env, "nerves_fw_devpath", ${NERVES_FW_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_product", ${NERVES_FW_PRODUCT})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_version", ${NERVES_FW_VERSION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_platform", ${NERVES_FW_PLATFORM})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_author", ${NERVES_FW_AUTHOR})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_misc", ${NERVES_FW_MISC})
|
||||
info("Upgrading partition B")
|
||||
|
||||
# Clear some firmware information just in case this update gets
|
||||
# interrupted midway.
|
||||
uboot_unsetenv(uboot-env, "b.nerves_fw_version")
|
||||
|
||||
# Reset the previous contents of the B boot partition
|
||||
fat_mkfs(${BOOT_B_PART_OFFSET}, ${BOOT_B_PART_COUNT})
|
||||
fat_setlabel(${BOOT_B_PART_OFFSET}, "BOOT-B")
|
||||
fat_mkdir(${BOOT_B_PART_OFFSET}, "overlays")
|
||||
|
||||
trim(${ROOTFS_B_PART_OFFSET}, ${ROOTFS_B_PART_COUNT})
|
||||
}
|
||||
|
||||
# Write the new boot partition files and rootfs. The MBR still points
|
||||
# to the A partition, so an error or power failure during this part
|
||||
# won't hurt anything.
|
||||
on-resource config.txt { fat_write(${BOOT_B_PART_OFFSET}, "config.txt") }
|
||||
on-resource cmdline.txt { fat_write(${BOOT_B_PART_OFFSET}, "cmdline.txt") }
|
||||
on-resource bootcode.bin { fat_write(${BOOT_B_PART_OFFSET}, "bootcode.bin") }
|
||||
on-resource start.elf { fat_write(${BOOT_B_PART_OFFSET}, "start.elf") }
|
||||
on-resource fixup.dat { fat_write(${BOOT_B_PART_OFFSET}, "fixup.dat") }
|
||||
on-resource zImage { fat_write(${BOOT_B_PART_OFFSET}, "zImage") }
|
||||
on-resource bcm2710-rpi-3-b.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2710-rpi-3-b.dtb") }
|
||||
on-resource bcm2710-rpi-cm3.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2710-rpi-cm3.dtb") }
|
||||
on-resource w1-gpio-pullup.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/w1-gpio-pullup.dtbo") }
|
||||
on-resource rootfs.img { raw_write(${ROOTFS_B_PART_OFFSET}) }
|
||||
|
||||
on-finish {
|
||||
# Update firmware metadata
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_product", ${NERVES_FW_PRODUCT})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_version", ${NERVES_FW_VERSION})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_platform", ${NERVES_FW_PLATFORM})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_author", ${NERVES_FW_AUTHOR})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_misc", ${NERVES_FW_MISC})
|
||||
|
||||
# Switch over to boot the new firmware
|
||||
uboot_setenv(uboot-env, "nerves_fw_active", "b")
|
||||
mbr_write(mbr-b)
|
||||
}
|
||||
|
||||
on-error {
|
||||
}
|
||||
}
|
||||
|
||||
task upgrade.wrongplatform {
|
||||
# This task upgrades the B partition
|
||||
require-partition-offset(1, ${ROOTFS_A_PART_OFFSET})
|
||||
|
||||
on-init {
|
||||
info("Initializing u-boot")
|
||||
uboot_clearenv(uboot-env)
|
||||
uboot_setenv(uboot-env, "nerves_fw_active", "a")
|
||||
uboot_setenv(uboot-env, "nerves_fw_devpath", ${NERVES_FW_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_product", ${NERVES_FW_PRODUCT})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_version", ${NERVES_FW_VERSION})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_platform", ${NERVES_FW_PLATFORM})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_author", ${NERVES_FW_AUTHOR})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
|
||||
uboot_setenv(uboot-env, "a.nerves_fw_misc", ${NERVES_FW_MISC})
|
||||
info("Upgrading partition B")
|
||||
|
||||
# Clear some firmware information just in case this update gets
|
||||
# interrupted midway.
|
||||
uboot_unsetenv(uboot-env, "b.nerves_fw_version")
|
||||
|
||||
# Reset the previous contents of the B boot partition
|
||||
fat_mkfs(${BOOT_B_PART_OFFSET}, ${BOOT_B_PART_COUNT})
|
||||
fat_setlabel(${BOOT_B_PART_OFFSET}, "BOOT-B")
|
||||
fat_mkdir(${BOOT_B_PART_OFFSET}, "overlays")
|
||||
|
||||
trim(${ROOTFS_B_PART_OFFSET}, ${ROOTFS_B_PART_COUNT})
|
||||
}
|
||||
|
||||
# Write the new boot partition files and rootfs. The MBR still points
|
||||
# to the A partition, so an error or power failure during this part
|
||||
# won't hurt anything.
|
||||
on-resource config.txt { fat_write(${BOOT_B_PART_OFFSET}, "config.txt") }
|
||||
on-resource cmdline.txt { fat_write(${BOOT_B_PART_OFFSET}, "cmdline.txt") }
|
||||
on-resource bootcode.bin { fat_write(${BOOT_B_PART_OFFSET}, "bootcode.bin") }
|
||||
on-resource start.elf { fat_write(${BOOT_B_PART_OFFSET}, "start.elf") }
|
||||
on-resource fixup.dat { fat_write(${BOOT_B_PART_OFFSET}, "fixup.dat") }
|
||||
on-resource zImage { fat_write(${BOOT_B_PART_OFFSET}, "zImage") }
|
||||
on-resource bcm2710-rpi-3-b.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2710-rpi-3-b.dtb") }
|
||||
on-resource bcm2710-rpi-cm3.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2710-rpi-cm3.dtb") }
|
||||
on-resource w1-gpio-pullup.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/w1-gpio-pullup.dtbo") }
|
||||
on-resource rootfs.img { raw_write(${ROOTFS_B_PART_OFFSET}) }
|
||||
|
||||
on-finish {
|
||||
# Update firmware metadata
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_product", ${NERVES_FW_PRODUCT})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_version", ${NERVES_FW_VERSION})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_platform", ${NERVES_FW_PLATFORM})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_author", ${NERVES_FW_AUTHOR})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
|
||||
uboot_setenv(uboot-env, "b.nerves_fw_misc", ${NERVES_FW_MISC})
|
||||
|
||||
# Switch over to boot the new firmware
|
||||
uboot_setenv(uboot-env, "nerves_fw_active", "b")
|
||||
mbr_write(mbr-b)
|
||||
}
|
||||
|
||||
on-error {
|
||||
}
|
||||
}
|
|
@ -24,23 +24,44 @@ defmodule Farmbot.Bootstrap.Authorization do
|
|||
# this is the default authorize implementation.
|
||||
# It gets overwrote in the Test Environment.
|
||||
@doc "Authorizes with the farmbot api."
|
||||
def authorize(email, password, server) do
|
||||
with {:ok, rsa_key} <- fetch_rsa_key(server),
|
||||
{:ok, payload} <- build_payload(email, password, rsa_key),
|
||||
{:ok, resp} <- request_token(server, payload),
|
||||
{:ok, body} <- Poison.decode(resp),
|
||||
{:ok, map} <- Map.fetch(body, "token") do
|
||||
Map.fetch(map, "encoded")
|
||||
else
|
||||
:error -> {:error, "unknown error."}
|
||||
{:error, :invalid, _} -> authorize(email, password, server)
|
||||
# If we got maintance mode, a 5xx error etc, just sleep for a few seconds
|
||||
# and try again.
|
||||
{:ok, {{_, code, _}, _, _}} ->
|
||||
Logger.error 1, "Failed to authorize due to server error: #{code}"
|
||||
Process.sleep(5000)
|
||||
authorize(email, password, server)
|
||||
err -> err
|
||||
def authorize(email, password_or_secret, server) do
|
||||
case Farmbot.System.ConfigStorage.get_config_value(:bool, "settings", "first_boot") do
|
||||
true ->
|
||||
with {:ok, rsa_key} <- fetch_rsa_key(server),
|
||||
{:ok, payload} <- build_payload(email, password_or_secret, rsa_key),
|
||||
{:ok, resp} <- request_token(server, payload),
|
||||
{:ok, body} <- Poison.decode(resp),
|
||||
{:ok, map} <- Map.fetch(body, "token") do
|
||||
Farmbot.System.ConfigStorage.update_config_value(:bool, "settings", "first_boot", false)
|
||||
Map.fetch(map, "encoded")
|
||||
else
|
||||
:error -> {:error, "unknown error."}
|
||||
{:error, :invalid, _} -> authorize(email, password_or_secret, server)
|
||||
# If we got maintance mode, a 5xx error etc, just sleep for a few seconds
|
||||
# and try again.
|
||||
{:ok, {{_, code, _}, _, _}} ->
|
||||
Logger.error 1, "Failed to authorize due to server error: #{code}"
|
||||
Process.sleep(5000)
|
||||
authorize(email, password_or_secret, server)
|
||||
err -> err
|
||||
end
|
||||
false ->
|
||||
with {:ok, payload} <- build_payload(password_or_secret),
|
||||
{:ok, resp} <- request_token(server, payload),
|
||||
{:ok, body} <- Poison.decode(resp),
|
||||
{:ok, map} <- Map.fetch(body, "token") do
|
||||
Map.fetch(map, "encoded")
|
||||
else
|
||||
:error -> {:error, "unknown error."}
|
||||
{:error, :invalid, _} -> authorize(email, password_or_secret, server)
|
||||
# If we got maintance mode, a 5xx error etc, just sleep for a few seconds
|
||||
# and try again.
|
||||
{:ok, {{_, code, _}, _, _}} ->
|
||||
Logger.error 1, "Failed to authorize due to server error: #{code}"
|
||||
Process.sleep(5000)
|
||||
authorize(email, password_or_secret, server)
|
||||
err -> err
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -56,9 +77,14 @@ defmodule Farmbot.Bootstrap.Authorization do
|
|||
%{email: email, password: password, id: UUID.uuid1(), version: 1}
|
||||
|> Poison.encode!()
|
||||
|> RSA.encrypt({:public, rsa_key})
|
||||
|> Base.encode64()
|
||||
Farmbot.System.ConfigStorage.update_config_value(:string, "authorization", "password", secret)
|
||||
|
||||
%{user: %{credentials: secret}} |> Poison.encode()
|
||||
%{user: %{credentials: secret |> Base.encode64()}} |> Poison.encode()
|
||||
end
|
||||
|
||||
defp build_payload(secret) do
|
||||
user = %{credentials: secret |> :base64.encode_to_string |> to_string}
|
||||
Poison.encode(%{user: user})
|
||||
end
|
||||
|
||||
defp request_token(server, payload) do
|
||||
|
|
|
@ -223,9 +223,11 @@ defmodule Farmbot.BotState do
|
|||
end
|
||||
|
||||
def init([]) do
|
||||
settings = Farmbot.System.ConfigStorage.get_config_as_map()["settings"]
|
||||
user_env = Poison.decode!(settings["user_env"])
|
||||
{
|
||||
:producer_consumer,
|
||||
struct(__MODULE__, configuration: Farmbot.System.ConfigStorage.get_config_as_map()["settings"]),
|
||||
struct(__MODULE__, configuration: Map.delete(settings, "user_env"), user_env: user_env),
|
||||
subscribe_to: [Farmbot.Firmware, Farmbot.System.ConfigStorage.Dispatcher],
|
||||
dispatcher: GenStage.BroadcastDispatcher
|
||||
}
|
||||
|
@ -272,9 +274,13 @@ defmodule Farmbot.BotState do
|
|||
end
|
||||
|
||||
def handle_call({:set_user_env, key, val}, _, state) do
|
||||
new_user_env = Map.put(state.user_env, key, val)
|
||||
new_state = %{state | user_env: new_user_env}
|
||||
{:reply, :ok, [new_state], new_state}
|
||||
new_user_env = Map.merge(state.user_env, %{to_string(key) => val})
|
||||
case Poison.encode(new_user_env) do
|
||||
{:ok, encoded} ->
|
||||
Farmbot.System.ConfigStorage.update_config_value(:string, "settings", "user_env", encoded)
|
||||
{:reply, :ok, [], state}
|
||||
_ -> {:reply, :error, [], state}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call(:get_user_env, _from, state) do
|
||||
|
@ -325,6 +331,12 @@ defmodule Farmbot.BotState do
|
|||
|
||||
defp do_handle([], state), do: state
|
||||
|
||||
defp do_handle([{:config, "settings", "user_env", val} | rest], state) do
|
||||
new_env = Map.merge(state.user_env, Poison.decode!(val))
|
||||
new_state = %{state | user_env: new_env}
|
||||
do_handle(rest, new_state)
|
||||
end
|
||||
|
||||
defp do_handle([{:config, "settings", key, val} | rest], state) do
|
||||
new_config = Map.put(state.configuration, key, val)
|
||||
new_state = %{state | configuration: new_config}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule Farmbot.Firmware.UartHandler.Update do
|
||||
@moduledoc false
|
||||
use Farmbot.Logger
|
||||
|
||||
def maybe_update_firmware(hardware \\ nil) do
|
||||
tty = Application.get_all_env(:farmbot)[:uart_handler][:tty]
|
||||
hardware = case hardware do
|
||||
|
|
|
@ -7,7 +7,8 @@ defmodule Farmbot.Jwt do
|
|||
:iss,
|
||||
:mqtt,
|
||||
:vhost,
|
||||
:os_update_server
|
||||
:os_update_server,
|
||||
:interim_email
|
||||
]
|
||||
|
||||
@typedoc "Type def for Farmbot Web Token."
|
||||
|
@ -18,6 +19,7 @@ defmodule Farmbot.Jwt do
|
|||
mqtt: binary,
|
||||
os_update_server: binary,
|
||||
vhost: binary,
|
||||
interim_email: binary
|
||||
}
|
||||
|
||||
@doc "Decode a token."
|
||||
|
|
|
@ -117,7 +117,7 @@ defmodule Farmbot.System.Updates.SlackUpdater do
|
|||
end
|
||||
|
||||
def init(nil) do
|
||||
Logger.warn(3, "Not setting up slack (No token)")
|
||||
Logger.warn(3, "Not setting up slack (No slack token)")
|
||||
:ignore
|
||||
end
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@ defmodule Farmbot.Host.Bootstrap.Configurator do
|
|||
pass = Application.get_env(:farmbot, :authorization)[:password] || raise error("password")
|
||||
server = Application.get_env(:farmbot, :authorization)[:server] || raise error("server")
|
||||
ConfigStorage.update_config_value(:string, "authorization", "email", email)
|
||||
ConfigStorage.update_config_value(:string, "authorization", "password", pass)
|
||||
if ConfigStorage.get_config_value(:bool, "settings", "first_boot") do
|
||||
ConfigStorage.update_config_value(:string, "authorization", "password", pass)
|
||||
end
|
||||
ConfigStorage.update_config_value(:string, "authorization", "server", server)
|
||||
ConfigStorage.update_config_value(:string, "authorization", "token", nil)
|
||||
:ignore
|
||||
|
|
|
@ -4,6 +4,7 @@ defmodule Farmbot.Host.SystemTasks do
|
|||
@behaviour Farmbot.System
|
||||
|
||||
def factory_reset(reason) do
|
||||
IO.inspect reason
|
||||
shutdown(reason)
|
||||
end
|
||||
|
||||
|
|
|
@ -41,13 +41,6 @@ defmodule Farmbot.Target.Bootstrap.Configurator do
|
|||
end
|
||||
end
|
||||
|
||||
@data_path Application.get_env(:farmbot, :data_path) || Mix.raise("Unconfigured data path.")
|
||||
|
||||
def flag_configured do
|
||||
check_file = Path.join(@data_path, "configured")
|
||||
File.write(check_file, "configured")
|
||||
end
|
||||
|
||||
def init(_) do
|
||||
first_boot? = ConfigStorage.get_config_value(:bool, "settings", "first_boot")
|
||||
if first_boot? do
|
||||
|
|
84
nerves/target/config_migration/after_network.ex
Normal file
84
nerves/target/config_migration/after_network.ex
Normal file
|
@ -0,0 +1,84 @@
|
|||
defmodule Farmbot.Target.ConfigMigration.AfterNetwork do
|
||||
@moduledoc "Finish the migration. Before authorization but after network."
|
||||
|
||||
use GenServer
|
||||
alias Farmbot.System.ConfigStorage
|
||||
use Farmbot.Logger
|
||||
|
||||
@data_path Application.get_env(:farmbot, :data_path)
|
||||
|
||||
@doc false
|
||||
def start_link(_, _) do
|
||||
GenServer.start_link(__MODULE__, [], [name: __MODULE__])
|
||||
end
|
||||
|
||||
def init([]) do
|
||||
old_config_json_file = Path.join(@data_path, "config.json")
|
||||
if File.exists?(old_config_json_file) do
|
||||
server = ConfigStorage.get_config_value(:string, "authorization", "server")
|
||||
secret = ConfigStorage.get_config_value(:string, "authorization", "password")
|
||||
case authorize(server, secret) do
|
||||
{:ok, encoded} when is_binary(encoded) ->
|
||||
{:ok, %{interim_email: email}} = Farmbot.Jwt.decode(encoded)
|
||||
ConfigStorage.update_config_value(:string, "authorization", "token", encoded)
|
||||
ConfigStorage.update_config_value(:string, "authorization", "email", email)
|
||||
Logger.success 1, "Successfully migrated secret."
|
||||
File.rm(old_config_json_file)
|
||||
:ignore
|
||||
{:error, reason} ->
|
||||
{:stop, reason}
|
||||
end
|
||||
else
|
||||
:ignore
|
||||
end
|
||||
end
|
||||
|
||||
def authorize(server, secret) do
|
||||
with {:ok, payload} <- build_payload(secret),
|
||||
{:ok, resp} <- request_token(server, payload),
|
||||
{:ok, body} <- Poison.decode(resp),
|
||||
{:ok, map} <- Map.fetch(body, "token") do
|
||||
Map.fetch(map, "encoded")
|
||||
else
|
||||
:error -> {:error, "unknown error."}
|
||||
{:error, :invalid, _} -> authorize(server, secret)
|
||||
# If we got maintance mode, a 5xx error etc, just sleep for a few seconds
|
||||
# and try again.
|
||||
{:ok, {{_, code, _}, _, _}} ->
|
||||
Logger.error 1, "Failed to authorize due to server error: #{code}"
|
||||
Process.sleep(5000)
|
||||
authorize(server, secret)
|
||||
err -> err
|
||||
end
|
||||
end
|
||||
|
||||
defp build_payload(secret) do
|
||||
user = %{credentials: secret |> :base64.encode_to_string |> to_string}
|
||||
Poison.encode(%{user: user})
|
||||
end
|
||||
|
||||
defp request_token(server, payload) do
|
||||
request = {
|
||||
'#{server}/api/tokens',
|
||||
['UserAgent', 'FarmbotOSBootstrap'],
|
||||
'application/json',
|
||||
payload
|
||||
}
|
||||
|
||||
case :httpc.request(:post, request, [], []) do
|
||||
{:ok, {{_, 200, _}, _, resp}} ->
|
||||
{:ok, resp}
|
||||
|
||||
# if the error is a 4xx code, it was a failed auth.
|
||||
{:ok, {{_, code, _}, _, resp}} when code > 399 and code < 500 ->
|
||||
{
|
||||
:error,
|
||||
"Failed to authorize with the Farmbot web application at: #{server} with code: #{code}: #{inspect resp}"
|
||||
}
|
||||
|
||||
# if the error is not 2xx and not 4xx, probably maintance mode.
|
||||
{:ok, _} = err -> err
|
||||
{:error, error} -> {:error, error}
|
||||
end
|
||||
end
|
||||
end
|
163
nerves/target/config_migration/before_network.ex
Normal file
163
nerves/target/config_migration/before_network.ex
Normal file
|
@ -0,0 +1,163 @@
|
|||
defmodule Farmbot.Target.ConfigMigration.BeforeNetwork do
|
||||
@moduledoc "Init module for migrating the old JSON based config."
|
||||
use GenServer
|
||||
alias Farmbot.System.ConfigStorage
|
||||
use Farmbot.Logger
|
||||
|
||||
@data_path Application.get_env(:farmbot, :data_path)
|
||||
|
||||
@doc false
|
||||
def start_link(_, _) do
|
||||
GenServer.start_link(__MODULE__, [], [name: __MODULE__])
|
||||
end
|
||||
|
||||
def init([]) do
|
||||
|
||||
old_farmware_dir = Path.join(@data_path, "farmware")
|
||||
old_config_json_file = Path.join(@data_path, "config.json")
|
||||
old_secret_file = Path.join(@data_path, "secret")
|
||||
|
||||
if File.exists?(old_config_json_file) do
|
||||
File.rm_rf(old_farmware_dir)
|
||||
Logger.busy 1, "Migrating json based config."
|
||||
with :ok <- migrate_config_file(old_config_json_file),
|
||||
:ok <- migrate_secret(old_secret_file),
|
||||
_ <- migrate_fw()
|
||||
do
|
||||
Logger.success 1, "Successfully migrated."
|
||||
:ignore
|
||||
else
|
||||
{:error, step, sub_step, reason} ->
|
||||
msg = "Migration failed at step: #{step} substep: #{sub_step} reason: #{inspect reason}"
|
||||
Logger.error 1, msg
|
||||
{:stop, msg}
|
||||
end
|
||||
else
|
||||
:ignore
|
||||
end
|
||||
end
|
||||
|
||||
def migrate_fw do
|
||||
hw = ConfigStorage.get_config_value(:string, "settings", "firmware_hardware")
|
||||
Farmbot.Firmware.UartHandler.Update.maybe_update_firmware(hw)
|
||||
end
|
||||
|
||||
def migrate_config_file(filename) do
|
||||
with {:file_read, {:ok, file}} <- {:file_read, File.read(filename)},
|
||||
{:json_decode, {:ok, data}} <- {:json_decode, Poison.decode(file)},
|
||||
{:authorization, {:ok, auth}} <- {:authorization, Map.fetch(data, "authorization")},
|
||||
{:authorization, :ok} <- {:authorization, migrate_auth(auth)},
|
||||
{:configuration, {:ok, conf}} <- {:configuration, Map.fetch(data, "configuration")},
|
||||
{:configuration, :ok} <- {:configuration, migrate_configuration(conf)},
|
||||
{:hardware, {:ok, hw }} <- {:hardware, Map.fetch(data, "hardware")},
|
||||
{:hardware, :ok} <- {:hardware, migrate_hw(hw)},
|
||||
{:network, {:ok, net }} <- {:network, Map.fetch(data, "network")},
|
||||
{:network, :ok} <- {:network, migrate_network(net)} do
|
||||
Logger.success 1, "JSON config migrated"
|
||||
:ok
|
||||
else
|
||||
{step, {:error, reason}} -> {:error, :migrate_config_file, step, reason}
|
||||
end
|
||||
end
|
||||
|
||||
def migrate_secret(filename) do
|
||||
with {:ok, bin_term} <- File.read(filename) do
|
||||
bin = :erlang.binary_to_term(bin_term)
|
||||
ConfigStorage.update_config_value(:string, "authorization", "password", bin)
|
||||
File.rm(filename)
|
||||
else
|
||||
{:error, reason} ->
|
||||
{:error, :migrate_secret_file, :file_read, reason}
|
||||
end
|
||||
end
|
||||
|
||||
defp migrate_auth(%{"server" => server}) do
|
||||
ConfigStorage.update_config_value(:string, "authorization", "server", server)
|
||||
end
|
||||
|
||||
defp migrate_configuration(%{"firmware_hardware" => hw,
|
||||
"first_party_farmware" => fpf,
|
||||
"os_auto_update" => os_auto_update,
|
||||
"timezone" => tz, "user_env" => user_env})
|
||||
do
|
||||
import ConfigStorage, only: [update_config_value: 4]
|
||||
with {:first_party_farmware, :type_check, true} <- {:first_party_farmware, :type_check, is_boolean(fpf)},
|
||||
{:first_party_farmware, :update_config_value, :ok} <- {:first_party_farmware, :update_config_value, update_config_value(:bool, "settings", "first_party_farmware", fpf)},
|
||||
{:firmware_hardware, :type_check, true} <- {:firmware_hardware, :type_check, hw in ["arduino", "arduino"]},
|
||||
{:firmware_hardware, :update_config_value, :ok} <- {:firmware_hardware, :update_config_value, update_config_value(:string, "settings", "firmware_hardware", hw)},
|
||||
{:os_auto_update, :type_check, true} <- {:os_auto_update, :type_check, is_boolean(os_auto_update)},
|
||||
{:os_auto_update, :update_config_value, :ok} <- {:os_auto_update, :update_config_value, update_config_value(:bool, "settings", "os_auto_update", os_auto_update)},
|
||||
{:timezone, :type_check, true} <- {:timezone, :type_check, (is_binary(tz) or is_nil(tz))},
|
||||
{:timezone, :update_config_value, :ok} <- {:timezone, :update_config_value, update_config_value(:string, "settings", "timezone", tz)},
|
||||
{:user_env, :type_check, true} <- {:user_env, :type_check, is_map(user_env)},
|
||||
{:user_env, :type_cast, {:ok, user_env_enc}} <- {:user_env, :type_cast, Poison.encode(user_env)},
|
||||
{:user_env, :update_config_value, :ok} <- {:user_env, :update_config_value, update_config_value(:string, "settings", "user_env", user_env_enc)},
|
||||
{:first_boot, :update_config_value, :ok} <- {:first_boot, :update_config_value, update_config_value(:bool, "settings", "first_boot", false)} do
|
||||
Logger.success 1, "Configuration data from jsono file was merged."
|
||||
:ok
|
||||
else
|
||||
{step, :type_check, _} -> {:error, "#{step} failed type checking."}
|
||||
{step, sub_step, err} ->
|
||||
{:error, reason} = err
|
||||
{:error, "#{step} failed at #{sub_step} reason: #{inspect reason}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp migrate_hw(%{"params" => params}) do
|
||||
expected_keys = struct(Farmbot.BotState).mcu_params |> Enum.map(fn({key, _}) -> Atom.to_string(key) end)
|
||||
migrated = Map.take(params, expected_keys)
|
||||
if Enum.all?(migrated, fn({param, val}) ->
|
||||
cond do
|
||||
is_integer(val) ->
|
||||
ConfigStorage.update_config_value(:float, "hardware_params", param, (val / 1))
|
||||
is_float(val) ->
|
||||
ConfigStorage.update_config_value(:float, "hardware_params", param, val)
|
||||
is_nil(val) -> :ok
|
||||
end
|
||||
end) do
|
||||
:ok
|
||||
else
|
||||
{:error, "Failed to migrate params"}
|
||||
end
|
||||
end
|
||||
|
||||
defp migrate_network(%{"interfaces" => ifaces}) do
|
||||
case do_migrate_network(Map.to_list(ifaces)) do
|
||||
[] -> {:error, "No networks were migrated."}
|
||||
_ -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
defp do_migrate_network(ifaces, acc \\ [])
|
||||
|
||||
defp do_migrate_network([], acc), do: acc
|
||||
|
||||
defp do_migrate_network([{ifname, %{"default" => "dhcp", "type" => "wired"}} | rest], acc) do
|
||||
%ConfigStorage.NetworkInterface{name: ifname, type: "wired", ipv4_method: "dhcp"}
|
||||
|> ConfigStorage.insert()
|
||||
|> case do
|
||||
{:ok, res} -> do_migrate_network(rest, [res | acc])
|
||||
_ -> do_migrate_network(rest, acc)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_migrate_network([{ifname, %{"default" => "dhcp", "type" => "wireless",
|
||||
"settings" => %{"key_mgmt" => "WPA-PSK", "psk" => psk, "ssid" => ssid}}} | rest], acc) do
|
||||
%ConfigStorage.NetworkInterface{
|
||||
name: ifname,
|
||||
type: "wireless",
|
||||
ssid: ssid,
|
||||
psk: psk,
|
||||
security: "WPA-PSK",
|
||||
ipv4_method: "dhcp" }
|
||||
|> ConfigStorage.insert()
|
||||
|> case do
|
||||
{:ok, res} -> do_migrate_network(rest, [res | acc])
|
||||
_ -> do_migrate_network(rest, acc)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_migrate_network([_ | rest], acc) do
|
||||
do_migrate_network(rest, acc)
|
||||
end
|
||||
end
|
|
@ -138,7 +138,8 @@ defmodule Farmbot.System.ConfigStorage.Migrations.SeedGroups do
|
|||
create_value(BoolValue, false) |> create_config(group_id, "auto_sync")
|
||||
create_value(StringValue, nil) |> create_config(group_id, "firmware_hardware")
|
||||
create_value(StringValue, nil) |> create_config(group_id, "timezone")
|
||||
create_value(FloatValue, nil) |> create_config(group_id, "network_not_found_timer")
|
||||
create_value(FloatValue, nil) |> create_config(group_id, "network_not_found_timer")
|
||||
create_value(StringValue, "{}") |> create_config(group_id, "user_env")
|
||||
fpf_url = Application.get_env(:farmbot, :farmware)[:first_part_farmware_manifest_url]
|
||||
create_value(StringValue, fpf_url) |> create_config(group_id, "first_party_farmware_url")
|
||||
end
|
||||
|
|
|
@ -1 +1 @@
|
|||
iex --sname farmbot --cookie democookie --remsh farmbot@farmbot-eb30
|
||||
iex --sname farmbot --cookie democookie --remsh farmbot@farmbot-7e4b
|
||||
|
|
Loading…
Reference in a new issue