From b2e49776a38af04d6aadc5574688ce71f29e96bb Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto <mudler@users.noreply.github.com> Date: Mon, 4 Jul 2022 22:39:34 +0200 Subject: [PATCH] Split off cli into separate binaries (#37) * :art: Split off cli into separate binaries This commit splits off the cli into 3 binaries: - agent - cli - provider The provider now is a separate component that can be tested by itself and have its own lifecycle. This paves the way to a ligher c3os variant, HA support and other features that can be provided on runtime. This is working, but still there are low hanging fruit to care about. Fixes #14 * :robot: Add provider bin to releases * :gear: Handle signals * :gear: Reduce buildsize footprint * :art: Scan for providers also in /system/providers * :robot: Run goreleaser * :art: Refactoring --- cmd/agent/agent.go | 57 +++ cmd/agent/iconunix.go | 740 ++++++++++++++++++++++++++++++ cmd/agent/installer.go | 163 +++++++ cmd/agent/installer_suite_test.go | 13 + cmd/agent/main.go | 286 ++++++++++++ cmd/agent/make_art.sh | 36 ++ cmd/agent/manual_installer.go | 221 +++++++++ cmd/agent/recovery.go | 130 ++++++ cmd/agent/recovery_linux.go | 12 + cmd/agent/recovery_windows.go | 8 + cmd/agent/reset.go | 78 ++++ cmd/agent/rotate.go | 33 ++ cmd/agent/upgrade.go | 51 ++ cmd/cli/bridge.go | 118 +++++ cmd/cli/main.go | 181 ++++++++ cmd/cli/register.go | 46 ++ cmd/provider/main.go | 26 ++ 17 files changed, 2199 insertions(+) create mode 100644 cmd/agent/agent.go create mode 100644 cmd/agent/iconunix.go create mode 100644 cmd/agent/installer.go create mode 100644 cmd/agent/installer_suite_test.go create mode 100644 cmd/agent/main.go create mode 100755 cmd/agent/make_art.sh create mode 100644 cmd/agent/manual_installer.go create mode 100644 cmd/agent/recovery.go create mode 100644 cmd/agent/recovery_linux.go create mode 100644 cmd/agent/recovery_windows.go create mode 100644 cmd/agent/reset.go create mode 100644 cmd/agent/rotate.go create mode 100644 cmd/agent/upgrade.go create mode 100644 cmd/cli/bridge.go create mode 100644 cmd/cli/main.go create mode 100644 cmd/cli/register.go create mode 100644 cmd/provider/main.go diff --git a/cmd/agent/agent.go b/cmd/agent/agent.go new file mode 100644 index 0000000..882bfee --- /dev/null +++ b/cmd/agent/agent.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "syscall" + + "github.com/c3os-io/c3os/internal/bus" + "github.com/c3os-io/c3os/internal/utils" + events "github.com/c3os-io/c3os/pkg/bus" + config "github.com/c3os-io/c3os/pkg/config" + "github.com/nxadm/tail" +) + +// setup needs edgevpn and k3s installed locally +// (both k3s and k3s-agent systemd services) +func agent(apiAddress string, dir []string, force bool) error { + + os.MkdirAll("/usr/local/.c3os", 0600) + + // Reads config + c, err := config.Scan(dir...) + if err != nil { + return err + } + + f, err := ioutil.TempFile(os.TempDir(), "c3os") + if err != nil { + return err + } + + err = ioutil.WriteFile(f.Name(), []byte{}, os.ModePerm) + if err != nil { + return err + } + + t, err := tail.TailFile(f.Name(), tail.Config{Follow: true}) + if err != nil { + return err + } + + defer os.RemoveAll(f.Name()) + + utils.OnSignal(func() { + os.RemoveAll(f.Name()) + }, syscall.SIGINT, syscall.SIGTERM) + + go func() { + for line := range t.Lines { + fmt.Println(line.Text) + } + }() + + _, err = bus.Manager.Publish(events.EventBootstrap, events.BootstrapPayload{APIAddress: apiAddress, Config: c.String(), Logfile: f.Name()}) + return err +} diff --git a/cmd/agent/iconunix.go b/cmd/agent/iconunix.go new file mode 100644 index 0000000..3480aa3 --- /dev/null +++ b/cmd/agent/iconunix.go @@ -0,0 +1,740 @@ +package main + +var banner []byte = []byte{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x08, 0x06, 0x00, 0x00, 0x00, 0xf4, 0x78, 0xd4, 0xfa, 0x00, 0x00, 0x02, + 0xf3, 0x7a, 0x54, 0x58, 0x74, 0x52, 0x61, 0x77, 0x20, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x78, + 0x69, 0x66, 0x00, 0x00, 0x78, 0xda, 0xed, 0x97, 0x51, 0x72, 0xec, 0x28, + 0x0c, 0x45, 0xff, 0x59, 0xc5, 0x2c, 0xc1, 0x92, 0x10, 0x92, 0x96, 0x83, + 0xc1, 0x54, 0xbd, 0x1d, 0xcc, 0xf2, 0xe7, 0x82, 0xed, 0x9e, 0xa4, 0xd3, + 0xef, 0xa3, 0x67, 0xbe, 0x5e, 0x55, 0x9b, 0x18, 0x88, 0x50, 0x5f, 0x84, + 0x0e, 0xd0, 0x49, 0x3a, 0xfe, 0xfe, 0x35, 0xd2, 0x5f, 0x78, 0x28, 0xbc, + 0xa4, 0xac, 0xe6, 0x25, 0x4a, 0xd9, 0xf0, 0xe4, 0xc8, 0xc1, 0x15, 0x1d, + 0xdf, 0xce, 0xa7, 0xae, 0x9a, 0xb6, 0xbc, 0xea, 0xfb, 0xe1, 0xcb, 0xfa, + 0xcd, 0x9e, 0x1e, 0x03, 0x0c, 0x93, 0xa0, 0x95, 0x73, 0xc0, 0xcb, 0xd9, + 0xd2, 0x6d, 0xbf, 0x85, 0xae, 0x96, 0x2a, 0x7a, 0xfa, 0x45, 0xc8, 0xdb, + 0x35, 0xb0, 0x7f, 0x1f, 0x88, 0x7c, 0x4d, 0xef, 0x4f, 0x42, 0xd7, 0x44, + 0x32, 0x23, 0x9a, 0x21, 0xf4, 0x4b, 0x28, 0x2e, 0x21, 0xe1, 0x73, 0x80, + 0x2e, 0x81, 0x7a, 0x2e, 0x6b, 0x2b, 0xe1, 0xf6, 0x75, 0x09, 0xfb, 0x71, + 0xb6, 0xfd, 0x5e, 0xa8, 0x9f, 0x6f, 0x9a, 0x95, 0xd8, 0xb9, 0xbc, 0x5b, + 0xe4, 0xf9, 0xf7, 0x6c, 0xc8, 0x5e, 0x57, 0x18, 0x85, 0xf9, 0x10, 0x92, + 0x0d, 0x35, 0x8b, 0x9f, 0x01, 0xc8, 0x7c, 0x39, 0x49, 0x5d, 0x9d, 0x59, + 0x07, 0x1c, 0x67, 0xa9, 0x70, 0x55, 0xd4, 0x59, 0xee, 0x25, 0x21, 0x21, + 0xaf, 0xf2, 0xf4, 0x78, 0x02, 0x11, 0x8d, 0x19, 0x6a, 0x7e, 0xe9, 0xf4, + 0x4c, 0x8b, 0x5e, 0xd1, 0xba, 0x7b, 0xe9, 0x99, 0x56, 0xe6, 0xcb, 0x45, + 0x9e, 0x92, 0x5c, 0x1e, 0xed, 0x4b, 0x7b, 0x22, 0x7d, 0x4d, 0x65, 0xa5, + 0xfe, 0xcb, 0xcc, 0xd9, 0xaf, 0x1e, 0x7f, 0xb7, 0x9b, 0x9c, 0x52, 0x69, + 0x7b, 0xca, 0xfe, 0x7c, 0xc7, 0xe8, 0x3e, 0xd6, 0x9a, 0xb1, 0x8a, 0x9a, + 0x0b, 0x52, 0x5d, 0xae, 0x45, 0xdd, 0x4b, 0x59, 0x3d, 0xf8, 0xed, 0x98, + 0x62, 0x4e, 0xed, 0x09, 0x7a, 0x65, 0x33, 0xbc, 0x0a, 0x09, 0x5b, 0x25, + 0x50, 0x1c, 0xbb, 0xba, 0x81, 0x5a, 0xdf, 0xda, 0xb6, 0xa3, 0x34, 0x0a, + 0x62, 0xe0, 0x1a, 0x94, 0xa9, 0x53, 0xa5, 0x41, 0xc7, 0x6a, 0x1b, 0x35, + 0x84, 0x98, 0xf9, 0x48, 0x6c, 0xe8, 0x30, 0x37, 0x96, 0x65, 0x74, 0x31, + 0x0e, 0x6e, 0x60, 0x47, 0xa0, 0x86, 0x42, 0x83, 0x4d, 0x42, 0xba, 0x38, + 0xd8, 0x36, 0x60, 0x17, 0x58, 0xf9, 0x11, 0x0b, 0xad, 0x69, 0x63, 0x6b, + 0x69, 0xcd, 0xe6, 0x98, 0xb9, 0x13, 0x5c, 0x99, 0x20, 0x46, 0x73, 0x5f, + 0xbc, 0x5b, 0xd2, 0xbb, 0x1f, 0x18, 0x63, 0x1e, 0x05, 0xa2, 0xcd, 0x1f, + 0xb9, 0x42, 0x5c, 0xcc, 0xeb, 0xd0, 0xd2, 0x4c, 0xa3, 0xcc, 0x1a, 0x6e, + 0x20, 0x42, 0xe3, 0x4a, 0xaa, 0xae, 0x04, 0xdf, 0xe5, 0xf9, 0x99, 0x5c, + 0x05, 0x04, 0x75, 0x66, 0x79, 0x1e, 0x91, 0x40, 0x62, 0xf7, 0x53, 0x62, + 0x57, 0xfa, 0xf7, 0x26, 0x90, 0x05, 0x5a, 0xe0, 0xa8, 0x68, 0xcf, 0xe3, + 0x42, 0xd6, 0x2f, 0x01, 0xa4, 0x08, 0x53, 0x2b, 0x82, 0x21, 0x01, 0x01, + 0x50, 0xc3, 0x79, 0xa0, 0x42, 0x9b, 0x31, 0x1b, 0x11, 0x12, 0xe9, 0x00, + 0x54, 0x11, 0x3a, 0x4b, 0xe6, 0x1d, 0x04, 0x48, 0x95, 0x3b, 0x82, 0x64, + 0x1c, 0x99, 0x02, 0x36, 0xce, 0x73, 0x6a, 0x7c, 0xc4, 0x68, 0xb9, 0xb2, + 0x32, 0xcc, 0x09, 0x76, 0x5c, 0x66, 0x20, 0xa1, 0x52, 0xc4, 0xc0, 0x26, + 0xa4, 0x02, 0x56, 0xce, 0x8a, 0xfd, 0x63, 0xd9, 0xb1, 0x87, 0xaa, 0x8a, + 0x66, 0x55, 0x2d, 0x6a, 0xea, 0x1a, 0x5a, 0x8b, 0x94, 0x5c, 0xb4, 0x94, + 0x62, 0x65, 0x5e, 0x8a, 0xd5, 0xc4, 0x72, 0x32, 0xb5, 0x62, 0x66, 0x6e, + 0x61, 0xd5, 0xc5, 0xb3, 0xab, 0x17, 0x37, 0x77, 0x0f, 0xaf, 0xc1, 0x21, + 0xb8, 0x34, 0x35, 0x4a, 0x58, 0x78, 0x44, 0xd4, 0x8a, 0x39, 0x2b, 0x94, + 0x2b, 0x3e, 0x5d, 0xe1, 0x50, 0xeb, 0xce, 0xbb, 0xec, 0x79, 0xd7, 0xb4, + 0x97, 0xdd, 0x76, 0xdf, 0x63, 0xaf, 0x0d, 0xdb, 0xa7, 0xe5, 0xa6, 0xad, + 0x34, 0x6b, 0xde, 0xa2, 0xd5, 0xce, 0x5d, 0x3a, 0xee, 0x8f, 0x5e, 0xba, + 0x75, 0xef, 0xd1, 0xeb, 0x41, 0x07, 0xb6, 0xd2, 0x91, 0x0f, 0x3d, 0xca, + 0x61, 0x87, 0x1f, 0x71, 0xd4, 0x81, 0xad, 0x36, 0x24, 0x8d, 0x3c, 0x74, + 0x94, 0x61, 0xc3, 0x47, 0x8c, 0xfa, 0xa0, 0x76, 0x61, 0xfd, 0x51, 0xde, + 0xa0, 0x46, 0x17, 0x35, 0x5e, 0xa4, 0xa6, 0xa3, 0x3d, 0xa8, 0xc1, 0x6a, + 0x76, 0x4b, 0xd0, 0xbc, 0x4e, 0x74, 0x32, 0x03, 0x30, 0x4e, 0x99, 0x40, + 0xdc, 0x26, 0x02, 0x6c, 0x68, 0x9e, 0xcc, 0x36, 0xa7, 0x9c, 0x79, 0x92, + 0x9b, 0xcc, 0xb6, 0x60, 0x9c, 0x0a, 0x65, 0x04, 0xa9, 0x93, 0x59, 0xa7, + 0x49, 0x0c, 0x04, 0xf3, 0x41, 0xac, 0x83, 0x6e, 0x76, 0x89, 0x4f, 0xa2, + 0x93, 0xdc, 0xff, 0xe2, 0x96, 0x2c, 0x7f, 0xe3, 0xc6, 0xff, 0x95, 0x5c, + 0x9a, 0xe8, 0xde, 0x24, 0xf7, 0x93, 0xdb, 0x2b, 0x6a, 0x7d, 0x7e, 0x0d, + 0xb5, 0x45, 0xec, 0x3c, 0x85, 0x33, 0xa9, 0x9b, 0xe0, 0xf4, 0xc1, 0xa7, + 0xb2, 0xe3, 0x07, 0xdf, 0x55, 0x3f, 0xdb, 0xf4, 0xbb, 0x81, 0x77, 0xdb, + 0x8f, 0xd0, 0x47, 0xe8, 0x23, 0xf4, 0x11, 0xfa, 0x08, 0x7d, 0x84, 0x3e, + 0x42, 0x7f, 0x8e, 0x90, 0x0c, 0xfc, 0xf1, 0x30, 0xff, 0x0b, 0xfc, 0x07, + 0xa3, 0xff, 0xa7, 0x64, 0x64, 0x9a, 0xbd, 0x42, 0x00, 0x00, 0x00, 0x61, + 0x7a, 0x54, 0x58, 0x74, 0x52, 0x61, 0x77, 0x20, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x70, 0x74, + 0x63, 0x00, 0x00, 0x78, 0xda, 0x3d, 0x89, 0xc1, 0x0d, 0x80, 0x30, 0x0c, + 0x03, 0xff, 0x99, 0x82, 0x11, 0x9c, 0xd8, 0x88, 0x76, 0x1d, 0xd2, 0x0f, + 0xbf, 0x3e, 0xd8, 0x5f, 0x44, 0x45, 0x60, 0xcb, 0x96, 0x4f, 0xb6, 0x6b, + 0xde, 0x69, 0xdb, 0x92, 0xc2, 0xd8, 0x14, 0xea, 0x1a, 0x50, 0xf9, 0x97, + 0x0f, 0x4f, 0x04, 0x8f, 0x9a, 0x8d, 0x41, 0x54, 0xde, 0x06, 0xfb, 0x7a, + 0xb2, 0x9e, 0xb3, 0x08, 0x74, 0xee, 0x14, 0x69, 0x31, 0x3e, 0x04, 0xec, + 0x01, 0x30, 0x95, 0x14, 0xd2, 0x64, 0x95, 0xf3, 0x4b, 0x00, 0x00, 0x01, + 0x85, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x78, 0x9c, 0x7d, 0x91, 0x3d, 0x48, + 0xc3, 0x40, 0x1c, 0xc5, 0x5f, 0x53, 0xa5, 0x45, 0x2a, 0x0e, 0x16, 0x11, + 0x71, 0xc8, 0x50, 0xc5, 0xc1, 0x82, 0xa8, 0x88, 0xe0, 0xa2, 0x55, 0x28, + 0x42, 0x85, 0x52, 0x2b, 0xb4, 0xea, 0x60, 0x72, 0xe9, 0x17, 0x34, 0x69, + 0x48, 0x52, 0x5c, 0x1c, 0x05, 0xd7, 0x82, 0x83, 0x1f, 0x8b, 0x55, 0x07, + 0x17, 0x67, 0x5d, 0x1d, 0x5c, 0x05, 0x41, 0xf0, 0x03, 0xc4, 0xd1, 0xc9, + 0x49, 0xd1, 0x45, 0x4a, 0xfc, 0x5f, 0x52, 0x68, 0x11, 0xe3, 0xc1, 0x71, + 0x3f, 0xde, 0xdd, 0x7b, 0xdc, 0xbd, 0x03, 0x84, 0x7a, 0x99, 0xa9, 0x66, + 0xc7, 0x18, 0xa0, 0x6a, 0x96, 0x91, 0x8a, 0xc7, 0xc4, 0x4c, 0x76, 0x55, + 0x0c, 0xbc, 0x42, 0x40, 0x10, 0x7d, 0x98, 0xc1, 0x88, 0xc4, 0x4c, 0x7d, + 0x2e, 0x99, 0x4c, 0xc0, 0x73, 0x7c, 0xdd, 0xc3, 0xc7, 0xd7, 0xbb, 0x28, + 0xcf, 0xf2, 0x3e, 0xf7, 0xe7, 0xe8, 0x56, 0x72, 0x26, 0x03, 0x7c, 0x22, + 0xf1, 0x2c, 0xd3, 0x0d, 0x8b, 0x78, 0x83, 0x78, 0x6a, 0xd3, 0xd2, 0x39, + 0xef, 0x13, 0x87, 0x59, 0x51, 0x52, 0x88, 0xcf, 0x89, 0x47, 0x0d, 0xba, + 0x20, 0xf1, 0x23, 0xd7, 0x65, 0x97, 0xdf, 0x38, 0x17, 0x1c, 0x16, 0x78, + 0x66, 0xd8, 0x48, 0xa7, 0xe6, 0x89, 0xc3, 0xc4, 0x62, 0xa1, 0x8d, 0xe5, + 0x36, 0x66, 0x45, 0x43, 0x25, 0x9e, 0x24, 0x8e, 0x28, 0xaa, 0x46, 0xf9, + 0x42, 0xc6, 0x65, 0x85, 0xf3, 0x16, 0x67, 0xb5, 0x5c, 0x65, 0xcd, 0x7b, + 0xf2, 0x17, 0x86, 0x72, 0xda, 0xca, 0x32, 0xd7, 0x69, 0x0e, 0x22, 0x8e, + 0x45, 0x2c, 0x21, 0x09, 0x11, 0x32, 0xaa, 0x28, 0xa1, 0x0c, 0x0b, 0x51, + 0x5a, 0x35, 0x52, 0x4c, 0xa4, 0x68, 0x3f, 0xe6, 0xe1, 0x1f, 0x70, 0xfc, + 0x49, 0x72, 0xc9, 0xe4, 0x2a, 0x81, 0x91, 0x63, 0x01, 0x15, 0xa8, 0x90, + 0x1c, 0x3f, 0xf8, 0x1f, 0xfc, 0xee, 0xd6, 0xcc, 0x4f, 0x8c, 0xbb, 0x49, + 0xa1, 0x18, 0xd0, 0xf9, 0x62, 0xdb, 0x1f, 0x43, 0x40, 0x60, 0x17, 0x68, + 0xd4, 0x6c, 0xfb, 0xfb, 0xd8, 0xb6, 0x1b, 0x27, 0x80, 0xff, 0x19, 0xb8, + 0xd2, 0x5a, 0xfe, 0x4a, 0x1d, 0x98, 0xfe, 0x24, 0xbd, 0xd6, 0xd2, 0x22, + 0x47, 0x40, 0xcf, 0x36, 0x70, 0x71, 0xdd, 0xd2, 0xe4, 0x3d, 0xe0, 0x72, + 0x07, 0xe8, 0x7f, 0xd2, 0x25, 0x43, 0x72, 0x24, 0x3f, 0x4d, 0x21, 0x9f, + 0x07, 0xde, 0xcf, 0xe8, 0x9b, 0xb2, 0x40, 0xef, 0x2d, 0xd0, 0xb5, 0xe6, + 0xf6, 0xd6, 0xdc, 0xc7, 0xe9, 0x03, 0x90, 0xa6, 0xae, 0x12, 0x37, 0xc0, + 0xc1, 0x21, 0x30, 0x5c, 0xa0, 0xec, 0x75, 0x8f, 0x77, 0x07, 0xdb, 0x7b, + 0xfb, 0xf7, 0x4c, 0xb3, 0xbf, 0x1f, 0xb6, 0x32, 0x72, 0xc2, 0xf4, 0xb2, + 0x50, 0x0c, 0x00, 0x00, 0x0d, 0x1a, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, + 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, + 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3f, 0x78, 0x70, + 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, + 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, + 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, + 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, + 0x3e, 0x0a, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, + 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, + 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, + 0x22, 0x20, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, + 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x34, 0x2e, 0x34, 0x2e, + 0x30, 0x2d, 0x45, 0x78, 0x69, 0x76, 0x32, 0x22, 0x3e, 0x0a, 0x20, 0x3c, + 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, + 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, + 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, + 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, + 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, + 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, + 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, + 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, + 0x3a, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, + 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x23, 0x22, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, + 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x22, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x47, 0x49, 0x4d, 0x50, + 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, + 0x2e, 0x67, 0x69, 0x6d, 0x70, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x78, 0x6d, + 0x70, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, + 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f, 0x31, 0x2e, 0x30, + 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, + 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x0a, + 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x67, 0x69, 0x6d, + 0x70, 0x3a, 0x64, 0x6f, 0x63, 0x69, 0x64, 0x3a, 0x67, 0x69, 0x6d, 0x70, + 0x3a, 0x63, 0x38, 0x31, 0x32, 0x64, 0x35, 0x31, 0x62, 0x2d, 0x38, 0x35, + 0x65, 0x63, 0x2d, 0x34, 0x39, 0x30, 0x38, 0x2d, 0x38, 0x33, 0x35, 0x33, + 0x2d, 0x32, 0x66, 0x38, 0x38, 0x39, 0x38, 0x32, 0x66, 0x36, 0x39, 0x64, + 0x64, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, + 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x34, 0x62, 0x37, 0x39, + 0x34, 0x38, 0x38, 0x66, 0x2d, 0x32, 0x38, 0x33, 0x36, 0x2d, 0x34, 0x34, + 0x61, 0x62, 0x2d, 0x38, 0x30, 0x62, 0x35, 0x2d, 0x30, 0x31, 0x37, 0x38, + 0x37, 0x64, 0x35, 0x63, 0x62, 0x33, 0x33, 0x39, 0x22, 0x0a, 0x20, 0x20, + 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x4f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, + 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x37, + 0x38, 0x63, 0x64, 0x64, 0x32, 0x39, 0x64, 0x2d, 0x31, 0x30, 0x38, 0x35, + 0x2d, 0x34, 0x63, 0x33, 0x64, 0x2d, 0x39, 0x31, 0x62, 0x35, 0x2d, 0x35, + 0x38, 0x66, 0x64, 0x33, 0x64, 0x35, 0x34, 0x64, 0x63, 0x63, 0x62, 0x22, + 0x0a, 0x20, 0x20, 0x20, 0x64, 0x63, 0x3a, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x3d, 0x22, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, + 0x22, 0x0a, 0x20, 0x20, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x3a, 0x41, 0x50, + 0x49, 0x3d, 0x22, 0x32, 0x2e, 0x30, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x47, + 0x49, 0x4d, 0x50, 0x3a, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, + 0x3d, 0x22, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x22, 0x0a, 0x20, 0x20, 0x20, + 0x47, 0x49, 0x4d, 0x50, 0x3a, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, + 0x6d, 0x70, 0x3d, 0x22, 0x31, 0x36, 0x34, 0x34, 0x33, 0x36, 0x32, 0x31, + 0x34, 0x34, 0x38, 0x38, 0x31, 0x35, 0x36, 0x37, 0x22, 0x0a, 0x20, 0x20, + 0x20, 0x47, 0x49, 0x4d, 0x50, 0x3a, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x3d, 0x22, 0x32, 0x2e, 0x31, 0x30, 0x2e, 0x32, 0x38, 0x22, 0x0a, + 0x20, 0x20, 0x20, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, + 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x22, 0x0a, + 0x20, 0x20, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3d, 0x22, 0x47, 0x49, 0x4d, 0x50, + 0x20, 0x32, 0x2e, 0x31, 0x30, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, + 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, + 0x53, 0x65, 0x71, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, + 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x3d, 0x22, 0x73, 0x61, 0x76, 0x65, 0x64, 0x22, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x64, 0x3d, 0x22, 0x2f, 0x22, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, + 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x32, 0x36, 0x37, 0x37, 0x62, 0x64, 0x36, + 0x65, 0x2d, 0x39, 0x38, 0x30, 0x62, 0x2d, 0x34, 0x66, 0x30, 0x30, 0x2d, + 0x39, 0x61, 0x37, 0x34, 0x2d, 0x65, 0x66, 0x33, 0x39, 0x61, 0x64, 0x31, + 0x32, 0x65, 0x63, 0x34, 0x36, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x73, 0x6f, 0x66, 0x74, 0x77, + 0x61, 0x72, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x47, 0x69, + 0x6d, 0x70, 0x20, 0x32, 0x2e, 0x31, 0x30, 0x20, 0x28, 0x4c, 0x69, 0x6e, + 0x75, 0x78, 0x29, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, + 0x74, 0x45, 0x76, 0x74, 0x3a, 0x77, 0x68, 0x65, 0x6e, 0x3d, 0x22, 0x32, + 0x30, 0x32, 0x32, 0x2d, 0x30, 0x32, 0x2d, 0x30, 0x39, 0x54, 0x30, 0x30, + 0x3a, 0x31, 0x35, 0x3a, 0x34, 0x34, 0x2b, 0x30, 0x31, 0x3a, 0x30, 0x30, + 0x22, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, + 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, + 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, + 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, + 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x3c, 0x3f, 0x78, + 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, + 0x77, 0x22, 0x3f, 0x3e, 0x59, 0x94, 0xa1, 0xe5, 0x00, 0x00, 0x00, 0x06, + 0x62, 0x4b, 0x47, 0x44, 0x00, 0xe0, 0x00, 0xfb, 0x00, 0xfc, 0xdc, 0x0d, + 0x5e, 0xa6, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, + 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, + 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x02, 0x08, 0x17, + 0x0f, 0x2c, 0x1f, 0x94, 0xec, 0x69, 0x00, 0x00, 0x0f, 0xe9, 0x49, 0x44, + 0x41, 0x54, 0x78, 0xda, 0xed, 0xdd, 0xd1, 0x72, 0xdc, 0x46, 0xb2, 0x45, + 0x51, 0x27, 0xa3, 0xfe, 0xff, 0x97, 0xe1, 0x17, 0x2b, 0x42, 0x92, 0xc3, + 0x6e, 0x52, 0x04, 0x09, 0x14, 0xf6, 0x5a, 0xcf, 0xf7, 0xc6, 0x58, 0x55, + 0x95, 0x27, 0x0f, 0xda, 0x33, 0xf6, 0xfc, 0x05, 0x24, 0x1c, 0xc7, 0x71, + 0xbc, 0xe7, 0xff, 0x6e, 0x66, 0xc6, 0x69, 0xc1, 0xf3, 0x19, 0x74, 0xb0, + 0xf4, 0x95, 0x01, 0x50, 0x00, 0x00, 0x8b, 0x5f, 0x11, 0x00, 0x05, 0x00, + 0x48, 0x2f, 0x7e, 0x45, 0x00, 0x14, 0x00, 0x20, 0xbc, 0xf8, 0x15, 0x01, + 0x50, 0x00, 0x80, 0xf8, 0xf2, 0x57, 0x02, 0x40, 0x01, 0x00, 0x82, 0x8b, + 0x5f, 0x11, 0x00, 0x05, 0x00, 0x08, 0x2f, 0x7e, 0x45, 0x00, 0x14, 0x00, + 0x20, 0xbc, 0xf8, 0x15, 0x01, 0x50, 0x00, 0x80, 0xf0, 0xe2, 0x57, 0x04, + 0x40, 0x01, 0x00, 0xc2, 0x8b, 0x5f, 0x11, 0x00, 0x05, 0x00, 0x08, 0x2f, + 0x7e, 0x45, 0x00, 0x14, 0x00, 0x20, 0xbc, 0xf8, 0x15, 0x01, 0x50, 0x00, + 0x80, 0xf0, 0xe2, 0x57, 0x04, 0x40, 0x01, 0x00, 0xe2, 0xcb, 0x5f, 0x09, + 0x00, 0x05, 0x00, 0x2c, 0x7e, 0x14, 0x01, 0x50, 0x00, 0xc0, 0xe2, 0x57, + 0x04, 0x00, 0x05, 0x00, 0x2c, 0x7e, 0x45, 0x00, 0x50, 0x00, 0xc0, 0xe2, + 0x57, 0x04, 0x00, 0x05, 0x00, 0x2c, 0x7e, 0x45, 0x00, 0x50, 0x00, 0xc0, + 0xe2, 0x57, 0x04, 0x00, 0x05, 0x00, 0x2c, 0x7e, 0x45, 0x00, 0x50, 0x00, + 0xc0, 0xe2, 0x57, 0x04, 0x00, 0x05, 0x00, 0x2c, 0x7f, 0x25, 0x00, 0x14, + 0x00, 0xc0, 0xe2, 0x57, 0x04, 0x40, 0x01, 0x00, 0x2c, 0x7e, 0x45, 0x00, + 0x14, 0x00, 0xb0, 0xf8, 0x9d, 0x82, 0x22, 0x00, 0x0a, 0x00, 0x58, 0xfc, + 0x28, 0x02, 0xa0, 0x00, 0x80, 0xc5, 0x8f, 0x22, 0x00, 0x0a, 0x00, 0x58, + 0xfc, 0x28, 0x02, 0xa0, 0x00, 0x80, 0xc5, 0x8f, 0x22, 0x00, 0x0a, 0x00, + 0x58, 0xfc, 0x28, 0x02, 0xa0, 0x00, 0x80, 0xe5, 0x8f, 0x12, 0x00, 0x0a, + 0x00, 0x58, 0xfc, 0x28, 0x02, 0xa0, 0x00, 0x80, 0xc5, 0x8f, 0x22, 0x00, + 0x0a, 0x00, 0x58, 0xfc, 0x28, 0x02, 0xa0, 0x00, 0x80, 0xc5, 0x8f, 0x22, + 0x00, 0x0a, 0x00, 0x58, 0xfc, 0x28, 0x02, 0xa0, 0x00, 0x60, 0xf1, 0x83, + 0x22, 0x00, 0x0a, 0x00, 0x16, 0x3f, 0x28, 0x02, 0xa0, 0x00, 0x60, 0xf1, + 0x83, 0x22, 0x80, 0x02, 0x00, 0x16, 0x3f, 0x28, 0x02, 0x28, 0x00, 0x60, + 0xf9, 0x83, 0x12, 0x80, 0x02, 0x00, 0x16, 0x3f, 0x28, 0x02, 0x28, 0x00, + 0x60, 0xf1, 0x83, 0x22, 0x80, 0x02, 0x00, 0x16, 0x3f, 0x28, 0x02, 0x28, + 0x00, 0x60, 0xf1, 0x83, 0x22, 0x80, 0x02, 0x00, 0x16, 0x3f, 0x28, 0x02, + 0x28, 0x00, 0x60, 0xf1, 0x83, 0x22, 0x80, 0x02, 0x80, 0xc5, 0x0f, 0x28, + 0x02, 0x28, 0x00, 0x58, 0xfc, 0x80, 0x22, 0x80, 0x02, 0x80, 0xe5, 0x0f, + 0x4a, 0x00, 0x28, 0x00, 0x58, 0xfc, 0xa0, 0x08, 0x80, 0x02, 0x80, 0x12, + 0x00, 0x96, 0x3f, 0x28, 0x00, 0x28, 0x02, 0x60, 0xf1, 0x83, 0x02, 0x80, + 0x22, 0x00, 0x16, 0x3f, 0x28, 0x00, 0x28, 0x02, 0x60, 0xf1, 0x83, 0x02, + 0x80, 0x22, 0x00, 0x16, 0x3f, 0x28, 0x00, 0x28, 0x02, 0x60, 0xf1, 0xa3, + 0x00, 0x80, 0x22, 0x00, 0x16, 0x3f, 0x0a, 0x00, 0x28, 0x02, 0x60, 0xf1, + 0xa3, 0x00, 0x80, 0x22, 0x00, 0x16, 0x3f, 0x0a, 0x00, 0x28, 0x02, 0x60, + 0xf1, 0xa3, 0x00, 0x80, 0x22, 0x80, 0xc5, 0x0f, 0x0a, 0x00, 0x28, 0x02, + 0x58, 0xfc, 0xa0, 0x00, 0x80, 0x22, 0x80, 0xc5, 0x0f, 0x0a, 0x00, 0x28, + 0x02, 0x58, 0xfc, 0xa0, 0x00, 0x80, 0x22, 0x80, 0xc5, 0x0f, 0x0a, 0x00, + 0x28, 0x02, 0x58, 0xfc, 0xa0, 0x00, 0x80, 0x22, 0x80, 0xc5, 0x0f, 0x0a, + 0x00, 0x28, 0x02, 0x58, 0xfc, 0xa0, 0x00, 0x80, 0x22, 0x80, 0xc5, 0x0f, + 0x0a, 0x00, 0x28, 0x02, 0x58, 0xfc, 0xa0, 0x00, 0x80, 0x22, 0x80, 0xc5, + 0x0f, 0x0a, 0x00, 0x28, 0x02, 0x16, 0xbf, 0xc5, 0x0f, 0x0a, 0x00, 0x28, + 0x02, 0x16, 0x3f, 0x28, 0x00, 0x80, 0x22, 0x60, 0xf1, 0x83, 0x02, 0x00, + 0x28, 0x02, 0x16, 0x3f, 0x28, 0x00, 0xa0, 0x08, 0x28, 0x02, 0x16, 0x3f, + 0x28, 0x00, 0xa0, 0x08, 0x60, 0xf1, 0x83, 0x02, 0x00, 0x8a, 0x00, 0x16, + 0x3f, 0x28, 0x00, 0xa0, 0x08, 0x60, 0xf1, 0x83, 0x02, 0x00, 0x8a, 0x00, + 0x16, 0x3f, 0x28, 0x00, 0xa0, 0x08, 0x60, 0xf1, 0x83, 0x02, 0x00, 0x8a, + 0x00, 0x16, 0x3f, 0x28, 0x00, 0x3c, 0x6e, 0x01, 0xd6, 0x03, 0x5c, 0x11, + 0xb0, 0xf8, 0xcd, 0x0f, 0x0a, 0x00, 0xe9, 0xc5, 0x27, 0xd0, 0x15, 0x01, + 0x8b, 0xdf, 0xdc, 0xa0, 0x00, 0x10, 0x5e, 0x78, 0x02, 0x5e, 0x11, 0xf0, + 0x2e, 0xcc, 0x0b, 0x0a, 0x00, 0xe1, 0x45, 0x27, 0xf0, 0x15, 0x01, 0xef, + 0xc0, 0x9c, 0xa0, 0x00, 0x10, 0x5e, 0x70, 0x16, 0x40, 0xb3, 0x08, 0xb8, + 0x77, 0xf3, 0x81, 0x02, 0x80, 0xc5, 0x26, 0xe8, 0x42, 0x45, 0xc0, 0x3d, + 0x9b, 0x0b, 0x14, 0x00, 0x04, 0x9d, 0xc0, 0x0b, 0x15, 0x01, 0xf7, 0x6a, + 0x1e, 0x50, 0x00, 0x10, 0x78, 0x82, 0x2f, 0x54, 0x04, 0xdc, 0xa3, 0x39, + 0x40, 0x01, 0x40, 0xf0, 0x09, 0xc0, 0x50, 0x11, 0x70, 0x6f, 0xde, 0x3f, + 0x0a, 0x00, 0x02, 0xd0, 0x42, 0x09, 0x15, 0x01, 0xf7, 0xe4, 0xdd, 0xa3, + 0x00, 0xa0, 0x00, 0x7c, 0xc9, 0xc2, 0xb2, 0x60, 0xee, 0x59, 0x04, 0xdc, + 0x8b, 0xf7, 0x8e, 0x02, 0x00, 0xdf, 0xb2, 0xa8, 0x2c, 0x9c, 0x7b, 0x14, + 0x01, 0xf7, 0xe0, 0x9d, 0xa3, 0x00, 0xc0, 0x25, 0x0b, 0xca, 0x02, 0xba, + 0xa6, 0x08, 0x38, 0x77, 0xef, 0x1b, 0x05, 0x00, 0x6e, 0xb1, 0x98, 0x2c, + 0x24, 0x0b, 0xc9, 0xbb, 0x06, 0x05, 0x00, 0x5f, 0xa6, 0xce, 0xdd, 0xb9, + 0x7a, 0xcf, 0xa0, 0x00, 0x50, 0x0a, 0x4c, 0xc1, 0x79, 0xee, 0xf9, 0x3b, + 0x47, 0xef, 0x18, 0x05, 0x00, 0xb6, 0x0a, 0x4e, 0x01, 0xfa, 0xb9, 0x7b, + 0x70, 0x6e, 0xde, 0x2f, 0x0a, 0x00, 0x6c, 0x1d, 0xa0, 0x82, 0xf4, 0x63, + 0xf7, 0xe1, 0x9c, 0xbc, 0x5b, 0x14, 0x00, 0x78, 0x54, 0x90, 0x0a, 0xd4, + 0xff, 0xbf, 0x17, 0xe7, 0xe2, 0xbd, 0xa2, 0x00, 0xc0, 0xa3, 0x03, 0x55, + 0xb0, 0xfe, 0x7a, 0x3f, 0xce, 0xc1, 0x3b, 0x45, 0x01, 0x80, 0x54, 0xb0, + 0x0a, 0x58, 0xef, 0xd3, 0xfb, 0xa4, 0x6c, 0x39, 0x02, 0x2c, 0x02, 0x5f, + 0xc2, 0x16, 0x3f, 0x28, 0x00, 0xa0, 0x08, 0x28, 0x02, 0x16, 0x3f, 0x28, + 0x00, 0xa0, 0x08, 0x60, 0xf1, 0x83, 0x02, 0x00, 0x8a, 0x00, 0x16, 0x3f, + 0x28, 0x00, 0xa0, 0x08, 0x60, 0xf1, 0x83, 0x02, 0x00, 0x8a, 0x00, 0x16, + 0x3f, 0x28, 0x00, 0xa0, 0x08, 0x60, 0xf1, 0x83, 0x02, 0x00, 0x8a, 0x00, + 0x16, 0x3f, 0x28, 0x00, 0xa0, 0x08, 0x60, 0xf1, 0x83, 0x02, 0x00, 0x8a, + 0x00, 0x16, 0x3f, 0x28, 0x00, 0xa0, 0x08, 0x60, 0xf1, 0x83, 0x02, 0x00, + 0x8a, 0x80, 0xc5, 0x0f, 0x28, 0x00, 0xa0, 0x08, 0x58, 0xfc, 0x80, 0x02, + 0x00, 0x8a, 0x80, 0xc5, 0x0f, 0x28, 0x00, 0xa0, 0x08, 0x58, 0xfc, 0x80, + 0x02, 0x00, 0x8a, 0x80, 0xc5, 0x0f, 0x28, 0x00, 0xa0, 0x08, 0x58, 0xfc, + 0x80, 0x02, 0x00, 0x8a, 0x80, 0xc5, 0x0f, 0x0a, 0x00, 0xa0, 0x08, 0x58, + 0xfc, 0xa0, 0x00, 0x00, 0x8a, 0x80, 0xc5, 0x0f, 0x0a, 0x00, 0xa0, 0x08, + 0x58, 0xfc, 0xa0, 0x00, 0x00, 0x8a, 0x80, 0xc5, 0x0f, 0x0a, 0x00, 0xd0, + 0x2c, 0x02, 0x16, 0x3f, 0x28, 0x00, 0x40, 0xa8, 0x08, 0x58, 0xfc, 0xb0, + 0xb7, 0x37, 0x47, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, + 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, + 0x28, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, + 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, + 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x80, 0x02, 0xc0, 0xde, 0x66, 0x66, 0x9c, 0x02, 0x98, 0x21, 0x14, + 0x00, 0x00, 0x40, 0x01, 0xc0, 0x17, 0x0c, 0x60, 0x76, 0x50, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x5f, 0x32, 0x60, 0x66, 0x40, 0x01, 0x40, 0xa0, 0x81, + 0x59, 0x01, 0x05, 0x00, 0xc1, 0x06, 0x66, 0x04, 0x14, 0x00, 0x04, 0x1c, + 0x98, 0x0d, 0xf8, 0x90, 0xe5, 0x08, 0xb8, 0x2a, 0xe8, 0x8e, 0xe3, 0x38, + 0x9c, 0x06, 0xe6, 0xc1, 0xe2, 0x47, 0x01, 0x40, 0x11, 0x00, 0x8b, 0x1f, + 0x14, 0x00, 0x14, 0x01, 0xb0, 0xf8, 0x41, 0x01, 0x40, 0x11, 0x00, 0x8b, + 0x1f, 0x14, 0x00, 0x14, 0x01, 0xb0, 0xf8, 0x41, 0x01, 0x40, 0x11, 0x00, + 0x8b, 0x1f, 0x14, 0x00, 0x14, 0x01, 0xb0, 0xf8, 0x41, 0x01, 0x40, 0x11, + 0x00, 0x8b, 0x1f, 0x14, 0x00, 0x14, 0x01, 0xb0, 0xf8, 0x51, 0x00, 0x40, + 0x11, 0x00, 0x8b, 0x1f, 0x05, 0x00, 0x14, 0x01, 0xb0, 0xf8, 0x79, 0x3e, + 0xff, 0x2e, 0x00, 0x04, 0x33, 0x78, 0x63, 0x28, 0x00, 0xb0, 0x2f, 0xbf, + 0x00, 0xe0, 0x8d, 0x81, 0x02, 0x80, 0x60, 0x06, 0x6f, 0x0d, 0x14, 0x00, + 0x00, 0x40, 0x01, 0xc0, 0x17, 0x19, 0x78, 0x73, 0x28, 0x00, 0x00, 0x80, + 0x02, 0x00, 0xbe, 0xc4, 0xc0, 0xdb, 0x43, 0x01, 0x00, 0x00, 0x14, 0x00, + 0xf0, 0x05, 0x06, 0xde, 0x20, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x80, 0x2f, + 0x2f, 0xbc, 0x45, 0x50, 0x00, 0x00, 0x00, 0x05, 0x00, 0x7c, 0x71, 0xe1, + 0x4d, 0x82, 0x02, 0x00, 0x82, 0x16, 0x6f, 0x13, 0x14, 0x00, 0xd8, 0xcc, + 0xfc, 0xc3, 0x49, 0x38, 0x7f, 0x50, 0x00, 0x20, 0xf8, 0x85, 0x65, 0x09, + 0x39, 0x73, 0xbf, 0x02, 0xa0, 0x00, 0x40, 0x74, 0x01, 0xf9, 0x1a, 0xbd, + 0xee, 0xab, 0xdf, 0xb9, 0x83, 0x02, 0x80, 0xaf, 0xff, 0x5b, 0x2e, 0x28, + 0x5a, 0xe7, 0xea, 0x57, 0x00, 0x14, 0x00, 0x88, 0x7c, 0xfd, 0x2b, 0x02, + 0xd7, 0x2f, 0x7e, 0x67, 0x0d, 0x0a, 0x00, 0xbe, 0xfe, 0x7d, 0xb9, 0xfa, + 0xe2, 0xf7, 0x66, 0x41, 0x01, 0x80, 0xef, 0xff, 0xfa, 0x57, 0x04, 0xae, + 0x59, 0xfc, 0xce, 0x17, 0x14, 0x00, 0x7c, 0xfd, 0xfb, 0xb2, 0x75, 0x2e, + 0xde, 0x2e, 0xbc, 0x9a, 0x3b, 0x47, 0x80, 0x00, 0xbd, 0xee, 0x0b, 0xf3, + 0x8c, 0x3f, 0xe3, 0x55, 0x8b, 0xf3, 0xae, 0x7f, 0xed, 0x85, 0x77, 0x03, + 0x67, 0x58, 0x8e, 0x00, 0xae, 0x5f, 0x0e, 0xb5, 0xaf, 0x45, 0x4b, 0x11, + 0xae, 0xe7, 0x6f, 0x01, 0xe0, 0xeb, 0xff, 0x06, 0x0b, 0xab, 0xb4, 0x10, + 0x6b, 0x67, 0xe9, 0x6f, 0x05, 0xa0, 0x00, 0x80, 0x85, 0xf5, 0xf2, 0x3f, + 0xe7, 0xc9, 0x45, 0xe0, 0x3b, 0xff, 0x7c, 0x7e, 0x61, 0x00, 0x05, 0x00, + 0x5f, 0xff, 0x16, 0xa5, 0x3f, 0x8f, 0x37, 0x0d, 0x0a, 0x00, 0xec, 0xf3, + 0x25, 0xb9, 0xfb, 0xe2, 0xbc, 0xfa, 0xaf, 0xdf, 0xaf, 0x00, 0xa0, 0x00, + 0xe0, 0x4b, 0xc9, 0x17, 0xb4, 0xbf, 0x5e, 0x6f, 0x1b, 0x14, 0x00, 0xd8, + 0xf3, 0x0b, 0xf2, 0xee, 0x8b, 0xf5, 0x8e, 0x7f, 0x7d, 0x8a, 0x08, 0x28, + 0x00, 0xf8, 0x42, 0xf2, 0x8b, 0x80, 0x2f, 0x7e, 0x6f, 0x1c, 0x7e, 0x9e, + 0x5d, 0x47, 0x80, 0x70, 0xf4, 0xe5, 0xe8, 0x6d, 0x79, 0x5b, 0xf8, 0x05, + 0x00, 0x7c, 0x19, 0x81, 0xb7, 0x8e, 0x5f, 0x00, 0xa0, 0x17, 0x88, 0xbe, + 0xd0, 0xbc, 0x33, 0xef, 0x0c, 0xbf, 0x00, 0x00, 0x00, 0x7e, 0x01, 0x00, + 0x5f, 0x65, 0x78, 0x6f, 0xde, 0x1b, 0x7e, 0x01, 0x80, 0x67, 0xb5, 0x61, + 0x61, 0xec, 0x8e, 0x41, 0x01, 0x80, 0xde, 0xd7, 0x18, 0x98, 0x01, 0x14, + 0x00, 0xf0, 0x65, 0x88, 0xbb, 0x06, 0x05, 0x00, 0x7c, 0xf9, 0x80, 0x59, + 0x40, 0x01, 0x00, 0x5f, 0x84, 0xb8, 0x73, 0x50, 0x00, 0xc0, 0x17, 0x0f, + 0x98, 0x09, 0x14, 0x00, 0xf0, 0x25, 0x88, 0xbb, 0x07, 0x05, 0x00, 0x7c, + 0xe9, 0x80, 0xd9, 0xe0, 0x86, 0x25, 0xd8, 0x11, 0x50, 0x0d, 0x38, 0x5f, + 0x80, 0x78, 0x97, 0xf8, 0x05, 0x00, 0x6a, 0xcd, 0x57, 0xc8, 0xe2, 0x2d, + 0xa0, 0x00, 0x40, 0xef, 0x2b, 0x0b, 0xcc, 0x0a, 0x0a, 0x00, 0xf8, 0xe2, + 0xc3, 0x9b, 0xf0, 0x26, 0x50, 0x00, 0xc0, 0x17, 0x0d, 0x98, 0x19, 0x14, + 0x00, 0xf0, 0xa5, 0x87, 0xb7, 0x01, 0x0a, 0x00, 0xf8, 0x92, 0x01, 0xb3, + 0x83, 0x02, 0x00, 0xbe, 0xf0, 0xf0, 0x46, 0x40, 0x01, 0x00, 0x5f, 0x30, + 0x60, 0x86, 0xd8, 0xa4, 0xf0, 0x3a, 0x02, 0x0a, 0xc1, 0xe5, 0xcb, 0x0e, + 0xef, 0x17, 0xfc, 0x02, 0x00, 0x00, 0x7e, 0x01, 0x70, 0x04, 0xf8, 0x7a, + 0x02, 0xef, 0x18, 0xbf, 0x00, 0xc0, 0xb3, 0x1a, 0xae, 0xd0, 0xc4, 0xdb, + 0x01, 0x05, 0x80, 0xde, 0x57, 0x13, 0x98, 0x2d, 0x50, 0x00, 0xf0, 0x05, + 0x07, 0xde, 0x10, 0x28, 0x00, 0xf8, 0x42, 0x01, 0x33, 0x86, 0x02, 0x00, + 0xbe, 0xdc, 0xc0, 0x5b, 0x22, 0x64, 0x39, 0x82, 0x7d, 0x5b, 0xfe, 0x9d, + 0x82, 0xc9, 0x97, 0x09, 0x7c, 0xdf, 0xac, 0xed, 0x38, 0xfb, 0x8a, 0xd4, + 0x0d, 0xcb, 0xad, 0x23, 0x78, 0xc6, 0x02, 0xbd, 0x7a, 0xb8, 0xee, 0x54, + 0x00, 0x04, 0x0d, 0xde, 0xb8, 0xbc, 0x42, 0x01, 0xc8, 0x85, 0xca, 0x15, + 0x83, 0xe5, 0x7f, 0x2f, 0x8d, 0x59, 0x6d, 0xbc, 0xf3, 0x27, 0xe4, 0x15, + 0x0a, 0xc0, 0xe3, 0xc3, 0xe4, 0xbb, 0x06, 0x4b, 0x28, 0x62, 0x6e, 0x9f, + 0xff, 0xde, 0x9f, 0x92, 0x57, 0xfc, 0xca, 0x7f, 0x07, 0xe0, 0xa1, 0x01, + 0xf2, 0xe3, 0x3f, 0xc7, 0x60, 0x01, 0xf2, 0x0a, 0xbf, 0x00, 0x84, 0xbf, + 0x1c, 0xbe, 0x62, 0xb0, 0x7c, 0xfd, 0x63, 0x96, 0x9f, 0xf9, 0xee, 0x9f, + 0x98, 0x57, 0x28, 0x00, 0xc2, 0xe2, 0xc4, 0xc1, 0xf2, 0x5f, 0x8a, 0xc2, + 0x5c, 0x9b, 0x69, 0x73, 0xad, 0x00, 0x70, 0xf3, 0x41, 0x3a, 0x7b, 0xa8, + 0x04, 0x05, 0xe6, 0xdb, 0x4c, 0x9b, 0x6f, 0x05, 0x80, 0x4d, 0x86, 0xe8, + 0xcc, 0xa1, 0xf2, 0xf5, 0x8f, 0x59, 0x7f, 0xc6, 0x0c, 0x54, 0x32, 0x0b, + 0x05, 0xc0, 0xf2, 0x3f, 0x61, 0xa8, 0x7c, 0xfd, 0xc3, 0xfe, 0x73, 0x50, + 0xc9, 0x2b, 0x14, 0x00, 0x8b, 0xff, 0xc4, 0xc1, 0xf2, 0xf5, 0x0f, 0xfb, + 0xce, 0x42, 0x2d, 0xaf, 0x50, 0x00, 0x2c, 0xfe, 0x93, 0x06, 0xcb, 0xd7, + 0x3f, 0xec, 0x39, 0x0f, 0xc5, 0xbc, 0x42, 0x01, 0xb0, 0xf8, 0x4f, 0x1a, + 0x2c, 0xcb, 0x1f, 0xf6, 0x2b, 0x01, 0xd5, 0xbc, 0xe2, 0x35, 0xff, 0x20, + 0x20, 0x8b, 0xff, 0xe5, 0x9f, 0xd7, 0x60, 0x81, 0xbc, 0x92, 0x57, 0x7e, + 0x01, 0x30, 0x48, 0x68, 0xfb, 0x20, 0x23, 0x64, 0x84, 0x02, 0x60, 0xa8, + 0x31, 0xd8, 0x20, 0x2f, 0xe4, 0xc5, 0x8e, 0xfc, 0x2d, 0x00, 0x83, 0x0c, + 0xf0, 0xf8, 0x3c, 0x57, 0x04, 0xfc, 0x02, 0x60, 0xf1, 0x6b, 0xf3, 0x20, + 0x43, 0x64, 0x08, 0x7e, 0x01, 0x30, 0xb4, 0x00, 0x7e, 0x11, 0xf0, 0x0b, + 0x80, 0xc5, 0x8f, 0xe6, 0x0e, 0x72, 0x45, 0xae, 0xf8, 0x05, 0xc0, 0x80, + 0x02, 0xe0, 0x17, 0x01, 0xbf, 0x00, 0x58, 0xfc, 0x68, 0xe9, 0x20, 0x6b, + 0x64, 0x8d, 0x5f, 0x00, 0x0c, 0x23, 0x00, 0x7e, 0x11, 0xf0, 0x0b, 0x80, + 0xc5, 0x8f, 0x46, 0x0e, 0xf2, 0x47, 0xfe, 0xf8, 0x05, 0xc0, 0xe0, 0x01, + 0xe0, 0x17, 0x01, 0xbf, 0x00, 0x58, 0xfc, 0x68, 0xdf, 0xf8, 0x15, 0x00, + 0x99, 0xa4, 0x00, 0x18, 0x32, 0x2c, 0x7f, 0x94, 0x00, 0xe4, 0xd3, 0xb5, + 0x96, 0xc1, 0x02, 0x80, 0x3f, 0xdb, 0x3f, 0x3b, 0x17, 0x81, 0xd9, 0xf9, + 0xe0, 0xd1, 0xae, 0x41, 0x66, 0x21, 0xb3, 0x22, 0x05, 0xc0, 0x20, 0x19, + 0x2a, 0x90, 0x57, 0xc8, 0xab, 0x50, 0x01, 0x30, 0x48, 0x06, 0xcb, 0x29, + 0x20, 0xaf, 0x90, 0x57, 0xa1, 0x02, 0x60, 0x90, 0x50, 0x04, 0xb0, 0xf8, + 0x91, 0x57, 0xa1, 0x02, 0x60, 0x90, 0x50, 0x04, 0xb0, 0xf8, 0x91, 0x57, + 0xa1, 0x02, 0x60, 0x90, 0x50, 0x04, 0xb0, 0xf8, 0x91, 0x57, 0xa1, 0x02, + 0x60, 0x90, 0x50, 0x04, 0xb0, 0xf8, 0x91, 0x57, 0xa1, 0x02, 0x60, 0x90, + 0x50, 0x02, 0xb0, 0xfc, 0x91, 0x59, 0xa1, 0x02, 0x60, 0x88, 0x50, 0x04, + 0xb0, 0xf8, 0xe1, 0xba, 0xcc, 0x1a, 0x43, 0x84, 0xa1, 0x02, 0x99, 0x45, + 0x2f, 0xb3, 0xc6, 0x10, 0x61, 0xa8, 0x40, 0x66, 0xd1, 0xcb, 0xac, 0x31, + 0x44, 0x18, 0x2a, 0x90, 0x59, 0xf4, 0x32, 0x6b, 0x0c, 0x11, 0x86, 0x0a, + 0x64, 0x16, 0xbd, 0xcc, 0x1a, 0x43, 0x84, 0xa1, 0xc2, 0xe2, 0x87, 0x5e, + 0x66, 0x8d, 0x21, 0xc2, 0x50, 0x29, 0x02, 0x16, 0x3f, 0xf4, 0x32, 0x6b, + 0x0c, 0x11, 0x28, 0x02, 0x16, 0x3f, 0xf4, 0x32, 0x6b, 0x0c, 0x11, 0x28, + 0x02, 0x16, 0x3f, 0xf4, 0x32, 0x6b, 0x0c, 0x11, 0x28, 0x02, 0x16, 0x3f, + 0xf4, 0x32, 0x6b, 0x0c, 0x11, 0x28, 0x02, 0x16, 0x3f, 0xf4, 0x32, 0x6b, + 0x0c, 0x11, 0x28, 0x02, 0x16, 0x3f, 0xf4, 0x32, 0x6b, 0x0c, 0x12, 0x28, + 0x02, 0x16, 0x3f, 0xf4, 0xf2, 0x6a, 0x0c, 0x12, 0x28, 0x01, 0x96, 0x3f, + 0xf4, 0x32, 0x6b, 0x0c, 0x11, 0x28, 0x02, 0x16, 0x3f, 0xf4, 0x32, 0x6b, + 0x0c, 0x11, 0x28, 0x02, 0x16, 0x3f, 0xf4, 0x32, 0x6b, 0x0c, 0x14, 0x58, + 0xfe, 0x4a, 0x00, 0xf4, 0x32, 0x6b, 0x0c, 0x15, 0x58, 0xfc, 0x8a, 0x00, + 0xf4, 0x32, 0x6b, 0x0c, 0x15, 0x58, 0xfc, 0x8a, 0x00, 0xf4, 0x32, 0xcb, + 0x3f, 0x07, 0x00, 0x2c, 0x7e, 0x45, 0x00, 0x82, 0x99, 0xe5, 0x9f, 0x04, + 0x08, 0x16, 0xbf, 0x22, 0x00, 0xc1, 0xcc, 0xf2, 0xef, 0x02, 0x00, 0x8b, + 0x5f, 0x11, 0x80, 0x60, 0x66, 0xf9, 0xb7, 0x01, 0x82, 0xc5, 0xaf, 0x08, + 0x40, 0x30, 0xb3, 0xc6, 0x50, 0x81, 0xc5, 0xaf, 0x08, 0x40, 0x2f, 0xb3, + 0xc6, 0x50, 0x61, 0x88, 0x2c, 0x7e, 0x45, 0x00, 0x7a, 0x99, 0x35, 0x86, + 0x0a, 0x43, 0x84, 0x22, 0x00, 0xbd, 0xcc, 0x1a, 0x43, 0x85, 0x21, 0x02, + 0x99, 0x45, 0x2f, 0xb3, 0xc6, 0x50, 0x61, 0x88, 0x40, 0x66, 0xd1, 0xcb, + 0xac, 0x31, 0x54, 0x18, 0x22, 0x90, 0x59, 0xf4, 0x32, 0x6b, 0x0c, 0x15, + 0x86, 0x08, 0x64, 0x16, 0xbd, 0xcc, 0x1a, 0x43, 0x85, 0x21, 0x02, 0x99, + 0x45, 0x2f, 0xb3, 0xc6, 0x50, 0x61, 0x88, 0x40, 0x66, 0xd1, 0xcb, 0xac, + 0x31, 0x54, 0x18, 0x22, 0x90, 0x59, 0xf4, 0x32, 0x6b, 0x0c, 0x16, 0x86, + 0x08, 0xe4, 0x15, 0xbd, 0xbc, 0x1a, 0x83, 0x85, 0x41, 0x02, 0x79, 0x45, + 0x2f, 0xaf, 0xc6, 0x60, 0x61, 0x90, 0x40, 0x5e, 0xd1, 0xcb, 0xab, 0x31, + 0x58, 0x18, 0x24, 0x90, 0x57, 0xf4, 0xf2, 0x6a, 0x0c, 0x16, 0x06, 0x09, + 0xe4, 0x15, 0xbd, 0xbc, 0x1a, 0x83, 0x85, 0x41, 0x02, 0x79, 0x45, 0x2f, + 0xaf, 0xc6, 0x60, 0x61, 0x90, 0x40, 0x5e, 0xd1, 0xcb, 0xab, 0x31, 0x58, + 0x18, 0x24, 0x90, 0x57, 0xf4, 0xf2, 0x6a, 0x0c, 0x16, 0x06, 0x09, 0xe4, + 0x15, 0xbd, 0xbc, 0x1a, 0x83, 0x85, 0x41, 0x02, 0x79, 0x45, 0x2f, 0xaf, + 0xc6, 0x60, 0x61, 0x90, 0x40, 0x5e, 0xd1, 0xcb, 0xab, 0x31, 0x58, 0x18, + 0x24, 0x90, 0x57, 0xf4, 0xf2, 0x6a, 0x0c, 0x16, 0x06, 0x09, 0xe4, 0x95, + 0xbc, 0xea, 0xe5, 0xd5, 0x18, 0x2c, 0x0c, 0x12, 0xc8, 0x2b, 0x79, 0x15, + 0xfc, 0xb3, 0xbb, 0x7e, 0x83, 0x65, 0x90, 0x40, 0x5e, 0xc9, 0xab, 0xe0, + 0x19, 0xfc, 0x78, 0x4c, 0x0e, 0xc3, 0x60, 0x19, 0xa4, 0xcf, 0xbd, 0x1b, + 0x33, 0xe4, 0xbc, 0xe5, 0x95, 0xbc, 0xda, 0x6a, 0x86, 0x7e, 0x7f, 0x44, + 0x0e, 0xc7, 0x60, 0x19, 0xa4, 0x3f, 0x7b, 0x27, 0x66, 0xc7, 0xb9, 0xcb, + 0x2b, 0x79, 0xb5, 0xd5, 0xec, 0xfc, 0xd7, 0xe3, 0x71, 0x58, 0x06, 0xcb, + 0x20, 0x7d, 0xec, 0x5d, 0x98, 0x19, 0xe7, 0x2f, 0xaf, 0xe4, 0xd5, 0x56, + 0x33, 0xf3, 0xea, 0xd1, 0x38, 0x3c, 0x83, 0xe5, 0x2d, 0xbc, 0xef, 0x1d, + 0x98, 0x15, 0xf7, 0x20, 0xaf, 0xe4, 0xd5, 0x56, 0xb3, 0xf2, 0xde, 0xc7, + 0xe2, 0x30, 0xbb, 0x83, 0x25, 0x50, 0xcd, 0x88, 0xfb, 0x90, 0x57, 0xf2, + 0xea, 0x81, 0x33, 0xf2, 0xd1, 0x47, 0xe2, 0x70, 0x3b, 0x83, 0x25, 0x40, + 0xcd, 0x86, 0x7b, 0x91, 0x57, 0xf2, 0xea, 0xc1, 0xb3, 0xf1, 0xa7, 0x8f, + 0xc3, 0x61, 0x3f, 0x77, 0xb0, 0x04, 0xa6, 0x99, 0x70, 0x3f, 0xce, 0x4d, + 0x5e, 0x05, 0x66, 0xe2, 0xb3, 0x8f, 0xc2, 0xe1, 0x3f, 0x67, 0xb0, 0x04, + 0xa4, 0x59, 0x70, 0x4f, 0xce, 0x4f, 0x5e, 0x85, 0x66, 0xe1, 0xac, 0xc7, + 0xe0, 0x32, 0xf6, 0x1d, 0x2c, 0x81, 0x68, 0x06, 0xdc, 0x97, 0x73, 0x94, + 0x57, 0xc1, 0x19, 0x38, 0xfb, 0x11, 0xb8, 0x9c, 0x7d, 0x06, 0x4b, 0x00, + 0x7a, 0xfb, 0xee, 0xcd, 0xbd, 0xc9, 0xab, 0xf0, 0xdb, 0xff, 0xaa, 0xcb, + 0x77, 0x59, 0xf7, 0x1d, 0x2c, 0x81, 0xe7, 0xcd, 0xbb, 0x3f, 0xf7, 0x27, + 0xaf, 0xdc, 0xcd, 0x7c, 0xf5, 0xa5, 0xbb, 0xbc, 0xfb, 0x0c, 0x96, 0x80, + 0xf3, 0xd6, 0xdd, 0xa3, 0x7b, 0x94, 0x57, 0xee, 0xe2, 0xdb, 0x0a, 0x80, + 0xcb, 0xbc, 0x7e, 0xb0, 0x04, 0x9a, 0x37, 0xee, 0x3e, 0xdd, 0xa7, 0xbc, + 0x72, 0xf6, 0x97, 0x15, 0x00, 0x97, 0x2b, 0xc0, 0x9e, 0x1e, 0x60, 0xde, + 0xb6, 0x7b, 0x75, 0xde, 0xce, 0x79, 0xab, 0xb7, 0xad, 0xe5, 0x3d, 0xef, + 0xb2, 0x05, 0x96, 0x37, 0xed, 0x7e, 0xdd, 0xaf, 0xbc, 0x72, 0xb6, 0xb7, + 0x2d, 0x00, 0x2e, 0xff, 0xfc, 0xcb, 0x17, 0x50, 0xde, 0xb2, 0x7b, 0x76, + 0xcf, 0xf2, 0xca, 0x5b, 0xde, 0xa6, 0x00, 0x78, 0x0c, 0x9f, 0x7f, 0x0c, + 0x02, 0xc9, 0x1b, 0x76, 0xdf, 0xee, 0x5b, 0x5e, 0x79, 0xc3, 0xdb, 0x16, + 0x00, 0x8f, 0xe3, 0xe3, 0x8f, 0x43, 0x00, 0x79, 0xbb, 0xee, 0xdd, 0xbd, + 0xcb, 0x2b, 0x6f, 0xf7, 0x31, 0x05, 0xc0, 0x63, 0x79, 0xfd, 0x58, 0x04, + 0x8e, 0x37, 0xeb, 0xfe, 0xdd, 0xbf, 0xbc, 0xf2, 0x66, 0x1f, 0x5b, 0x00, + 0x3c, 0x9e, 0x7f, 0x3f, 0x1e, 0x01, 0xe3, 0xad, 0xe2, 0x1d, 0xc8, 0x2b, + 0x6f, 0x35, 0x53, 0x00, 0x0c, 0x15, 0xde, 0x28, 0xde, 0x03, 0xde, 0xe8, + 0xf9, 0x96, 0x56, 0x89, 0x21, 0x02, 0x99, 0x45, 0x2f, 0xb3, 0x96, 0xa1, + 0xc2, 0x10, 0x81, 0xcc, 0xa2, 0x97, 0x59, 0xcb, 0x50, 0x61, 0x88, 0x40, + 0x66, 0xd1, 0xcb, 0xac, 0x65, 0xa8, 0x30, 0x44, 0x20, 0xb3, 0xe8, 0x65, + 0xd6, 0x32, 0x54, 0x18, 0x22, 0x90, 0x59, 0xf4, 0x32, 0x6b, 0x19, 0x2a, + 0x0c, 0x11, 0xc8, 0x2c, 0x7a, 0x99, 0xb5, 0x0c, 0x15, 0x86, 0x08, 0x64, + 0x16, 0xbd, 0xcc, 0x5a, 0x86, 0x0a, 0x43, 0x04, 0x32, 0x8b, 0x5e, 0x66, + 0x2d, 0x43, 0x85, 0x21, 0x02, 0x99, 0x45, 0x2f, 0xb3, 0x96, 0xa1, 0xc2, + 0x10, 0x81, 0xcc, 0x72, 0xc6, 0x3d, 0xcb, 0x85, 0x1b, 0x2c, 0x43, 0x04, + 0x8a, 0x80, 0xbc, 0x52, 0x00, 0x0c, 0x16, 0x06, 0x09, 0xe4, 0x95, 0xbc, + 0x52, 0x00, 0x0c, 0x16, 0x06, 0x09, 0xe4, 0x95, 0xbc, 0x52, 0x00, 0x0c, + 0x16, 0x06, 0x09, 0xe4, 0x95, 0xbc, 0x52, 0x00, 0x0c, 0x96, 0x41, 0x02, + 0xe4, 0x95, 0xbc, 0xda, 0xd3, 0x38, 0xa4, 0x0f, 0x1c, 0x56, 0x74, 0xb0, + 0xbc, 0x11, 0x90, 0x57, 0xf2, 0x4a, 0x01, 0x20, 0x34, 0x58, 0xde, 0x06, + 0xc8, 0x2b, 0x79, 0xa5, 0x00, 0x10, 0x1a, 0x2c, 0x6f, 0x02, 0xe4, 0x95, + 0xbc, 0x52, 0x00, 0x08, 0x0d, 0x96, 0xb7, 0x00, 0xf2, 0x4a, 0x5e, 0x29, + 0x00, 0x84, 0x06, 0xcb, 0x1b, 0x00, 0x79, 0x25, 0xaf, 0x14, 0x00, 0x42, + 0x83, 0xe5, 0xee, 0x01, 0x79, 0xa5, 0x00, 0x10, 0x1a, 0x2c, 0x77, 0x0e, + 0xc8, 0x2b, 0x14, 0x80, 0xd0, 0x60, 0xb9, 0x6b, 0x40, 0x5e, 0xa1, 0x00, + 0x84, 0x06, 0xcb, 0x1d, 0x03, 0xf2, 0x8a, 0xdf, 0xbd, 0x39, 0x02, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x50, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x00, 0x50, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x50, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x9e, 0x62, + 0x39, 0x02, 0x38, 0xc7, 0xcc, 0x8c, 0x53, 0xf8, 0x7a, 0xc7, 0x71, 0x1c, + 0x4e, 0x01, 0xfc, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, + 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, + 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xb9, 0x02, + 0x30, 0x33, 0xe3, 0x18, 0xe0, 0x73, 0xcc, 0x91, 0xb3, 0x86, 0xdd, 0xe6, + 0xc8, 0x2f, 0x00, 0x00, 0x50, 0xfc, 0x05, 0x40, 0xa3, 0x06, 0x5f, 0xa4, + 0xce, 0x1c, 0x7a, 0xf3, 0xe3, 0x17, 0x00, 0x00, 0xa8, 0xfe, 0x02, 0xa0, + 0x51, 0x83, 0x2f, 0x51, 0x67, 0x0f, 0xad, 0xb9, 0x79, 0x33, 0x50, 0x60, + 0x01, 0xb9, 0x03, 0xe8, 0xcd, 0xcb, 0x9b, 0x81, 0x02, 0x8b, 0xc7, 0x5d, + 0x40, 0x6f, 0x4e, 0xde, 0x0c, 0x14, 0x58, 0x38, 0xee, 0x04, 0x7a, 0xf3, + 0xb1, 0x5e, 0xfd, 0x3f, 0x1c, 0xc7, 0x71, 0x38, 0x3e, 0xb0, 0x64, 0x76, + 0xb9, 0x1f, 0x99, 0x05, 0xef, 0xcb, 0xac, 0x65, 0xa8, 0xc0, 0xe2, 0x57, + 0x04, 0xa0, 0x97, 0x59, 0xcb, 0x50, 0x81, 0xc5, 0xaf, 0x08, 0x40, 0x2f, + 0xb3, 0x96, 0xa1, 0x02, 0x8b, 0x5f, 0x11, 0x80, 0x5e, 0x66, 0x2d, 0x43, + 0x05, 0x16, 0xbf, 0x22, 0x00, 0xbd, 0xcc, 0x5a, 0x86, 0x0a, 0x43, 0x64, + 0xf1, 0x2b, 0x02, 0xd0, 0xcb, 0xac, 0x65, 0xa8, 0x30, 0x44, 0x28, 0x02, + 0xd0, 0xcb, 0xac, 0x65, 0xa8, 0x30, 0x44, 0x78, 0x0f, 0x32, 0x8b, 0x5e, + 0x66, 0x2d, 0x43, 0x85, 0x21, 0x02, 0x99, 0x45, 0x2f, 0xb3, 0x96, 0xa1, + 0xc2, 0x10, 0x81, 0xcc, 0xa2, 0x97, 0x59, 0xcb, 0x50, 0x61, 0x88, 0x40, + 0x66, 0xd1, 0xcb, 0xac, 0x65, 0xa8, 0x30, 0x44, 0x20, 0xb3, 0xe8, 0x65, + 0xd6, 0x32, 0x54, 0x18, 0x22, 0x90, 0x59, 0xf4, 0x32, 0x6b, 0x19, 0x2a, + 0x0c, 0x11, 0xc8, 0x2c, 0x7a, 0x99, 0xb5, 0x0c, 0x15, 0x86, 0x08, 0x64, + 0x16, 0xbd, 0xcc, 0x5a, 0x86, 0x0a, 0x43, 0x04, 0x32, 0x8b, 0x5e, 0x66, + 0x2d, 0x43, 0x85, 0x21, 0x02, 0x99, 0x45, 0x2f, 0xb3, 0x96, 0xa1, 0xc2, + 0x10, 0x81, 0xcc, 0xa2, 0x97, 0x59, 0xcb, 0x50, 0x61, 0x88, 0x40, 0x66, + 0xd1, 0xcb, 0xac, 0x65, 0xa8, 0x30, 0x44, 0x20, 0xb3, 0xe8, 0x65, 0xd6, + 0x32, 0x54, 0x18, 0x22, 0x90, 0x59, 0xf4, 0x32, 0x6b, 0x19, 0x2a, 0x0c, + 0x11, 0xc8, 0x2c, 0x7a, 0x99, 0xb5, 0x0c, 0x15, 0x86, 0x08, 0x64, 0x16, + 0xbd, 0xcc, 0x5a, 0x86, 0x0a, 0x43, 0x04, 0x32, 0x8b, 0x5e, 0x66, 0x2d, + 0x43, 0x85, 0x21, 0x02, 0x99, 0x45, 0x2f, 0xb3, 0x96, 0xa1, 0xc2, 0x10, + 0x81, 0xcc, 0xa2, 0x97, 0x59, 0xcb, 0x50, 0x61, 0x88, 0x40, 0x66, 0xd1, + 0xcb, 0xac, 0x65, 0xa8, 0x30, 0x44, 0x20, 0xb3, 0x9c, 0x71, 0xcf, 0x32, + 0x54, 0x86, 0xca, 0x10, 0x81, 0xcc, 0x92, 0x59, 0x0a, 0x80, 0xa1, 0xc2, + 0x10, 0x81, 0xcc, 0x92, 0x59, 0x0a, 0x80, 0xa1, 0xc2, 0x10, 0x81, 0xcc, + 0x92, 0x59, 0x0a, 0x80, 0xa1, 0xc2, 0x10, 0x81, 0xcc, 0x92, 0x59, 0x0a, + 0x80, 0xa1, 0x32, 0x44, 0x80, 0xcc, 0x92, 0x59, 0x0a, 0x80, 0xa1, 0x32, + 0x44, 0x80, 0xcc, 0x92, 0x59, 0x0a, 0x80, 0xa1, 0x32, 0x44, 0x80, 0xcc, + 0x92, 0x59, 0x0a, 0x80, 0xa1, 0x32, 0x44, 0x80, 0xcc, 0x92, 0x59, 0x0a, + 0x80, 0xa1, 0x32, 0x44, 0x80, 0xcc, 0x92, 0x59, 0x0a, 0xc0, 0x23, 0x1e, + 0xe0, 0xae, 0x83, 0x65, 0x88, 0x40, 0x11, 0x90, 0x57, 0x0a, 0x00, 0xa1, + 0xc1, 0x32, 0x48, 0x20, 0xaf, 0xe4, 0x95, 0x02, 0x40, 0x68, 0xb0, 0x0c, + 0x12, 0x20, 0xaf, 0x50, 0x00, 0x42, 0x83, 0x65, 0x90, 0x00, 0x79, 0xc5, + 0x0f, 0x7f, 0x03, 0x6b, 0x00, 0x01, 0xd3, 0x9f, 0x8a, 0xff, 0x63, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +} diff --git a/cmd/agent/installer.go b/cmd/agent/installer.go new file mode 100644 index 0000000..3e7b408 --- /dev/null +++ b/cmd/agent/installer.go @@ -0,0 +1,163 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "syscall" + "time" + + events "github.com/c3os-io/c3os/pkg/bus" + config "github.com/c3os-io/c3os/pkg/config" + + "github.com/c3os-io/c3os/internal/bus" + "github.com/c3os-io/c3os/internal/c3os" + "github.com/c3os-io/c3os/internal/cmd" + "github.com/c3os-io/c3os/internal/utils" + + machine "github.com/c3os-io/c3os/internal/machine" + qr "github.com/mudler/go-nodepair/qrcode" + "github.com/mudler/go-pluggable" + "github.com/pterm/pterm" + "gopkg.in/yaml.v2" +) + +func optsToArgs(options map[string]string) (res []string) { + for k, v := range options { + if k != "device" && k != "cc" && k != "reboot" && k != "poweroff" { + res = append(res, fmt.Sprintf("--%s", k)) + res = append(res, fmt.Sprintf("%s", v)) + } + } + return +} + +func install(dir ...string) error { + utils.OnSignal(func() { + svc, err := machine.Getty(1) + if err == nil { + svc.Start() + } + }, syscall.SIGINT, syscall.SIGTERM) + + tk := "" + r := map[string]string{} + bus.Manager.Response(events.EventChallenge, func(p *pluggable.Plugin, r *pluggable.EventResponse) { + tk = r.Data + }) + bus.Manager.Response(events.EventInstall, func(p *pluggable.Plugin, resp *pluggable.EventResponse) { + err := json.Unmarshal([]byte(resp.Data), &r) + if err != nil { + fmt.Println(err) + } + }) + + // Reads config, and if present and offline is defined, + // runs the installation + cc, err := config.Scan(dir...) + if err == nil && cc.C3OS != nil && cc.C3OS.Offline { + runInstall(map[string]string{ + "device": cc.C3OS.Device, + "cc": cc.String(), + }) + + svc, err := machine.Getty(1) + if err == nil { + svc.Start() + } + + return nil + } + + _, err = bus.Manager.Publish(events.EventChallenge, events.EventPayload{Config: cc.String()}) + if err != nil { + return err + } + + cmd.PrintBranding(banner) + + cmd.PrintTextFromFile(c3os.BrandingFile("install_text"), "Installation") + + time.Sleep(5 * time.Second) + + if tk != "" { + qr.Print(tk) + } + + if _, err := bus.Manager.Publish(events.EventInstall, events.InstallPayload{Token: tk, Config: cc.String()}); err != nil { + return err + } + + if len(r) == 0 { + return errors.New("no configuration, stopping installation") + } + + pterm.Info.Println("Starting installation") + utils.SH("elemental run-stage c3os-install.pre") + bus.RunHookScript("/usr/bin/c3os-agent.install.pre.hook") + + runInstall(r) + + pterm.Info.Println("Installation completed, press enter to go back to the shell.") + + utils.Prompt("") + + // give tty1 back + svc, err := machine.Getty(1) + if err == nil { + svc.Start() + } + + return nil +} + +func runInstall(options map[string]string) error { + f, _ := ioutil.TempFile("", "xxxx") + + device, ok := options["device"] + if !ok { + fmt.Println("device must be specified among options") + os.Exit(1) + } + + cloudInit, ok := options["cc"] + if !ok { + fmt.Println("cloudInit must be specified among options") + os.Exit(1) + } + + c := &config.Config{} + yaml.Unmarshal([]byte(cloudInit), c) + + _, reboot := options["reboot"] + _, poweroff := options["poweroff"] + + ioutil.WriteFile(f.Name(), []byte(cloudInit), os.ModePerm) + args := []string{"install"} + args = append(args, optsToArgs(options)...) + args = append(args, "-c", f.Name(), fmt.Sprintf("%s", device)) + + cmd := exec.Command("elemental", args...) + cmd.Env = os.Environ() + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Println(err) + os.Exit(1) + } + utils.SH("elemental run-stage c3os-install.after") + bus.RunHookScript("/usr/bin/c3os-agent.install.after.hook") + + if reboot || c.C3OS != nil && c.C3OS.Reboot { + utils.Reboot() + } + + if poweroff || c.C3OS != nil && c.C3OS.Poweroff { + utils.PowerOFF() + } + return nil +} diff --git a/cmd/agent/installer_suite_test.go b/cmd/agent/installer_suite_test.go new file mode 100644 index 0000000..6984f88 --- /dev/null +++ b/cmd/agent/installer_suite_test.go @@ -0,0 +1,13 @@ +package main_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestInstaller(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Installer Suite") +} diff --git a/cmd/agent/main.go b/cmd/agent/main.go new file mode 100644 index 0000000..80d910b --- /dev/null +++ b/cmd/agent/main.go @@ -0,0 +1,286 @@ +package main + +import ( + //"fmt" + + "context" + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/c3os-io/c3os/internal/bus" + cmd "github.com/c3os-io/c3os/internal/cmd" + machine "github.com/c3os-io/c3os/internal/machine" + + "github.com/c3os-io/c3os/internal/github" + config "github.com/c3os-io/c3os/pkg/config" + "github.com/urfave/cli" + "gopkg.in/yaml.v2" +) + +var cmds = []cli.Command{ + { + Name: "upgrade", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "force", + Usage: "Force an upgrade", + }, + &cli.StringFlag{ + Name: "image", + Usage: "Specify an full image reference, e.g.: quay.io/some/image:tag", + }, + }, + Description: ` +Manually upgrade a c3os node. + +By default takes no arguments, defaulting to latest available release, to specify a version, pass it as argument: + +$ c3os upgrade v1.20.... + +To retrieve all the available versions, use "c3os upgrade list-releases" + +$ c3os upgrade list-releases + +See https://docs.c3os.io/after_install/upgrades/#manual for documentation. + +`, + Subcommands: []cli.Command{ + { + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "output", + Usage: "Output format (json|yaml|terminal)", + }, + }, + Name: "list-releases", + Description: `List all available releases versions`, + Action: func(c *cli.Context) error { + rels, err := github.FindReleases(context.Background(), "", "c3os-io/c3os") + if err != nil { + return err + } + + switch strings.ToLower(c.String("output")) { + case "yaml": + d, _ := yaml.Marshal(rels) + fmt.Println(string(d)) + case "json": + d, _ := json.Marshal(rels) + fmt.Println(string(d)) + default: + for _, r := range rels { + fmt.Println(r) + } + } + + return nil + }, + }, + }, + Action: func(c *cli.Context) error { + args := c.Args() + var v string + if len(args) == 1 { + v = args[0] + } + + return upgrade(v, c.String("image"), c.Bool("force")) + }, + }, + + { + Name: "start", + Usage: "Starts the c3os agent", + UsageText: "starts the agent", + Description: ` +Starts the c3os agent which automatically bootstrap and advertize to the c3os network. +`, + Aliases: []string{"s"}, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "force", + }, + &cli.StringFlag{ + Name: "api", + Value: "http://127.0.0.1:8080", + }, + }, + Action: func(c *cli.Context) error { + dirs := []string{"/oem", "/usr/local/cloud-config"} + args := c.Args() + if len(args) > 0 { + dirs = args + } + + return agent(c.String("api"), dirs, c.Bool("force")) + }, + }, + { + Name: "rotate", + Usage: "Rotate a c3os node network configuration via CLI", + Description: ` + +Updates a c3os node VPN configuration. + +For example, to update the network token in a node: +$ c3os rotate --network-token XXX +`, + Aliases: []string{"r"}, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "restart", + }, + &cli.StringFlag{ + Name: "network-token", + EnvVar: "NETWORK_TOKEN", + }, + &cli.StringFlag{ + Name: "api", + Value: "127.0.0.1:8080", + }, + &cli.StringFlag{ + Name: "root-dir", + }, + }, + UsageText: "Rotate network token manually in the node", + Action: func(c *cli.Context) error { + dirs := []string{"/oem", "/usr/local/cloud-config"} + args := c.Args() + if len(args) > 0 { + dirs = args + } + + return rotate(dirs, c.String("network-token"), c.String("api"), c.String("root-dir"), c.Bool("restart")) + }, + }, + + { + Name: "get-network-token", + Description: "Print network token from local configuration", + Usage: "Print network token from local configuration", + Action: func(c *cli.Context) error { + dirs := []string{"/oem", "/usr/local/cloud-config"} + args := c.Args() + if len(args) > 0 { + dirs = args + } + cc, err := config.Scan(dirs...) + if err != nil { + return err + } + fmt.Print(cc.C3OS.NetworkToken) + return nil + }, + }, + { + Name: "uuid", + Usage: "Prints the local UUID", + Description: "Print node uuid", + Aliases: []string{"u"}, + Action: func(c *cli.Context) error { + fmt.Print(machine.UUID()) + return nil + }, + }, + { + Name: "interactive-install", + Description: ` +Starts c3os in interactive mode. + +It will ask prompt for several questions and perform an install + +See also https://docs.c3os.io/installation/interactive_install/ for documentation. + +This command is meant to be used from the boot GRUB menu, but can be started manually`, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "shell", + }, + }, + Usage: "Starts interactive installation", + Action: func(c *cli.Context) error { + return interactiveInstall(c.Bool("shell")) + }, + }, + { + Name: "install", + Usage: "Starts the c3os pairing installation", + Description: ` +Starts c3os in pairing mode. + +It will print out a QR code which can be used with "c3os register" to send over a configuration and bootstraping a c3os node. + +See also https://docs.c3os.io/installation/device_pairing/ for documentation. + +This command is meant to be used from the boot GRUB menu, but can be started manually`, + Aliases: []string{"i"}, + Action: func(c *cli.Context) error { + return install("/oem", "/usr/local/cloud-config", "/run/initramfs/live") + }, + }, + { + Name: "recovery", + Aliases: []string{"r"}, + Action: recovery, + Usage: "Starts c3os recovery mode", + Description: ` +Starts c3os recovery mode. + +In recovery mode a QR code will be printed out on the screen which should be used in conjuction with "c3os bridge". Pass by the QR code as snapshot +to the bridge to connect over the machine which runs the "c3os recovery" command. + +See also https://docs.c3os.io/after_install/recovery_mode/ for documentation. + +This command is meant to be used from the boot GRUB menu, but can likely be used standalone`, + }, + + { + Name: "reset", + Action: reset, + Usage: "Starts c3os reset mode", + Description: ` +Starts c3os reset mode, it will nuke completely the node data and restart fresh. +Attention ! this will delete any persistent data on the node. It is equivalent to re-init the node right after the installation. + +In reset mode a the node will automatically reset + +See also https://docs.c3os.io/after_install/reset_mode/ for documentation. + +This command is meant to be used from the boot GRUB menu, but can likely be used standalone`, + }, +} + +func main() { + bus.Manager.Initialize() + + app := &cli.App{ + Name: "c3os", + Version: "0.1", + Author: "Ettore Di Giacinto", + Usage: "c3os CLI to bootstrap, upgrade, connect and manage a c3os network", + Description: ` +The c3os CLI can be used to manage a c3os box and perform all day-two tasks, like: +- register a node +- connect to a node in recovery mode +- to establish a VPN connection +- set, list roles +- interact with the network API + +and much more. + +For all the example cases, see: https://docs.c3os.io . +`, + UsageText: ``, + Copyright: "Ettore Di Giacinto", + + Commands: cmd.CommonCommand(cmds...), + } + + err := app.Run(os.Args) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/cmd/agent/make_art.sh b/cmd/agent/make_art.sh new file mode 100755 index 0000000..6663c11 --- /dev/null +++ b/cmd/agent/make_art.sh @@ -0,0 +1,36 @@ +#/bin/sh + +if [ -z "$GOPATH" ]; then + echo GOPATH environment variable not set + exit +fi + +if [ ! -e "$GOPATH/bin/2goarray" ]; then + echo "Installing 2goarray..." + go get github.com/cratonica/2goarray + if [ $? -ne 0 ]; then + echo Failure executing go get github.com/cratonica/2goarray + exit + fi +fi + +if [ -z "$1" ]; then + echo Please specify a PNG file + exit +fi + +if [ ! -f "$1" ]; then + echo $1 is not a valid file + exit +fi + +OUTPUT=iconunix.go +echo Generating $OUTPUT +echo "//+build linux darwin" > $OUTPUT +echo >> $OUTPUT +cat "$1" | $GOPATH/bin/2goarray banner main >> $OUTPUT +if [ $? -ne 0 ]; then + echo Failure generating $OUTPUT + exit +fi +echo Finished diff --git a/cmd/agent/manual_installer.go b/cmd/agent/manual_installer.go new file mode 100644 index 0000000..656a1f8 --- /dev/null +++ b/cmd/agent/manual_installer.go @@ -0,0 +1,221 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/c3os-io/c3os/internal/cmd" + "github.com/c3os-io/c3os/internal/utils" + config "github.com/c3os-io/c3os/pkg/config" + "github.com/erikgeiser/promptkit/textinput" + "github.com/jaypipes/ghw" + "github.com/mudler/edgevpn/pkg/node" + "github.com/mudler/yip/pkg/schema" + "github.com/pterm/pterm" + "gopkg.in/yaml.v2" +) + +const ( + canBeEmpty = "Unset" + yesNo = "[y]es/[N]o" +) + +func prompt(prompt, initialValue, placeHolder string, canBeEmpty, hidden bool) (string, error) { + input := textinput.New(prompt) + input.InitialValue = initialValue + input.Placeholder = placeHolder + if canBeEmpty { + input.Validate = func(s string) bool { return true } + } + input.Hidden = hidden + + return input.RunPrompt() +} + +func isYes(s string) bool { + i := strings.ToLower(s) + if i == "y" || i == "yes" { + return true + } + return false +} + +const ( + _ = 1 << (10 * iota) + KiB + MiB + GiB + TiB +) + +func interactiveInstall(spawnShell bool) error { + cmd.PrintBranding(banner) + pterm.DefaultBox.WithTitle("Installation").WithTitleBottomRight().WithRightPadding(0).WithBottomPadding(0).Println( + `Interactive installation. Documentation is available at https://docs.c3os.io.`) + + disks := []string{} + maxSize := float64(0) + preferedDevice := "/dev/sda" + + block, err := ghw.Block() + if err == nil { + for _, disk := range block.Disks { + size := float64(disk.SizeBytes) / float64(GiB) + if size > maxSize { + maxSize = size + preferedDevice = "/dev/" + disk.Name + } + disks = append(disks, fmt.Sprintf("/dev/%s: %s (%.2f GiB) ", disk.Name, disk.Model, float64(disk.SizeBytes)/float64(GiB))) + } + } + + pterm.Info.Println("Available Disks:") + for _, d := range disks { + pterm.Info.Println(" " + d) + } + var networkToken string + + device, err := prompt("What's the target install device?", preferedDevice, "Cannot be empty", false, false) + if err != nil { + return err + } + + userName, err := prompt("User to setup", "c3os", canBeEmpty, true, false) + if err != nil { + return err + } + + userPassword, err := prompt("Password", "", canBeEmpty, true, true) + if err != nil { + return err + } + + if userPassword == "" { + userPassword = "!" + } + + sshUsername, err := prompt("Username to grant SSH access to (github/gitlab supported)", "github:someuser", canBeEmpty, true, false) + if err != nil { + return err + } + + sshPubkey, err := prompt("SSH pubkey", "github:username", canBeEmpty, true, false) + if err != nil { + return err + } + + k3sAuto, err := prompt("Do you want to enable k3s automated setup? (requires multiple nodes)", "n", yesNo, true, false) + if err != nil { + return err + } + + if isYes(k3sAuto) { + hasNetworkToken, err := prompt("Do you have a network token already?", "n", yesNo, true, false) + if err != nil { + return err + } + + if isYes(hasNetworkToken) { + networkToken, err = prompt("Input network token", "", "", false, true) + if err != nil { + return err + } + } else { + networkToken = node.GenerateNewConnectionData().Base64() + } + } + + k3sStandalone, err := prompt("Do you want to enable k3s standalone?", "n", yesNo, true, false) + if err != nil { + return err + } + + allGood, err := prompt("Are settings ok?", "n", yesNo, true, false) + if err != nil { + return err + } + + if !isYes(allGood) { + return interactiveInstall(spawnShell) + } + + c := &config.Config{ + C3OS: &config.C3OS{ + NetworkToken: networkToken, + Device: device, + }, + + K3s: config.K3s{ + Enabled: isYes(k3sStandalone), + }, + } + + usersToSet := map[string]schema.User{} + + if userName != "" { + user := schema.User{ + Name: userName, + PasswordHash: userPassword, + Groups: []string{"admin"}, + } + if sshUsername != "" { + user.SSHAuthorizedKeys = append(user.SSHAuthorizedKeys, sshUsername) + } + + if sshPubkey != "" { + user.SSHAuthorizedKeys = append(user.SSHAuthorizedKeys, sshPubkey) + } + + usersToSet = map[string]schema.User{ + userName: user, + } + } + + cloudConfig := schema.YipConfig{Name: "Config generated by the installer", + Stages: map[string][]schema.Stage{config.NetworkStage.String(): { + { + Users: usersToSet, + }, + }}} + + dat, err := yaml.Marshal(cloudConfig) + if err != nil { + return err + } + dat2, err := yaml.Marshal(c) + if err != nil { + return err + } + + content1 := make(map[string]interface{}) + + err = yaml.Unmarshal(dat, &content1) + if err != nil { + return err + } + + err = yaml.Unmarshal(dat2, &content1) + if err != nil { + return err + } + + dat, err = yaml.Marshal(content1) + if err != nil { + return err + } + + pterm.Info.Println("Starting installation") + + err = runInstall(map[string]string{ + "device": device, + "cc": string(dat), + }) + if err != nil { + pterm.Error.Println(err.Error()) + } + + if spawnShell { + return utils.Shell().Run() + } + return err +} diff --git a/cmd/agent/recovery.go b/cmd/agent/recovery.go new file mode 100644 index 0000000..e0ffa79 --- /dev/null +++ b/cmd/agent/recovery.go @@ -0,0 +1,130 @@ +package main + +import ( + "context" + "fmt" + "io" + "os/exec" + "time" + + "github.com/c3os-io/c3os/internal/c3os" + "github.com/c3os-io/c3os/internal/cmd" + "github.com/c3os-io/c3os/internal/utils" + config "github.com/c3os-io/c3os/pkg/config" + "github.com/ipfs/go-log" + "github.com/urfave/cli" + + machine "github.com/c3os-io/c3os/internal/machine" + "github.com/creack/pty" + "github.com/gliderlabs/ssh" + "github.com/mudler/edgevpn/pkg/logger" + "github.com/mudler/edgevpn/pkg/node" + "github.com/mudler/edgevpn/pkg/services" + nodepair "github.com/mudler/go-nodepair" + qr "github.com/mudler/go-nodepair/qrcode" + "github.com/pterm/pterm" +) + +const recoveryAddr = "127.0.0.1:2222" + +func startRecoveryService(ctx context.Context, token, name, address, loglevel string) error { + + nc := config.Network(token, "", loglevel, "c3osrecovery0") + + lvl, err := log.LevelFromString(loglevel) + if err != nil { + lvl = log.LevelError + } + llger := logger.New(lvl) + + o, _, err := nc.ToOpts(llger) + if err != nil { + llger.Fatal(err.Error()) + } + + o = append(o, + services.Alive( + time.Duration(20)*time.Second, + time.Duration(10)*time.Second, + time.Duration(10)*time.Second)...) + + // opts, err := vpn.Register(vpnOpts...) + // if err != nil { + // return err + // } + o = append(o, services.RegisterService(llger, time.Duration(5*time.Second), name, address)...) + + e, err := node.New(o...) + if err != nil { + return err + } + + return e.Start(ctx) +} + +func recovery(c *cli.Context) error { + + cmd.PrintBranding(banner) + tk := nodepair.GenerateToken() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + serviceUUID := utils.RandStringRunes(10) + generatedPassword := utils.RandStringRunes(7) + + startRecoveryService(ctx, tk, serviceUUID, recoveryAddr, "fatal") + + cmd.PrintTextFromFile(c3os.BrandingFile("recovery_text"), "Recovery") + + time.Sleep(5 * time.Second) + + pterm.Info.Printfln( + "starting ssh server on '%s', password: '%s' service: '%s' ", recoveryAddr, generatedPassword, serviceUUID) + + qr.Print(utils.EncodeRecoveryToken(tk, serviceUUID, generatedPassword)) + + go sshServer(recoveryAddr, generatedPassword) + + // Wait for user input and go back to shell + utils.Prompt("") + cancel() + // give tty1 back + svc, err := machine.Getty(1) + if err == nil { + svc.Start() + } + + return nil +} + +func sshServer(listenAdddr, password string) { + ssh.Handle(func(s ssh.Session) { + cmd := exec.Command("bash") + ptyReq, winCh, isPty := s.Pty() + if isPty { + cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term)) + f, err := pty.Start(cmd) + if err != nil { + pterm.Warning.Println("Failed reserving tty") + } + go func() { + for win := range winCh { + setWinsize(f, win.Width, win.Height) + } + }() + go func() { + io.Copy(f, s) // stdin + }() + io.Copy(s, f) // stdout + cmd.Wait() + } else { + io.WriteString(s, "No PTY requested.\n") + s.Exit(1) + } + }) + + pterm.Info.Println(ssh.ListenAndServe(listenAdddr, nil, ssh.PasswordAuth(func(ctx ssh.Context, pass string) bool { + return pass == password + }), + )) +} diff --git a/cmd/agent/recovery_linux.go b/cmd/agent/recovery_linux.go new file mode 100644 index 0000000..998014f --- /dev/null +++ b/cmd/agent/recovery_linux.go @@ -0,0 +1,12 @@ +package main + +import ( + "os" + "syscall" + "unsafe" +) + +func setWinsize(f *os.File, w, h int) { + syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), + uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) +} diff --git a/cmd/agent/recovery_windows.go b/cmd/agent/recovery_windows.go new file mode 100644 index 0000000..72acc83 --- /dev/null +++ b/cmd/agent/recovery_windows.go @@ -0,0 +1,8 @@ +package main + +import ( + "os" +) + +func setWinsize(f *os.File, w, h int) { +} diff --git a/cmd/agent/reset.go b/cmd/agent/reset.go new file mode 100644 index 0000000..e009556 --- /dev/null +++ b/cmd/agent/reset.go @@ -0,0 +1,78 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "sync" + "time" + + "github.com/c3os-io/c3os/internal/c3os" + "github.com/c3os-io/c3os/internal/cmd" + "github.com/c3os-io/c3os/internal/machine" + "github.com/c3os-io/c3os/internal/utils" + "github.com/pterm/pterm" + "github.com/urfave/cli" +) + +func reset(c *cli.Context) error { + + cmd.PrintBranding(banner) + + cmd.PrintTextFromFile(c3os.BrandingFile("reset_text"), "Reset") + + // We don't close the lock, as none of the following actions are expected to return + lock := sync.Mutex{} + go func() { + // Wait for user input and go back to shell + utils.Prompt("") + // give tty1 back + svc, err := machine.Getty(1) + if err == nil { + svc.Start() + } + + lock.Lock() + fmt.Println("Reset aborted") + panic(utils.Shell().Run()) + }() + + time.Sleep(60 * time.Second) + lock.Lock() + args := []string{"reset"} + args = append(args, "--reset-persistent") + + cmd := exec.Command("elemental", args...) + cmd.Env = os.Environ() + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + fmt.Println(err) + os.Exit(1) + } + + pterm.Info.Println("Rebooting in 60 seconds, press Enter to abort...") + + // We don't close the lock, as none of the following actions are expected to return + lock2 := sync.Mutex{} + go func() { + // Wait for user input and go back to shell + utils.Prompt("") + // give tty1 back + svc, err := machine.Getty(1) + if err == nil { + svc.Start() + } + + lock2.Lock() + fmt.Println("Reboot aborted") + panic(utils.Shell().Run()) + }() + + time.Sleep(60 * time.Second) + lock2.Lock() + utils.Reboot() + + return nil +} diff --git a/cmd/agent/rotate.go b/cmd/agent/rotate.go new file mode 100644 index 0000000..f98aa6f --- /dev/null +++ b/cmd/agent/rotate.go @@ -0,0 +1,33 @@ +package main + +import ( + machine "github.com/c3os-io/c3os/internal/machine" + "github.com/c3os-io/c3os/internal/vpn" + config "github.com/c3os-io/c3os/pkg/config" +) + +func rotate(configDir []string, newToken, apiAddress, rootDir string, restart bool) error { + if err := config.ReplaceToken(configDir, newToken); err != nil { + return err + } + + c, err := config.Scan(configDir...) + if err != nil { + return err + } + + err = vpn.Setup(machine.EdgeVPNDefaultInstance, apiAddress, rootDir, false, c) + if err != nil { + return err + } + + if restart { + svc, err := machine.EdgeVPN(machine.EdgeVPNDefaultInstance, rootDir) + if err != nil { + return err + } + + return svc.Restart() + } + return nil +} diff --git a/cmd/agent/upgrade.go b/cmd/agent/upgrade.go new file mode 100644 index 0000000..f5c345b --- /dev/null +++ b/cmd/agent/upgrade.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + "errors" + "fmt" + "os" + "os/exec" + + "github.com/c3os-io/c3os/internal/github" + "github.com/c3os-io/c3os/internal/utils" +) + +func upgrade(version, image string, force bool) error { + if version == "" && image == "" { + githubRepo, err := utils.OSRelease("GITHUB_REPO") + if err != nil { + return err + } + releases, _ := github.FindReleases(context.Background(), "", githubRepo) + version = releases[0] + fmt.Println("latest release is ", version) + } + + if utils.Version() == version && !force { + fmt.Println("latest version already installed. use --force to force upgrade") + return nil + } + + flavor := utils.Flavor() + if flavor == "" { + return errors.New("no flavor detected") + } + + registry, err := utils.OSRelease("IMAGE_REPO") + if err != nil { + return err + } + img := fmt.Sprintf("%s:%s-%s", registry, flavor, version) + if image != "" { + img = image + } + + args := []string{"upgrade", "--system.uri", fmt.Sprintf("docker:%s", img)} + cmd := exec.Command("elemental", args...) + cmd.Env = os.Environ() + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + return cmd.Run() +} diff --git a/cmd/cli/bridge.go b/cmd/cli/bridge.go new file mode 100644 index 0000000..35502e3 --- /dev/null +++ b/cmd/cli/bridge.go @@ -0,0 +1,118 @@ +package main + +import ( + "context" + "fmt" + "net" + "time" + + "github.com/c3os-io/c3os/internal/utils" + "github.com/c3os-io/c3os/pkg/config" + "github.com/ipfs/go-log" + "github.com/mudler/edgevpn/api" + "github.com/mudler/edgevpn/pkg/logger" + "github.com/mudler/edgevpn/pkg/node" + "github.com/mudler/edgevpn/pkg/services" + "github.com/mudler/edgevpn/pkg/vpn" + qr "github.com/mudler/go-nodepair/qrcode" + "github.com/urfave/cli" +) + +// bridge is just starting a VPN with edgevpn to the given network token. +func bridge(c *cli.Context) error { + qrCodePath := "" + fromQRCode := false + var serviceUUID, sshPassword string + + if c.String("qr-code-image") != "" { + qrCodePath = c.String("qr-code-image") + fromQRCode = true + } + if c.Bool("qr-code-snapshot") { + qrCodePath = "" + fromQRCode = true + } + + token := c.String("network-token") + + if fromQRCode { + recoveryToken := qr.Reader(qrCodePath) + data := utils.DecodeRecoveryToken(recoveryToken) + if len(data) != 3 { + fmt.Println("Token not decoded correctly") + return fmt.Errorf("invalid token") + } + token = data[0] + serviceUUID = data[1] + sshPassword = data[2] + if serviceUUID == "" || sshPassword == "" || token == "" { + return fmt.Errorf("decoded invalid values") + } + } + + ctx := context.Background() + + nc := config.Network(token, c.String("address"), c.String("log-level"), "c3os0") + + lvl, err := log.LevelFromString(nc.LogLevel) + if err != nil { + lvl = log.LevelError + } + llger := logger.New(lvl) + + o, vpnOpts, err := nc.ToOpts(llger) + if err != nil { + llger.Fatal(err.Error()) + } + + opts := []node.Option{} + + if !fromQRCode { + // We just connect to a VPN token + o = append(o, + services.Alive( + time.Duration(20)*time.Second, + time.Duration(10)*time.Second, + time.Duration(10)*time.Second)...) + + if c.Bool("dhcp") { + // Adds DHCP server + address, _, err := net.ParseCIDR(c.String("address")) + if err != nil { + return err + } + nodeOpts, vO := vpn.DHCP(llger, 15*time.Minute, c.String("lease-dir"), address.String()) + o = append(o, nodeOpts...) + vpnOpts = append(vpnOpts, vO...) + } + + opts, err = vpn.Register(vpnOpts...) + if err != nil { + return err + } + } else { + // We hook into a service + llger.Info("Connecting to service", serviceUUID) + llger.Info("SSH access password is", sshPassword) + llger.Info("SSH server reachable at 127.0.0.1:2200") + opts = append(opts, node.WithNetworkService( + services.ConnectNetworkService( + 30*time.Second, + serviceUUID, + "127.0.0.1:2200", + ), + )) + llger.Info("To connect, keep this terminal open and run in another terminal 'ssh 127.0.0.1 -p 2200' the password is ", sshPassword) + llger.Info("Note: the connection might not be available instantly and first attempts will likely fail.") + llger.Info(" Few attempts might be required before establishing a tunnel to the host.") + } + + e, err := node.New(append(o, opts...)...) + if err != nil { + return err + } + + go api.API(ctx, c.String("api"), 5*time.Second, 20*time.Second, e, nil, false) + + return e.Start(ctx) +} diff --git a/cmd/cli/main.go b/cmd/cli/main.go new file mode 100644 index 0000000..b1b79ce --- /dev/null +++ b/cmd/cli/main.go @@ -0,0 +1,181 @@ +package main + +import ( + //"fmt" + + "fmt" + "os" + + cmd "github.com/c3os-io/c3os/internal/cmd" + + "github.com/urfave/cli" +) + +var cliCmd = []cli.Command{ + { + Name: "register", + UsageText: "register --reboot --device /dev/sda /image/snapshot.png", + Usage: "Registers and bootstraps a node", + Description: ` +Bootstraps a node which is started in pairing mode. It can send over a configuration file used to install the c3os node. + +For example: +$ c3os register --config config.yaml --device /dev/sda ~/Downloads/screenshot.png + +will decode the QR code from ~/Downloads/screenshot.png and bootstrap the node remotely. + +If the image is omitted, a screenshot will be taken and used to decode the QR code. + +See also https://docs.c3os.io/installation/device_pairing/ for documentation. +`, + ArgsUsage: "Register optionally accepts an image. If nothing is passed will take a screenshot of the screen and try to decode the QR code", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Usage: "C3OS YAML configuration file", + }, + &cli.StringFlag{ + Name: "device", + Usage: "Device used for the installation target", + }, + &cli.BoolFlag{ + Name: "reboot", + Usage: "Reboot node after installation", + }, + &cli.BoolFlag{ + Name: "poweroff", + Usage: "Shutdown node after installation", + }, + &cli.StringFlag{ + Name: "log-level", + Usage: "Set log level", + }, + }, + + Action: func(c *cli.Context) error { + args := c.Args() + var ref string + if len(args) == 1 { + ref = args[0] + } + + return register(c.String("log-level"), ref, c.String("config"), c.String("device"), c.Bool("reboot"), c.Bool("poweroff")) + }, + }, + { + Name: "bridge", + UsageText: "bridge --network-token XXX", + Usage: "Connect to a c3os VPN network", + Description: ` +Starts a bridge with a c3os network or a node. + +# With a network + +By default, "bridge" will create a VPN network connection to the node with the token supplied, thus it requires elevated permissions in order to work. + +For example: + +$ sudo c3os bridge --network-token <TOKEN> + +Will start a VPN, which local ip is fixed to 10.1.0.254 (tweakable with --address). + +The API will also be accessible at http://127.0.0.1:8080 + +# With a node + +"c3os bridge" can be used also to connect over to a node in recovery mode. When operating in this modality c3os bridge requires no specific permissions, indeed a tunnel +will be created locally to connect to the machine remotely. + +For example: + +$ c3os bridge --qr-code-image /path/to/image.png + +Will scan the QR code in the image and connect over. Further instructions on how to connect over will be printed out to the screen. + +See also: https://docs.c3os.io/after_install/troubleshooting/#connect-to-the-cluster-network and https://docs.c3os.io/after_install/recovery_mode/ + +`, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "network-token", + Required: false, + EnvVar: "NETWORK_TOKEN", + Usage: "Network token to connect over", + }, + &cli.StringFlag{ + Name: "log-level", + Required: false, + EnvVar: "LOGLEVEL", + Value: "info", + Usage: "Bridge log level", + }, + &cli.BoolFlag{ + Name: "qr-code-snapshot", + Required: false, + Usage: "Bool to take a local snapshot instead of reading from an image file for recovery", + EnvVar: "QR_CODE_SNAPSHOT", + }, + &cli.StringFlag{ + Name: "qr-code-image", + Usage: "Path to an image containing a valid QR code for recovery mode", + Required: false, + EnvVar: "QR_CODE_IMAGE", + }, + &cli.StringFlag{ + Name: "api", + Value: "127.0.0.1:8080", + Usage: "Listening API url", + }, + &cli.BoolFlag{ + Name: "dhcp", + EnvVar: "DHCP", + Usage: "Enable DHCP", + }, + &cli.StringFlag{ + Value: "10.1.0.254/24", + Name: "address", + EnvVar: "ADDRESS", + Usage: "Specify an address for the bridge", + }, + &cli.StringFlag{ + Value: "/tmp/c3os", + Name: "lease-dir", + EnvVar: "lease-dir", + Usage: "DHCP Lease directory", + }, + }, + Action: bridge, + }, +} + +func main() { + + app := &cli.App{ + Name: "c3os", + Version: "0.1", + Author: "Ettore Di Giacinto", + Usage: "c3os CLI to bootstrap, upgrade, connect and manage a c3os network", + Description: ` +The c3os CLI can be used to manage a c3os box and perform all day-two tasks, like: +- register a node +- connect to a node in recovery mode +- to establish a VPN connection +- set, list roles +- interact with the network API + +and much more. + +For all the example cases, see: https://docs.c3os.io . +`, + UsageText: ``, + Copyright: "Ettore Di Giacinto", + + Commands: cmd.CommonCommand(cliCmd...), + } + + err := app.Run(os.Args) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/cmd/cli/register.go b/cmd/cli/register.go new file mode 100644 index 0000000..435b238 --- /dev/null +++ b/cmd/cli/register.go @@ -0,0 +1,46 @@ +package main + +import ( + "context" + "fmt" + "io/ioutil" + + nodepair "github.com/mudler/go-nodepair" + qr "github.com/mudler/go-nodepair/qrcode" +) + +func register(loglevel, arg, configFile, device string, reboot, poweroff bool) error { + b, _ := ioutil.ReadFile(configFile) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // dmesg -D to suppress tty ev + fmt.Println("Sending registration payload, please wait") + + config := map[string]string{ + "device": device, + "cc": string(b), + } + + if reboot { + config["reboot"] = "" + } + + if poweroff { + config["poweroff"] = "" + } + + err := nodepair.Send( + ctx, + config, + nodepair.WithReader(qr.Reader), + nodepair.WithToken(arg), + nodepair.WithLogLevel(loglevel), + ) + if err != nil { + return err + } + + fmt.Println("Payload sent, installation will start on the machine briefly") + return nil +} diff --git a/cmd/provider/main.go b/cmd/provider/main.go new file mode 100644 index 0000000..13ce25b --- /dev/null +++ b/cmd/provider/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "os" + + "github.com/c3os-io/c3os/internal/provider" + + "github.com/c3os-io/c3os/pkg/bus" + "github.com/mudler/go-pluggable" +) + +func main() { + factory := pluggable.NewPluginFactory() + + // Input: bus.EventInstallPayload + // Expected output: map[string]string{} + factory.Add(bus.EventInstall, provider.Install) + + factory.Add(bus.EventBootstrap, provider.Bootstrap) + + // Input: config + // Expected output: string + factory.Add(bus.EventChallenge, provider.Challenge) + + factory.Run(pluggable.EventType(os.Args[1]), os.Args[2], os.Stdout) +}