diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/debian/changelog goget-ubuntu-touch-0.4+15.04.20141125/debian/changelog --- goget-ubuntu-touch-0.4+14.10.20140929/debian/changelog 2014-11-26 13:30:32.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/debian/changelog 2014-11-26 13:30:32.000000000 +0000 @@ -1,3 +1,82 @@ +goget-ubuntu-touch (0.4+15.04.20141125-0ubuntu1) vivid; urgency=low + + [ Sergio Schvezov ] + * ubuntu-emulator: allow setting a default password for the phablet + user when creating (LP: #1390476) + * ubuntu-emulator: fixing password setup for phablet user when setting + up an armhf instance (LP: #1393398) + * ubuntu-device-flash: dual images for core + + -- Ubuntu daily release Tue, 25 Nov 2014 23:51:15 +0000 + +goget-ubuntu-touch (0.4+15.04.20141121-0ubuntu1) vivid; urgency=low + + [ Sergio Schvezov ] + * ubuntu-device-flash: better priv escalation handling + + -- Ubuntu daily release Fri, 21 Nov 2014 11:33:00 +0000 + +goget-ubuntu-touch (0.4+15.04.20141120.1-0ubuntu1) vivid; urgency=low + + [ Michael Vogt ] + * ubuntu-device-flash: adding /etc/default/grub.d/50-system-image.cfg + for core to fix grub issues, the oustanding one being + GRUB_RECORDFAIL_TIMEOUT=0. + + -- Ubuntu daily release Thu, 20 Nov 2014 10:53:28 +0000 + +goget-ubuntu-touch (0.4+15.04.20141120-0ubuntu1) vivid; urgency=low + + [ Sergio Schvezov ] + * ubuntu-device-flash: don't display hidden channels (LP: #1394257) + * ubuntu-device-flash: error message improvements and best effort to + block sigterm in some cases + * ubuntu-device-flash: add a --developer-mode that for now copies ssh + keys into the image + + -- Ubuntu daily release Thu, 20 Nov 2014 01:46:02 +0000 + +goget-ubuntu-touch (0.4+15.04.20141118.1-0ubuntu1) vivid; urgency=low + + [ Sergio Schvezov ] + * debian/control: explicitly depending on kpartx for ubuntu-device- + flash + + [ Michael Vogt ] + * ubuntu-device-flash: don't create user-data for core images. + + -- Ubuntu daily release Tue, 18 Nov 2014 16:50:22 +0000 + +goget-ubuntu-touch (0.4+15.04.20141113-0ubuntu1) vivid; urgency=low + + [ Michael Vogt ] + * ubuntu-device-flash: trivial branch to fix crash if no + /etc/default/keyboard file is available + + -- Ubuntu daily release Thu, 13 Nov 2014 15:06:36 +0000 + +goget-ubuntu-touch (0.4+15.04.20141104.1-0ubuntu1) vivid; urgency=low + + [ Sergio Schvezov ] + * ubuntu-device-flash: positional commands for query and flashing + touch and support for lvm partitions. + * ubuntu-device-flash: Adding core command + * ubuntu-device-flash: adding cloud-init logic and using the devl + channel by default + * ubuntu-device-flash: --developer-mode extended to now also inhibit + adb disabling when the screen is locked + * ubuntu-emulator: creation with flag to always have adb enabled. + + -- Ubuntu daily release Tue, 04 Nov 2014 18:41:55 +0000 + +goget-ubuntu-touch (0.4+14.10.20141002-0ubuntu1) utopic; urgency=low + + [ James Hunt ] + * ubuntu-device-flash: add '--list-images' option to display brief + image details, one per line. + + -- Ubuntu daily release Thu, 02 Oct 2014 18:32:17 +0000 + goget-ubuntu-touch (0.4+14.10.20140929-0ubuntu1) utopic; urgency=low [ Sergio Schvezov ] diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/debian/control goget-ubuntu-touch-0.4+15.04.20141125/debian/control --- goget-ubuntu-touch-0.4+14.10.20140929/debian/control 2014-11-26 13:30:32.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/debian/control 2014-11-26 13:30:32.000000000 +0000 @@ -18,10 +18,11 @@ Architecture: any Depends: android-tools-adb, android-tools-fastboot, + kpartx, ${misc:Depends}, ${shlibs:Depends}, Built-Using: ${misc:Built-Using} -Description: Flash supported Nexus devices with Ubuntu +Description: Flash supported devices with Ubuntu Use this tool to flash a suported device with Ubuntu by either bootstrapping from fastboot or reflashing from an already supported device. @@ -42,6 +43,7 @@ ${misc:Depends}, ${shlibs:Depends}, Recommends: android-tools-adb, + qemu-user-static, Built-Using: ${misc:Built-Using} Description: Create and run emulator images of Ubuntu Touch Create and destroy Ubuntu Touch instances and run them with the emulator diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/debian/rules goget-ubuntu-touch-0.4+15.04.20141125/debian/rules --- goget-ubuntu-touch-0.4+14.10.20140929/debian/rules 2014-11-26 13:30:32.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/debian/rules 2014-11-26 13:30:32.000000000 +0000 @@ -13,3 +13,5 @@ rm -rf ${CURDIR}/debian/tmp/usr/share/gocode/src/launchpad.net/goget-ubuntu-touch/ubuntu-emulator rm -rf ${CURDIR}/debian/tmp/usr/share/gocode/src/launchpad.net/goget-ubuntu-touch/ubuntu-device-flash rm -rf ${CURDIR}/debian/tmp/usr/share/gocode/src/launchpad.net/goget-ubuntu-touch/ubuntu-device-do + rm -rf ${CURDIR}/debian/tmp/usr/share/gocode/src/launchpad.net/goget-ubuntu-touch/diskimage + rm -rf ${CURDIR}/debian/tmp/usr/share/gocode/src/launchpad.net/goget-ubuntu-touch/sysutils diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/diskimage/customization.go goget-ubuntu-touch-0.4+15.04.20141125/diskimage/customization.go --- goget-ubuntu-touch-0.4+14.10.20140929/diskimage/customization.go 1970-01-01 00:00:00.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/diskimage/customization.go 2014-11-25 23:50:53.000000000 +0000 @@ -0,0 +1,85 @@ +// +// ubuntu-emu - Tool to download and run Ubuntu Touch emulator instances +// +// Copyright (c) 2013 Canonical Ltd. +// +// Written by Sergio Schvezov +// +package diskimage + +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 3, as published +// by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranties of +// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +// PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +const networkConfig string = `# interfaces(5) file used by ifup(8) and ifdown(8) +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet static + address 10.0.2.15 + netmask 255.255.255.0 + gateway 10.0.2.2 + dns-nameservers 10.0.2.3 + +iface eth1 inet manual +iface eth2 inet manual +iface eth3 inet manual +iface eth4 inet manual +iface eth5 inet manual +` + +type setupFile struct{ path, content string } + +var setupFiles = []setupFile{ + {"custom/custom.prop", "custom.location.fake=true"}, + {"etc/network/interfaces", networkConfig}, + {"etc/profile.d/hud-service.sh", "export HUD_DISABLE_VOICE=1"}, +} + +//setupFile writes a setup to a target file +func (img DiskImage) writeFile(file setupFile) error { + profilePath := filepath.Join(img.Mountpoint, file.path) + dirPath := filepath.Dir(profilePath) + if fi, err := os.Stat(dirPath); err != nil { + if err := os.MkdirAll(dirPath, 0755); err != nil { + fmt.Println(err) + } + } else if !fi.IsDir() { + return fmt.Errorf("%s is not a directory, customization failed", profilePath) + } + if err := ioutil.WriteFile(profilePath, []byte(file.content), 0644); err != nil { + return err + } + return nil +} + +//Writable allows writes on the created running image +func (img DiskImage) Writable() error { + writeFlag := filepath.Join(img.Mountpoint, ".writable_image") + if err := ioutil.WriteFile(writeFlag, []byte(""), 0644); err != nil { + return err + } + return nil +} + +//OverrideAdbInhibit will inhibit abd from shutting down when the screen is locked +func (img DiskImage) OverrideAdbInhibit() error { + writeFlag := filepath.Join(img.Mountpoint, ".adb_onlock") + return ioutil.WriteFile(writeFlag, []byte(""), 0644) +} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/diskimage/image.go goget-ubuntu-touch-0.4+15.04.20141125/diskimage/image.go --- goget-ubuntu-touch-0.4+14.10.20140929/diskimage/image.go 1970-01-01 00:00:00.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/diskimage/image.go 2014-11-25 23:51:02.000000000 +0000 @@ -0,0 +1,449 @@ +// +// ubuntu-emu - Tool to download and run Ubuntu Touch emulator instances +// +// Copyright (c) 2013 Canonical Ltd. +// +// Written by Sergio Schvezov +// +package diskimage + +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 3, as published +// by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranties of +// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +// PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +import ( + "bufio" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + + "launchpad.net/goget-ubuntu-touch/sysutils" +) + +type imageLabel string +type directory string + +const ( + systemDataLabel imageLabel = "system-data" + systemDataLabel2 imageLabel = "system-data-2" + userDataLabel imageLabel = "user-data" +) + +const ( + systemDataDir directory = "system" + systemDataDir2 directory = "system-2" + userDataDir directory = "user-data" +) + +type partition struct { + label imageLabel + dir directory + loop string +} + +type DiskImage struct { + Mountpoint string + label string + path string + size int64 + parts []partition +} + +//New creates a new DiskImage +func New(path, label string, size int64) *DiskImage { + var img DiskImage + img.path = path + img.label = label + img.size = size + return &img +} + +//New creates a new DiskImage +func NewExisting(path string) *DiskImage { + var img DiskImage + img.path = path + return &img +} + +func (img *DiskImage) Move(dst string) error { + if err := img.Copy(dst); err != nil { + return err + } + if err := os.Remove(img.path); err != nil { + return errors.New(fmt.Sprintf("Unable to remove %s when moving to %s", img.path, dst)) + } + img.path = dst + return nil +} + +func (img *DiskImage) Copy(dst string) (err error) { + dstFile, err := os.Create(dst) + if err != nil { + return err + } + defer dstFile.Close() + + srcFile, err := os.Open(img.path) + if err != nil { + return err + } + defer srcFile.Close() + + reader := bufio.NewReader(srcFile) + writer := bufio.NewWriter(dstFile) + defer func() { + if err != nil { + writer.Flush() + } + }() + if _, err = io.Copy(writer, reader); err != nil { + return err + } + writer.Flush() + return nil +} + +// User returns the user path +func (img *DiskImage) User() (string, error) { + if img.parts == nil { + return "", errors.New("img is not setup with partitions") + } + + if img.Mountpoint == "" { + return "", errors.New("img not mounted") + } + + return filepath.Join(img.Mountpoint, string(userDataDir)), nil +} + +//System returns the system path +func (img *DiskImage) System() ([]string, error) { + if img.parts == nil { + return nil, errors.New("img is not setup with partitions") + } + + if img.Mountpoint == "" { + return nil, errors.New("img not mounted") + } + + return []string{ + filepath.Join(img.Mountpoint, string(systemDataDir)), + filepath.Join(img.Mountpoint, string(systemDataDir2)), + }, nil +} + +//Mount the DiskImage +func (img *DiskImage) Mount() (err error) { + img.Mountpoint, err = ioutil.TempDir(os.TempDir(), "ubuntu-system") + if err != nil { + return errors.New(fmt.Sprintf("Unable to create temp dir to create system image: %s", err)) + } + //Remove Mountpoint if we fail along the way + defer func() { + if err != nil { + os.Remove(img.Mountpoint) + } + }() + if img.parts == nil { + return img.emulatorMount() + } + + return img.coreMount() +} + +func (img *DiskImage) coreMount() (err error) { + for _, part := range img.parts { + mountpoint := filepath.Join(img.Mountpoint, string(part.dir)) + if err := os.MkdirAll(mountpoint, 0755); err != nil { + return err + } + if out, err := exec.Command("mount", filepath.Join("/dev/mapper", part.loop), mountpoint).CombinedOutput(); err != nil { + return fmt.Errorf("unable to mount dir to create system image: %s", out) + } + } + return nil +} + +func (img *DiskImage) emulatorMount() (err error) { + if out, err := exec.Command("mount", img.path, img.Mountpoint).CombinedOutput(); err != nil { + return fmt.Errorf("unable to mount temp dir to create system image: %s", out) + } + return nil +} + +//Unmount the DiskImage +func (img *DiskImage) Unmount() error { + if img.Mountpoint == "" { + return nil + } + defer os.Remove(img.Mountpoint) + if out, err := exec.Command("sync").CombinedOutput(); err != nil { + return errors.New(fmt.Sprintf("Failed to sync filesystems before unmounting: %s", out)) + } + + if img.parts == nil { + if err := img.emulatorUnmount(); err != nil { + return err + } + } else { + if err := img.coreUnmount(); err != nil { + return err + } + } + + img.Mountpoint = "" + + return nil +} + +func (img *DiskImage) coreUnmount() (err error) { + for _, part := range img.parts { + mountpoint := filepath.Join(img.Mountpoint, string(part.dir)) + if out, err := exec.Command("umount", mountpoint).CombinedOutput(); err != nil { + return fmt.Errorf("unable to unmount dir for image: %s", out) + } + if err := os.Remove(mountpoint); err != nil { + return err + } + } + return nil +} + +func (img *DiskImage) emulatorUnmount() (err error) { + if err := exec.Command("umount", img.Mountpoint).Run(); err != nil { + return errors.New("Failed to unmount temp dir where system image was created") + } + return nil +} + +//Provision unpacks the tarList into the given DiskImage +func (img *DiskImage) Provision(tarList []string) error { + //TODO use archive/tar + for _, tar := range tarList { + if out, err := exec.Command("tar", "--numeric-owner", "--exclude", "partitions*", + "-xf", tar, "-C", img.Mountpoint).CombinedOutput(); err != nil { + return errors.New(fmt.Sprintf("Unable to extract rootfs %s to %s: %s", tar, img.Mountpoint, out)) + } + } + if err := img.unpackSystem(); err != nil { + return err + } + for _, file := range setupFiles { + if err := img.writeFile(file); err != nil { + return err + } + } + return nil +} + +//Partition creates a partitioned image from an img +func (img *DiskImage) Partition(dual bool) error { + if err := sysutils.CreateEmptyFile(img.path, img.size); err != nil { + return err + } + + partedCmd := exec.Command("parted", img.path) + stdin, err := partedCmd.StdinPipe() + if err != nil { + return err + } + + stdin.Write([]byte("mklabel msdos\n")) + stdin.Write([]byte("mkpart primary ext4 2048s 3905535s\n")) + if dual { + stdin.Write([]byte("mkpart primary ext4 3905536s 7809023s\n")) + stdin.Write([]byte("mkpart primary ext4 7809024s -1s\n")) + } else { + stdin.Write([]byte("mkpart primary ext4 3905536s -1s\n")) + } + stdin.Write([]byte("set 1 boot on\n")) + stdin.Write([]byte("unit s print\n")) + stdin.Write([]byte("quit\n")) + + return partedCmd.Run() +} + +//MapPartitions creates loop devices for the partitions +func (img *DiskImage) MapPartitions(dual bool) error { + kpartxCmd := exec.Command("kpartx", "-avs", img.path) + stdout, err := kpartxCmd.StdoutPipe() + if err != nil { + return err + } + + if err := kpartxCmd.Start(); err != nil { + return err + } + + loops := make([]string, 0, 3) + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + + if len(fields) > 2 { + loops = append(loops, fields[2]) + } else { + return errors.New("issues while determining drive mappings") + } + } + if err := scanner.Err(); err != nil { + return err + } + + expectedLoops := 2 + if dual { + expectedLoops = 3 + } + + if len(loops) != expectedLoops { + return errors.New("more partitions then expected while creating loop mapping") + } + + if dual { + img.parts = []partition{ + partition{label: systemDataLabel, dir: systemDataDir, loop: loops[0]}, + partition{label: systemDataLabel2, dir: systemDataDir2, loop: loops[1]}, + partition{label: userDataLabel, dir: userDataDir, loop: loops[2]}, + } + } else { + img.parts = []partition{ + partition{label: systemDataLabel, dir: systemDataDir, loop: loops[0]}, + partition{label: userDataLabel, dir: userDataDir, loop: loops[1]}, + } + } + + if err := kpartxCmd.Wait(); err != nil { + return err + } + + return nil +} + +//UnMapPartitions destroys loop devices for the partitions +func (img *DiskImage) UnMapPartitions() error { + for _, part := range img.parts { + if err := exec.Command("dmsetup", "clear", part.loop).Run(); err != nil { + return err + } + } + return exec.Command("kpartx", "-d", img.path).Run() +} + +//CreateExt4 returns a ext4 partition for a given file +func (img DiskImage) CreateExt4() error { + if img.parts == nil { + if err := sysutils.CreateEmptyFile(img.path, img.size); err != nil { + return err + } + return exec.Command("mkfs.ext4", "-F", "-L", img.label, img.path).Run() + } + + for _, part := range img.parts { + dev := filepath.Join("/dev/mapper", part.loop) + if out, err := exec.Command("mkfs.ext4", "-F", "-L", string(part.label), dev).CombinedOutput(); err != nil { + return fmt.Errorf("unable to create filesystem: %s", out) + } + } + + return nil +} + +//CreateVFat returns a vfat partition for a given file +func (img DiskImage) CreateVFat() error { + if err := sysutils.CreateEmptyFile(img.path, img.size); err != nil { + return err + } + return exec.Command("mkfs.vfat", "-n", img.label, img.path).Run() +} + +//unpackSystem moves the system partition up one level +func (img DiskImage) unpackSystem() error { + os.Rename(filepath.Join(img.Mountpoint, "system"), + filepath.Join(img.Mountpoint, "system-unpack")) + defer os.Remove(filepath.Join(img.Mountpoint, "system-unpack")) + dir, err := os.Open(filepath.Join(img.Mountpoint, "system-unpack")) + if err != nil { + return err + } + defer dir.Close() + fileInfos, err := dir.Readdir(-1) + if err != nil { + return err + } + for _, fileInfo := range fileInfos { + name := fileInfo.Name() + err := os.Rename(filepath.Join(img.Mountpoint, "system-unpack", name), + filepath.Join(img.Mountpoint, name)) + if err != nil { + return err + } + } + return nil +} + +//ExtractFile extracts a filePath relative to it's mountpoint and copies it to dir. +//This function takes care of mounting and unmounting the img +func (img DiskImage) ExtractFile(filePath string, dir string) error { + if err := sysutils.EscalatePrivs(); err != nil { + return err + } + if err := img.Mount(); err != nil { + return err + } + if err := sysutils.DropPrivs(); err != nil { + return err + } + defer func() (err error) { + if err := sysutils.EscalatePrivs(); err != nil { + return err + } + if err := img.Unmount(); err != nil { + return err + } + return sysutils.DropPrivs() + }() + if fi, err := os.Stat(dir); err != nil { + if err := os.MkdirAll(dir, 0755); err != nil { + return err + } + } else if !fi.IsDir() { + return fmt.Errorf("extract dir %s is not a directory") + } + dstFile, err := os.Create(filepath.Join(dir, filePath)) + if err != nil { + return err + } + defer dstFile.Close() + + srcFile, err := os.Open(filepath.Join(img.Mountpoint, filePath)) + if err != nil { + return err + } + defer srcFile.Close() + + reader := bufio.NewReader(srcFile) + writer := bufio.NewWriter(dstFile) + defer func() { + if err != nil { + writer.Flush() + } + }() + if _, err = io.Copy(writer, reader); err != nil { + return err + } + writer.Flush() + return nil +} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/diskimage/snapshot.go goget-ubuntu-touch-0.4+15.04.20141125/diskimage/snapshot.go --- goget-ubuntu-touch-0.4+14.10.20140929/diskimage/snapshot.go 1970-01-01 00:00:00.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/diskimage/snapshot.go 2014-11-25 23:50:32.000000000 +0000 @@ -0,0 +1,69 @@ +// +// ubuntu-emu - Tool to download and run Ubuntu Touch emulator instances +// +// Copyright (c) 2013 Canonical Ltd. +// +// Written by Sergio Schvezov +// +package diskimage + +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 3, as published +// by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranties of +// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +// PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +import ( + "errors" + "fmt" + "os" + "os/exec" +) + +const BaseSnapshot = "pristine" + +//ConvertQcow2 converts an image from raw to qcow2 +func (img *DiskImage) ConvertQcow2() error { + var cmd *exec.Cmd + // convert + cmd = exec.Command("qemu-img", "convert", "-f", "raw", img.path, + "-O", "qcow2", "-o", "compat=0.10", img.path+".qcow2") + if out, err := cmd.CombinedOutput(); err != nil { + return errors.New(fmt.Sprintf("Error while converting %s: %s", img.path, out)) + } + + // check + cmd = exec.Command("qemu-img", "check", img.path+".qcow2") + if out, err := cmd.CombinedOutput(); err != nil { + return errors.New(fmt.Sprintf("Error while converting %s: %s", img.path, out)) + } + os.Rename(img.path+".qcow2", img.path) + return img.Snapshot(BaseSnapshot) + +} + +//Snapshot creates a DiskImage's snapshot with the specified snapshot in label +func (img *DiskImage) Snapshot(label string) error { + // snap + cmd := exec.Command("qemu-img", "snapshot", "-c", label, img.path) + if out, err := cmd.CombinedOutput(); err != nil { + return errors.New(fmt.Sprintf("Error while converting %s: %s", img.path, out)) + } + return nil +} + +//RevertSnapshot reverts a DiskImage's snapshot to the specified snapshot in label +func (img *DiskImage) RevertSnapshot(label string) error { + // unsnap + cmd := exec.Command("qemu-img", "snapshot", "-c", label, img.path) + if out, err := cmd.CombinedOutput(); err != nil { + return errors.New(fmt.Sprintf("Error while converting %s: %s", img.path, out)) + } + return nil +} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/README goget-ubuntu-touch-0.4+15.04.20141125/README --- goget-ubuntu-touch-0.4+14.10.20140929/README 1970-01-01 00:00:00.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/README 2014-11-25 23:50:32.000000000 +0000 @@ -0,0 +1,5 @@ +To build run: +$ sudo apt-get build-dep goget-ubuntu-touch + +?!?! there is surely a better way +$ debian/rules clean build diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/sysutils/utils.go goget-ubuntu-touch-0.4+15.04.20141125/sysutils/utils.go --- goget-ubuntu-touch-0.4+14.10.20140929/sysutils/utils.go 1970-01-01 00:00:00.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/sysutils/utils.go 2014-11-25 23:50:32.000000000 +0000 @@ -0,0 +1,89 @@ +// +// ubuntu-emu - Tool to download and run Ubuntu Touch emulator instances +// +// Copyright (c) 2013 Canonical Ltd. +// +// Written by Sergio Schvezov +// +package sysutils + +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 3, as published +// by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranties of +// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +// PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +import ( + "errors" + "fmt" + "os" + "strconv" + "syscall" +) + +func CreateEmptyFile(path string, size int64) (err error) { + file, err := os.Create(path) + if err != nil { + return err + } + defer func() { + file.Close() + if err != nil { + os.Remove(path) + } + }() + size = size * 1024 * 1024 * 1024 + if err := file.Truncate(size); err != nil { + return errors.New(fmt.Sprintf("Error creating %s of size %d to stage image onto", path, size)) + } + return nil +} + +func DropPrivs() error { + uid, gid := GetUserEnvInt() + if err := syscall.Setregid(-1, gid); err != nil { + return errors.New(fmt.Sprintf("Can't drop gid: %s", err)) + } + if err := syscall.Setreuid(-1, uid); err != nil { + return errors.New(fmt.Sprintf("Can't drop uid: %s", err)) + } + return nil +} + +func EscalatePrivs() error { + err := syscall.Setreuid(-1, 0) + return err +} + +// GetUserEnv checks if the process can drop priviledges by checking if either +// SUDO_UID and SUDO_GID or PKEXEC_UID are set, it returns the corresponding +// uid and gid set in these or 0 otherwise. +func GetUserEnv() (uid, gid string) { + if v := os.Getenv("SUDO_UID"); v != "" { + uid = v + } else if v := os.Getenv("PKEXEC_UID"); v != "" { + uid = v + } else { + uid = "0" + } + + if v := os.Getenv("SUDO_GID"); v != "" { + gid = v + } else { + gid = "0" + } + return uid, gid +} + +func GetUserEnvInt() (uid, gid int) { + uidString, gidString := GetUserEnv() + uid, _ = strconv.Atoi(uidString) + gid, _ = strconv.Atoi(gidString) + return uid, gid +} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/args.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/args.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/args.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/args.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -// -// udbflash - Tool to download and flash devices with an Ubuntu Image based -// system -// -// Copyright (c) 2013 Canonical Ltd. -// -// Written by Sergio Schvezov -// -package main - -// This program is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License version 3, as published -// by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranties of -// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -// PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program. If not, see . - -import ( - flags "github.com/jessevdk/go-flags" -) - -type arguments struct { - Revision int `long:"revision" description:"revision to flash, absolute or relative allowed"` - Bootstrap bool `long:"bootstrap" description:"bootstrap the system, do this from the bootloader"` - ListChannels bool `long:"list-channels" description:"List available channels"` - Wipe bool `long:"wipe" description:"Clear all data after flashing"` - Channel string `long:"channel" description:"Specify an alternate channel"` - ShowImage bool `long:"show-image" description:"Show information for an image in the given channel"` - Device string `long:"device" description:"Specify the device to flash"` - DeviceTarball string `long:"device-tarball" description:"Specify a local device tarball to override the one from the server (using official Ubuntu images with custom device tarballs)"` - DownloadOnly bool `long:"download-only" description:"Only download tarballs, do not push to the device."` - Serial string `long:"serial" description:"Serial of the device to operate"` - Server string `long:"server" description:"Use a different image server"` - CleanCache bool `long:"clean-cache" description:"Cleans up cache with all downloaded bits"` - TLSSkipVerify bool `long:"tls-skip-verify" description:"Skip TLS certificate validation"` - DeveloperMode bool `long:"developer-mode" description:"Enables developer mode after the factory reset"` - RunScript string `long:"run-script" description:"Run a script given by path to finish the flashing process, instead of rebooting to recovery (mostly used during development to work around quirky or incomplete recovery images)"` - Password string `long:"password" description:"This sets up the default password for the phablet user. This option is meant for CI and not general use"` -} - -var args arguments -var parser = flags.NewParser(&args, flags.Default) - -const ( - defaultChannel = "ubuntu-touch/stable" - defaultServer = "https://system-image.ubuntu.com" -) - -func init() { - args.Channel = defaultChannel - args.Server = defaultServer -} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/common.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/common.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/common.go 1970-01-01 00:00:00.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/common.go 2014-11-25 23:50:32.000000000 +0000 @@ -0,0 +1,46 @@ +package main + +import ( + "log" + "path/filepath" + "runtime" + "syscall" + + "launchpad.net/goget-ubuntu-touch/sysutils" + "launchpad.net/goget-ubuntu-touch/ubuntuimage" +) + +func getImage(deviceChannel ubuntuimage.DeviceChannel) (image ubuntuimage.Image, err error) { + if globalArgs.Revision <= 0 { + image, err = deviceChannel.GetRelativeImage(globalArgs.Revision) + } else { + image, err = deviceChannel.GetImage(globalArgs.Revision) + } + return image, err +} + +type Files struct{ FilePath, SigPath string } + +// bitDownloader downloads +func bitDownloader(file ubuntuimage.File, files chan<- Files, server, downloadDir string) { + // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435 + if syscall.Getuid() == 0 { + runtime.GOMAXPROCS(1) + runtime.LockOSThread() + + if err := sysutils.DropPrivs(); err != nil { + log.Fatal(err) + } + } + + err := file.MakeRelativeToServer(server) + if err != nil { + log.Fatal(err) + } + err = file.Download(downloadDir) + if err != nil { + log.Fatal(err) + } + files <- Files{FilePath: filepath.Join(downloadDir, file.Path), + SigPath: filepath.Join(downloadDir, file.Signature)} +} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/core.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/core.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/core.go 1970-01-01 00:00:00.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/core.go 2014-11-25 23:51:02.000000000 +0000 @@ -0,0 +1,430 @@ +// +// ubuntu-device-flash - Tool to download and flash devices with an Ubuntu Image +// based system +// +// Copyright (c) 2013 Canonical Ltd. +// +// Written by Sergio Schvezov +// +package main + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "os/signal" + "path/filepath" + "runtime" + "strings" + "syscall" + "time" + + "launchpad.net/goget-ubuntu-touch/diskimage" + "launchpad.net/goget-ubuntu-touch/sysutils" + "launchpad.net/goget-ubuntu-touch/ubuntuimage" +) + +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 3, as published +// by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranties of +// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +// PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +func init() { + parser.AddCommand("core", + "Creates ubuntu core images", + "", + &coreCmd) +} + +type CoreCmd struct { + Channel string `long:"channel" description:"Specify the channel to use" default:"ubuntu-core/devel"` + Device string `long:"device" description:"Specify the device to use" default:"generic_amd64"` + Keyboard string `long:"keyboard-layout" description:"Specify the keyboard layout" default:"us"` + Output string `long:"output" short:"o" description:"Name of the image file to create" required:"true"` + Size int64 `long:"size" short:"s" description:"Size of image file to create in GB (min 6)" default:"20"` + DeveloperMode bool `long:"developer-mode" description:"Finds the latest public key in your ~/.ssh and sets it up"` + Dual bool `long:"dual-images" description:"Sets up two images to upgrade with providing rollback support"` +} + +var coreCmd CoreCmd + +const cloudInitMetaData = `instance-id: nocloud-static +` + +const cloudInitUserData = `#cloud-config +password: passw0rd +chpasswd: { expire: False } +ssh_pwauth: True +` + +const grubCfgContent = `# console only, no graphics/vga +GRUB_CMDLINE_LINUX_DEFAULT="console=tty1 console=ttyS0" +GRUB_TERMINAL=console +# LP: #1035279 +GRUB_RECORDFAIL_TIMEOUT=0 +` + +func (coreCmd *CoreCmd) Execute(args []string) error { + if syscall.Getuid() != 0 { + return errors.New("command requires sudo/pkexec (root)") + } + + // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435 + runtime.GOMAXPROCS(1) + runtime.LockOSThread() + if err := sysutils.DropPrivs(); err != nil { + return err + } + + channels, err := ubuntuimage.NewChannels(globalArgs.Server) + if err != nil { + return err + } + + deviceChannel, err := channels.GetDeviceChannel(globalArgs.Server, coreCmd.Channel, coreCmd.Device) + if err != nil { + return err + } + + image, err := getImage(deviceChannel) + if err != nil { + return err + } + + filesChan := make(chan Files, len(image.Files)) + defer close(filesChan) + + sigFiles := ubuntuimage.GetGPGFiles() + sigFilesChan := make(chan Files, len(sigFiles)) + defer close(sigFilesChan) + + for _, f := range sigFiles { + go bitDownloader(f, sigFilesChan, globalArgs.Server, cacheDir) + } + + for _, f := range image.Files { + go bitDownloader(f, filesChan, globalArgs.Server, cacheDir) + } + + filePathChan := make(chan string) + + go func() { + for i := 0; i < len(image.Files); i++ { + f := <-filesChan + fmt.Println("Download finished for", f.FilePath) + filePathChan <- f.FilePath + } + close(filePathChan) + }() + + img := diskimage.New(coreCmd.Output, "", coreCmd.Size) + if err := img.Partition(coreCmd.Dual); err != nil { + return err + } + defer func() { + if err != nil { + os.Remove(coreCmd.Output) + } + }() + + // Handle SIGINT and SIGTERM. + go func() { + ch := make(chan os.Signal) + signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) + + for sig := range ch { + fmt.Println("Received", sig, "... ignoring") + } + }() + + // Execute the following code with escalated privs and drop them when done + err = func() error { + // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435 + runtime.GOMAXPROCS(1) + runtime.LockOSThread() + if err := sysutils.EscalatePrivs(); err != nil { + return err + } + defer sysutils.DropPrivs() + + if err := coreCmd.partition(img); err != nil { + return err + } + + if err := coreCmd.setup(img, filePathChan); err != nil { + return err + } + + return nil + }() + if err != nil { + return err + } + + fmt.Println("New image complete, launch by running: kvm -m 768", coreCmd.Output) + + return nil +} + +func (coreCmd *CoreCmd) partition(img *diskimage.DiskImage) error { + if err := img.MapPartitions(coreCmd.Dual); err != nil { + return fmt.Errorf("issue while mapping partitions: %s", err) + } + defer img.UnMapPartitions() + + return img.CreateExt4() +} + +func (coreCmd *CoreCmd) setup(img *diskimage.DiskImage, filePathChan <-chan string) error { + if err := img.MapPartitions(coreCmd.Dual); err != nil { + return err + } + defer img.UnMapPartitions() + + if err := img.Mount(); err != nil { + return err + } + defer img.Unmount() + + for f := range filePathChan { + if out, err := exec.Command("tar", "--numeric-owner", "-axvf", f, "-C", img.Mountpoint).CombinedOutput(); err != nil { + return fmt.Errorf("issues while extracting: %s", out) + } + } + + systemPaths, err := img.System() + if err != nil { + return err + } + + userPath, err := img.User() + if err != nil { + return err + } + + if coreCmd.Dual { + src := fmt.Sprintf("%s/system/.", img.Mountpoint) + dst := fmt.Sprintf("%s/system-2", img.Mountpoint) + cmd := exec.Command("cp", "-r", "--preserve=all", src, dst) + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("failed to replicate image contents: %s", out) + } + } + + for _, dir := range []string{"system-data", "cache"} { + dirPath := filepath.Join(userPath, dir) + if err := os.Mkdir(dirPath, 0755); err != nil { + return err + } + } + + for i := range systemPaths { + if err := coreCmd.setupBootloader(systemPaths[i]); err != nil { + return err + } + + if err := coreCmd.setupKeyboardLayout(systemPaths[i]); err != nil { + return err + } + + if err := coreCmd.setupCloudInit(systemPaths[i], filepath.Join(userPath, "system-data")); err != nil { + return err + } + } + + return nil +} + +func (coreCmd *CoreCmd) setupCloudInit(systemPath, systemData string) error { + cloudBaseDir := filepath.Join("var", "lib", "cloud") + if err := os.MkdirAll(filepath.Join(systemPath, cloudBaseDir), 0755); err != nil { + return err + } + + // create a basic cloud-init seed + cloudDir := filepath.Join(systemData, cloudBaseDir, "seed", "nocloud-net") + if err := os.MkdirAll(cloudDir, 0755); err != nil { + return err + } + + metaDataFile, err := os.Create(filepath.Join(cloudDir, "meta-data")) + if err != nil { + return err + } + defer metaDataFile.Close() + + if _, err := io.WriteString(metaDataFile, cloudInitMetaData); err != nil { + return err + } + + if coreCmd.DeveloperMode { + authorizedKey, err := getAuthorizedSshKey() + if err != nil { + return fmt.Errorf("failed to obtain a public key for developer mode: %s", err) + } + + if _, err := io.WriteString(metaDataFile, "public-keys:\n"); err != nil { + return err + } + + if _, err := io.WriteString(metaDataFile, fmt.Sprintf(" - %s\n", authorizedKey)); err != nil { + return err + } + + } + + userDataFile, err := os.Create(filepath.Join(cloudDir, "user-data")) + if err != nil { + return err + } + defer userDataFile.Close() + + if _, err := io.WriteString(userDataFile, cloudInitUserData); err != nil { + return err + } + + return nil +} + +func (coreCmd *CoreCmd) setupKeyboardLayout(systemPath string) error { + kbFilePath := filepath.Join(systemPath, "etc", "default", "keyboard") + + // do not error if the image has no keyboard + if _, err := os.Stat(kbFilePath); err != nil && os.IsNotExist(err) { + return nil + } + + kbFileContents, err := ioutil.ReadFile(kbFilePath) + if err != nil { + return err + } + + r := strings.NewReplacer("XKBLAYOUT=\"us\"", fmt.Sprintf("XKBLAYOUT=\"%s\"", coreCmd.Keyboard)) + kbFileContents = []byte(r.Replace(string(kbFileContents))) + + return ioutil.WriteFile(kbFilePath, kbFileContents, 0644) +} + +func (coreCmd *CoreCmd) setupBootloader(systemPath string) error { + for _, dev := range []string{"dev", "proc", "sys"} { + src := filepath.Join("/", dev) + dst := filepath.Join(systemPath, dev) + if err := bindMount(src, dst); err != nil { + return err + } + defer unmount(dst) + } + + firmwarePath := filepath.Join(systemPath, "sys", "firmware") + if err := bindMount(filepath.Join(systemPath, "mnt"), firmwarePath); err != nil { + return err + } + defer unmount(firmwarePath) + + outputPath, err := filepath.Abs(coreCmd.Output) + if err != nil { + return errors.New("cannot determined absolute path for output image") + } + + rootDevPath := filepath.Join(systemPath, "root_dev") + + if f, err := os.Create(rootDevPath); err != nil { + return err + } else { + f.Close() + defer os.Remove(rootDevPath) + } + + if err := bindMount(outputPath, rootDevPath); err != nil { + return err + } + defer unmount(rootDevPath) + + if out, err := exec.Command("chroot", systemPath, "grub-install", "/root_dev").CombinedOutput(); err != nil { + return fmt.Errorf("unable to install grub: %s", out) + } else { + fmt.Println(string(out)) + } + + // ensure we run not into recordfail issue + grubDir := filepath.Join(systemPath, "etc", "default", "grub.d") + if err := os.MkdirAll(grubDir, 0755); err != nil { + return fmt.Errorf("unable to create %s dir: %s", grubDir, err) + } + grubFile, err := os.Create(filepath.Join(grubDir, "50-system-image.cfg")) + if err != nil { + return fmt.Errorf("unable to create %s file: %s", grubFile, err) + } + defer grubFile.Close() + if _, err := io.WriteString(grubFile, grubCfgContent); err != nil { + return err + } + + // I don't know why this is needed, I just picked it up from the original implementation + time.Sleep(3 * time.Second) + + if out, err := exec.Command("chroot", systemPath, "update-grub").CombinedOutput(); err != nil { + return fmt.Errorf("unable to update grub: %s", out) + } else { + fmt.Println(string(out)) + } + + return nil +} + +func bindMount(src, dst string) error { + if out, err := exec.Command("mount", "--bind", src, dst).CombinedOutput(); err != nil { + return fmt.Errorf("issues while bind mounting: %s", out) + } + + return nil +} + +func unmount(dst string) error { + if out, err := exec.Command("umount", dst).CombinedOutput(); err != nil { + return fmt.Errorf("issues while unmounting: %s", out) + } + + return nil +} + +func getAuthorizedSshKey() (string, error) { + sshDir := os.ExpandEnv("$HOME/.ssh") + + fis, err := ioutil.ReadDir(sshDir) + if err != nil { + return "", fmt.Errorf("%s: no pub ssh key found, run ssh-keygen first", err) + } + + var preferredPubKey string + var latestModTime time.Time + for i := range fis { + file := fis[i].Name() + if strings.HasSuffix(file, ".pub") && !strings.HasSuffix(file, "cert.pub") { + fileMod := fis[i].ModTime() + + if fileMod.After(latestModTime) { + latestModTime = fileMod + preferredPubKey = file + } + } + } + + if preferredPubKey == "" { + return "", errors.New("no pub ssh key found, run ssh-keygen first") + } + + pubKey, err := ioutil.ReadFile(filepath.Join(sshDir, preferredPubKey)) + + return string(pubKey), err +} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/main.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/main.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/main.go 2014-09-29 17:27:50.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/main.go 2014-11-25 23:50:32.000000000 +0000 @@ -1,6 +1,6 @@ // -// udbflash - Tool to download and flash devices with an Ubuntu Image based -// system +// ubuntu-device-flash - Tool to download and flash devices with an Ubuntu Image +// based system // // Copyright (c) 2013 Canonical Ltd. // @@ -21,387 +21,45 @@ // with this program. If not, see . import ( - "archive/tar" - "errors" "fmt" - "io" - "io/ioutil" - "log" "os" - "os/exec" - "path/filepath" - "strings" - "syscall" - "launchpad.net/goget-ubuntu-touch/devices" "launchpad.net/goget-ubuntu-touch/ubuntuimage" ) -func main() { - if _, err := parser.Parse(); err != nil { - os.Exit(1) - } - if args.TLSSkipVerify { - ubuntuimage.TLSSkipVerify() - } - script := args.RunScript - if script != "" { - if p, err := filepath.Abs(script); err != nil { - log.Fatal("Run script not found:", err) - } else { - script = p - } - - fi, err := os.Lstat(script) - if err != nil { - log.Fatal(err) - } - if fi.Mode()&0100 == 0 || !fi.Mode().IsRegular() { - log.Fatalf("The script %s passed via --run-script is not executable", script) - } - } - - if args.Bootstrap { - args.Wipe = true - } - - if args.Password != "" && !args.Wipe && !args.DeveloperMode { - log.Fatal("Default password setup requires --developer-mode, and --wipe or --bootstrap") - } - - tarballPath := args.DeviceTarball - if tarballPath != "" { - if p, err := filepath.Abs(tarballPath); err != nil { - log.Fatal("Device tarball not found", err) - } else { - tarballPath = p - } - fi, err := os.Lstat(tarballPath) - if err != nil { - log.Fatal(err) - } - if !fi.Mode().IsRegular() { - log.Fatalf("The file %s passed via --device-tarball is not a regular file\n", tarballPath) - } - } - channels, err := ubuntuimage.NewChannels(args.Server) - if err != nil { - log.Fatal(err) - } - if args.ListChannels { - for k, v := range channels { - if v.Alias != "" { - fmt.Printf("%s (alias to %s)\n", k, v.Alias) - } else { - fmt.Println(k) - } - } - return - } - cacheDir := ubuntuimage.GetCacheDir() - if args.CleanCache { - log.Print("Cleaning prevously downloaded content") - if err := os.RemoveAll(cacheDir); err != nil { - log.Fatal(err) - } - return - } - adb, err := devices.NewUbuntuDebugBridge() - var fastboot devices.Fastboot - if err != nil { - log.Fatal(err) - } - if args.Serial != "" { - adb.SetSerial(args.Serial) - fastboot.SetSerial(args.Serial) - } - if args.Device == "" { - if args.Bootstrap { - log.Print("Expecting the device to be in the bootloader... waiting") - args.Device, err = fastboot.GetDevice() - } else { - log.Print("Expecting the device to expose an adb interface...") - // TODO needs to work from recovery as well - //adb.WaitForDevice() - args.Device, err = adb.GetDevice() - } - if err != nil { - log.Fatalln("Cannot determine the device name:", err) - } - } - log.Printf("Device is |%s|", args.Device) - deviceChannel, err := channels.GetDeviceChannel( - args.Server, args.Channel, args.Device) - if err != nil { - log.Fatal(err) - } - var image ubuntuimage.Image - if args.Revision <= 0 { - image, err = deviceChannel.GetRelativeImage(args.Revision) - } else { - image, err = deviceChannel.GetImage(args.Revision) - } - if err != nil { - log.Fatal(err) - } - if args.ShowImage { - fmt.Printf("Description: %s\nVersion: %d\nChannel: %s\n", image.Description, image.Version, args.Channel) - for _, f := range image.Files { - f.MakeRelativeToServer(args.Server) - fmt.Printf("%d %s%s %d %s\n", f.Order, f.Server, f.Path, f.Size, f.Checksum) - } - return - } - log.Printf("Flashing version %d from %s channel and server %s to device %s", - image.Version, args.Channel, args.Server, args.Device) - if deviceChannel.Alias != "" { - log.Printf("%s is a channel alias to %s", deviceChannel.Alias, args.Channel) - } - - // TODO use closures - signFiles := ubuntuimage.GetGPGFiles() - totalFiles := len(image.Files) + len(signFiles) - files := make(chan Files, totalFiles) - done := make(chan bool, totalFiles) - for i, file := range image.Files { - if tarballPath != "" && strings.HasPrefix(file.Path, "/pool/device") { - //change the file paths so they are correctly picked up by bitPusher later on - image.Files[i].Path = tarballPath - image.Files[i].Signature = tarballPath + ".asc" - useLocalTarball(image.Files[i], files) - } else { - go bitDownloader(file, files, args.Server, cacheDir) - } - } - for _, file := range signFiles { - go bitDownloader(file, files, args.Server, cacheDir) - } - if args.DownloadOnly { - for i := 0; i < totalFiles; i++ { - <-files - } - log.Printf("Downloaded files for version %d, channel %s, exiting without flashing as requested.\n", image.Version, args.Channel) - os.Exit(0) - } - if args.Bootstrap { - var downloadedFiles []Files - for i := 0; i < totalFiles; i++ { - downloadedFiles = append(downloadedFiles, <-files) - } - //Find the recovery image - var recovery string - for _, file := range downloadedFiles { - if strings.HasSuffix(file.FilePath, ".xz") { - fmt.Println(file.FilePath) - recovery, err = tryExtractRecovery(file.FilePath) - if err == nil { - break - } - } - } - if recovery == "" { - log.Fatal("Recovery image not found, cannot continue with bootstrap") - } - if err := fastboot.Flash("recovery", recovery); err != nil { - log.Fatal("Can't flash recovery image") - } - if err := fastboot.Format("cache"); err != nil { - log.Print("Cache formatting was not successful, flashing may fail, " + - "check your partitions on device") - } - - if err := fastboot.BootImage(recovery); err != nil { - log.Fatal("Can't boot recovery image") - } - os.Remove(recovery) - if err := adb.WaitForRecovery(); err != nil { - log.Fatal(err) - } - // Resend all the files - for _, file := range downloadedFiles { - files <- file - } - } - go bitPusher(adb, files, done) - for i := 0; i < totalFiles; i++ { - <-done - } - - var enableList []string - if args.DeveloperMode { - enableList = append(enableList, "developer_mode") - } - if args.Password != "" { - enableList = append(enableList, "default_password "+args.Password) - } - - ubuntuCommands, err := ubuntuimage.GetUbuntuCommands(image.Files, cacheDir, args.Wipe, enableList) - if err != nil { - log.Fatal("Cannot create commands file") - } - log.Printf("Created ubuntu_command: %s", ubuntuCommands) - - adb.Push(ubuntuCommands, "/cache/recovery/ubuntu_command") - defer func() { - if err == nil { - err = os.Remove(ubuntuCommands) - if err != nil { - log.Fatal(err) - } - } - }() - - // either customize the flashing process by running a user provided script - // or reboot into recovery to let the standard upgrade script to run - if script != "" { - log.Printf("Preparing to run %s to finish the flashing process\n", script) - cmd := exec.Command(script) - cmd.Stdout = os.Stdout - err = cmd.Run() - if err != nil { - log.Fatal(err) - } - - } else { - log.Print("Rebooting into recovery to flash") - adb.RebootRecovery() - err = adb.WaitForRecovery() - if err != nil { - log.Fatal(err) - } - } -} +import flags "github.com/jessevdk/go-flags" -// ensureExists touches a file. It can be used to create a dummy .asc file if none exists -func ensureExists(path string) { - f, err := os.OpenFile(path, syscall.O_WRONLY|syscall.O_CREAT, 0666) - if err != nil { - log.Fatal("Cannot touch %s : %s", path, err) - } - f.Close() +type arguments struct { + Revision int `long:"revision" description:"revision to use, absolute or relative allowed"` + DownloadOnly bool `long:"download-only" description:"Only download."` + Server string `long:"server" description:"Use a different image server" default:"https://system-image.ubuntu.com"` + CleanCache bool `long:"clean-cache" description:"Cleans up cache with all downloaded bits"` + TLSSkipVerify bool `long:"tls-skip-verify" description:"Skip TLS certificate validation"` } -type Files struct{ FilePath, SigPath string } - -// useLocalTarball adds a local file to the ones to be pushed -func useLocalTarball(file ubuntuimage.File, files chan<- Files) { - ensureExists(file.Signature) - files <- Files{FilePath: file.Path, SigPath: file.Signature} -} +var globalArgs arguments +var parser = flags.NewParser(&globalArgs, flags.HelpFlag) +var cacheDir = ubuntuimage.GetCacheDir() -// bitDownloader downloads -func bitDownloader(file ubuntuimage.File, files chan<- Files, server, downloadDir string) { - err := file.MakeRelativeToServer(server) - if err != nil { - log.Fatal(err) - } - err = file.Download(downloadDir) - if err != nil { - log.Fatal(err) - } - files <- Files{FilePath: filepath.Join(downloadDir, file.Path), - SigPath: filepath.Join(downloadDir, file.Signature)} -} +func main() { + args := os.Args -// bitPusher -func bitPusher(adb devices.UbuntuDebugBridge, files <-chan Files, done chan<- bool) { - if err := adb.Ping(); err != nil { - log.Fatal("Target device cannot be reached over adb") - } - if _, err := adb.Shell("rm -rf /cache/recovery/*.xz /cache/recovery/*.xz.asc"); err != nil { - log.Fatal("Cannot cleanup /cache/recovery/ to ensure clean deployment", err) - } - freeSpace := "unknown" - dfCacheCmd := "df -h | grep /android/cache" - if free, err := adb.Shell(dfCacheCmd); err != nil { - log.Fatal("Unable to retrieve free space on target") - } else { - //Filesystem Size Used Avail Use% Mounted on - free := strings.Fields(free) - if len(free) > 3 { - freeSpace = free[3] - } - } - errMsg := "Cannot push %s to device: free space on /cache/recovery is %s" - for { - file := <-files - go func() { - log.Printf("Start pushing %s to device", file.FilePath) - err := adb.Push(file.FilePath, "/cache/recovery/") - if err != nil { - log.Fatalf(errMsg, file.SigPath, freeSpace) + if _, err := parser.ParseArgs(args); err != nil && parser.Active == nil { + if e, ok := err.(*flags.Error); ok { + if e.Type == flags.ErrHelp { + fmt.Println(err) + os.Exit(0) } - err = adb.Push(file.SigPath, "/cache/recovery/") - if err != nil { - log.Fatalf(errMsg, file.SigPath, freeSpace) - } - log.Printf("Done pushing %s to device", file.FilePath) - done <- true - }() - } -} - -func tryExtractRecovery(tarxz string) (recovery string, err error) { - f, err := os.Open(tarxz) - if err != nil { - log.Fatalf("Can't open %s in search for recovery", tarxz) - } - defer f.Close() - r := xzReader(f) - tempfile, err := ioutil.TempFile(os.TempDir(), "recoverytar") - if err != nil { - log.Fatal("Can't create tempfile to search for for recovery") - } - defer func() { - tempfile.Close() - os.Remove(tempfile.Name()) - }() - n, err := io.Copy(tempfile, r) - if err != nil { - log.Fatalf("copied %d bytes with err: %v", n, err) - } - _, err = tempfile.Seek(0, 0) - if err != nil { - log.Fatal("Failed to rewind") - } - tr := tar.NewReader(tempfile) - var recoveryFile *os.File - //Going to return inside this loop, ugly, yeah - for { - hdr, err := tr.Next() - if err == io.EOF { - // end of tar archive - break - } - if err != nil { - log.Fatalln(err) } - if strings.Contains(hdr.Name, "recovery.img") { - recoveryFile, err = ioutil.TempFile(os.TempDir(), "recovery") - defer recoveryFile.Close() - if err != nil { - log.Fatal(err) - } - if _, err := io.Copy(recoveryFile, tr); err != nil { - log.Fatal(err) - } - return recoveryFile.Name(), nil + + fmt.Println("DEPRECATED: Implicit 'touch' subcommand assumed") + args = append(args[:1], append([]string{"touch"}, args[1:]...)...) + if _, err := parser.ParseArgs(args); err != nil { + fmt.Println(err) + os.Exit(1) } + } else if err != nil { + fmt.Println(err) + os.Exit(1) } - return "", errors.New("Recovery Partition not found") -} - -func xzReader(r io.Reader) io.ReadCloser { - rpipe, wpipe := io.Pipe() - - cmd := exec.Command("xz", "--decompress", "--stdout") - cmd.Stdin = r - cmd.Stdout = wpipe - - go func() { - err := cmd.Run() - wpipe.CloseWithError(err) - }() - - return rpipe } diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/query.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/query.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/query.go 1970-01-01 00:00:00.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/query.go 2014-11-25 23:50:32.000000000 +0000 @@ -0,0 +1,126 @@ +// +// ubuntu-device-flash - Tool to download and flash devices with an Ubuntu Image +// based system +// +// Copyright (c) 2013 Canonical Ltd. +// +// Written by Sergio Schvezov +// +package main + +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 3, as published +// by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranties of +// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +// PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +import ( + "errors" + "fmt" + + "launchpad.net/goget-ubuntu-touch/ubuntuimage" +) + +func init() { + parser.AddCommand("query", + "Run queries against the image server", + "Choose from the list of query options to retrieve information from the server", + &queryCmd) + + //queryCmd.AddCommand("list-channels", "List available channels", "", &channelCmd) +} + +type QueryCmd struct { + ListChannels bool `long:"list-channels" description:"List available channels"` + ListImages bool `long:"list-images" description:"List available images for a channel"` + ShowImage bool `long:"show-image" description:"Show information for an image in the given channel"` + Channel string `long:"channel" description:"Specify an alternate channel"` + Device string `long:"device" description:"Specify the device to use as a base for querying" required:"true"` +} + +var queryCmd QueryCmd + +func (queryCmd *QueryCmd) Execute(args []string) error { + if queryCmd.ListChannels { + return queryCmd.printChannelList() + } + + if queryCmd.ShowImage { + return queryCmd.printImageInformation() + } + + if queryCmd.ListImages { + return queryCmd.printImageList() + } + + return errors.New("A query option is requrired") +} + +func (queryCmd *QueryCmd) printImageList() error { + channels, err := ubuntuimage.NewChannels(globalArgs.Server) + if err != nil { + return err + } + + deviceChannel, err := channels.GetDeviceChannel( + globalArgs.Server, queryCmd.Channel, queryCmd.Device) + if err != nil { + return err + } + + return deviceChannel.ListImageVersions() +} + +func (queryCmd *QueryCmd) printChannelList() error { + channels, err := ubuntuimage.NewChannels(globalArgs.Server) + if err != nil { + return err + } + + for k, v := range channels { + if !v.Hidden { + if v.Alias != "" { + fmt.Printf("%s (alias to %s)\n", k, v.Alias) + } else { + fmt.Println(k) + } + } + } + + return nil +} + +func (queryCmd *QueryCmd) printImageInformation() error { + channels, err := ubuntuimage.NewChannels(globalArgs.Server) + if err != nil { + return err + } + + if queryCmd.Channel == "" { + return errors.New("channel required") + } + + deviceChannel, err := channels.GetDeviceChannel(globalArgs.Server, queryCmd.Channel, queryCmd.Device) + if err != nil { + return err + } + + image, err := getImage(deviceChannel) + if err != nil { + return err + } + + fmt.Printf("Device: %s\nDescription: %s\nVersion: %d\nChannel: %s\nFiles:\n", queryCmd.Device, image.Description, image.Version, queryCmd.Channel) + for _, f := range image.Files { + f.MakeRelativeToServer(globalArgs.Server) + fmt.Printf(" %d %s%s %d %s\n", f.Order, f.Server, f.Path, f.Size, f.Checksum) + } + + return nil +} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/touch.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/touch.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-device-flash/touch.go 1970-01-01 00:00:00.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-device-flash/touch.go 2014-11-25 23:50:32.000000000 +0000 @@ -0,0 +1,389 @@ +// +// ubuntu-device-flash - Tool to download and flash devices with an Ubuntu Image +// based system +// +// Copyright (c) 2013-2014 Canonical Ltd. +// +// Written by Sergio Schvezov +// +package main + +import ( + "archive/tar" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "syscall" + + "launchpad.net/goget-ubuntu-touch/devices" + "launchpad.net/goget-ubuntu-touch/ubuntuimage" +) + +func init() { + parser.AddCommand("touch", + "Flashes ubuntu touch images", + "", + &touchCmd) +} + +type TouchCmd struct { + Bootstrap bool `long:"bootstrap" description:"bootstrap the system, do this from the bootloader"` + Wipe bool `long:"wipe" description:"Clear all data after flashing"` + Serial string `long:"serial" description:"Serial of the device to operate"` + DeveloperMode bool `long:"developer-mode" description:"Enables developer mode after the factory reset, this is meant for automation and makes the device insecure by default"` + DeviceTarball string `long:"device-tarball" description:"Specify a local device tarball to override the one from the server (using official Ubuntu images with custom device tarballs)"` + RunScript string `long:"run-script" description:"Run a script given by path to finish the flashing process, instead of rebooting to recovery (mostly used during development to work around quirky or incomplete recovery images)"` + Password string `long:"password" description:"This sets up the default password for the phablet user. This option is meant for CI and not general use"` + Channel string `long:"channel" description:"Specify the channel to use" default:"ubuntu-touch/stable"` + Device string `long:"device" description:"Specify the device to flash"` + fastboot devices.Fastboot + adb devices.UbuntuDebugBridge +} + +var touchCmd TouchCmd + +func (touchCmd *TouchCmd) Execute(args []string) error { + if touchCmd.Password != "" || touchCmd.DeveloperMode { + fmt.Println("WARNING --developer-mode and --password are dangerous as they remove security features from your device") + } + + if globalArgs.TLSSkipVerify { + ubuntuimage.TLSSkipVerify() + } + + script := touchCmd.RunScript + if script != "" { + if p, err := filepath.Abs(script); err != nil { + log.Fatal("Run script not found:", err) + } else { + script = p + } + + fi, err := os.Lstat(script) + if err != nil { + log.Fatal(err) + } + if fi.Mode()&0100 == 0 || !fi.Mode().IsRegular() { + log.Fatalf("The script %s passed via --run-script is not executable", script) + } + } + + if touchCmd.Bootstrap { + touchCmd.Wipe = true + } + + if touchCmd.Password != "" && !touchCmd.Wipe && !touchCmd.DeveloperMode { + log.Fatal("Default password setup requires --developer-mode, and --wipe or --bootstrap") + } + + tarballPath := touchCmd.DeviceTarball + if tarballPath != "" { + if p, err := filepath.Abs(tarballPath); err != nil { + log.Fatal("Device tarball not found", err) + } else { + tarballPath = p + } + fi, err := os.Lstat(tarballPath) + if err != nil { + log.Fatal(err) + } + if !fi.Mode().IsRegular() { + log.Fatalf("The file %s passed via --device-tarball is not a regular file\n", tarballPath) + } + } + + channels, err := ubuntuimage.NewChannels(globalArgs.Server) + if err != nil { + return err + } + + if globalArgs.CleanCache { + log.Print("Cleaning prevously downloaded content") + return os.RemoveAll(cacheDir) + } + + if err := touchCmd.setupDevice(); err != nil { + return err + } + log.Printf("Device is |%s|", touchCmd.Device) + + deviceChannel, err := channels.GetDeviceChannel(globalArgs.Server, touchCmd.Channel, touchCmd.Device) + if err != nil { + return err + } + + image, err := getImage(deviceChannel) + if err != nil { + return err + } + + log.Printf("Flashing version %d from %s channel and server %s to device %s", + image.Version, touchCmd.Channel, globalArgs.Server, touchCmd.Device) + + // TODO use closures + signFiles := ubuntuimage.GetGPGFiles() + totalFiles := len(image.Files) + len(signFiles) + files := make(chan Files, totalFiles) + done := make(chan bool, totalFiles) + + for i, file := range image.Files { + if tarballPath != "" && strings.HasPrefix(file.Path, "/pool/device") { + //change the file paths so they are correctly picked up by bitPusher later on + image.Files[i].Path = tarballPath + image.Files[i].Signature = tarballPath + ".asc" + useLocalTarball(image.Files[i], files) + } else { + go bitDownloader(file, files, globalArgs.Server, cacheDir) + } + } + + for _, file := range signFiles { + go bitDownloader(file, files, globalArgs.Server, cacheDir) + } + + if globalArgs.DownloadOnly { + for i := 0; i < totalFiles; i++ { + <-files + } + log.Printf("Downloaded files for version %d, channel %s, exiting without flashing as requested.\n", image.Version, touchCmd.Channel) + return nil + } + + if touchCmd.Bootstrap { + var downloadedFiles []Files + for i := 0; i < totalFiles; i++ { + downloadedFiles = append(downloadedFiles, <-files) + } + //Find the recovery image + var recovery string + for _, file := range downloadedFiles { + if strings.HasSuffix(file.FilePath, ".xz") { + fmt.Println(file.FilePath) + recovery, err = tryExtractRecovery(file.FilePath) + if err == nil { + break + } + } + } + if recovery == "" { + return errors.New("recovery image not found, cannot continue with bootstrap") + } + if err := touchCmd.fastboot.Flash("recovery", recovery); err != nil { + return errors.New("can't flash recovery image") + } + if err := touchCmd.fastboot.Format("cache"); err != nil { + log.Print("Cache formatting was not successful, flashing may fail, " + + "check your partitions on device") + } + + if err := touchCmd.fastboot.BootImage(recovery); err != nil { + return errors.New("Can't boot recovery image") + } + os.Remove(recovery) + if err := touchCmd.adb.WaitForRecovery(); err != nil { + return err + } + // Resend all the files + for _, file := range downloadedFiles { + files <- file + } + } + go bitPusher(touchCmd.adb, files, done) + for i := 0; i < totalFiles; i++ { + <-done + } + + var enableList []string + if touchCmd.DeveloperMode { + enableList = append(enableList, "developer_mode") + enableList = append(enableList, "adb_onlock") + } + if touchCmd.Password != "" { + enableList = append(enableList, "default_password "+touchCmd.Password) + } + + ubuntuCommands, err := ubuntuimage.GetUbuntuCommands(image.Files, cacheDir, touchCmd.Wipe, enableList) + if err != nil { + return errors.New("cannot create commands file") + } + log.Printf("Created ubuntu_command: %s", ubuntuCommands) + + touchCmd.adb.Push(ubuntuCommands, "/cache/recovery/ubuntu_command") + defer os.Remove(ubuntuCommands) + + // either customize the flashing process by running a user provided script + // or reboot into recovery to let the standard upgrade script to run + if script != "" { + log.Printf("Preparing to run %s to finish the flashing process\n", script) + cmd := exec.Command(script) + cmd.Stdout = os.Stdout + err = cmd.Run() + if err != nil { + return err + } + + } else { + log.Print("Rebooting into recovery to flash") + touchCmd.adb.RebootRecovery() + err = touchCmd.adb.WaitForRecovery() + if err != nil { + return err + } + } + return nil +} + +func (touchCmd *TouchCmd) setupDevice() (err error) { + if adb, err := devices.NewUbuntuDebugBridge(); err == nil { + touchCmd.adb = adb + } else { + return err + } + + if touchCmd.Serial != "" { + touchCmd.adb.SetSerial(touchCmd.Serial) + touchCmd.fastboot.SetSerial(touchCmd.Serial) + } + + if touchCmd.Device == "" { + if touchCmd.Bootstrap { + log.Print("Expecting the device to be in the bootloader... waiting") + touchCmd.Device, err = touchCmd.fastboot.GetDevice() + return err + } else { + log.Print("Expecting the device to expose an adb interface...") + // TODO needs to work from recovery as well + //adb.WaitForDevice() + touchCmd.Device, err = touchCmd.adb.GetDevice() + if err != nil { + return errors.New("device cannot be detected over adb") + } + } + } + + return nil +} + +// ensureExists touches a file. It can be used to create a dummy .asc file if none exists +func ensureExists(path string) { + f, err := os.OpenFile(path, syscall.O_WRONLY|syscall.O_CREAT, 0666) + if err != nil { + log.Fatal("Cannot touch %s : %s", path, err) + } + f.Close() +} + +// useLocalTarball adds a local file to the ones to be pushed +func useLocalTarball(file ubuntuimage.File, files chan<- Files) { + ensureExists(file.Signature) + files <- Files{FilePath: file.Path, SigPath: file.Signature} +} + +// bitPusher +func bitPusher(adb devices.UbuntuDebugBridge, files <-chan Files, done chan<- bool) { + if err := adb.Ping(); err != nil { + log.Fatal("Target device cannot be reached over adb") + } + if _, err := adb.Shell("rm -rf /cache/recovery/*.xz /cache/recovery/*.xz.asc"); err != nil { + log.Fatal("Cannot cleanup /cache/recovery/ to ensure clean deployment", err) + } + freeSpace := "unknown" + dfCacheCmd := "df -h | grep /android/cache" + if free, err := adb.Shell(dfCacheCmd); err != nil { + log.Fatal("Unable to retrieve free space on target") + } else { + //Filesystem Size Used Avail Use% Mounted on + free := strings.Fields(free) + if len(free) > 3 { + freeSpace = free[3] + } + } + errMsg := "Cannot push %s to device: free space on /cache/recovery is %s" + for { + file := <-files + go func() { + log.Printf("Start pushing %s to device", file.FilePath) + err := adb.Push(file.FilePath, "/cache/recovery/") + if err != nil { + log.Fatalf(errMsg, file.SigPath, freeSpace) + } + err = adb.Push(file.SigPath, "/cache/recovery/") + if err != nil { + log.Fatalf(errMsg, file.SigPath, freeSpace) + } + log.Printf("Done pushing %s to device", file.FilePath) + done <- true + }() + } +} + +func tryExtractRecovery(tarxz string) (recovery string, err error) { + f, err := os.Open(tarxz) + if err != nil { + log.Fatalf("Can't open %s in search for recovery", tarxz) + } + defer f.Close() + r := xzReader(f) + tempfile, err := ioutil.TempFile(os.TempDir(), "recoverytar") + if err != nil { + log.Fatal("Can't create tempfile to search for for recovery") + } + defer func() { + tempfile.Close() + os.Remove(tempfile.Name()) + }() + n, err := io.Copy(tempfile, r) + if err != nil { + log.Fatalf("copied %d bytes with err: %v", n, err) + } + _, err = tempfile.Seek(0, 0) + if err != nil { + log.Fatal("Failed to rewind") + } + tr := tar.NewReader(tempfile) + var recoveryFile *os.File + //Going to return inside this loop, ugly, yeah + for { + hdr, err := tr.Next() + if err == io.EOF { + // end of tar archive + break + } + if err != nil { + log.Fatalln(err) + } + if strings.Contains(hdr.Name, "recovery.img") { + recoveryFile, err = ioutil.TempFile(os.TempDir(), "recovery") + defer recoveryFile.Close() + if err != nil { + log.Fatal(err) + } + if _, err := io.Copy(recoveryFile, tr); err != nil { + log.Fatal(err) + } + return recoveryFile.Name(), nil + } + } + return "", errors.New("Recovery Partition not found") +} + +func xzReader(r io.Reader) io.ReadCloser { + rpipe, wpipe := io.Pipe() + + cmd := exec.Command("xz", "--decompress", "--stdout") + cmd.Stdin = r + cmd.Stdout = wpipe + + go func() { + err := cmd.Run() + wpipe.CloseWithError(err) + }() + + return rpipe +} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/create.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/create.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/create.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/create.go 2014-11-25 23:50:53.000000000 +0000 @@ -23,12 +23,13 @@ "errors" "fmt" "os" + "os/exec" "path/filepath" "runtime" "syscall" - "launchpad.net/goget-ubuntu-touch/ubuntu-emulator/diskimage" - "launchpad.net/goget-ubuntu-touch/ubuntu-emulator/sysutils" + "launchpad.net/goget-ubuntu-touch/diskimage" + "launchpad.net/goget-ubuntu-touch/sysutils" "launchpad.net/goget-ubuntu-touch/ubuntuimage" ) @@ -39,6 +40,7 @@ RawDisk bool `long:"use-raw-disk" description:"Use raw disks instead of qcow2"` SDCard bool `long:"with-sdcard" description:"Create an external vfat sdcard"` Arch string `long:"arch" description:"Device architecture to use (i386 or armhf)"` + Password string `long:"password" description:"This sets up the default password for the phablet user" default:"0000"` } var createCmd CreateCmd @@ -49,6 +51,11 @@ defaultArch = "i386" ) +const ( + binQemuArmStatic = "/usr/bin/qemu-arm-static" + pkgQemuUserStatic = "qemu-user-static" +) + func init() { createCmd.Arch = defaultArch createCmd.Channel = defaultChannel @@ -66,6 +73,10 @@ } instanceName := args[0] + if err := createCmd.verifyDependencies(); err != nil { + return err + } + var device string if d, ok := devices[createCmd.Arch]; ok { device = d["name"] @@ -118,7 +129,7 @@ sdcardImage := diskimage.New(filepath.Join(dataDir, "sdcard.img"), "USERDATA", 4) systemImage := diskimage.NewExisting(filepath.Join(dataDir, "system.img")) - if err := createSystem(ubuntuImage, sdcardImage, files); err != nil { + if err := createCmd.createSystem(ubuntuImage, sdcardImage, files); err != nil { return err } @@ -161,10 +172,10 @@ } } - if err = sysutils.WriteStamp(dataDir, image); err != nil { + if err = writeStamp(dataDir, image); err != nil { return err } - if err = sysutils.WriteDeviceStamp(dataDir, createCmd.Arch); err != nil { + if err = writeDeviceStamp(dataDir, createCmd.Arch); err != nil { return err } @@ -179,7 +190,18 @@ return systemImage.ExtractFile("build.prop", filepath.Join(dataDir, "system")) } -func createSystem(ubuntuImage, sdcardImage *diskimage.DiskImage, files []string) (err error) { +func (createCmd *CreateCmd) verifyDependencies() error { + switch createCmd.Arch { + case "armhf": + if _, err := os.Stat(binQemuArmStatic); err != nil { + return fmt.Errorf("missing dependency %s (apt install %s)", binQemuArmStatic, pkgQemuUserStatic) + } + } + + return nil +} + +func (createCmd *CreateCmd) createSystem(ubuntuImage, sdcardImage *diskimage.DiskImage, files []string) (err error) { for _, img := range []*diskimage.DiskImage{ubuntuImage, sdcardImage} { if err := img.CreateExt4(); err != nil { return err @@ -205,13 +227,15 @@ } return err } - fmt.Println("Setting up a default password for phablet to: '0000'") - if err := ubuntuImage.SetPassword("phablet", "0000"); err != nil { + + fmt.Printf("Setting up a default password for phablet to: '%s'\n", createCmd.Password) + if err := createCmd.setPassword(ubuntuImage.Mountpoint); err != nil { if err := ubuntuImage.Unmount(); err != nil { fmt.Println("Unmount error :", err) } return err } + if err := ubuntuImage.Unmount(); err != nil { return err } @@ -222,11 +246,33 @@ if err = sdcardImage.Writable(); err != nil { return err } + if err = sdcardImage.OverrideAdbInhibit(); err != nil { + return err + } if err := ubuntuImage.Move(filepath.Join(sdcardImage.Mountpoint, "system.img")); err != nil { return err } return nil } + +// setPassword is an ugly hack to set the password +func (createCmd *CreateCmd) setPassword(chroot string) error { + if createCmd.Arch == "armhf" { + dst := filepath.Join(chroot, binQemuArmStatic) + if out, err := exec.Command("cp", binQemuArmStatic, dst).CombinedOutput(); err != nil { + return fmt.Errorf("issues while setting up password: %s", out) + } + defer os.Remove(dst) + } + + // Run something that would look like this + // PATH=$path chroot "$SYSTEM_MOUNTPOINT" /bin/sh -c "echo -n "$user:$password" | chpasswd" + chrootCmd := fmt.Sprintf("echo -n '%s:%s' | chpasswd", "phablet", createCmd.Password) + if out, err := exec.Command("chroot", chroot, "/bin/sh", "-c", chrootCmd).CombinedOutput(); err != nil { + return errors.New(string(out)) + } + return nil +} func download(image ubuntuimage.Image) (files []string, err error) { cacheDir := ubuntuimage.GetCacheDir() diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/diskimage/customization.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/diskimage/customization.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/diskimage/customization.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/diskimage/customization.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -// -// ubuntu-emu - Tool to download and run Ubuntu Touch emulator instances -// -// Copyright (c) 2013 Canonical Ltd. -// -// Written by Sergio Schvezov -// -package diskimage - -// This program is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License version 3, as published -// by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranties of -// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -// PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program. If not, see . - -import ( - "errors" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" -) - -const networkConfig string = `# interfaces(5) file used by ifup(8) and ifdown(8) -auto lo -iface lo inet loopback - -auto eth0 -iface eth0 inet static - address 10.0.2.15 - netmask 255.255.255.0 - gateway 10.0.2.2 - dns-nameservers 10.0.2.3 - -iface eth1 inet manual -iface eth2 inet manual -iface eth3 inet manual -iface eth4 inet manual -iface eth5 inet manual -` - -type setupFile struct{ path, content string } - -var setupFiles = []setupFile{ - {"custom/custom.prop", "custom.location.fake=true"}, - {"etc/network/interfaces", networkConfig}, - {"etc/profile.d/hud-service.sh", "export HUD_DISABLE_VOICE=1"}, -} - -//setupFile writes a setup to a target file -func (img DiskImage) writeFile(file setupFile) error { - profilePath := filepath.Join(img.Mountpoint, file.path) - dirPath := filepath.Dir(profilePath) - if fi, err := os.Stat(dirPath); err != nil { - if err := os.MkdirAll(dirPath, 0755); err != nil { - fmt.Println(err) - } - } else if !fi.IsDir() { - return fmt.Errorf("%s is not a directory, customization failed", profilePath) - } - if err := ioutil.WriteFile(profilePath, []byte(file.content), 0644); err != nil { - return err - } - return nil -} - -//Writable allows writes on the created running image -func (img DiskImage) Writable() error { - writeFlag := filepath.Join(img.Mountpoint, ".writable_image") - if err := ioutil.WriteFile(writeFlag, []byte(""), 0644); err != nil { - return err - } - return nil -} - -//SetPassword is an ugly hack to set the password -func (img DiskImage) SetPassword(user, password string) error { - // Run something that would look like this - // PATH=$path chroot "$SYSTEM_MOUNTPOINT" /bin/sh -c "echo -n "$user:$password" | chpasswd" - chrootCmd := fmt.Sprintf("echo -n '%s:%s' | chpasswd", user, password) - if out, err := exec.Command("chroot", img.Mountpoint, "/bin/sh", "-c", chrootCmd).CombinedOutput(); err != nil { - return errors.New(string(out)) - } - return nil -} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/diskimage/image.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/diskimage/image.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/diskimage/image.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/diskimage/image.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,242 +0,0 @@ -// -// ubuntu-emu - Tool to download and run Ubuntu Touch emulator instances -// -// Copyright (c) 2013 Canonical Ltd. -// -// Written by Sergio Schvezov -// -package diskimage - -// This program is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License version 3, as published -// by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranties of -// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -// PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program. If not, see . - -import ( - "bufio" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - - "launchpad.net/goget-ubuntu-touch/ubuntu-emulator/sysutils" -) - -type DiskImage struct { - path, label, Mountpoint string - size int64 -} - -//New creates a new DiskImage -func New(path, label string, size int64) *DiskImage { - var img DiskImage - img.path = path - img.label = label - img.size = size - return &img -} - -//New creates a new DiskImage -func NewExisting(path string) *DiskImage { - var img DiskImage - img.path = path - return &img -} - -func (img *DiskImage) Move(dst string) error { - if err := img.Copy(dst); err != nil { - return err - } - if err := os.Remove(img.path); err != nil { - return errors.New(fmt.Sprintf("Unable to remove %s when moving to %s", img.path, dst)) - } - img.path = dst - return nil -} - -func (img *DiskImage) Copy(dst string) (err error) { - dstFile, err := os.Create(dst) - if err != nil { - return err - } - defer dstFile.Close() - - srcFile, err := os.Open(img.path) - if err != nil { - return err - } - defer srcFile.Close() - - reader := bufio.NewReader(srcFile) - writer := bufio.NewWriter(dstFile) - defer func() { - if err != nil { - writer.Flush() - } - }() - if _, err = io.Copy(writer, reader); err != nil { - return err - } - writer.Flush() - return nil -} - -//Mount the DiskImage -func (img *DiskImage) Mount() (err error) { - img.Mountpoint, err = ioutil.TempDir(os.TempDir(), "ubuntu-system") - if err != nil { - return errors.New(fmt.Sprintf("Unable to create temp dir to create system image: %s", err)) - } - //Remove Mountpoint if we fail along the way - defer func() { - if err != nil { - os.Remove(img.Mountpoint) - } - }() - if out, err := exec.Command("mount", img.path, img.Mountpoint).CombinedOutput(); err != nil { - return errors.New(fmt.Sprintf("Unable to mount temp dir to create system image %s", out)) - } - return nil -} - -//Unmount the DiskImage -func (img *DiskImage) Unmount() error { - if img.Mountpoint == "" { - return nil - } - defer os.Remove(img.Mountpoint) - if out, err := exec.Command("sync").CombinedOutput(); err != nil { - exec.Command("umount", img.Mountpoint).Run() - return errors.New(fmt.Sprintf("Failed to sync filesystems before unmounting: %s", out)) - } - if err := exec.Command("umount", img.Mountpoint).Run(); err != nil { - return errors.New("Failed to unmount temp dir where system image was created") - } - img.Mountpoint = "" - return nil -} - -//Provision unpacks the tarList into the given DiskImage -func (img *DiskImage) Provision(tarList []string) error { - //TODO use archive/tar - for _, tar := range tarList { - if out, err := exec.Command("tar", "--numeric-owner", "--exclude", "partitions*", - "-xf", tar, "-C", img.Mountpoint).CombinedOutput(); err != nil { - return errors.New(fmt.Sprintf("Unable to extract rootfs %s to %s: %s", tar, img.Mountpoint, out)) - } - } - if err := img.unpackSystem(); err != nil { - return err - } - for _, file := range setupFiles { - if err := img.writeFile(file); err != nil { - return err - } - } - return nil -} - -//Create returns a ext4 partition for a given file -func (img DiskImage) CreateExt4() error { - if err := sysutils.CreateEmptyFile(img.path, img.size); err != nil { - return err - } - return exec.Command("mkfs.ext4", "-F", "-L", img.label, img.path).Run() -} - -//Create returns a vfat partition for a given file -func (img DiskImage) CreateVFat() error { - if err := sysutils.CreateEmptyFile(img.path, img.size); err != nil { - return err - } - return exec.Command("mkfs.vfat", "-n", img.label, img.path).Run() -} - -//unpackSystem moves the system partition up one level -func (img DiskImage) unpackSystem() error { - os.Rename(filepath.Join(img.Mountpoint, "system"), - filepath.Join(img.Mountpoint, "system-unpack")) - defer os.Remove(filepath.Join(img.Mountpoint, "system-unpack")) - dir, err := os.Open(filepath.Join(img.Mountpoint, "system-unpack")) - if err != nil { - return err - } - defer dir.Close() - fileInfos, err := dir.Readdir(-1) - if err != nil { - return err - } - for _, fileInfo := range fileInfos { - name := fileInfo.Name() - err := os.Rename(filepath.Join(img.Mountpoint, "system-unpack", name), - filepath.Join(img.Mountpoint, name)) - if err != nil { - return err - } - } - return nil -} - -//ExtractFile extracts a filePath relative to it's mountpoint and copies it to dir. -//This function takes care of mounting and unmounting the img -func (img DiskImage) ExtractFile(filePath string, dir string) error { - if err := sysutils.EscalatePrivs(); err != nil { - return err - } - if err := img.Mount(); err != nil { - return err - } - if err := sysutils.DropPrivs(); err != nil { - return err - } - defer func() (err error) { - if err := sysutils.EscalatePrivs(); err != nil { - return err - } - if err := img.Unmount(); err != nil { - return err - } - return sysutils.DropPrivs() - }() - if fi, err := os.Stat(dir); err != nil { - if err := os.MkdirAll(dir, 0755); err != nil { - return err - } - } else if !fi.IsDir() { - return fmt.Errorf("extract dir %s is not a directory") - } - dstFile, err := os.Create(filepath.Join(dir, filePath)) - if err != nil { - return err - } - defer dstFile.Close() - - srcFile, err := os.Open(filepath.Join(img.Mountpoint, filePath)) - if err != nil { - return err - } - defer srcFile.Close() - - reader := bufio.NewReader(srcFile) - writer := bufio.NewWriter(dstFile) - defer func() { - if err != nil { - writer.Flush() - } - }() - if _, err = io.Copy(writer, reader); err != nil { - return err - } - writer.Flush() - return nil -} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/diskimage/snapshot.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/diskimage/snapshot.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/diskimage/snapshot.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/diskimage/snapshot.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -// -// ubuntu-emu - Tool to download and run Ubuntu Touch emulator instances -// -// Copyright (c) 2013 Canonical Ltd. -// -// Written by Sergio Schvezov -// -package diskimage - -// This program is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License version 3, as published -// by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranties of -// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -// PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program. If not, see . - -import ( - "errors" - "fmt" - "os" - "os/exec" -) - -const BaseSnapshot = "pristine" - -//ConvertQcow2 converts an image from raw to qcow2 -func (img *DiskImage) ConvertQcow2() error { - var cmd *exec.Cmd - // convert - cmd = exec.Command("qemu-img", "convert", "-f", "raw", img.path, - "-O", "qcow2", "-o", "compat=0.10", img.path+".qcow2") - if out, err := cmd.CombinedOutput(); err != nil { - return errors.New(fmt.Sprintf("Error while converting %s: %s", img.path, out)) - } - - // check - cmd = exec.Command("qemu-img", "check", img.path+".qcow2") - if out, err := cmd.CombinedOutput(); err != nil { - return errors.New(fmt.Sprintf("Error while converting %s: %s", img.path, out)) - } - os.Rename(img.path+".qcow2", img.path) - return img.Snapshot(BaseSnapshot) - -} - -//Snapshot creates a DiskImage's snapshot with the specified snapshot in label -func (img *DiskImage) Snapshot(label string) error { - // snap - cmd := exec.Command("qemu-img", "snapshot", "-c", label, img.path) - if out, err := cmd.CombinedOutput(); err != nil { - return errors.New(fmt.Sprintf("Error while converting %s: %s", img.path, out)) - } - return nil -} - -//RevertSnapshot reverts a DiskImage's snapshot to the specified snapshot in label -func (img *DiskImage) RevertSnapshot(label string) error { - // unsnap - cmd := exec.Command("qemu-img", "snapshot", "-c", label, img.path) - if out, err := cmd.CombinedOutput(); err != nil { - return errors.New(fmt.Sprintf("Error while converting %s: %s", img.path, out)) - } - return nil -} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/list.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/list.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/list.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/list.go 2014-11-25 23:50:32.000000000 +0000 @@ -22,7 +22,6 @@ import ( "fmt" "io/ioutil" - "launchpad.net/goget-ubuntu-touch/ubuntu-emulator/sysutils" "path/filepath" ) @@ -49,7 +48,7 @@ if !entry.IsDir() { continue } - if image, err := sysutils.ReadStamp(filepath.Join(dataDir, entry.Name())); err == nil { + if image, err := readStamp(filepath.Join(dataDir, entry.Name())); err == nil { fmt.Printf("%s\t%s\n", entry.Name(), image.Description) } else { fmt.Println(entry.Name()) diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/run.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/run.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/run.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/run.go 2014-11-25 23:50:32.000000000 +0000 @@ -26,8 +26,6 @@ "os/exec" "path" "path/filepath" - - "launchpad.net/goget-ubuntu-touch/ubuntu-emulator/sysutils" ) type RunCmd struct { @@ -85,7 +83,7 @@ return err } - device, err := sysutils.ReadDeviceStamp(dataDir) + device, err := readDeviceStamp(dataDir) if err != nil { return err } diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/snapshot.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/snapshot.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/snapshot.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/snapshot.go 2014-11-25 23:50:32.000000000 +0000 @@ -22,8 +22,9 @@ import ( "errors" "fmt" - "launchpad.net/goget-ubuntu-touch/ubuntu-emulator/diskimage" "path/filepath" + + "launchpad.net/goget-ubuntu-touch/diskimage" ) type SnapshotCmd struct { diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/stamp.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/stamp.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/stamp.go 1970-01-01 00:00:00.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/stamp.go 2014-11-25 23:50:32.000000000 +0000 @@ -0,0 +1,80 @@ +// +// ubuntu-emu - Tool to download and run Ubuntu Touch emulator instances +// +// Copyright (c) 2013 Canonical Ltd. +// +// Written by Sergio Schvezov +// +package main + +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 3, as published +// by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranties of +// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +// PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program. If not, see . + +import ( + "bufio" + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + + "launchpad.net/goget-ubuntu-touch/ubuntuimage" +) + +func writeDeviceStamp(dataDir, device string) (err error) { + path := filepath.Join(dataDir, ".device") + return ioutil.WriteFile(path, []byte(device), 0600) +} + +func readDeviceStamp(dataDir string) (string, error) { + path := filepath.Join(dataDir, ".device") + device, err := ioutil.ReadFile(path) + if err != nil { + return "", err + } + return string(device), nil +} + +func writeStamp(dataDir string, image ubuntuimage.Image) (err error) { + path := filepath.Join(dataDir, ".stamp") + file, err := os.Create(path) + if err != nil { + return err + } + defer func() { + file.Close() + if err != nil { + os.Remove(path) + } + }() + w := bufio.NewWriter(file) + defer w.Flush() + jsonWriter := json.NewEncoder(w) + if err := jsonWriter.Encode(image); err != nil { + return err + } + + return nil +} + +func readStamp(dataDir string) (image ubuntuimage.Image, err error) { + path := filepath.Join(dataDir, ".stamp") + file, err := os.Open(path) + if err != nil { + return image, err + } + jsonReader := json.NewDecoder(file) + if err := jsonReader.Decode(&image); err != nil { + return image, err + } + + return image, nil +} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/sysutils/stamp.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/sysutils/stamp.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/sysutils/stamp.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/sysutils/stamp.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -// -// ubuntu-emu - Tool to download and run Ubuntu Touch emulator instances -// -// Copyright (c) 2013 Canonical Ltd. -// -// Written by Sergio Schvezov -// -package sysutils - -// This program is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License version 3, as published -// by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranties of -// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -// PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program. If not, see . - -import ( - "bufio" - "encoding/json" - "io/ioutil" - "launchpad.net/goget-ubuntu-touch/ubuntuimage" - "os" - "path/filepath" -) - -func WriteDeviceStamp(dataDir, device string) (err error) { - path := filepath.Join(dataDir, ".device") - return ioutil.WriteFile(path, []byte(device), 0600) -} - -func ReadDeviceStamp(dataDir string) (string, error) { - path := filepath.Join(dataDir, ".device") - device, err := ioutil.ReadFile(path) - if err != nil { - return "", err - } - return string(device), nil -} - -func WriteStamp(dataDir string, image ubuntuimage.Image) (err error) { - path := filepath.Join(dataDir, ".stamp") - file, err := os.Create(path) - if err != nil { - return err - } - defer func() { - file.Close() - if err != nil { - os.Remove(path) - } - }() - w := bufio.NewWriter(file) - defer w.Flush() - jsonWriter := json.NewEncoder(w) - if err := jsonWriter.Encode(image); err != nil { - return err - } - - return nil -} - -func ReadStamp(dataDir string) (image ubuntuimage.Image, err error) { - path := filepath.Join(dataDir, ".stamp") - file, err := os.Open(path) - if err != nil { - return image, err - } - jsonReader := json.NewDecoder(file) - if err := jsonReader.Decode(&image); err != nil { - return image, err - } - - return image, nil -} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/sysutils/utils.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/sysutils/utils.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntu-emulator/sysutils/utils.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntu-emulator/sysutils/utils.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -// -// ubuntu-emu - Tool to download and run Ubuntu Touch emulator instances -// -// Copyright (c) 2013 Canonical Ltd. -// -// Written by Sergio Schvezov -// -package sysutils - -// This program is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License version 3, as published -// by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranties of -// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -// PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program. If not, see . - -import ( - "errors" - "fmt" - "os" - "strconv" - "syscall" -) - -func CreateEmptyFile(path string, size int64) (err error) { - file, err := os.Create(path) - if err != nil { - return err - } - defer func() { - file.Close() - if err != nil { - os.Remove(path) - } - }() - size = size * 1024 * 1024 * 1024 - if err := file.Truncate(size); err != nil { - return errors.New(fmt.Sprintf("Error creating %s of size %d to stage image onto", path, size)) - } - return nil -} - -func DropPrivs() error { - uid, gid := GetUserEnvInt() - if err := syscall.Setregid(-1, gid); err != nil { - return errors.New(fmt.Sprintf("Can't drop gid: %s", err)) - } - if err := syscall.Setreuid(-1, uid); err != nil { - return errors.New(fmt.Sprintf("Can't drop uid: %s", err)) - } - return nil -} - -func EscalatePrivs() error { - err := syscall.Setreuid(-1, 0) - return err -} - -// GetUserEnv checks if the process can drop priviledges by checking if either -// SUDO_UID and SUDO_GID or PKEXEC_UID are set, it returns the corresponding -// uid and gid set in these or 0 otherwise. -func GetUserEnv() (uid, gid string) { - if v := os.Getenv("SUDO_UID"); v != "" { - uid = v - } else if v := os.Getenv("PKEXEC_UID"); v != "" { - uid = v - } else { - uid = "0" - } - - if v := os.Getenv("SUDO_GID"); v != "" { - gid = v - } else { - gid = "0" - } - return uid, gid -} - -func GetUserEnvInt() (uid, gid int) { - uidString, gidString := GetUserEnv() - uid, _ = strconv.Atoi(uidString) - gid, _ = strconv.Atoi(gidString) - return uid, gid -} diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntuimage/channels.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntuimage/channels.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntuimage/channels.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntuimage/channels.go 2014-11-25 23:50:32.000000000 +0000 @@ -26,10 +26,12 @@ "errors" "fmt" "net/http" + "io/ioutil" ) const ( channelsPath = "/channels.json" + indexName = "index.json" FULL_IMAGE = "full" ) @@ -80,6 +82,8 @@ return i1.Version > i2.Version } ImageBy(order).ImageSort(deviceChannel.Images) + + deviceChannel.Url = channelUri return deviceChannel, err } @@ -93,6 +97,53 @@ return image, fmt.Errorf("Failed to locate image %d", revision) } +func (deviceChannel *DeviceChannel) ListImageVersions() (err error) { + + jsonData := map[string]interface{}{} + + resp, err := client.Get(deviceChannel.Url) + if err != nil { + return err + } + + if (resp.StatusCode != 200) { + statusErr := errors.New(fmt.Sprintf("Invalid HTTP response: %d", resp.StatusCode)) + return statusErr + } + + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + err = json.Unmarshal(body, &jsonData) + if err != nil { + return err + } + + images := jsonData["images"].([]interface{}) + + for i := range images { + entry := images[i].(map[string]interface{}) + + imageType := entry["type"] + + if imageType != FULL_IMAGE { + // ignore delta images as they cannot be used to + // perform an initial device flash + continue + } + + fmt.Printf("%d: description='%s'\n", + int(entry["version"].(float64)), + entry["description"]) + } + + return nil +} + func (deviceChannel *DeviceChannel) GetRelativeImage(revision int) (image Image, err error) { var steps int if revision < 0 { diff -Nru goget-ubuntu-touch-0.4+14.10.20140929/ubuntuimage/types.go goget-ubuntu-touch-0.4+15.04.20141125/ubuntuimage/types.go --- goget-ubuntu-touch-0.4+14.10.20140929/ubuntuimage/types.go 2014-09-29 17:27:41.000000000 +0000 +++ goget-ubuntu-touch-0.4+15.04.20141125/ubuntuimage/types.go 2014-11-25 23:50:32.000000000 +0000 @@ -24,12 +24,19 @@ } type Channel struct { - Devices map[string]Device - Alias string + Devices map[string]Device `json:"devices"` + Alias string `json:"alias"` + Hidden bool `json:"hidden,omitempty"` } type Channels map[string]Channel +type ImageVersion struct { + Description string +} + +type ImageVersions map[int]ImageVersion + type File struct { Server string Checksum, Path, Signature string @@ -43,6 +50,7 @@ } type DeviceChannel struct { + Url string Alias string Images []Image }